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
348#[derive(Error, Debug)]
351#[error("Failed to convert U256 to Amount. {0} has more than 128 bits")]
352pub struct AmountConversionError(U256);
353
354impl TryFrom<U256> for Amount {
355 type Error = AmountConversionError;
356 fn try_from(value: U256) -> Result<Amount, Self::Error> {
357 let value = u128::try_from(&value).map_err(|_| AmountConversionError(value))?;
358 Ok(Amount(value))
359 }
360}
361
362#[derive(
365 Clone,
366 Copy,
367 Debug,
368 Default,
369 Eq,
370 Ord,
371 PartialEq,
372 PartialOrd,
373 Hash,
374 derive_more::Display,
375 derive_more::From,
376 derive_more::Into,
377 derive_more::Deref,
378 derive_more::DerefMut,
379 derive_more::FromStr,
380)]
381pub struct U128(pub u128);
382
383impl Serialize for U128 {
384 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
385 where
386 S: Serializer,
387 {
388 if serializer.is_human_readable() {
389 serializer.serialize_str(&self.0.to_string())
390 } else {
391 self.0.serialize(serializer)
392 }
393 }
394}
395
396impl<'de> Deserialize<'de> for U128 {
397 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
398 where
399 D: Deserializer<'de>,
400 {
401 if deserializer.is_human_readable() {
402 let s = String::deserialize(deserializer)?;
403 s.parse().map(U128).map_err(serde::de::Error::custom)
404 } else {
405 u128::deserialize(deserializer).map(U128)
406 }
407 }
408}
409
410#[derive(
412 Eq,
413 PartialEq,
414 Ord,
415 PartialOrd,
416 Copy,
417 Clone,
418 Hash,
419 Default,
420 Debug,
421 Serialize,
422 Deserialize,
423 WitType,
424 WitLoad,
425 WitStore,
426 Allocative,
427)]
428#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
429pub struct BlockHeight(pub u64);
430
431#[derive(
433 Eq,
434 PartialEq,
435 Ord,
436 PartialOrd,
437 Copy,
438 Clone,
439 Hash,
440 Default,
441 Debug,
442 Serialize,
443 Deserialize,
444 Allocative,
445)]
446#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
447pub enum Round {
448 #[default]
450 Fast,
451 MultiLeader(u32),
453 SingleLeader(u32),
455 Validator(u32),
457}
458
459#[derive(
461 Eq,
462 PartialEq,
463 Ord,
464 PartialOrd,
465 Copy,
466 Clone,
467 Hash,
468 Default,
469 Debug,
470 Serialize,
471 Deserialize,
472 WitType,
473 WitLoad,
474 WitStore,
475 Allocative,
476)]
477pub struct TimeDelta(u64);
478
479impl TimeDelta {
480 pub const fn from_micros(micros: u64) -> Self {
482 TimeDelta(micros)
483 }
484
485 pub const fn from_millis(millis: u64) -> Self {
487 TimeDelta(millis.saturating_mul(1_000))
488 }
489
490 pub const fn from_secs(secs: u64) -> Self {
492 TimeDelta(secs.saturating_mul(1_000_000))
493 }
494
495 pub fn from_duration(duration: Duration) -> Self {
497 TimeDelta(u64::try_from(duration.as_micros()).unwrap_or(u64::MAX))
498 }
499
500 pub const fn as_micros(&self) -> u64 {
502 self.0
503 }
504
505 pub const fn as_duration(&self) -> Duration {
507 Duration::from_micros(self.as_micros())
508 }
509}
510
511#[derive(
513 Eq,
514 PartialEq,
515 Ord,
516 PartialOrd,
517 Copy,
518 Clone,
519 Hash,
520 Default,
521 Debug,
522 Serialize,
523 Deserialize,
524 WitType,
525 WitLoad,
526 WitStore,
527 Allocative,
528)]
529pub struct Timestamp(u64);
530
531impl Timestamp {
532 pub fn now() -> Timestamp {
534 Timestamp(
535 SystemTime::UNIX_EPOCH
536 .elapsed()
537 .expect("system time should be after Unix epoch")
538 .as_micros()
539 .try_into()
540 .unwrap_or(u64::MAX),
541 )
542 }
543
544 pub const fn micros(&self) -> u64 {
546 self.0
547 }
548
549 pub const fn delta_since(&self, other: Timestamp) -> TimeDelta {
552 TimeDelta::from_micros(self.0.saturating_sub(other.0))
553 }
554
555 pub const fn duration_since(&self, other: Timestamp) -> Duration {
558 Duration::from_micros(self.0.saturating_sub(other.0))
559 }
560
561 pub const fn saturating_add(&self, duration: TimeDelta) -> Timestamp {
563 Timestamp(self.0.saturating_add(duration.0))
564 }
565
566 pub const fn saturating_sub(&self, duration: TimeDelta) -> Timestamp {
568 Timestamp(self.0.saturating_sub(duration.0))
569 }
570
571 pub const fn saturating_sub_micros(&self, micros: u64) -> Timestamp {
574 Timestamp(self.0.saturating_sub(micros))
575 }
576}
577
578impl From<u64> for Timestamp {
579 fn from(t: u64) -> Timestamp {
580 Timestamp(t)
581 }
582}
583
584impl Display for Timestamp {
585 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
586 let seconds = i64::try_from(self.0 / 1_000_000).unwrap_or(i64::MAX);
587 let nanos = u32::try_from((self.0 % 1_000_000) * 1_000)
589 .expect("microseconds modulo 1_000_000 multiplied by 1_000 fits in u32");
590 if let Some(date_time) = chrono::DateTime::from_timestamp(seconds, nanos) {
591 return date_time.naive_utc().fmt(f);
592 }
593 self.0.fmt(f)
594 }
595}
596
597impl FromStr for Timestamp {
598 type Err = chrono::ParseError;
599
600 fn from_str(s: &str) -> Result<Self, Self::Err> {
601 let naive = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S")
602 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S"))?;
603 let micros = naive
604 .and_utc()
605 .timestamp_micros()
606 .try_into()
607 .unwrap_or(u64::MAX);
608 Ok(Timestamp(micros))
609 }
610}
611
612#[derive(
615 Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, WitLoad, WitStore, WitType,
616)]
617pub struct Resources {
618 pub wasm_fuel: u64,
620 pub evm_fuel: u64,
622 pub read_operations: u32,
624 pub write_operations: u32,
626 pub bytes_runtime: u32,
628 pub bytes_to_read: u32,
630 pub bytes_to_write: u32,
632 pub blobs_to_read: u32,
634 pub blobs_to_publish: u32,
636 pub blob_bytes_to_read: u32,
638 pub blob_bytes_to_publish: u32,
640 pub messages: u32,
642 pub message_size: u32,
645 pub service_as_oracle_queries: u32,
647 pub http_requests: u32,
649 }
652
653#[derive(Clone, Debug, Deserialize, Serialize, WitLoad, WitType)]
655#[cfg_attr(with_testing, derive(Eq, PartialEq, WitStore))]
656#[witty_specialize_with(Message = Vec<u8>)]
657pub struct SendMessageRequest<Message> {
658 pub destination: ChainId,
660 pub authenticated: bool,
662 pub is_tracked: bool,
664 pub grant: Resources,
666 pub message: Message,
668}
669
670#[derive(Debug, Error)]
672#[allow(missing_docs)]
673pub enum ArithmeticError {
674 #[error("Number overflow")]
675 Overflow,
676 #[error("Number underflow")]
677 Underflow,
678}
679
680macro_rules! impl_wrapped_number {
681 ($name:ident, $wrapped:ident) => {
682 impl $name {
683 pub const ZERO: Self = Self(0);
685
686 pub const MAX: Self = Self($wrapped::MAX);
688
689 pub fn try_add(self, other: Self) -> Result<Self, ArithmeticError> {
691 let val = self
692 .0
693 .checked_add(other.0)
694 .ok_or(ArithmeticError::Overflow)?;
695 Ok(Self(val))
696 }
697
698 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
700 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
701 Ok(Self(val))
702 }
703
704 pub const fn saturating_add(self, other: Self) -> Self {
706 let val = self.0.saturating_add(other.0);
707 Self(val)
708 }
709
710 pub fn try_sub(self, other: Self) -> Result<Self, ArithmeticError> {
712 let val = self
713 .0
714 .checked_sub(other.0)
715 .ok_or(ArithmeticError::Underflow)?;
716 Ok(Self(val))
717 }
718
719 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
721 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
722 Ok(Self(val))
723 }
724
725 pub const fn saturating_sub(self, other: Self) -> Self {
727 let val = self.0.saturating_sub(other.0);
728 Self(val)
729 }
730
731 pub fn abs_diff(self, other: Self) -> Self {
733 Self(self.0.abs_diff(other.0))
734 }
735
736 pub const fn midpoint(self, other: Self) -> Self {
738 Self(self.0.midpoint(other.0))
739 }
740
741 pub fn try_add_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
743 self.0 = self
744 .0
745 .checked_add(other.0)
746 .ok_or(ArithmeticError::Overflow)?;
747 Ok(())
748 }
749
750 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
752 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
753 Ok(())
754 }
755
756 pub const fn saturating_add_assign(&mut self, other: Self) {
758 self.0 = self.0.saturating_add(other.0);
759 }
760
761 pub fn try_sub_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
763 self.0 = self
764 .0
765 .checked_sub(other.0)
766 .ok_or(ArithmeticError::Underflow)?;
767 Ok(())
768 }
769
770 pub fn saturating_div(&self, other: $wrapped) -> Self {
772 Self(self.0.checked_div(other).unwrap_or($wrapped::MAX))
773 }
774
775 pub const fn saturating_mul(&self, other: $wrapped) -> Self {
777 Self(self.0.saturating_mul(other))
778 }
779
780 pub fn try_mul(self, other: $wrapped) -> Result<Self, ArithmeticError> {
782 let val = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
783 Ok(Self(val))
784 }
785
786 pub fn try_mul_assign(&mut self, other: $wrapped) -> Result<(), ArithmeticError> {
788 self.0 = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
789 Ok(())
790 }
791 }
792
793 impl From<$name> for $wrapped {
794 fn from(value: $name) -> Self {
795 value.0
796 }
797 }
798
799 #[cfg(with_testing)]
801 impl From<$wrapped> for $name {
802 fn from(value: $wrapped) -> Self {
803 Self(value)
804 }
805 }
806
807 #[cfg(with_testing)]
808 impl ops::Add for $name {
809 type Output = Self;
810
811 fn add(self, other: Self) -> Self {
812 Self(self.0 + other.0)
813 }
814 }
815
816 #[cfg(with_testing)]
817 impl ops::Sub for $name {
818 type Output = Self;
819
820 fn sub(self, other: Self) -> Self {
821 Self(self.0 - other.0)
822 }
823 }
824
825 #[cfg(with_testing)]
826 impl ops::Mul<$wrapped> for $name {
827 type Output = Self;
828
829 fn mul(self, other: $wrapped) -> Self {
830 Self(self.0 * other)
831 }
832 }
833 };
834}
835
836impl TryFrom<BlockHeight> for usize {
837 type Error = ArithmeticError;
838
839 fn try_from(height: BlockHeight) -> Result<usize, ArithmeticError> {
840 usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)
841 }
842}
843
844impl_wrapped_number!(Amount, u128);
845impl_wrapped_number!(BlockHeight, u64);
846impl_wrapped_number!(TimeDelta, u64);
847
848impl Display for Amount {
849 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
850 let places = Amount::DECIMAL_PLACES as usize;
852 let min_digits = places + 1;
853 let decimals = format!("{:0min_digits$}", self.0);
854 let integer_part = &decimals[..(decimals.len() - places)];
855 let fractional_part = decimals[(decimals.len() - places)..].trim_end_matches('0');
856
857 let precision = f.precision().unwrap_or(0).max(fractional_part.len());
859 let sign = if f.sign_plus() && self.0 > 0 { "+" } else { "" };
860 let pad_width = f.width().map_or(0, |w| {
862 w.saturating_sub(precision)
863 .saturating_sub(sign.len() + integer_part.len() + 1)
864 });
865 let left_pad = match f.align() {
866 None | Some(fmt::Alignment::Right) => pad_width,
867 Some(fmt::Alignment::Center) => pad_width / 2,
868 Some(fmt::Alignment::Left) => 0,
869 };
870
871 for _ in 0..left_pad {
872 write!(f, "{}", f.fill())?;
873 }
874 write!(f, "{sign}{integer_part}.{fractional_part:0<precision$}")?;
875 for _ in left_pad..pad_width {
876 write!(f, "{}", f.fill())?;
877 }
878 Ok(())
879 }
880}
881
882#[derive(Error, Debug)]
883#[allow(missing_docs)]
884pub enum ParseAmountError {
885 #[error("cannot parse amount")]
886 Parse,
887 #[error("cannot represent amount: number too high")]
888 TooHigh,
889 #[error("cannot represent amount: too many decimal places after the point")]
890 TooManyDigits,
891}
892
893impl FromStr for Amount {
894 type Err = ParseAmountError;
895
896 fn from_str(src: &str) -> Result<Self, Self::Err> {
897 let mut result: u128 = 0;
898 let mut decimals: Option<u8> = None;
899 let mut chars = src.trim().chars().peekable();
900 if chars.peek() == Some(&'+') {
901 chars.next();
902 }
903 for char in chars {
904 match char {
905 '_' => {}
906 '.' if decimals.is_some() => return Err(ParseAmountError::Parse),
907 '.' => decimals = Some(Amount::DECIMAL_PLACES),
908 char => {
909 let digit = u128::from(char.to_digit(10).ok_or(ParseAmountError::Parse)?);
910 if let Some(d) = &mut decimals {
911 *d = d.checked_sub(1).ok_or(ParseAmountError::TooManyDigits)?;
912 }
913 result = result
914 .checked_mul(10)
915 .and_then(|r| r.checked_add(digit))
916 .ok_or(ParseAmountError::TooHigh)?;
917 }
918 }
919 }
920 result = result
921 .checked_mul(10u128.pow(decimals.unwrap_or(Amount::DECIMAL_PLACES) as u32))
922 .ok_or(ParseAmountError::TooHigh)?;
923 Ok(Amount(result))
924 }
925}
926
927impl Display for BlockHeight {
928 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
929 self.0.fmt(f)
930 }
931}
932
933impl FromStr for BlockHeight {
934 type Err = ParseIntError;
935
936 fn from_str(src: &str) -> Result<Self, Self::Err> {
937 Ok(Self(u64::from_str(src)?))
938 }
939}
940
941#[derive(
945 Debug,
946 Default,
947 Clone,
948 Copy,
949 Hash,
950 Eq,
951 PartialEq,
952 Ord,
953 PartialOrd,
954 Serialize,
955 Deserialize,
956 SimpleObject,
957 Allocative,
958)]
959pub struct Cursor {
960 pub height: BlockHeight,
962 pub index: u32,
964}
965
966impl Cursor {
967 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
970 let value = Self {
971 height: self.height,
972 index: self.index.checked_add(1).ok_or(ArithmeticError::Overflow)?,
973 };
974 Ok(value)
975 }
976}
977
978impl Display for Round {
979 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
980 match self {
981 Round::Fast => write!(f, "fast round"),
982 Round::MultiLeader(r) => write!(f, "multi-leader round {r}"),
983 Round::SingleLeader(r) => write!(f, "single-leader round {r}"),
984 Round::Validator(r) => write!(f, "validator round {r}"),
985 }
986 }
987}
988
989impl Round {
990 pub fn is_multi_leader(&self) -> bool {
992 matches!(self, Round::MultiLeader(_))
993 }
994
995 pub fn multi_leader(&self) -> Option<u32> {
997 match self {
998 Round::MultiLeader(number) => Some(*number),
999 _ => None,
1000 }
1001 }
1002
1003 pub fn is_validator(&self) -> bool {
1005 matches!(self, Round::Validator(_))
1006 }
1007
1008 pub fn is_fast(&self) -> bool {
1010 matches!(self, Round::Fast)
1011 }
1012
1013 pub fn number(&self) -> u32 {
1015 match self {
1016 Round::Fast => 0,
1017 Round::MultiLeader(r) | Round::SingleLeader(r) | Round::Validator(r) => *r,
1018 }
1019 }
1020
1021 pub fn type_name(&self) -> &'static str {
1023 match self {
1024 Round::Fast => "fast",
1025 Round::MultiLeader(_) => "multi",
1026 Round::SingleLeader(_) => "single",
1027 Round::Validator(_) => "validator",
1028 }
1029 }
1030}
1031
1032impl<'a> iter::Sum<&'a Amount> for Amount {
1033 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
1034 iter.fold(Self::ZERO, |a, b| a.saturating_add(*b))
1035 }
1036}
1037
1038impl Amount {
1039 pub const DECIMAL_PLACES: u8 = 18;
1041
1042 pub const ONE: Amount = Amount(10u128.pow(Amount::DECIMAL_PLACES as u32));
1044
1045 pub const fn from_tokens(tokens: u128) -> Amount {
1047 Self::ONE.saturating_mul(tokens)
1048 }
1049
1050 pub const fn from_millis(millitokens: u128) -> Amount {
1052 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 3)).saturating_mul(millitokens)
1053 }
1054
1055 pub const fn from_micros(microtokens: u128) -> Amount {
1057 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 6)).saturating_mul(microtokens)
1058 }
1059
1060 pub const fn from_nanos(nanotokens: u128) -> Amount {
1062 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 9)).saturating_mul(nanotokens)
1063 }
1064
1065 pub const fn from_attos(attotokens: u128) -> Amount {
1067 Amount(attotokens)
1068 }
1069
1070 pub const fn to_attos(self) -> u128 {
1072 self.0
1073 }
1074
1075 pub const fn upper_half(self) -> u64 {
1077 (self.0 >> 64) as u64
1078 }
1079
1080 #[expect(
1082 clippy::cast_possible_truncation,
1083 reason = "intentional: returns the low 64 bits"
1084 )]
1085 pub const fn lower_half(self) -> u64 {
1086 self.0 as u64
1087 }
1088
1089 pub fn saturating_ratio(self, other: Amount) -> u128 {
1091 self.0.checked_div(other.0).unwrap_or(u128::MAX)
1092 }
1093
1094 pub fn is_zero(&self) -> bool {
1096 *self == Amount::ZERO
1097 }
1098}
1099
1100#[derive(
1102 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug, Serialize, Deserialize, Allocative,
1103)]
1104pub enum ChainOrigin {
1105 Root(u32),
1107 Child {
1109 parent: ChainId,
1111 block_height: BlockHeight,
1113 chain_index: u32,
1116 },
1117}
1118
1119impl ChainOrigin {
1120 pub fn root(&self) -> Option<u32> {
1122 match self {
1123 ChainOrigin::Root(i) => Some(*i),
1124 ChainOrigin::Child { .. } => None,
1125 }
1126 }
1127}
1128
1129#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Allocative)]
1131pub struct Epoch(pub u32);
1132
1133impl Epoch {
1134 pub const ZERO: Epoch = Epoch(0);
1136}
1137
1138impl Serialize for Epoch {
1139 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1140 where
1141 S: serde::ser::Serializer,
1142 {
1143 if serializer.is_human_readable() {
1144 serializer.serialize_str(&self.0.to_string())
1145 } else {
1146 serializer.serialize_newtype_struct("Epoch", &self.0)
1147 }
1148 }
1149}
1150
1151impl<'de> Deserialize<'de> for Epoch {
1152 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1153 where
1154 D: serde::de::Deserializer<'de>,
1155 {
1156 if deserializer.is_human_readable() {
1157 let s = String::deserialize(deserializer)?;
1158 Ok(Epoch(u32::from_str(&s).map_err(serde::de::Error::custom)?))
1159 } else {
1160 #[derive(Deserialize)]
1161 #[serde(rename = "Epoch")]
1162 struct EpochDerived(u32);
1163
1164 let value = EpochDerived::deserialize(deserializer)?;
1165 Ok(Self(value.0))
1166 }
1167 }
1168}
1169
1170impl std::fmt::Display for Epoch {
1171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1172 write!(f, "{}", self.0)
1173 }
1174}
1175
1176impl std::str::FromStr for Epoch {
1177 type Err = CryptoError;
1178
1179 fn from_str(s: &str) -> Result<Self, Self::Err> {
1180 Ok(Epoch(s.parse()?))
1181 }
1182}
1183
1184impl From<u32> for Epoch {
1185 fn from(value: u32) -> Self {
1186 Epoch(value)
1187 }
1188}
1189
1190impl Epoch {
1191 #[inline]
1194 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
1195 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1196 Ok(Self(val))
1197 }
1198
1199 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
1202 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
1203 Ok(Self(val))
1204 }
1205
1206 #[inline]
1208 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
1209 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1210 Ok(())
1211 }
1212}
1213
1214#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1216pub struct InitialChainConfig {
1217 pub ownership: ChainOwnership,
1219 pub epoch: Epoch,
1221 pub balance: Amount,
1223 pub application_permissions: ApplicationPermissions,
1225}
1226
1227#[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize, Allocative)]
1229pub struct ChainDescription {
1230 origin: ChainOrigin,
1231 timestamp: Timestamp,
1232 config: InitialChainConfig,
1233}
1234
1235impl ChainDescription {
1236 pub fn new(origin: ChainOrigin, config: InitialChainConfig, timestamp: Timestamp) -> Self {
1238 Self {
1239 origin,
1240 config,
1241 timestamp,
1242 }
1243 }
1244
1245 pub fn id(&self) -> ChainId {
1247 ChainId::from(self)
1248 }
1249
1250 pub fn origin(&self) -> ChainOrigin {
1252 self.origin
1253 }
1254
1255 pub fn config(&self) -> &InitialChainConfig {
1257 &self.config
1258 }
1259
1260 pub fn timestamp(&self) -> Timestamp {
1262 self.timestamp
1263 }
1264}
1265
1266impl BcsHashable<'_> for ChainDescription {}
1267
1268#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
1270pub struct NetworkDescription {
1271 pub name: String,
1273 pub genesis_config_hash: CryptoHash,
1275 pub genesis_timestamp: Timestamp,
1277 pub genesis_committee_blob_hash: CryptoHash,
1279 pub admin_chain_id: ChainId,
1281}
1282
1283#[derive(
1285 Default,
1286 Debug,
1287 PartialEq,
1288 Eq,
1289 PartialOrd,
1290 Ord,
1291 Hash,
1292 Clone,
1293 Serialize,
1294 Deserialize,
1295 WitType,
1296 WitLoad,
1297 WitStore,
1298 InputObject,
1299 Allocative,
1300)]
1301pub struct ApplicationPermissions {
1302 #[debug(skip_if = Option::is_none)]
1306 pub execute_operations: Option<Vec<ApplicationId>>,
1307 #[graphql(default)]
1310 #[debug(skip_if = Vec::is_empty)]
1311 pub mandatory_applications: Vec<ApplicationId>,
1312 #[graphql(default)]
1315 #[debug(skip_if = Vec::is_empty)]
1316 pub manage_chain: Vec<ApplicationId>,
1317 #[graphql(default)]
1319 #[debug(skip_if = Option::is_none)]
1320 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
1321 #[graphql(default)]
1323 #[debug(skip_if = Option::is_none)]
1324 pub make_http_requests: Option<Vec<ApplicationId>>,
1325}
1326
1327impl ApplicationPermissions {
1328 pub fn new_single(app_id: ApplicationId) -> Self {
1331 Self {
1332 execute_operations: Some(vec![app_id]),
1333 mandatory_applications: vec![app_id],
1334 manage_chain: vec![app_id],
1335 call_service_as_oracle: Some(vec![app_id]),
1336 make_http_requests: Some(vec![app_id]),
1337 }
1338 }
1339
1340 #[cfg(with_testing)]
1343 pub fn new_multiple(app_ids: Vec<ApplicationId>) -> Self {
1344 Self {
1345 execute_operations: Some(app_ids.clone()),
1346 mandatory_applications: app_ids.clone(),
1347 manage_chain: app_ids.clone(),
1348 call_service_as_oracle: Some(app_ids.clone()),
1349 make_http_requests: Some(app_ids),
1350 }
1351 }
1352
1353 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
1355 match (app_id, &self.execute_operations) {
1356 (_, None) => true,
1357 (GenericApplicationId::System, Some(_)) => false,
1358 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
1359 }
1360 }
1361
1362 pub fn can_manage_chain(&self, app_id: &ApplicationId) -> bool {
1365 self.manage_chain.contains(app_id)
1366 }
1367
1368 pub fn can_call_services(&self, app_id: &ApplicationId) -> bool {
1370 self.call_service_as_oracle
1371 .as_ref()
1372 .is_none_or(|app_ids| app_ids.contains(app_id))
1373 }
1374
1375 pub fn can_make_http_requests(&self, app_id: &ApplicationId) -> bool {
1377 self.make_http_requests
1378 .as_ref()
1379 .is_none_or(|app_ids| app_ids.contains(app_id))
1380 }
1381}
1382
1383#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1385pub enum OracleResponse {
1386 Service(
1388 #[debug(with = "hex_debug")]
1389 #[serde(with = "serde_bytes")]
1390 Vec<u8>,
1391 ),
1392 Http(http::Response),
1394 Blob(BlobId),
1396 Assert,
1398 Round(Option<u32>),
1400 Event(
1402 EventId,
1403 #[debug(with = "hex_debug")]
1404 #[serde(with = "serde_bytes")]
1405 Vec<u8>,
1406 ),
1407 EventExists(EventId),
1409 Checkpoint {
1414 execution_state_blobs: Vec<CryptoHash>,
1416 used_blobs: Vec<BlobId>,
1421 outbox_block_hashes: Vec<CryptoHash>,
1429 inbox_cursors: Vec<(ChainId, Cursor)>,
1435 },
1436}
1437
1438impl BcsHashable<'_> for OracleResponse {}
1439
1440#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize, WitType, WitLoad, WitStore)]
1442pub struct ApplicationDescription {
1443 pub module_id: ModuleId,
1445 pub creator_chain_id: ChainId,
1447 pub block_height: BlockHeight,
1449 pub application_index: u32,
1451 #[serde(with = "serde_bytes")]
1453 #[debug(with = "hex_debug")]
1454 pub parameters: Vec<u8>,
1455 pub required_application_ids: Vec<ApplicationId>,
1457}
1458
1459impl From<&ApplicationDescription> for ApplicationId {
1460 fn from(description: &ApplicationDescription) -> Self {
1461 let mut hash = CryptoHash::new(&BlobContent::new_application_description(description));
1462 if matches!(description.module_id.vm_runtime, VmRuntime::Evm) {
1463 hash.make_evm_compatible();
1464 }
1465 ApplicationId::new(hash)
1466 }
1467}
1468
1469impl BcsHashable<'_> for ApplicationDescription {}
1470
1471impl ApplicationDescription {
1472 pub fn to_bytes(&self) -> Vec<u8> {
1474 bcs::to_bytes(self).expect("Serializing blob bytes should not fail!")
1475 }
1476
1477 pub fn contract_bytecode_blob_id(&self) -> BlobId {
1479 self.module_id.contract_bytecode_blob_id()
1480 }
1481
1482 pub fn service_bytecode_blob_id(&self) -> BlobId {
1484 self.module_id.service_bytecode_blob_id()
1485 }
1486}
1487
1488#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, WitType, WitLoad, WitStore)]
1490pub struct Bytecode {
1491 #[serde(with = "serde_bytes")]
1493 #[debug(with = "hex_debug")]
1494 pub bytes: Vec<u8>,
1495}
1496
1497impl Bytecode {
1498 pub fn new(bytes: Vec<u8>) -> Self {
1500 Bytecode { bytes }
1501 }
1502
1503 #[cfg(not(target_arch = "wasm32"))]
1505 pub async fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
1506 let path = path.as_ref();
1507 let bytes = tokio::fs::read(path).await.map_err(|error| {
1508 std::io::Error::new(error.kind(), format!("{}: {error}", path.display()))
1509 })?;
1510 Ok(Bytecode { bytes })
1511 }
1512
1513 #[cfg(not(target_arch = "wasm32"))]
1515 pub fn compress(&self) -> CompressedBytecode {
1516 #[cfg(with_metrics)]
1517 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1518 let compressed_bytes_vec = zstd::stream::encode_all(&*self.bytes, 19)
1519 .expect("Compressing bytes in memory should not fail");
1520
1521 CompressedBytecode {
1522 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1523 }
1524 }
1525
1526 #[cfg(target_arch = "wasm32")]
1528 pub fn compress(&self) -> CompressedBytecode {
1529 use ruzstd::encoding::{CompressionLevel, FrameCompressor};
1530
1531 #[cfg(with_metrics)]
1532 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1533
1534 let mut compressed_bytes_vec = Vec::new();
1535 let mut compressor = FrameCompressor::new(CompressionLevel::Fastest);
1536 compressor.set_source(&*self.bytes);
1537 compressor.set_drain(&mut compressed_bytes_vec);
1538 compressor.compress();
1539
1540 CompressedBytecode {
1541 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1542 }
1543 }
1544}
1545
1546impl AsRef<[u8]> for Bytecode {
1547 fn as_ref(&self) -> &[u8] {
1548 self.bytes.as_ref()
1549 }
1550}
1551
1552#[derive(Error, Debug)]
1554pub enum DecompressionError {
1555 #[error("Bytecode could not be decompressed: {0}")]
1557 InvalidCompressedBytecode(#[from] io::Error),
1558}
1559
1560#[serde_as]
1562#[derive(Clone, Debug, Deserialize, Hash, Serialize, WitType, WitStore)]
1563#[cfg_attr(with_testing, derive(Eq, PartialEq))]
1564pub struct CompressedBytecode {
1565 #[serde_as(as = "Arc<Bytes>")]
1567 #[debug(skip)]
1568 pub compressed_bytes: Arc<Box<[u8]>>,
1569}
1570
1571#[cfg(not(target_arch = "wasm32"))]
1572impl CompressedBytecode {
1573 pub fn decompressed_size_at_most(
1575 compressed_bytes: &[u8],
1576 limit: u64,
1577 ) -> Result<bool, DecompressionError> {
1578 let mut decoder = zstd::stream::Decoder::new(compressed_bytes)?;
1579 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1580 let mut writer = LimitedWriter::new(io::sink(), limit);
1581 match io::copy(&mut decoder, &mut writer) {
1582 Ok(_) => Ok(true),
1583 Err(error) => {
1584 error.downcast::<LimitedWriterError>()?;
1585 Ok(false)
1586 }
1587 }
1588 }
1589
1590 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1592 #[cfg(with_metrics)]
1593 let _decompression_latency = metrics::BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1594 let bytes = zstd::stream::decode_all(&**self.compressed_bytes)?;
1595
1596 #[cfg(with_metrics)]
1597 metrics::BYTECODE_DECOMPRESSED_SIZE_BYTES
1598 .with_label_values(&[])
1599 .observe(bytes.len() as f64);
1600
1601 Ok(Bytecode { bytes })
1602 }
1603}
1604
1605#[cfg(target_arch = "wasm32")]
1606impl CompressedBytecode {
1607 pub fn decompressed_size_at_most(
1609 compressed_bytes: &[u8],
1610 limit: u64,
1611 ) -> Result<bool, DecompressionError> {
1612 use ruzstd::decoding::StreamingDecoder;
1613 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1614 let mut writer = LimitedWriter::new(io::sink(), limit);
1615 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
1616
1617 match io::copy(&mut decoder, &mut writer) {
1619 Ok(_) => Ok(true),
1620 Err(error) => {
1621 error.downcast::<LimitedWriterError>()?;
1622 Ok(false)
1623 }
1624 }
1625 }
1626
1627 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1629 use ruzstd::{decoding::StreamingDecoder, io::Read};
1630
1631 #[cfg(with_metrics)]
1632 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1633
1634 let compressed_bytes = &*self.compressed_bytes;
1635 let mut bytes = Vec::new();
1636 let mut decoder = StreamingDecoder::new(&**compressed_bytes).map_err(io::Error::other)?;
1637
1638 while !decoder.get_ref().is_empty() {
1640 decoder
1641 .read_to_end(&mut bytes)
1642 .expect("Reading from a slice in memory should not result in I/O errors");
1643 }
1644
1645 #[cfg(with_metrics)]
1646 BYTECODE_DECOMPRESSED_SIZE_BYTES
1647 .with_label_values(&[])
1648 .observe(bytes.len() as f64);
1649
1650 Ok(Bytecode { bytes })
1651 }
1652}
1653
1654impl BcsHashable<'_> for BlobContent {}
1655
1656#[serde_as]
1658#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Allocative)]
1659pub struct BlobContent {
1660 blob_type: BlobType,
1662 #[debug(skip)]
1664 #[serde_as(as = "Arc<Bytes>")]
1665 bytes: Arc<Box<[u8]>>,
1666}
1667
1668impl BlobContent {
1669 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1671 let bytes = bytes.into();
1672 BlobContent {
1673 blob_type,
1674 bytes: Arc::new(bytes),
1675 }
1676 }
1677
1678 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1680 BlobContent::new(BlobType::Data, bytes)
1681 }
1682
1683 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1685 BlobContent {
1686 blob_type: BlobType::ContractBytecode,
1687 bytes: compressed_bytecode.compressed_bytes,
1688 }
1689 }
1690
1691 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1693 BlobContent {
1694 blob_type: BlobType::EvmBytecode,
1695 bytes: compressed_bytecode.compressed_bytes,
1696 }
1697 }
1698
1699 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1701 BlobContent {
1702 blob_type: BlobType::ServiceBytecode,
1703 bytes: compressed_bytecode.compressed_bytes,
1704 }
1705 }
1706
1707 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1709 let bytes = application_description.to_bytes();
1710 BlobContent::new(BlobType::ApplicationDescription, bytes)
1711 }
1712
1713 pub fn new_application_formats(bytes: impl Into<Box<[u8]>>) -> Self {
1716 BlobContent::new(BlobType::ApplicationFormats, bytes)
1717 }
1718
1719 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1721 BlobContent::new(BlobType::Committee, committee)
1722 }
1723
1724 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1726 let bytes = bcs::to_bytes(&chain_description)
1727 .expect("Serializing a ChainDescription should not fail!");
1728 BlobContent::new(BlobType::ChainDescription, bytes)
1729 }
1730
1731 pub fn bytes(&self) -> &[u8] {
1733 &self.bytes
1734 }
1735
1736 pub fn into_vec_or_clone(self) -> Vec<u8> {
1738 let bytes = Arc::unwrap_or_clone(self.bytes);
1739 bytes.into_vec()
1740 }
1741
1742 pub fn into_arc_bytes(self) -> Arc<Box<[u8]>> {
1744 self.bytes
1745 }
1746
1747 pub fn blob_type(&self) -> BlobType {
1749 self.blob_type
1750 }
1751}
1752
1753impl From<Blob> for BlobContent {
1754 fn from(blob: Blob) -> BlobContent {
1755 blob.content
1756 }
1757}
1758
1759impl From<Arc<Blob>> for BlobContent {
1760 fn from(blob: Arc<Blob>) -> BlobContent {
1761 blob.content().clone()
1762 }
1763}
1764
1765#[derive(Debug, Hash, PartialEq, Eq, Clone, Allocative)]
1767pub struct Blob {
1768 hash: CryptoHash,
1770 content: BlobContent,
1772}
1773
1774impl Blob {
1775 pub fn new(content: BlobContent) -> Self {
1777 let mut hash = CryptoHash::new(&content);
1778 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1779 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1780 .expect("to obtain an application description");
1781 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1782 hash.make_evm_compatible();
1783 }
1784 }
1785 Blob { hash, content }
1786 }
1787
1788 pub fn new_with_hash_unchecked(blob_id: BlobId, content: BlobContent) -> Self {
1790 Blob {
1791 hash: blob_id.hash,
1792 content,
1793 }
1794 }
1795
1796 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1798 let bytes = bytes.into();
1799 Blob {
1800 hash: blob_id.hash,
1801 content: BlobContent {
1802 blob_type: blob_id.blob_type,
1803 bytes: Arc::new(bytes),
1804 },
1805 }
1806 }
1807
1808 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1810 Blob::new(BlobContent::new_data(bytes))
1811 }
1812
1813 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1815 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1816 }
1817
1818 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1820 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1821 }
1822
1823 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1825 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1826 }
1827
1828 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1830 Blob::new(BlobContent::new_application_description(
1831 application_description,
1832 ))
1833 }
1834
1835 pub fn new_application_formats(bytes: impl Into<Box<[u8]>>) -> Self {
1838 Blob::new(BlobContent::new_application_formats(bytes))
1839 }
1840
1841 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1843 Blob::new(BlobContent::new_committee(committee))
1844 }
1845
1846 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1848 Blob::new(BlobContent::new_chain_description(chain_description))
1849 }
1850
1851 pub fn id(&self) -> BlobId {
1853 BlobId {
1854 hash: self.hash,
1855 blob_type: self.content.blob_type,
1856 }
1857 }
1858
1859 pub fn content(&self) -> &BlobContent {
1861 &self.content
1862 }
1863
1864 pub fn into_content(self) -> BlobContent {
1866 self.content
1867 }
1868
1869 pub fn bytes(&self) -> &[u8] {
1871 self.content.bytes()
1872 }
1873
1874 pub fn is_committee_blob(&self) -> bool {
1876 self.content().blob_type().is_committee_blob()
1877 }
1878
1879 pub fn is_checkpoint_blob(&self) -> bool {
1881 self.content().blob_type().is_checkpoint_blob()
1882 }
1883}
1884
1885impl Serialize for Blob {
1886 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1887 where
1888 S: Serializer,
1889 {
1890 if serializer.is_human_readable() {
1891 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1892 serializer.serialize_str(&hex::encode(blob_bytes))
1893 } else {
1894 BlobContent::serialize(self.content(), serializer)
1895 }
1896 }
1897}
1898
1899impl<'a> Deserialize<'a> for Blob {
1900 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1901 where
1902 D: Deserializer<'a>,
1903 {
1904 if deserializer.is_human_readable() {
1905 let s = String::deserialize(deserializer)?;
1906 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1907 let content: BlobContent =
1908 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1909
1910 Ok(Blob::new(content))
1911 } else {
1912 let content = BlobContent::deserialize(deserializer)?;
1913 Ok(Blob::new(content))
1914 }
1915 }
1916}
1917
1918impl BcsHashable<'_> for Blob {}
1919
1920#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1922pub struct Event {
1923 pub stream_id: StreamId,
1925 pub index: u32,
1927 #[debug(with = "hex_debug")]
1929 #[serde(with = "serde_bytes")]
1930 pub value: Vec<u8>,
1931}
1932
1933impl Event {
1934 pub fn id(&self, chain_id: ChainId) -> EventId {
1936 EventId {
1937 chain_id,
1938 stream_id: self.stream_id.clone(),
1939 index: self.index,
1940 }
1941 }
1942}
1943
1944#[derive(Clone, Debug, Serialize, Deserialize, WitType, WitLoad, WitStore)]
1946pub struct StreamUpdate {
1947 pub chain_id: ChainId,
1949 pub stream_id: StreamId,
1951 pub previous_index: u32,
1953 pub first_index: u32,
1957 pub next_index: u32,
1959}
1960
1961impl StreamUpdate {
1962 pub fn new_indices(&self) -> impl Iterator<Item = u32> {
1964 self.previous_index..self.next_index
1965 }
1966}
1967
1968impl BcsHashable<'_> for Event {}
1969
1970#[derive(
1972 Clone,
1973 Debug,
1974 Default,
1975 PartialEq,
1976 serde::Serialize,
1977 serde::Deserialize,
1978 async_graphql::SimpleObject,
1979)]
1980pub struct MessagePolicy {
1981 pub blanket: BlanketMessagePolicy,
1983 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
1987 pub ignore_chain_ids: HashSet<ChainId>,
1989 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
1992 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
1995 pub process_events_from_application_ids: Option<HashSet<GenericApplicationId>>,
1998 pub never_reject_application_ids: HashSet<GenericApplicationId>,
2004}
2005
2006#[derive(
2008 Default,
2009 Copy,
2010 Clone,
2011 Debug,
2012 PartialEq,
2013 Eq,
2014 serde::Serialize,
2015 serde::Deserialize,
2016 async_graphql::Enum,
2017)]
2018#[cfg_attr(web, derive(tsify::Tsify), tsify(from_wasm_abi, into_wasm_abi))]
2019#[cfg_attr(any(web, not(target_arch = "wasm32")), derive(clap::ValueEnum))]
2020pub enum BlanketMessagePolicy {
2021 #[default]
2023 Accept,
2024 Reject,
2027 Ignore,
2030}
2031
2032impl MessagePolicy {
2033 #[instrument(level = "trace", skip(self))]
2035 pub fn is_ignore(&self) -> bool {
2036 matches!(self.blanket, BlanketMessagePolicy::Ignore)
2037 }
2038
2039 #[instrument(level = "trace", skip(self))]
2041 pub fn is_reject(&self) -> bool {
2042 matches!(self.blanket, BlanketMessagePolicy::Reject)
2043 }
2044
2045 #[instrument(level = "trace", skip(self))]
2049 pub fn ignores_origin(&self, origin: &ChainId) -> bool {
2050 self.is_ignore()
2051 || self.ignore_chain_ids.contains(origin)
2052 || self
2053 .restrict_chain_ids_to
2054 .as_ref()
2055 .is_some_and(|set| !set.contains(origin))
2056 }
2057}
2058
2059doc_scalar!(Bytecode, "A module bytecode (WebAssembly or EVM)");
2060doc_scalar!(Amount, "A non-negative amount of tokens.");
2061doc_scalar!(U128, "A 128-bit unsigned integer.");
2062doc_scalar!(
2063 Epoch,
2064 "A number identifying the configuration of the chain (aka the committee)"
2065);
2066doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
2067doc_scalar!(
2068 Timestamp,
2069 "A timestamp, in microseconds since the Unix epoch"
2070);
2071doc_scalar!(TimeDelta, "A duration in microseconds");
2072doc_scalar!(
2073 Round,
2074 "A number to identify successive attempts to decide a value in a consensus protocol."
2075);
2076doc_scalar!(
2077 ChainDescription,
2078 "Initial chain configuration and chain origin."
2079);
2080doc_scalar!(OracleResponse, "A record of a single oracle response.");
2081doc_scalar!(BlobContent, "A blob of binary data.");
2082doc_scalar!(
2083 Blob,
2084 "A blob of binary data, with its content-addressed blob ID."
2085);
2086doc_scalar!(ApplicationDescription, "Description of a user application");
2087
2088#[cfg(with_metrics)]
2089mod metrics {
2090 use std::sync::LazyLock;
2091
2092 use prometheus::HistogramVec;
2093
2094 use crate::prometheus_util::{
2095 exponential_bucket_interval, exponential_bucket_latencies, register_histogram_vec,
2096 };
2097
2098 pub static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
2100 register_histogram_vec(
2101 "bytecode_compression_latency",
2102 "Bytecode compression latency",
2103 &[],
2104 exponential_bucket_latencies(10.0),
2105 )
2106 });
2107
2108 pub static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
2110 register_histogram_vec(
2111 "bytecode_decompression_latency",
2112 "Bytecode decompression latency",
2113 &[],
2114 exponential_bucket_latencies(10.0),
2115 )
2116 });
2117
2118 pub static BYTECODE_DECOMPRESSED_SIZE_BYTES: LazyLock<HistogramVec> = LazyLock::new(|| {
2119 register_histogram_vec(
2120 "wasm_bytecode_decompressed_size_bytes",
2121 "Decompressed size in bytes of WASM bytecodes stored on-chain",
2122 &[],
2123 exponential_bucket_interval(10_000.0, 100_000_000.0),
2124 )
2125 });
2126}
2127
2128#[cfg(test)]
2129mod tests {
2130 use std::str::FromStr;
2131
2132 use alloy_primitives::U256;
2133
2134 use super::{Amount, ApplicationDescription, BlobContent};
2135 use crate::{
2136 crypto::CryptoHash,
2137 data_types::BlockHeight,
2138 identifiers::{BlobType, ChainId, ModuleId},
2139 vm::VmRuntime,
2140 };
2141
2142 #[test]
2143 fn non_canonical_btree_map_serializes_like_vec() {
2144 use std::collections::BTreeMap;
2145
2146 use super::NonCanonicalBTreeMap;
2147
2148 let map = NonCanonicalBTreeMap::from(BTreeMap::from([
2151 (1u32, 10u8),
2152 (256u32, 20u8),
2153 (2u32, 30u8),
2154 ]));
2155
2156 let entries = map
2159 .iter()
2160 .map(|(k, v)| (*k, *v))
2161 .collect::<Vec<(u32, u8)>>();
2162 assert_eq!(
2163 bcs::to_bytes(&map).unwrap(),
2164 bcs::to_bytes(&entries).unwrap()
2165 );
2166
2167 let canonical = map
2169 .iter()
2170 .map(|(k, v)| (*k, *v))
2171 .collect::<BTreeMap<u32, u8>>();
2172 assert_ne!(
2173 bcs::to_bytes(&map).unwrap(),
2174 bcs::to_bytes(&canonical).unwrap()
2175 );
2176
2177 let deserialized: NonCanonicalBTreeMap<u32, u8> =
2179 bcs::from_bytes(&bcs::to_bytes(&map).unwrap()).unwrap();
2180 assert_eq!(map, deserialized);
2181 }
2182
2183 #[test]
2184 fn canonical_btree_set_serializes_like_map() {
2185 use std::collections::{BTreeMap, BTreeSet};
2186
2187 use super::CanonicalBTreeSet;
2188
2189 let set = CanonicalBTreeSet::from(BTreeSet::from([1u32, 256u32, 2u32]));
2190
2191 let map = set.iter().map(|t| (*t, ())).collect::<BTreeMap<u32, ()>>();
2194 assert_eq!(bcs::to_bytes(&set).unwrap(), bcs::to_bytes(&map).unwrap());
2195
2196 let plain = set.iter().copied().collect::<BTreeSet<u32>>();
2199 assert_ne!(bcs::to_bytes(&set).unwrap(), bcs::to_bytes(&plain).unwrap());
2200
2201 let deserialized: CanonicalBTreeSet<u32> =
2203 bcs::from_bytes(&bcs::to_bytes(&set).unwrap()).unwrap();
2204 assert_eq!(set, deserialized);
2205 }
2206
2207 #[test]
2208 fn display_amount() {
2209 assert_eq!("1.", Amount::ONE.to_string());
2210 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
2211 assert_eq!(
2212 Amount(10_000_000_000_000_000_000),
2213 Amount::from_str("10").unwrap()
2214 );
2215 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
2216 assert_eq!(
2217 "1001.3",
2218 (Amount::from_str("1.1")
2219 .unwrap()
2220 .saturating_add(Amount::from_str("1_000.2").unwrap()))
2221 .to_string()
2222 );
2223 assert_eq!(
2224 " 1.00000000000000000000",
2225 format!("{:25.20}", Amount::ONE)
2226 );
2227 assert_eq!(
2228 "~+12.34~~",
2229 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
2230 );
2231 }
2232
2233 #[test]
2234 fn blob_content_serialization_deserialization() {
2235 let test_data = b"Hello, world!".as_slice();
2236 let original_blob = BlobContent::new(BlobType::Data, test_data);
2237
2238 let serialized = bcs::to_bytes(&original_blob).expect("Failed to serialize BlobContent");
2239 let deserialized: BlobContent =
2240 bcs::from_bytes(&serialized).expect("Failed to deserialize BlobContent");
2241 assert_eq!(original_blob, deserialized);
2242
2243 let serialized =
2244 serde_json::to_vec(&original_blob).expect("Failed to serialize BlobContent");
2245 let deserialized: BlobContent =
2246 serde_json::from_slice(&serialized).expect("Failed to deserialize BlobContent");
2247 assert_eq!(original_blob, deserialized);
2248 }
2249
2250 #[test]
2251 fn blob_content_hash_consistency() {
2252 let test_data = b"Hello, world!";
2253 let blob1 = BlobContent::new(BlobType::Data, test_data.as_slice());
2254 let blob2 = BlobContent::new(BlobType::Data, Vec::from(test_data.as_slice()));
2255
2256 let hash1 = crate::crypto::CryptoHash::new(&blob1);
2258 let hash2 = crate::crypto::CryptoHash::new(&blob2);
2259
2260 assert_eq!(hash1, hash2, "Hashes should be equal for same content");
2261 assert_eq!(blob1.bytes(), blob2.bytes(), "Byte content should be equal");
2262 }
2263
2264 #[test]
2265 fn test_conversion_amount_u256() {
2266 let value_amount = Amount::from_tokens(15656565652209004332);
2267 let value_u256: U256 = value_amount.into();
2268 let value_amount_rev = Amount::try_from(value_u256).expect("Failed conversion");
2269 assert_eq!(value_amount, value_amount_rev);
2270 }
2271
2272 #[test]
2281 fn application_description_serializes_module_id_as_hex_string() {
2282 let module_id = ModuleId::new(
2283 CryptoHash::test_hash("contract-bytecode"),
2284 CryptoHash::test_hash("service-bytecode"),
2285 VmRuntime::Wasm,
2286 );
2287 let description = ApplicationDescription {
2288 module_id,
2289 creator_chain_id: ChainId(CryptoHash::test_hash("chain")),
2290 block_height: BlockHeight(0),
2291 application_index: 0,
2292 parameters: Vec::new(),
2293 required_application_ids: Vec::new(),
2294 };
2295
2296 let value = serde_json::to_value(&description).unwrap();
2297 let module_id_value = value
2298 .get("module_id")
2299 .expect("`module_id` is the field name the explorer indexes into");
2300 let hex = module_id_value
2301 .as_str()
2302 .expect("`module_id` must serialize as a hex string in human-readable form");
2303 let roundtrip: ModuleId =
2304 serde_json::from_value(serde_json::Value::String(hex.to_owned())).unwrap();
2305 assert_eq!(roundtrip, module_id);
2306 }
2307}