1#[cfg(with_testing)]
8use std::ops;
9use std::{
10 collections::{BTreeMap, BTreeSet, HashSet},
11 fmt::{self, Display},
12 hash::Hash,
13 io, iter,
14 num::ParseIntError,
15 str::FromStr,
16 sync::Arc,
17};
18
19use allocative::{Allocative, Visitor};
20use alloy_primitives::U256;
21use async_graphql::{InputObject, SimpleObject};
22use custom_debug_derive::Debug;
23use linera_witty::{WitLoad, WitStore, WitType};
24use serde::{Deserialize, Deserializer, Serialize, Serializer};
25use serde_with::{serde_as, Bytes};
26use thiserror::Error;
27use tracing::instrument;
28
29#[cfg(with_metrics)]
30use crate::prometheus_util::MeasureLatency as _;
31use crate::{
32 crypto::{BcsHashable, CryptoError, CryptoHash},
33 doc_scalar, hex_debug, http,
34 identifiers::{
35 ApplicationId, BlobId, BlobType, ChainId, EventId, GenericApplicationId, ModuleId, StreamId,
36 },
37 limited_writer::{LimitedWriter, LimitedWriterError},
38 ownership::ChainOwnership,
39 time::{Duration, SystemTime},
40 vm::VmRuntime,
41};
42
43#[derive(Debug, Clone, PartialEq, Eq, Allocative)]
60pub struct NonCanonicalBTreeMap<K, V>(BTreeMap<K, V>);
61
62impl<K, V> Default for NonCanonicalBTreeMap<K, V> {
63 fn default() -> Self {
64 Self(BTreeMap::new())
65 }
66}
67
68impl<K, V> std::ops::Deref for NonCanonicalBTreeMap<K, V> {
69 type Target = BTreeMap<K, V>;
70
71 fn deref(&self) -> &Self::Target {
72 &self.0
73 }
74}
75
76impl<K, V> std::ops::DerefMut for NonCanonicalBTreeMap<K, V> {
77 fn deref_mut(&mut self) -> &mut Self::Target {
78 &mut self.0
79 }
80}
81
82impl<K, V> From<BTreeMap<K, V>> for NonCanonicalBTreeMap<K, V> {
83 fn from(map: BTreeMap<K, V>) -> Self {
84 Self(map)
85 }
86}
87
88impl<K, V> From<NonCanonicalBTreeMap<K, V>> for BTreeMap<K, V> {
89 fn from(map: NonCanonicalBTreeMap<K, V>) -> Self {
90 map.0
91 }
92}
93
94impl<K: Ord, V> FromIterator<(K, V)> for NonCanonicalBTreeMap<K, V> {
95 fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
96 Self(BTreeMap::from_iter(iter))
97 }
98}
99
100impl<K, V> IntoIterator for NonCanonicalBTreeMap<K, V> {
101 type Item = (K, V);
102 type IntoIter = std::collections::btree_map::IntoIter<K, V>;
103
104 fn into_iter(self) -> Self::IntoIter {
105 self.0.into_iter()
106 }
107}
108
109impl<'a, K, V> IntoIterator for &'a NonCanonicalBTreeMap<K, V> {
110 type Item = (&'a K, &'a V);
111 type IntoIter = std::collections::btree_map::Iter<'a, K, V>;
112
113 fn into_iter(self) -> Self::IntoIter {
114 self.0.iter()
115 }
116}
117
118impl<K, V> Serialize for NonCanonicalBTreeMap<K, V>
119where
120 K: Serialize,
121 V: Serialize,
122{
123 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
124 serializer.collect_seq(self.0.iter())
127 }
128}
129
130impl<'de, K, V> Deserialize<'de> for NonCanonicalBTreeMap<K, V>
131where
132 K: Deserialize<'de> + Ord,
133 V: Deserialize<'de>,
134{
135 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
136 let entries = Vec::<(K, V)>::deserialize(deserializer)?;
137 Ok(Self(entries.into_iter().collect()))
138 }
139}
140
141impl<K, V> async_graphql::OutputType for NonCanonicalBTreeMap<K, V>
142where
143 BTreeMap<K, V>: async_graphql::OutputType,
144{
145 fn type_name() -> std::borrow::Cow<'static, str> {
146 <BTreeMap<K, V> as async_graphql::OutputType>::type_name()
147 }
148
149 fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
150 <BTreeMap<K, V> as async_graphql::OutputType>::create_type_info(registry)
151 }
152
153 async fn resolve(
154 &self,
155 ctx: &async_graphql::ContextSelectionSet<'_>,
156 field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
157 ) -> async_graphql::ServerResult<async_graphql::Value> {
158 self.0.resolve(ctx, field).await
159 }
160}
161
162pub type NonCanonicalBTreeSet<T> = BTreeSet<T>;
172
173pub type CanonicalBTreeMap<K, V> = BTreeMap<K, V>;
181
182#[derive(Debug, Clone, PartialEq, Eq, Allocative)]
194pub struct CanonicalBTreeSet<T>(BTreeSet<T>);
195
196impl<T> Default for CanonicalBTreeSet<T> {
197 fn default() -> Self {
198 Self(BTreeSet::new())
199 }
200}
201
202impl<T> std::ops::Deref for CanonicalBTreeSet<T> {
203 type Target = BTreeSet<T>;
204
205 fn deref(&self) -> &Self::Target {
206 &self.0
207 }
208}
209
210impl<T> std::ops::DerefMut for CanonicalBTreeSet<T> {
211 fn deref_mut(&mut self) -> &mut Self::Target {
212 &mut self.0
213 }
214}
215
216impl<T> From<BTreeSet<T>> for CanonicalBTreeSet<T> {
217 fn from(set: BTreeSet<T>) -> Self {
218 Self(set)
219 }
220}
221
222impl<T> From<CanonicalBTreeSet<T>> for BTreeSet<T> {
223 fn from(set: CanonicalBTreeSet<T>) -> Self {
224 set.0
225 }
226}
227
228impl<T: Ord> FromIterator<T> for CanonicalBTreeSet<T> {
229 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
230 Self(BTreeSet::from_iter(iter))
231 }
232}
233
234impl<T> IntoIterator for CanonicalBTreeSet<T> {
235 type Item = T;
236 type IntoIter = std::collections::btree_set::IntoIter<T>;
237
238 fn into_iter(self) -> Self::IntoIter {
239 self.0.into_iter()
240 }
241}
242
243impl<'a, T> IntoIterator for &'a CanonicalBTreeSet<T> {
244 type Item = &'a T;
245 type IntoIter = std::collections::btree_set::Iter<'a, T>;
246
247 fn into_iter(self) -> Self::IntoIter {
248 self.0.iter()
249 }
250}
251
252impl<T> Serialize for CanonicalBTreeSet<T>
253where
254 T: Serialize,
255{
256 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
257 serializer.collect_map(self.0.iter().map(|element| (element, ())))
260 }
261}
262
263impl<'de, T> Deserialize<'de> for CanonicalBTreeSet<T>
264where
265 T: Deserialize<'de> + Ord,
266{
267 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
268 let map = BTreeMap::<T, ()>::deserialize(deserializer)?;
269 Ok(Self(map.into_keys().collect()))
270 }
271}
272
273impl<T> async_graphql::OutputType for CanonicalBTreeSet<T>
274where
275 BTreeSet<T>: async_graphql::OutputType,
276{
277 fn type_name() -> std::borrow::Cow<'static, str> {
278 <BTreeSet<T> as async_graphql::OutputType>::type_name()
279 }
280
281 fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
282 <BTreeSet<T> as async_graphql::OutputType>::create_type_info(registry)
283 }
284
285 async fn resolve(
286 &self,
287 ctx: &async_graphql::ContextSelectionSet<'_>,
288 field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
289 ) -> async_graphql::ServerResult<async_graphql::Value> {
290 self.0.resolve(ctx, field).await
291 }
292}
293
294#[derive(
299 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, WitType, WitLoad, WitStore,
300)]
301#[cfg_attr(
302 all(with_testing, not(target_arch = "wasm32")),
303 derive(test_strategy::Arbitrary)
304)]
305pub struct Amount(u128);
306
307impl Allocative for Amount {
308 fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
309 visitor.visit_simple_sized::<Self>();
310 }
311}
312
313#[derive(Serialize, Deserialize)]
314#[serde(rename = "Amount")]
315struct AmountString(String);
316
317#[derive(Serialize, Deserialize)]
318#[serde(rename = "Amount")]
319struct AmountU128(u128);
320
321impl Serialize for Amount {
322 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
323 if serializer.is_human_readable() {
324 AmountString(self.to_string()).serialize(serializer)
325 } else {
326 AmountU128(self.0).serialize(serializer)
327 }
328 }
329}
330
331impl<'de> Deserialize<'de> for Amount {
332 fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
333 if deserializer.is_human_readable() {
334 let AmountString(s) = AmountString::deserialize(deserializer)?;
335 s.parse().map_err(serde::de::Error::custom)
336 } else {
337 Ok(Amount(AmountU128::deserialize(deserializer)?.0))
338 }
339 }
340}
341
342impl From<Amount> for U256 {
343 fn from(amount: Amount) -> U256 {
344 U256::from(amount.0)
345 }
346}
347
348impl From<Amount> for f64 {
349 fn from(amount: Amount) -> f64 {
353 amount.0 as f64 / Amount::ONE.0 as f64
354 }
355}
356
357#[derive(Error, Debug)]
360#[error("Failed to convert U256 to Amount. {0} has more than 128 bits")]
361pub struct AmountConversionError(U256);
362
363impl TryFrom<U256> for Amount {
364 type Error = AmountConversionError;
365 fn try_from(value: U256) -> Result<Amount, Self::Error> {
366 let value = u128::try_from(&value).map_err(|_| AmountConversionError(value))?;
367 Ok(Amount(value))
368 }
369}
370
371#[derive(
374 Clone,
375 Copy,
376 Debug,
377 Default,
378 Eq,
379 Ord,
380 PartialEq,
381 PartialOrd,
382 Hash,
383 derive_more::Display,
384 derive_more::Deref,
385 derive_more::DerefMut,
386 derive_more::FromStr,
387)]
388pub struct U128(pub u128);
389
390impl Serialize for U128 {
391 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
392 where
393 S: Serializer,
394 {
395 if serializer.is_human_readable() {
396 serializer.serialize_str(&self.0.to_string())
397 } else {
398 self.0.serialize(serializer)
399 }
400 }
401}
402
403impl<'de> Deserialize<'de> for U128 {
404 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
405 where
406 D: Deserializer<'de>,
407 {
408 if deserializer.is_human_readable() {
409 let s = String::deserialize(deserializer)?;
410 s.parse().map(U128).map_err(serde::de::Error::custom)
411 } else {
412 u128::deserialize(deserializer).map(U128)
413 }
414 }
415}
416
417#[derive(
419 Eq,
420 PartialEq,
421 Ord,
422 PartialOrd,
423 Copy,
424 Clone,
425 Hash,
426 Default,
427 Debug,
428 Serialize,
429 Deserialize,
430 WitType,
431 WitLoad,
432 WitStore,
433 Allocative,
434)]
435#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
436pub struct BlockHeight(pub u64);
437
438#[derive(
440 Eq,
441 PartialEq,
442 Ord,
443 PartialOrd,
444 Copy,
445 Clone,
446 Hash,
447 Default,
448 Debug,
449 Serialize,
450 Deserialize,
451 Allocative,
452)]
453#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
454pub enum Round {
455 #[default]
457 Fast,
458 MultiLeader(u32),
460 SingleLeader(u32),
462 Validator(u32),
464}
465
466#[derive(
468 Eq,
469 PartialEq,
470 Ord,
471 PartialOrd,
472 Copy,
473 Clone,
474 Hash,
475 Default,
476 Debug,
477 Serialize,
478 Deserialize,
479 WitType,
480 WitLoad,
481 WitStore,
482 Allocative,
483)]
484pub struct TimeDelta(u64);
485
486impl TimeDelta {
487 pub const fn from_micros(micros: u64) -> Self {
489 TimeDelta(micros)
490 }
491
492 pub const fn from_millis(millis: u64) -> Self {
494 TimeDelta(millis.saturating_mul(1_000))
495 }
496
497 pub const fn from_secs(secs: u64) -> Self {
499 TimeDelta(secs.saturating_mul(1_000_000))
500 }
501
502 pub fn from_duration(duration: Duration) -> Self {
504 TimeDelta(u64::try_from(duration.as_micros()).unwrap_or(u64::MAX))
505 }
506
507 pub const fn as_micros(&self) -> u64 {
509 self.0
510 }
511
512 pub const fn as_duration(&self) -> Duration {
514 Duration::from_micros(self.as_micros())
515 }
516}
517
518#[derive(
520 Eq,
521 PartialEq,
522 Ord,
523 PartialOrd,
524 Copy,
525 Clone,
526 Hash,
527 Default,
528 Debug,
529 Serialize,
530 Deserialize,
531 WitType,
532 WitLoad,
533 WitStore,
534 Allocative,
535)]
536pub struct Timestamp(u64);
537
538impl Timestamp {
539 pub fn now() -> Timestamp {
541 Timestamp(
542 SystemTime::UNIX_EPOCH
543 .elapsed()
544 .expect("system time should be after Unix epoch")
545 .as_micros()
546 .try_into()
547 .unwrap_or(u64::MAX),
548 )
549 }
550
551 pub const fn micros(&self) -> u64 {
553 self.0
554 }
555
556 pub const fn delta_since(&self, other: Timestamp) -> TimeDelta {
559 TimeDelta::from_micros(self.0.saturating_sub(other.0))
560 }
561
562 pub const fn duration_since(&self, other: Timestamp) -> Duration {
565 Duration::from_micros(self.0.saturating_sub(other.0))
566 }
567
568 pub const fn saturating_add(&self, duration: TimeDelta) -> Timestamp {
570 Timestamp(self.0.saturating_add(duration.0))
571 }
572
573 pub const fn saturating_sub(&self, duration: TimeDelta) -> Timestamp {
575 Timestamp(self.0.saturating_sub(duration.0))
576 }
577
578 pub const fn saturating_sub_micros(&self, micros: u64) -> Timestamp {
581 Timestamp(self.0.saturating_sub(micros))
582 }
583}
584
585impl From<u64> for Timestamp {
586 fn from(t: u64) -> Timestamp {
587 Timestamp(t)
588 }
589}
590
591impl Display for Timestamp {
592 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
593 let seconds = i64::try_from(self.0 / 1_000_000).unwrap_or(i64::MAX);
594 let nanos = u32::try_from((self.0 % 1_000_000) * 1_000)
596 .expect("microseconds modulo 1_000_000 multiplied by 1_000 fits in u32");
597 if let Some(date_time) = chrono::DateTime::from_timestamp(seconds, nanos) {
598 return date_time.naive_utc().fmt(f);
599 }
600 self.0.fmt(f)
601 }
602}
603
604impl FromStr for Timestamp {
605 type Err = chrono::ParseError;
606
607 fn from_str(s: &str) -> Result<Self, Self::Err> {
608 let naive = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S")
609 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S"))?;
610 let micros = naive
611 .and_utc()
612 .timestamp_micros()
613 .try_into()
614 .unwrap_or(u64::MAX);
615 Ok(Timestamp(micros))
616 }
617}
618
619#[derive(
622 Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, WitLoad, WitStore, WitType,
623)]
624pub struct Resources {
625 pub wasm_fuel: u64,
627 pub evm_fuel: u64,
629 pub read_operations: u32,
631 pub write_operations: u32,
633 pub bytes_runtime: u32,
635 pub bytes_to_read: u32,
637 pub bytes_to_write: u32,
639 pub blobs_to_read: u32,
641 pub blobs_to_publish: u32,
643 pub blob_bytes_to_read: u32,
645 pub blob_bytes_to_publish: u32,
647 pub messages: u32,
649 pub message_size: u32,
652 pub service_as_oracle_queries: u32,
654 pub http_requests: u32,
656 }
659
660#[derive(Clone, Debug, Deserialize, Serialize, WitLoad, WitType)]
662#[cfg_attr(with_testing, derive(Eq, PartialEq, WitStore))]
663#[witty_specialize_with(Message = Vec<u8>)]
664pub struct SendMessageRequest<Message> {
665 pub destination: ChainId,
667 pub authenticated: bool,
669 pub is_tracked: bool,
671 pub grant: Resources,
673 pub message: Message,
675}
676
677#[derive(Debug, Error)]
679#[allow(missing_docs)]
680pub enum ArithmeticError {
681 #[error("Number overflow")]
682 Overflow,
683 #[error("Number underflow")]
684 Underflow,
685}
686
687macro_rules! impl_wrapped_number {
688 ($name:ident, $wrapped:ident) => {
689 impl $name {
690 pub const ZERO: Self = Self(0);
692
693 pub const MAX: Self = Self($wrapped::MAX);
695
696 pub fn try_add(self, other: Self) -> Result<Self, ArithmeticError> {
698 let val = self
699 .0
700 .checked_add(other.0)
701 .ok_or(ArithmeticError::Overflow)?;
702 Ok(Self(val))
703 }
704
705 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
707 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
708 Ok(Self(val))
709 }
710
711 pub const fn saturating_add(self, other: Self) -> Self {
713 let val = self.0.saturating_add(other.0);
714 Self(val)
715 }
716
717 pub fn try_sub(self, other: Self) -> Result<Self, ArithmeticError> {
719 let val = self
720 .0
721 .checked_sub(other.0)
722 .ok_or(ArithmeticError::Underflow)?;
723 Ok(Self(val))
724 }
725
726 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
728 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
729 Ok(Self(val))
730 }
731
732 pub const fn saturating_sub(self, other: Self) -> Self {
734 let val = self.0.saturating_sub(other.0);
735 Self(val)
736 }
737
738 pub fn abs_diff(self, other: Self) -> Self {
740 Self(self.0.abs_diff(other.0))
741 }
742
743 pub const fn midpoint(self, other: Self) -> Self {
745 Self(self.0.midpoint(other.0))
746 }
747
748 pub fn try_add_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
750 self.0 = self
751 .0
752 .checked_add(other.0)
753 .ok_or(ArithmeticError::Overflow)?;
754 Ok(())
755 }
756
757 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
759 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
760 Ok(())
761 }
762
763 pub const fn saturating_add_assign(&mut self, other: Self) {
765 self.0 = self.0.saturating_add(other.0);
766 }
767
768 pub fn try_sub_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
770 self.0 = self
771 .0
772 .checked_sub(other.0)
773 .ok_or(ArithmeticError::Underflow)?;
774 Ok(())
775 }
776
777 pub fn saturating_div(&self, other: $wrapped) -> Self {
779 Self(self.0.checked_div(other).unwrap_or($wrapped::MAX))
780 }
781
782 pub const fn saturating_mul(&self, other: $wrapped) -> Self {
784 Self(self.0.saturating_mul(other))
785 }
786
787 pub fn try_mul(self, other: $wrapped) -> Result<Self, ArithmeticError> {
789 let val = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
790 Ok(Self(val))
791 }
792
793 pub fn try_mul_assign(&mut self, other: $wrapped) -> Result<(), ArithmeticError> {
795 self.0 = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
796 Ok(())
797 }
798 }
799
800 impl From<$name> for $wrapped {
801 fn from(value: $name) -> Self {
802 value.0
803 }
804 }
805
806 #[cfg(with_testing)]
808 impl From<$wrapped> for $name {
809 fn from(value: $wrapped) -> Self {
810 Self(value)
811 }
812 }
813
814 #[cfg(with_testing)]
815 impl ops::Add for $name {
816 type Output = Self;
817
818 fn add(self, other: Self) -> Self {
819 Self(self.0 + other.0)
820 }
821 }
822
823 #[cfg(with_testing)]
824 impl ops::Sub for $name {
825 type Output = Self;
826
827 fn sub(self, other: Self) -> Self {
828 Self(self.0 - other.0)
829 }
830 }
831
832 #[cfg(with_testing)]
833 impl ops::Mul<$wrapped> for $name {
834 type Output = Self;
835
836 fn mul(self, other: $wrapped) -> Self {
837 Self(self.0 * other)
838 }
839 }
840 };
841}
842
843impl TryFrom<BlockHeight> for usize {
844 type Error = ArithmeticError;
845
846 fn try_from(height: BlockHeight) -> Result<usize, ArithmeticError> {
847 usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)
848 }
849}
850
851impl_wrapped_number!(Amount, u128);
852impl_wrapped_number!(U128, u128);
853impl_wrapped_number!(BlockHeight, u64);
854impl_wrapped_number!(TimeDelta, u64);
855
856impl Display for Amount {
857 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
858 let places = Amount::DECIMAL_PLACES as usize;
860 let min_digits = places + 1;
861 let decimals = format!("{:0min_digits$}", self.0);
862 let integer_part = &decimals[..(decimals.len() - places)];
863 let fractional_part = decimals[(decimals.len() - places)..].trim_end_matches('0');
864
865 let precision = f.precision().unwrap_or(0).max(fractional_part.len());
867 let sign = if f.sign_plus() && self.0 > 0 { "+" } else { "" };
868 let pad_width = f.width().map_or(0, |w| {
870 w.saturating_sub(precision)
871 .saturating_sub(sign.len() + integer_part.len() + 1)
872 });
873 let left_pad = match f.align() {
874 None | Some(fmt::Alignment::Right) => pad_width,
875 Some(fmt::Alignment::Center) => pad_width / 2,
876 Some(fmt::Alignment::Left) => 0,
877 };
878
879 for _ in 0..left_pad {
880 write!(f, "{}", f.fill())?;
881 }
882 write!(f, "{sign}{integer_part}.{fractional_part:0<precision$}")?;
883 for _ in left_pad..pad_width {
884 write!(f, "{}", f.fill())?;
885 }
886 Ok(())
887 }
888}
889
890#[derive(Error, Debug)]
891#[allow(missing_docs)]
892pub enum ParseAmountError {
893 #[error("cannot parse amount")]
894 Parse,
895 #[error("cannot represent amount: number too high")]
896 TooHigh,
897 #[error("cannot represent amount: too many decimal places after the point")]
898 TooManyDigits,
899}
900
901impl FromStr for Amount {
902 type Err = ParseAmountError;
903
904 fn from_str(src: &str) -> Result<Self, Self::Err> {
905 let mut result: u128 = 0;
906 let mut decimals: Option<u8> = None;
907 let mut chars = src.trim().chars().peekable();
908 if chars.peek() == Some(&'+') {
909 chars.next();
910 }
911 for char in chars {
912 match char {
913 '_' => {}
914 '.' if decimals.is_some() => return Err(ParseAmountError::Parse),
915 '.' => decimals = Some(Amount::DECIMAL_PLACES),
916 char => {
917 let digit = u128::from(char.to_digit(10).ok_or(ParseAmountError::Parse)?);
918 if let Some(d) = &mut decimals {
919 *d = d.checked_sub(1).ok_or(ParseAmountError::TooManyDigits)?;
920 }
921 result = result
922 .checked_mul(10)
923 .and_then(|r| r.checked_add(digit))
924 .ok_or(ParseAmountError::TooHigh)?;
925 }
926 }
927 }
928 result = result
929 .checked_mul(10u128.pow(decimals.unwrap_or(Amount::DECIMAL_PLACES) as u32))
930 .ok_or(ParseAmountError::TooHigh)?;
931 Ok(Amount(result))
932 }
933}
934
935impl Display for BlockHeight {
936 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
937 self.0.fmt(f)
938 }
939}
940
941impl FromStr for BlockHeight {
942 type Err = ParseIntError;
943
944 fn from_str(src: &str) -> Result<Self, Self::Err> {
945 Ok(Self(u64::from_str(src)?))
946 }
947}
948
949#[derive(
953 Debug,
954 Default,
955 Clone,
956 Copy,
957 Hash,
958 Eq,
959 PartialEq,
960 Ord,
961 PartialOrd,
962 Serialize,
963 Deserialize,
964 SimpleObject,
965 Allocative,
966)]
967pub struct Cursor {
968 pub height: BlockHeight,
970 pub index: u32,
972}
973
974impl Cursor {
975 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
978 let value = Self {
979 height: self.height,
980 index: self.index.checked_add(1).ok_or(ArithmeticError::Overflow)?,
981 };
982 Ok(value)
983 }
984}
985
986impl Display for Round {
987 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
988 match self {
989 Round::Fast => write!(f, "fast round"),
990 Round::MultiLeader(r) => write!(f, "multi-leader round {r}"),
991 Round::SingleLeader(r) => write!(f, "single-leader round {r}"),
992 Round::Validator(r) => write!(f, "validator round {r}"),
993 }
994 }
995}
996
997impl Round {
998 pub fn is_multi_leader(&self) -> bool {
1000 matches!(self, Round::MultiLeader(_))
1001 }
1002
1003 pub fn multi_leader(&self) -> Option<u32> {
1005 match self {
1006 Round::MultiLeader(number) => Some(*number),
1007 _ => None,
1008 }
1009 }
1010
1011 pub fn is_validator(&self) -> bool {
1013 matches!(self, Round::Validator(_))
1014 }
1015
1016 pub fn is_fast(&self) -> bool {
1018 matches!(self, Round::Fast)
1019 }
1020
1021 pub fn number(&self) -> u32 {
1023 match self {
1024 Round::Fast => 0,
1025 Round::MultiLeader(r) | Round::SingleLeader(r) | Round::Validator(r) => *r,
1026 }
1027 }
1028
1029 pub fn type_name(&self) -> &'static str {
1031 match self {
1032 Round::Fast => "fast",
1033 Round::MultiLeader(_) => "multi",
1034 Round::SingleLeader(_) => "single",
1035 Round::Validator(_) => "validator",
1036 }
1037 }
1038}
1039
1040impl<'a> iter::Sum<&'a Amount> for Amount {
1041 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
1042 iter.fold(Self::ZERO, |a, b| a.saturating_add(*b))
1043 }
1044}
1045
1046impl Amount {
1047 pub const DECIMAL_PLACES: u8 = 18;
1049
1050 pub const ONE: Amount = Amount(10u128.pow(Amount::DECIMAL_PLACES as u32));
1052
1053 pub const fn from_tokens(tokens: u128) -> Amount {
1055 Self::ONE.saturating_mul(tokens)
1056 }
1057
1058 pub const fn from_millis(millitokens: u128) -> Amount {
1060 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 3)).saturating_mul(millitokens)
1061 }
1062
1063 pub const fn from_micros(microtokens: u128) -> Amount {
1065 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 6)).saturating_mul(microtokens)
1066 }
1067
1068 pub const fn from_nanos(nanotokens: u128) -> Amount {
1070 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 9)).saturating_mul(nanotokens)
1071 }
1072
1073 pub const fn from_attos(attotokens: u128) -> Amount {
1075 Amount(attotokens)
1076 }
1077
1078 pub const fn to_attos(self) -> u128 {
1080 self.0
1081 }
1082
1083 pub const fn upper_half(self) -> u64 {
1085 (self.0 >> 64) as u64
1086 }
1087
1088 #[expect(
1090 clippy::cast_possible_truncation,
1091 reason = "intentional: returns the low 64 bits"
1092 )]
1093 pub const fn lower_half(self) -> u64 {
1094 self.0 as u64
1095 }
1096
1097 pub fn saturating_ratio(self, other: Amount) -> u128 {
1099 self.0.checked_div(other.0).unwrap_or(u128::MAX)
1100 }
1101
1102 pub fn is_zero(&self) -> bool {
1104 *self == Amount::ZERO
1105 }
1106}
1107
1108#[derive(
1110 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug, Serialize, Deserialize, Allocative,
1111)]
1112pub enum ChainOrigin {
1113 Root(u32),
1115 Child {
1117 parent: ChainId,
1119 block_height: BlockHeight,
1121 chain_index: u32,
1124 },
1125}
1126
1127impl ChainOrigin {
1128 pub fn root(&self) -> Option<u32> {
1130 match self {
1131 ChainOrigin::Root(i) => Some(*i),
1132 ChainOrigin::Child { .. } => None,
1133 }
1134 }
1135}
1136
1137#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Allocative)]
1139pub struct Epoch(pub u32);
1140
1141impl Epoch {
1142 pub const ZERO: Epoch = Epoch(0);
1144}
1145
1146impl Serialize for Epoch {
1147 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1148 where
1149 S: serde::ser::Serializer,
1150 {
1151 if serializer.is_human_readable() {
1152 serializer.serialize_str(&self.0.to_string())
1153 } else {
1154 serializer.serialize_newtype_struct("Epoch", &self.0)
1155 }
1156 }
1157}
1158
1159impl<'de> Deserialize<'de> for Epoch {
1160 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1161 where
1162 D: serde::de::Deserializer<'de>,
1163 {
1164 if deserializer.is_human_readable() {
1165 let s = String::deserialize(deserializer)?;
1166 Ok(Epoch(u32::from_str(&s).map_err(serde::de::Error::custom)?))
1167 } else {
1168 #[derive(Deserialize)]
1169 #[serde(rename = "Epoch")]
1170 struct EpochDerived(u32);
1171
1172 let value = EpochDerived::deserialize(deserializer)?;
1173 Ok(Self(value.0))
1174 }
1175 }
1176}
1177
1178impl std::fmt::Display for Epoch {
1179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1180 write!(f, "{}", self.0)
1181 }
1182}
1183
1184impl std::str::FromStr for Epoch {
1185 type Err = CryptoError;
1186
1187 fn from_str(s: &str) -> Result<Self, Self::Err> {
1188 Ok(Epoch(s.parse()?))
1189 }
1190}
1191
1192impl From<u32> for Epoch {
1193 fn from(value: u32) -> Self {
1194 Epoch(value)
1195 }
1196}
1197
1198impl Epoch {
1199 #[inline]
1202 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
1203 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1204 Ok(Self(val))
1205 }
1206
1207 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
1210 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
1211 Ok(Self(val))
1212 }
1213
1214 #[inline]
1216 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
1217 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1218 Ok(())
1219 }
1220}
1221
1222#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1224pub struct InitialChainConfig {
1225 pub ownership: ChainOwnership,
1227 pub epoch: Epoch,
1229 pub balance: Amount,
1231 pub application_permissions: ApplicationPermissions,
1233}
1234
1235#[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize, Allocative)]
1237pub struct ChainDescription {
1238 origin: ChainOrigin,
1239 timestamp: Timestamp,
1240 config: InitialChainConfig,
1241}
1242
1243impl ChainDescription {
1244 pub fn new(origin: ChainOrigin, config: InitialChainConfig, timestamp: Timestamp) -> Self {
1246 Self {
1247 origin,
1248 config,
1249 timestamp,
1250 }
1251 }
1252
1253 pub fn id(&self) -> ChainId {
1255 ChainId::from(self)
1256 }
1257
1258 pub fn origin(&self) -> ChainOrigin {
1260 self.origin
1261 }
1262
1263 pub fn config(&self) -> &InitialChainConfig {
1265 &self.config
1266 }
1267
1268 pub fn timestamp(&self) -> Timestamp {
1270 self.timestamp
1271 }
1272}
1273
1274impl BcsHashable<'_> for ChainDescription {}
1275
1276#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
1278pub struct NetworkDescription {
1279 pub name: String,
1281 pub genesis_config_hash: CryptoHash,
1283 pub genesis_timestamp: Timestamp,
1285 pub genesis_committee_blob_hash: CryptoHash,
1287 pub admin_chain_id: ChainId,
1289}
1290
1291#[derive(
1293 Default,
1294 Debug,
1295 PartialEq,
1296 Eq,
1297 PartialOrd,
1298 Ord,
1299 Hash,
1300 Clone,
1301 Serialize,
1302 Deserialize,
1303 WitType,
1304 WitLoad,
1305 WitStore,
1306 InputObject,
1307 Allocative,
1308)]
1309pub struct ApplicationPermissions {
1310 #[debug(skip_if = Option::is_none)]
1314 pub execute_operations: Option<Vec<ApplicationId>>,
1315 #[graphql(default)]
1318 #[debug(skip_if = Vec::is_empty)]
1319 pub mandatory_applications: Vec<ApplicationId>,
1320 #[graphql(default)]
1323 #[debug(skip_if = Vec::is_empty)]
1324 pub manage_chain: Vec<ApplicationId>,
1325 #[graphql(default)]
1327 #[debug(skip_if = Option::is_none)]
1328 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
1329 #[graphql(default)]
1331 #[debug(skip_if = Option::is_none)]
1332 pub make_http_requests: Option<Vec<ApplicationId>>,
1333}
1334
1335impl ApplicationPermissions {
1336 pub fn new_single(app_id: ApplicationId) -> Self {
1339 Self {
1340 execute_operations: Some(vec![app_id]),
1341 mandatory_applications: vec![app_id],
1342 manage_chain: vec![app_id],
1343 call_service_as_oracle: Some(vec![app_id]),
1344 make_http_requests: Some(vec![app_id]),
1345 }
1346 }
1347
1348 #[cfg(with_testing)]
1351 pub fn new_multiple(app_ids: Vec<ApplicationId>) -> Self {
1352 Self {
1353 execute_operations: Some(app_ids.clone()),
1354 mandatory_applications: app_ids.clone(),
1355 manage_chain: app_ids.clone(),
1356 call_service_as_oracle: Some(app_ids.clone()),
1357 make_http_requests: Some(app_ids),
1358 }
1359 }
1360
1361 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
1363 match (app_id, &self.execute_operations) {
1364 (_, None) => true,
1365 (GenericApplicationId::System, Some(_)) => false,
1366 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
1367 }
1368 }
1369
1370 pub fn can_manage_chain(&self, app_id: &ApplicationId) -> bool {
1373 self.manage_chain.contains(app_id)
1374 }
1375
1376 pub fn can_call_services(&self, app_id: &ApplicationId) -> bool {
1378 self.call_service_as_oracle
1379 .as_ref()
1380 .is_none_or(|app_ids| app_ids.contains(app_id))
1381 }
1382
1383 pub fn can_make_http_requests(&self, app_id: &ApplicationId) -> bool {
1385 self.make_http_requests
1386 .as_ref()
1387 .is_none_or(|app_ids| app_ids.contains(app_id))
1388 }
1389}
1390
1391#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1393pub enum OracleResponse {
1394 Service(
1396 #[debug(with = "hex_debug")]
1397 #[serde(with = "serde_bytes")]
1398 Vec<u8>,
1399 ),
1400 Http(http::Response),
1402 Blob(BlobId),
1404 Assert,
1406 Round(Option<u32>),
1408 Event(
1410 EventId,
1411 #[debug(with = "hex_debug")]
1412 #[serde(with = "serde_bytes")]
1413 Vec<u8>,
1414 ),
1415 EventExists(EventId),
1417 Checkpoint {
1422 execution_state_blobs: Vec<CryptoHash>,
1424 used_blobs: Vec<BlobId>,
1429 outbox_block_hashes: Vec<CryptoHash>,
1437 inbox_cursors: Vec<(ChainId, Cursor)>,
1443 },
1444}
1445
1446impl BcsHashable<'_> for OracleResponse {}
1447
1448#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize, WitType, WitLoad, WitStore)]
1450pub struct ApplicationDescription {
1451 pub module_id: ModuleId,
1453 pub creator_chain_id: ChainId,
1455 pub block_height: BlockHeight,
1457 pub application_index: u32,
1459 #[serde(with = "serde_bytes")]
1461 #[debug(with = "hex_debug")]
1462 pub parameters: Vec<u8>,
1463 pub required_application_ids: Vec<ApplicationId>,
1465}
1466
1467impl From<&ApplicationDescription> for ApplicationId {
1468 fn from(description: &ApplicationDescription) -> Self {
1469 let mut hash = CryptoHash::new(&BlobContent::new_application_description(description));
1470 if matches!(description.module_id.vm_runtime, VmRuntime::Evm) {
1471 hash.make_evm_compatible();
1472 }
1473 ApplicationId::new(hash)
1474 }
1475}
1476
1477impl BcsHashable<'_> for ApplicationDescription {}
1478
1479impl ApplicationDescription {
1480 pub fn to_bytes(&self) -> Vec<u8> {
1482 bcs::to_bytes(self).expect("Serializing blob bytes should not fail!")
1483 }
1484
1485 pub fn contract_bytecode_blob_id(&self) -> BlobId {
1487 self.module_id.contract_bytecode_blob_id()
1488 }
1489
1490 pub fn service_bytecode_blob_id(&self) -> BlobId {
1492 self.module_id.service_bytecode_blob_id()
1493 }
1494}
1495
1496#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, WitType, WitLoad, WitStore)]
1498pub struct Bytecode {
1499 #[serde(with = "serde_bytes")]
1501 #[debug(with = "hex_debug")]
1502 pub bytes: Vec<u8>,
1503}
1504
1505impl Bytecode {
1506 pub fn new(bytes: Vec<u8>) -> Self {
1508 Bytecode { bytes }
1509 }
1510
1511 #[cfg(not(target_arch = "wasm32"))]
1513 pub async fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
1514 let path = path.as_ref();
1515 let bytes = tokio::fs::read(path).await.map_err(|error| {
1516 std::io::Error::new(error.kind(), format!("{}: {error}", path.display()))
1517 })?;
1518 Ok(Bytecode { bytes })
1519 }
1520
1521 #[cfg(not(target_arch = "wasm32"))]
1523 pub fn compress(&self) -> CompressedBytecode {
1524 #[cfg(with_metrics)]
1525 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1526 let compressed_bytes_vec = zstd::stream::encode_all(&*self.bytes, 19)
1527 .expect("Compressing bytes in memory should not fail");
1528
1529 CompressedBytecode {
1530 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1531 }
1532 }
1533
1534 #[cfg(target_arch = "wasm32")]
1536 pub fn compress(&self) -> CompressedBytecode {
1537 use ruzstd::encoding::{CompressionLevel, FrameCompressor};
1538
1539 #[cfg(with_metrics)]
1540 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1541
1542 let mut compressed_bytes_vec = Vec::new();
1543 let mut compressor = FrameCompressor::new(CompressionLevel::Fastest);
1544 compressor.set_source(&*self.bytes);
1545 compressor.set_drain(&mut compressed_bytes_vec);
1546 compressor.compress();
1547
1548 CompressedBytecode {
1549 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1550 }
1551 }
1552}
1553
1554impl AsRef<[u8]> for Bytecode {
1555 fn as_ref(&self) -> &[u8] {
1556 self.bytes.as_ref()
1557 }
1558}
1559
1560#[derive(Error, Debug)]
1562pub enum DecompressionError {
1563 #[error("Bytecode could not be decompressed: {0}")]
1565 InvalidCompressedBytecode(#[from] io::Error),
1566}
1567
1568#[serde_as]
1570#[derive(Clone, Debug, Deserialize, Hash, Serialize, WitType, WitStore)]
1571#[cfg_attr(with_testing, derive(Eq, PartialEq))]
1572pub struct CompressedBytecode {
1573 #[serde_as(as = "Arc<Bytes>")]
1575 #[debug(skip)]
1576 pub compressed_bytes: Arc<Box<[u8]>>,
1577}
1578
1579#[cfg(not(target_arch = "wasm32"))]
1580impl CompressedBytecode {
1581 pub fn decompressed_size_at_most(
1583 compressed_bytes: &[u8],
1584 limit: u64,
1585 ) -> Result<bool, DecompressionError> {
1586 let mut decoder = zstd::stream::Decoder::new(compressed_bytes)?;
1587 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1588 let mut writer = LimitedWriter::new(io::sink(), limit);
1589 match io::copy(&mut decoder, &mut writer) {
1590 Ok(_) => Ok(true),
1591 Err(error) => {
1592 error.downcast::<LimitedWriterError>()?;
1593 Ok(false)
1594 }
1595 }
1596 }
1597
1598 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1600 #[cfg(with_metrics)]
1601 let _decompression_latency = metrics::BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1602 let bytes = zstd::stream::decode_all(&**self.compressed_bytes)?;
1603
1604 #[cfg(with_metrics)]
1605 metrics::BYTECODE_DECOMPRESSED_SIZE_BYTES
1606 .with_label_values(&[])
1607 .observe(bytes.len() as f64);
1608
1609 Ok(Bytecode { bytes })
1610 }
1611}
1612
1613#[cfg(target_arch = "wasm32")]
1614impl CompressedBytecode {
1615 pub fn decompressed_size_at_most(
1617 compressed_bytes: &[u8],
1618 limit: u64,
1619 ) -> Result<bool, DecompressionError> {
1620 use ruzstd::decoding::StreamingDecoder;
1621 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1622 let mut writer = LimitedWriter::new(io::sink(), limit);
1623 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
1624
1625 match io::copy(&mut decoder, &mut writer) {
1627 Ok(_) => Ok(true),
1628 Err(error) => {
1629 error.downcast::<LimitedWriterError>()?;
1630 Ok(false)
1631 }
1632 }
1633 }
1634
1635 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1637 use ruzstd::{decoding::StreamingDecoder, io::Read};
1638
1639 #[cfg(with_metrics)]
1640 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1641
1642 let compressed_bytes = &*self.compressed_bytes;
1643 let mut bytes = Vec::new();
1644 let mut decoder = StreamingDecoder::new(&**compressed_bytes).map_err(io::Error::other)?;
1645
1646 while !decoder.get_ref().is_empty() {
1648 decoder
1649 .read_to_end(&mut bytes)
1650 .expect("Reading from a slice in memory should not result in I/O errors");
1651 }
1652
1653 #[cfg(with_metrics)]
1654 BYTECODE_DECOMPRESSED_SIZE_BYTES
1655 .with_label_values(&[])
1656 .observe(bytes.len() as f64);
1657
1658 Ok(Bytecode { bytes })
1659 }
1660}
1661
1662impl BcsHashable<'_> for BlobContent {}
1663
1664#[serde_as]
1666#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Allocative)]
1667pub struct BlobContent {
1668 blob_type: BlobType,
1670 #[debug(skip)]
1672 #[serde_as(as = "Arc<Bytes>")]
1673 bytes: Arc<Box<[u8]>>,
1674}
1675
1676impl BlobContent {
1677 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1679 let bytes = bytes.into();
1680 BlobContent {
1681 blob_type,
1682 bytes: Arc::new(bytes),
1683 }
1684 }
1685
1686 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1688 BlobContent::new(BlobType::Data, bytes)
1689 }
1690
1691 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1693 BlobContent {
1694 blob_type: BlobType::ContractBytecode,
1695 bytes: compressed_bytecode.compressed_bytes,
1696 }
1697 }
1698
1699 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1701 BlobContent {
1702 blob_type: BlobType::EvmBytecode,
1703 bytes: compressed_bytecode.compressed_bytes,
1704 }
1705 }
1706
1707 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1709 BlobContent {
1710 blob_type: BlobType::ServiceBytecode,
1711 bytes: compressed_bytecode.compressed_bytes,
1712 }
1713 }
1714
1715 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1717 let bytes = application_description.to_bytes();
1718 BlobContent::new(BlobType::ApplicationDescription, bytes)
1719 }
1720
1721 pub fn new_application_formats(bytes: impl Into<Box<[u8]>>) -> Self {
1724 BlobContent::new(BlobType::ApplicationFormats, bytes)
1725 }
1726
1727 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1729 BlobContent::new(BlobType::Committee, committee)
1730 }
1731
1732 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1734 let bytes = bcs::to_bytes(&chain_description)
1735 .expect("Serializing a ChainDescription should not fail!");
1736 BlobContent::new(BlobType::ChainDescription, bytes)
1737 }
1738
1739 pub fn bytes(&self) -> &[u8] {
1741 &self.bytes
1742 }
1743
1744 pub fn into_vec_or_clone(self) -> Vec<u8> {
1746 let bytes = Arc::unwrap_or_clone(self.bytes);
1747 bytes.into_vec()
1748 }
1749
1750 pub fn into_arc_bytes(self) -> Arc<Box<[u8]>> {
1752 self.bytes
1753 }
1754
1755 pub fn blob_type(&self) -> BlobType {
1757 self.blob_type
1758 }
1759}
1760
1761impl From<Blob> for BlobContent {
1762 fn from(blob: Blob) -> BlobContent {
1763 blob.content
1764 }
1765}
1766
1767impl From<Arc<Blob>> for BlobContent {
1768 fn from(blob: Arc<Blob>) -> BlobContent {
1769 blob.content().clone()
1770 }
1771}
1772
1773#[derive(Debug, Hash, PartialEq, Eq, Clone, Allocative)]
1775pub struct Blob {
1776 hash: CryptoHash,
1778 content: BlobContent,
1780}
1781
1782impl Blob {
1783 pub fn new(content: BlobContent) -> Self {
1785 let mut hash = CryptoHash::new(&content);
1786 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1787 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1788 .expect("to obtain an application description");
1789 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1790 hash.make_evm_compatible();
1791 }
1792 }
1793 Blob { hash, content }
1794 }
1795
1796 pub fn new_with_hash_unchecked(blob_id: BlobId, content: BlobContent) -> Self {
1798 Blob {
1799 hash: blob_id.hash,
1800 content,
1801 }
1802 }
1803
1804 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1806 let bytes = bytes.into();
1807 Blob {
1808 hash: blob_id.hash,
1809 content: BlobContent {
1810 blob_type: blob_id.blob_type,
1811 bytes: Arc::new(bytes),
1812 },
1813 }
1814 }
1815
1816 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1818 Blob::new(BlobContent::new_data(bytes))
1819 }
1820
1821 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1823 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1824 }
1825
1826 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1828 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1829 }
1830
1831 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1833 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1834 }
1835
1836 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1838 Blob::new(BlobContent::new_application_description(
1839 application_description,
1840 ))
1841 }
1842
1843 pub fn new_application_formats(bytes: impl Into<Box<[u8]>>) -> Self {
1846 Blob::new(BlobContent::new_application_formats(bytes))
1847 }
1848
1849 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1851 Blob::new(BlobContent::new_committee(committee))
1852 }
1853
1854 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1856 Blob::new(BlobContent::new_chain_description(chain_description))
1857 }
1858
1859 pub fn id(&self) -> BlobId {
1861 BlobId {
1862 hash: self.hash,
1863 blob_type: self.content.blob_type,
1864 }
1865 }
1866
1867 pub fn content(&self) -> &BlobContent {
1869 &self.content
1870 }
1871
1872 pub fn into_content(self) -> BlobContent {
1874 self.content
1875 }
1876
1877 pub fn bytes(&self) -> &[u8] {
1879 self.content.bytes()
1880 }
1881
1882 pub fn is_committee_blob(&self) -> bool {
1884 self.content().blob_type().is_committee_blob()
1885 }
1886
1887 pub fn is_checkpoint_blob(&self) -> bool {
1889 self.content().blob_type().is_checkpoint_blob()
1890 }
1891}
1892
1893impl Serialize for Blob {
1894 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1895 where
1896 S: Serializer,
1897 {
1898 if serializer.is_human_readable() {
1899 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1900 serializer.serialize_str(&hex::encode(blob_bytes))
1901 } else {
1902 BlobContent::serialize(self.content(), serializer)
1903 }
1904 }
1905}
1906
1907impl<'a> Deserialize<'a> for Blob {
1908 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1909 where
1910 D: Deserializer<'a>,
1911 {
1912 if deserializer.is_human_readable() {
1913 let s = String::deserialize(deserializer)?;
1914 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1915 let content: BlobContent =
1916 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1917
1918 Ok(Blob::new(content))
1919 } else {
1920 let content = BlobContent::deserialize(deserializer)?;
1921 Ok(Blob::new(content))
1922 }
1923 }
1924}
1925
1926impl BcsHashable<'_> for Blob {}
1927
1928#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1930pub struct Event {
1931 pub stream_id: StreamId,
1933 pub index: u32,
1935 #[debug(with = "hex_debug")]
1937 #[serde(with = "serde_bytes")]
1938 pub value: Vec<u8>,
1939}
1940
1941impl Event {
1942 pub fn id(&self, chain_id: ChainId) -> EventId {
1944 EventId {
1945 chain_id,
1946 stream_id: self.stream_id.clone(),
1947 index: self.index,
1948 }
1949 }
1950}
1951
1952#[derive(Clone, Debug, Serialize, Deserialize, WitType, WitLoad, WitStore)]
1954pub struct StreamUpdate {
1955 pub chain_id: ChainId,
1957 pub stream_id: StreamId,
1959 pub previous_index: u32,
1961 pub first_index: u32,
1965 pub next_index: u32,
1967}
1968
1969impl StreamUpdate {
1970 pub fn new_indices(&self) -> impl Iterator<Item = u32> {
1972 self.previous_index..self.next_index
1973 }
1974}
1975
1976impl BcsHashable<'_> for Event {}
1977
1978#[derive(
1980 Clone,
1981 Debug,
1982 Default,
1983 PartialEq,
1984 serde::Serialize,
1985 serde::Deserialize,
1986 async_graphql::SimpleObject,
1987)]
1988pub struct MessagePolicy {
1989 pub blanket: BlanketMessagePolicy,
1991 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
1995 pub ignore_chain_ids: HashSet<ChainId>,
1997 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
2000 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
2003 pub process_events_from_application_ids: Option<HashSet<GenericApplicationId>>,
2006 pub never_reject_application_ids: HashSet<GenericApplicationId>,
2012}
2013
2014#[derive(
2016 Default,
2017 Copy,
2018 Clone,
2019 Debug,
2020 PartialEq,
2021 Eq,
2022 serde::Serialize,
2023 serde::Deserialize,
2024 async_graphql::Enum,
2025)]
2026#[cfg_attr(web, derive(tsify::Tsify), tsify(from_wasm_abi, into_wasm_abi))]
2027#[cfg_attr(any(web, not(target_arch = "wasm32")), derive(clap::ValueEnum))]
2028pub enum BlanketMessagePolicy {
2029 #[default]
2031 Accept,
2032 Reject,
2035 Ignore,
2038}
2039
2040impl MessagePolicy {
2041 #[instrument(level = "trace", skip(self))]
2043 pub fn is_ignore(&self) -> bool {
2044 matches!(self.blanket, BlanketMessagePolicy::Ignore)
2045 }
2046
2047 #[instrument(level = "trace", skip(self))]
2049 pub fn is_reject(&self) -> bool {
2050 matches!(self.blanket, BlanketMessagePolicy::Reject)
2051 }
2052
2053 #[instrument(level = "trace", skip(self))]
2057 pub fn ignores_origin(&self, origin: &ChainId) -> bool {
2058 self.is_ignore()
2059 || self.ignore_chain_ids.contains(origin)
2060 || self
2061 .restrict_chain_ids_to
2062 .as_ref()
2063 .is_some_and(|set| !set.contains(origin))
2064 }
2065}
2066
2067doc_scalar!(Bytecode, "A module bytecode (WebAssembly or EVM)");
2068doc_scalar!(Amount, "A non-negative amount of tokens.");
2069doc_scalar!(U128, "A 128-bit unsigned integer.");
2070doc_scalar!(
2071 Epoch,
2072 "A number identifying the configuration of the chain (aka the committee)"
2073);
2074doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
2075doc_scalar!(
2076 Timestamp,
2077 "A timestamp, in microseconds since the Unix epoch"
2078);
2079doc_scalar!(TimeDelta, "A duration in microseconds");
2080doc_scalar!(
2081 Round,
2082 "A number to identify successive attempts to decide a value in a consensus protocol."
2083);
2084doc_scalar!(
2085 ChainDescription,
2086 "Initial chain configuration and chain origin."
2087);
2088doc_scalar!(OracleResponse, "A record of a single oracle response.");
2089doc_scalar!(BlobContent, "A blob of binary data.");
2090doc_scalar!(
2091 Blob,
2092 "A blob of binary data, with its content-addressed blob ID."
2093);
2094doc_scalar!(ApplicationDescription, "Description of a user application");
2095
2096#[cfg(with_metrics)]
2097mod metrics {
2098 use std::sync::LazyLock;
2099
2100 use prometheus::HistogramVec;
2101
2102 use crate::prometheus_util::{
2103 exponential_bucket_interval, exponential_bucket_latencies, register_histogram_vec,
2104 };
2105
2106 pub static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
2108 register_histogram_vec(
2109 "bytecode_compression_latency",
2110 "Bytecode compression latency",
2111 &[],
2112 exponential_bucket_latencies(10.0),
2113 )
2114 });
2115
2116 pub static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
2118 register_histogram_vec(
2119 "bytecode_decompression_latency",
2120 "Bytecode decompression latency",
2121 &[],
2122 exponential_bucket_latencies(10.0),
2123 )
2124 });
2125
2126 pub static BYTECODE_DECOMPRESSED_SIZE_BYTES: LazyLock<HistogramVec> = LazyLock::new(|| {
2127 register_histogram_vec(
2128 "wasm_bytecode_decompressed_size_bytes",
2129 "Decompressed size in bytes of WASM bytecodes stored on-chain",
2130 &[],
2131 exponential_bucket_interval(10_000.0, 100_000_000.0),
2132 )
2133 });
2134}
2135
2136#[cfg(test)]
2137mod tests {
2138 use std::str::FromStr;
2139
2140 use alloy_primitives::U256;
2141
2142 use super::{Amount, ApplicationDescription, BlobContent};
2143 use crate::{
2144 crypto::CryptoHash,
2145 data_types::BlockHeight,
2146 identifiers::{BlobType, ChainId, ModuleId},
2147 vm::VmRuntime,
2148 };
2149
2150 #[test]
2151 fn non_canonical_btree_map_serializes_like_vec() {
2152 use std::collections::BTreeMap;
2153
2154 use super::NonCanonicalBTreeMap;
2155
2156 let map = NonCanonicalBTreeMap::from(BTreeMap::from([
2159 (1u32, 10u8),
2160 (256u32, 20u8),
2161 (2u32, 30u8),
2162 ]));
2163
2164 let entries = map
2167 .iter()
2168 .map(|(k, v)| (*k, *v))
2169 .collect::<Vec<(u32, u8)>>();
2170 assert_eq!(
2171 bcs::to_bytes(&map).unwrap(),
2172 bcs::to_bytes(&entries).unwrap()
2173 );
2174
2175 let canonical = map
2177 .iter()
2178 .map(|(k, v)| (*k, *v))
2179 .collect::<BTreeMap<u32, u8>>();
2180 assert_ne!(
2181 bcs::to_bytes(&map).unwrap(),
2182 bcs::to_bytes(&canonical).unwrap()
2183 );
2184
2185 let deserialized: NonCanonicalBTreeMap<u32, u8> =
2187 bcs::from_bytes(&bcs::to_bytes(&map).unwrap()).unwrap();
2188 assert_eq!(map, deserialized);
2189 }
2190
2191 #[test]
2192 fn canonical_btree_set_serializes_like_map() {
2193 use std::collections::{BTreeMap, BTreeSet};
2194
2195 use super::CanonicalBTreeSet;
2196
2197 let set = CanonicalBTreeSet::from(BTreeSet::from([1u32, 256u32, 2u32]));
2198
2199 let map = set.iter().map(|t| (*t, ())).collect::<BTreeMap<u32, ()>>();
2202 assert_eq!(bcs::to_bytes(&set).unwrap(), bcs::to_bytes(&map).unwrap());
2203
2204 let plain = set.iter().copied().collect::<BTreeSet<u32>>();
2207 assert_ne!(bcs::to_bytes(&set).unwrap(), bcs::to_bytes(&plain).unwrap());
2208
2209 let deserialized: CanonicalBTreeSet<u32> =
2211 bcs::from_bytes(&bcs::to_bytes(&set).unwrap()).unwrap();
2212 assert_eq!(set, deserialized);
2213 }
2214
2215 #[test]
2216 fn display_amount() {
2217 assert_eq!("1.", Amount::ONE.to_string());
2218 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
2219 assert_eq!(
2220 Amount(10_000_000_000_000_000_000),
2221 Amount::from_str("10").unwrap()
2222 );
2223 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
2224 assert_eq!(
2225 "1001.3",
2226 (Amount::from_str("1.1")
2227 .unwrap()
2228 .saturating_add(Amount::from_str("1_000.2").unwrap()))
2229 .to_string()
2230 );
2231 assert_eq!(
2232 " 1.00000000000000000000",
2233 format!("{:25.20}", Amount::ONE)
2234 );
2235 assert_eq!(
2236 "~+12.34~~",
2237 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
2238 );
2239 }
2240
2241 #[test]
2242 fn blob_content_serialization_deserialization() {
2243 let test_data = b"Hello, world!".as_slice();
2244 let original_blob = BlobContent::new(BlobType::Data, test_data);
2245
2246 let serialized = bcs::to_bytes(&original_blob).expect("Failed to serialize BlobContent");
2247 let deserialized: BlobContent =
2248 bcs::from_bytes(&serialized).expect("Failed to deserialize BlobContent");
2249 assert_eq!(original_blob, deserialized);
2250
2251 let serialized =
2252 serde_json::to_vec(&original_blob).expect("Failed to serialize BlobContent");
2253 let deserialized: BlobContent =
2254 serde_json::from_slice(&serialized).expect("Failed to deserialize BlobContent");
2255 assert_eq!(original_blob, deserialized);
2256 }
2257
2258 #[test]
2259 fn blob_content_hash_consistency() {
2260 let test_data = b"Hello, world!";
2261 let blob1 = BlobContent::new(BlobType::Data, test_data.as_slice());
2262 let blob2 = BlobContent::new(BlobType::Data, Vec::from(test_data.as_slice()));
2263
2264 let hash1 = crate::crypto::CryptoHash::new(&blob1);
2266 let hash2 = crate::crypto::CryptoHash::new(&blob2);
2267
2268 assert_eq!(hash1, hash2, "Hashes should be equal for same content");
2269 assert_eq!(blob1.bytes(), blob2.bytes(), "Byte content should be equal");
2270 }
2271
2272 #[test]
2273 fn test_conversion_amount_u256() {
2274 let value_amount = Amount::from_tokens(15656565652209004332);
2275 let value_u256: U256 = value_amount.into();
2276 let value_amount_rev = Amount::try_from(value_u256).expect("Failed conversion");
2277 assert_eq!(value_amount, value_amount_rev);
2278 }
2279
2280 #[test]
2289 fn application_description_serializes_module_id_as_hex_string() {
2290 let module_id = ModuleId::new(
2291 CryptoHash::test_hash("contract-bytecode"),
2292 CryptoHash::test_hash("service-bytecode"),
2293 VmRuntime::Wasm,
2294 );
2295 let description = ApplicationDescription {
2296 module_id,
2297 creator_chain_id: ChainId(CryptoHash::test_hash("chain")),
2298 block_height: BlockHeight(0),
2299 application_index: 0,
2300 parameters: Vec::new(),
2301 required_application_ids: Vec::new(),
2302 };
2303
2304 let value = serde_json::to_value(&description).unwrap();
2305 let module_id_value = value
2306 .get("module_id")
2307 .expect("`module_id` is the field name the explorer indexes into");
2308 let hex = module_id_value
2309 .as_str()
2310 .expect("`module_id` must serialize as a hex string in human-readable form");
2311 let roundtrip: ModuleId =
2312 serde_json::from_value(serde_json::Value::String(hex.to_owned())).unwrap();
2313 assert_eq!(roundtrip, module_id);
2314 }
2315}