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 const fn as_micros(&self) -> u64 {
497 self.0
498 }
499
500 pub const fn as_duration(&self) -> Duration {
502 Duration::from_micros(self.as_micros())
503 }
504}
505
506#[derive(
508 Eq,
509 PartialEq,
510 Ord,
511 PartialOrd,
512 Copy,
513 Clone,
514 Hash,
515 Default,
516 Debug,
517 Serialize,
518 Deserialize,
519 WitType,
520 WitLoad,
521 WitStore,
522 Allocative,
523)]
524pub struct Timestamp(u64);
525
526impl Timestamp {
527 pub fn now() -> Timestamp {
529 Timestamp(
530 SystemTime::UNIX_EPOCH
531 .elapsed()
532 .expect("system time should be after Unix epoch")
533 .as_micros()
534 .try_into()
535 .unwrap_or(u64::MAX),
536 )
537 }
538
539 pub const fn micros(&self) -> u64 {
541 self.0
542 }
543
544 pub const fn delta_since(&self, other: Timestamp) -> TimeDelta {
547 TimeDelta::from_micros(self.0.saturating_sub(other.0))
548 }
549
550 pub const fn duration_since(&self, other: Timestamp) -> Duration {
553 Duration::from_micros(self.0.saturating_sub(other.0))
554 }
555
556 pub const fn saturating_add(&self, duration: TimeDelta) -> Timestamp {
558 Timestamp(self.0.saturating_add(duration.0))
559 }
560
561 pub const fn saturating_sub(&self, duration: TimeDelta) -> Timestamp {
563 Timestamp(self.0.saturating_sub(duration.0))
564 }
565
566 pub const fn saturating_sub_micros(&self, micros: u64) -> Timestamp {
569 Timestamp(self.0.saturating_sub(micros))
570 }
571}
572
573impl From<u64> for Timestamp {
574 fn from(t: u64) -> Timestamp {
575 Timestamp(t)
576 }
577}
578
579impl Display for Timestamp {
580 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
581 let seconds = i64::try_from(self.0 / 1_000_000).unwrap_or(i64::MAX);
582 let nanos = u32::try_from((self.0 % 1_000_000) * 1_000)
584 .expect("microseconds modulo 1_000_000 multiplied by 1_000 fits in u32");
585 if let Some(date_time) = chrono::DateTime::from_timestamp(seconds, nanos) {
586 return date_time.naive_utc().fmt(f);
587 }
588 self.0.fmt(f)
589 }
590}
591
592impl FromStr for Timestamp {
593 type Err = chrono::ParseError;
594
595 fn from_str(s: &str) -> Result<Self, Self::Err> {
596 let naive = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S")
597 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S"))?;
598 let micros = naive
599 .and_utc()
600 .timestamp_micros()
601 .try_into()
602 .unwrap_or(u64::MAX);
603 Ok(Timestamp(micros))
604 }
605}
606
607#[derive(
610 Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, WitLoad, WitStore, WitType,
611)]
612pub struct Resources {
613 pub wasm_fuel: u64,
615 pub evm_fuel: u64,
617 pub read_operations: u32,
619 pub write_operations: u32,
621 pub bytes_runtime: u32,
623 pub bytes_to_read: u32,
625 pub bytes_to_write: u32,
627 pub blobs_to_read: u32,
629 pub blobs_to_publish: u32,
631 pub blob_bytes_to_read: u32,
633 pub blob_bytes_to_publish: u32,
635 pub messages: u32,
637 pub message_size: u32,
640 pub service_as_oracle_queries: u32,
642 pub http_requests: u32,
644 }
647
648#[derive(Clone, Debug, Deserialize, Serialize, WitLoad, WitType)]
650#[cfg_attr(with_testing, derive(Eq, PartialEq, WitStore))]
651#[witty_specialize_with(Message = Vec<u8>)]
652pub struct SendMessageRequest<Message> {
653 pub destination: ChainId,
655 pub authenticated: bool,
657 pub is_tracked: bool,
659 pub grant: Resources,
661 pub message: Message,
663}
664
665#[derive(Debug, Error)]
667#[allow(missing_docs)]
668pub enum ArithmeticError {
669 #[error("Number overflow")]
670 Overflow,
671 #[error("Number underflow")]
672 Underflow,
673}
674
675macro_rules! impl_wrapped_number {
676 ($name:ident, $wrapped:ident) => {
677 impl $name {
678 pub const ZERO: Self = Self(0);
680
681 pub const MAX: Self = Self($wrapped::MAX);
683
684 pub fn try_add(self, other: Self) -> Result<Self, ArithmeticError> {
686 let val = self
687 .0
688 .checked_add(other.0)
689 .ok_or(ArithmeticError::Overflow)?;
690 Ok(Self(val))
691 }
692
693 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
695 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
696 Ok(Self(val))
697 }
698
699 pub const fn saturating_add(self, other: Self) -> Self {
701 let val = self.0.saturating_add(other.0);
702 Self(val)
703 }
704
705 pub fn try_sub(self, other: Self) -> Result<Self, ArithmeticError> {
707 let val = self
708 .0
709 .checked_sub(other.0)
710 .ok_or(ArithmeticError::Underflow)?;
711 Ok(Self(val))
712 }
713
714 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
716 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
717 Ok(Self(val))
718 }
719
720 pub const fn saturating_sub(self, other: Self) -> Self {
722 let val = self.0.saturating_sub(other.0);
723 Self(val)
724 }
725
726 pub fn abs_diff(self, other: Self) -> Self {
728 Self(self.0.abs_diff(other.0))
729 }
730
731 pub const fn midpoint(self, other: Self) -> Self {
733 Self(self.0.midpoint(other.0))
734 }
735
736 pub fn try_add_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
738 self.0 = self
739 .0
740 .checked_add(other.0)
741 .ok_or(ArithmeticError::Overflow)?;
742 Ok(())
743 }
744
745 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
747 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
748 Ok(())
749 }
750
751 pub const fn saturating_add_assign(&mut self, other: Self) {
753 self.0 = self.0.saturating_add(other.0);
754 }
755
756 pub fn try_sub_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
758 self.0 = self
759 .0
760 .checked_sub(other.0)
761 .ok_or(ArithmeticError::Underflow)?;
762 Ok(())
763 }
764
765 pub fn saturating_div(&self, other: $wrapped) -> Self {
767 Self(self.0.checked_div(other).unwrap_or($wrapped::MAX))
768 }
769
770 pub const fn saturating_mul(&self, other: $wrapped) -> Self {
772 Self(self.0.saturating_mul(other))
773 }
774
775 pub fn try_mul(self, other: $wrapped) -> Result<Self, ArithmeticError> {
777 let val = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
778 Ok(Self(val))
779 }
780
781 pub fn try_mul_assign(&mut self, other: $wrapped) -> Result<(), ArithmeticError> {
783 self.0 = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
784 Ok(())
785 }
786 }
787
788 impl From<$name> for $wrapped {
789 fn from(value: $name) -> Self {
790 value.0
791 }
792 }
793
794 #[cfg(with_testing)]
796 impl From<$wrapped> for $name {
797 fn from(value: $wrapped) -> Self {
798 Self(value)
799 }
800 }
801
802 #[cfg(with_testing)]
803 impl ops::Add for $name {
804 type Output = Self;
805
806 fn add(self, other: Self) -> Self {
807 Self(self.0 + other.0)
808 }
809 }
810
811 #[cfg(with_testing)]
812 impl ops::Sub for $name {
813 type Output = Self;
814
815 fn sub(self, other: Self) -> Self {
816 Self(self.0 - other.0)
817 }
818 }
819
820 #[cfg(with_testing)]
821 impl ops::Mul<$wrapped> for $name {
822 type Output = Self;
823
824 fn mul(self, other: $wrapped) -> Self {
825 Self(self.0 * other)
826 }
827 }
828 };
829}
830
831impl TryFrom<BlockHeight> for usize {
832 type Error = ArithmeticError;
833
834 fn try_from(height: BlockHeight) -> Result<usize, ArithmeticError> {
835 usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)
836 }
837}
838
839impl_wrapped_number!(Amount, u128);
840impl_wrapped_number!(BlockHeight, u64);
841impl_wrapped_number!(TimeDelta, u64);
842
843impl Display for Amount {
844 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
845 let places = Amount::DECIMAL_PLACES as usize;
847 let min_digits = places + 1;
848 let decimals = format!("{:0min_digits$}", self.0);
849 let integer_part = &decimals[..(decimals.len() - places)];
850 let fractional_part = decimals[(decimals.len() - places)..].trim_end_matches('0');
851
852 let precision = f.precision().unwrap_or(0).max(fractional_part.len());
854 let sign = if f.sign_plus() && self.0 > 0 { "+" } else { "" };
855 let pad_width = f.width().map_or(0, |w| {
857 w.saturating_sub(precision)
858 .saturating_sub(sign.len() + integer_part.len() + 1)
859 });
860 let left_pad = match f.align() {
861 None | Some(fmt::Alignment::Right) => pad_width,
862 Some(fmt::Alignment::Center) => pad_width / 2,
863 Some(fmt::Alignment::Left) => 0,
864 };
865
866 for _ in 0..left_pad {
867 write!(f, "{}", f.fill())?;
868 }
869 write!(f, "{sign}{integer_part}.{fractional_part:0<precision$}")?;
870 for _ in left_pad..pad_width {
871 write!(f, "{}", f.fill())?;
872 }
873 Ok(())
874 }
875}
876
877#[derive(Error, Debug)]
878#[allow(missing_docs)]
879pub enum ParseAmountError {
880 #[error("cannot parse amount")]
881 Parse,
882 #[error("cannot represent amount: number too high")]
883 TooHigh,
884 #[error("cannot represent amount: too many decimal places after the point")]
885 TooManyDigits,
886}
887
888impl FromStr for Amount {
889 type Err = ParseAmountError;
890
891 fn from_str(src: &str) -> Result<Self, Self::Err> {
892 let mut result: u128 = 0;
893 let mut decimals: Option<u8> = None;
894 let mut chars = src.trim().chars().peekable();
895 if chars.peek() == Some(&'+') {
896 chars.next();
897 }
898 for char in chars {
899 match char {
900 '_' => {}
901 '.' if decimals.is_some() => return Err(ParseAmountError::Parse),
902 '.' => decimals = Some(Amount::DECIMAL_PLACES),
903 char => {
904 let digit = u128::from(char.to_digit(10).ok_or(ParseAmountError::Parse)?);
905 if let Some(d) = &mut decimals {
906 *d = d.checked_sub(1).ok_or(ParseAmountError::TooManyDigits)?;
907 }
908 result = result
909 .checked_mul(10)
910 .and_then(|r| r.checked_add(digit))
911 .ok_or(ParseAmountError::TooHigh)?;
912 }
913 }
914 }
915 result = result
916 .checked_mul(10u128.pow(decimals.unwrap_or(Amount::DECIMAL_PLACES) as u32))
917 .ok_or(ParseAmountError::TooHigh)?;
918 Ok(Amount(result))
919 }
920}
921
922impl Display for BlockHeight {
923 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
924 self.0.fmt(f)
925 }
926}
927
928impl FromStr for BlockHeight {
929 type Err = ParseIntError;
930
931 fn from_str(src: &str) -> Result<Self, Self::Err> {
932 Ok(Self(u64::from_str(src)?))
933 }
934}
935
936#[derive(
940 Debug,
941 Default,
942 Clone,
943 Copy,
944 Hash,
945 Eq,
946 PartialEq,
947 Ord,
948 PartialOrd,
949 Serialize,
950 Deserialize,
951 SimpleObject,
952 Allocative,
953)]
954pub struct Cursor {
955 pub height: BlockHeight,
957 pub index: u32,
959}
960
961impl Cursor {
962 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
965 let value = Self {
966 height: self.height,
967 index: self.index.checked_add(1).ok_or(ArithmeticError::Overflow)?,
968 };
969 Ok(value)
970 }
971}
972
973impl Display for Round {
974 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
975 match self {
976 Round::Fast => write!(f, "fast round"),
977 Round::MultiLeader(r) => write!(f, "multi-leader round {r}"),
978 Round::SingleLeader(r) => write!(f, "single-leader round {r}"),
979 Round::Validator(r) => write!(f, "validator round {r}"),
980 }
981 }
982}
983
984impl Round {
985 pub fn is_multi_leader(&self) -> bool {
987 matches!(self, Round::MultiLeader(_))
988 }
989
990 pub fn multi_leader(&self) -> Option<u32> {
992 match self {
993 Round::MultiLeader(number) => Some(*number),
994 _ => None,
995 }
996 }
997
998 pub fn is_validator(&self) -> bool {
1000 matches!(self, Round::Validator(_))
1001 }
1002
1003 pub fn is_fast(&self) -> bool {
1005 matches!(self, Round::Fast)
1006 }
1007
1008 pub fn number(&self) -> u32 {
1010 match self {
1011 Round::Fast => 0,
1012 Round::MultiLeader(r) | Round::SingleLeader(r) | Round::Validator(r) => *r,
1013 }
1014 }
1015
1016 pub fn type_name(&self) -> &'static str {
1018 match self {
1019 Round::Fast => "fast",
1020 Round::MultiLeader(_) => "multi",
1021 Round::SingleLeader(_) => "single",
1022 Round::Validator(_) => "validator",
1023 }
1024 }
1025}
1026
1027impl<'a> iter::Sum<&'a Amount> for Amount {
1028 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
1029 iter.fold(Self::ZERO, |a, b| a.saturating_add(*b))
1030 }
1031}
1032
1033impl Amount {
1034 pub const DECIMAL_PLACES: u8 = 18;
1036
1037 pub const ONE: Amount = Amount(10u128.pow(Amount::DECIMAL_PLACES as u32));
1039
1040 pub const fn from_tokens(tokens: u128) -> Amount {
1042 Self::ONE.saturating_mul(tokens)
1043 }
1044
1045 pub const fn from_millis(millitokens: u128) -> Amount {
1047 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 3)).saturating_mul(millitokens)
1048 }
1049
1050 pub const fn from_micros(microtokens: u128) -> Amount {
1052 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 6)).saturating_mul(microtokens)
1053 }
1054
1055 pub const fn from_nanos(nanotokens: u128) -> Amount {
1057 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 9)).saturating_mul(nanotokens)
1058 }
1059
1060 pub const fn from_attos(attotokens: u128) -> Amount {
1062 Amount(attotokens)
1063 }
1064
1065 pub const fn to_attos(self) -> u128 {
1067 self.0
1068 }
1069
1070 pub const fn upper_half(self) -> u64 {
1072 (self.0 >> 64) as u64
1073 }
1074
1075 #[expect(
1077 clippy::cast_possible_truncation,
1078 reason = "intentional: returns the low 64 bits"
1079 )]
1080 pub const fn lower_half(self) -> u64 {
1081 self.0 as u64
1082 }
1083
1084 pub fn saturating_ratio(self, other: Amount) -> u128 {
1086 self.0.checked_div(other.0).unwrap_or(u128::MAX)
1087 }
1088
1089 pub fn is_zero(&self) -> bool {
1091 *self == Amount::ZERO
1092 }
1093}
1094
1095#[derive(
1097 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug, Serialize, Deserialize, Allocative,
1098)]
1099pub enum ChainOrigin {
1100 Root(u32),
1102 Child {
1104 parent: ChainId,
1106 block_height: BlockHeight,
1108 chain_index: u32,
1111 },
1112}
1113
1114impl ChainOrigin {
1115 pub fn root(&self) -> Option<u32> {
1117 match self {
1118 ChainOrigin::Root(i) => Some(*i),
1119 ChainOrigin::Child { .. } => None,
1120 }
1121 }
1122}
1123
1124#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Allocative)]
1126pub struct Epoch(pub u32);
1127
1128impl Epoch {
1129 pub const ZERO: Epoch = Epoch(0);
1131}
1132
1133impl Serialize for Epoch {
1134 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1135 where
1136 S: serde::ser::Serializer,
1137 {
1138 if serializer.is_human_readable() {
1139 serializer.serialize_str(&self.0.to_string())
1140 } else {
1141 serializer.serialize_newtype_struct("Epoch", &self.0)
1142 }
1143 }
1144}
1145
1146impl<'de> Deserialize<'de> for Epoch {
1147 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1148 where
1149 D: serde::de::Deserializer<'de>,
1150 {
1151 if deserializer.is_human_readable() {
1152 let s = String::deserialize(deserializer)?;
1153 Ok(Epoch(u32::from_str(&s).map_err(serde::de::Error::custom)?))
1154 } else {
1155 #[derive(Deserialize)]
1156 #[serde(rename = "Epoch")]
1157 struct EpochDerived(u32);
1158
1159 let value = EpochDerived::deserialize(deserializer)?;
1160 Ok(Self(value.0))
1161 }
1162 }
1163}
1164
1165impl std::fmt::Display for Epoch {
1166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1167 write!(f, "{}", self.0)
1168 }
1169}
1170
1171impl std::str::FromStr for Epoch {
1172 type Err = CryptoError;
1173
1174 fn from_str(s: &str) -> Result<Self, Self::Err> {
1175 Ok(Epoch(s.parse()?))
1176 }
1177}
1178
1179impl From<u32> for Epoch {
1180 fn from(value: u32) -> Self {
1181 Epoch(value)
1182 }
1183}
1184
1185impl Epoch {
1186 #[inline]
1189 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
1190 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1191 Ok(Self(val))
1192 }
1193
1194 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
1197 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
1198 Ok(Self(val))
1199 }
1200
1201 #[inline]
1203 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
1204 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1205 Ok(())
1206 }
1207}
1208
1209#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1211pub struct InitialChainConfig {
1212 pub ownership: ChainOwnership,
1214 pub epoch: Epoch,
1216 pub balance: Amount,
1218 pub application_permissions: ApplicationPermissions,
1220}
1221
1222#[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize, Allocative)]
1224pub struct ChainDescription {
1225 origin: ChainOrigin,
1226 timestamp: Timestamp,
1227 config: InitialChainConfig,
1228}
1229
1230impl ChainDescription {
1231 pub fn new(origin: ChainOrigin, config: InitialChainConfig, timestamp: Timestamp) -> Self {
1233 Self {
1234 origin,
1235 config,
1236 timestamp,
1237 }
1238 }
1239
1240 pub fn id(&self) -> ChainId {
1242 ChainId::from(self)
1243 }
1244
1245 pub fn origin(&self) -> ChainOrigin {
1247 self.origin
1248 }
1249
1250 pub fn config(&self) -> &InitialChainConfig {
1252 &self.config
1253 }
1254
1255 pub fn timestamp(&self) -> Timestamp {
1257 self.timestamp
1258 }
1259}
1260
1261impl BcsHashable<'_> for ChainDescription {}
1262
1263#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
1265pub struct NetworkDescription {
1266 pub name: String,
1268 pub genesis_config_hash: CryptoHash,
1270 pub genesis_timestamp: Timestamp,
1272 pub genesis_committee_blob_hash: CryptoHash,
1274 pub admin_chain_id: ChainId,
1276}
1277
1278#[derive(
1280 Default,
1281 Debug,
1282 PartialEq,
1283 Eq,
1284 PartialOrd,
1285 Ord,
1286 Hash,
1287 Clone,
1288 Serialize,
1289 Deserialize,
1290 WitType,
1291 WitLoad,
1292 WitStore,
1293 InputObject,
1294 Allocative,
1295)]
1296pub struct ApplicationPermissions {
1297 #[debug(skip_if = Option::is_none)]
1301 pub execute_operations: Option<Vec<ApplicationId>>,
1302 #[graphql(default)]
1305 #[debug(skip_if = Vec::is_empty)]
1306 pub mandatory_applications: Vec<ApplicationId>,
1307 #[graphql(default)]
1310 #[debug(skip_if = Vec::is_empty)]
1311 pub manage_chain: Vec<ApplicationId>,
1312 #[graphql(default)]
1314 #[debug(skip_if = Option::is_none)]
1315 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
1316 #[graphql(default)]
1318 #[debug(skip_if = Option::is_none)]
1319 pub make_http_requests: Option<Vec<ApplicationId>>,
1320}
1321
1322impl ApplicationPermissions {
1323 pub fn new_single(app_id: ApplicationId) -> Self {
1326 Self {
1327 execute_operations: Some(vec![app_id]),
1328 mandatory_applications: vec![app_id],
1329 manage_chain: vec![app_id],
1330 call_service_as_oracle: Some(vec![app_id]),
1331 make_http_requests: Some(vec![app_id]),
1332 }
1333 }
1334
1335 #[cfg(with_testing)]
1338 pub fn new_multiple(app_ids: Vec<ApplicationId>) -> Self {
1339 Self {
1340 execute_operations: Some(app_ids.clone()),
1341 mandatory_applications: app_ids.clone(),
1342 manage_chain: app_ids.clone(),
1343 call_service_as_oracle: Some(app_ids.clone()),
1344 make_http_requests: Some(app_ids),
1345 }
1346 }
1347
1348 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
1350 match (app_id, &self.execute_operations) {
1351 (_, None) => true,
1352 (GenericApplicationId::System, Some(_)) => false,
1353 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
1354 }
1355 }
1356
1357 pub fn can_manage_chain(&self, app_id: &ApplicationId) -> bool {
1360 self.manage_chain.contains(app_id)
1361 }
1362
1363 pub fn can_call_services(&self, app_id: &ApplicationId) -> bool {
1365 self.call_service_as_oracle
1366 .as_ref()
1367 .is_none_or(|app_ids| app_ids.contains(app_id))
1368 }
1369
1370 pub fn can_make_http_requests(&self, app_id: &ApplicationId) -> bool {
1372 self.make_http_requests
1373 .as_ref()
1374 .is_none_or(|app_ids| app_ids.contains(app_id))
1375 }
1376}
1377
1378#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1380pub enum OracleResponse {
1381 Service(
1383 #[debug(with = "hex_debug")]
1384 #[serde(with = "serde_bytes")]
1385 Vec<u8>,
1386 ),
1387 Http(http::Response),
1389 Blob(BlobId),
1391 Assert,
1393 Round(Option<u32>),
1395 Event(
1397 EventId,
1398 #[debug(with = "hex_debug")]
1399 #[serde(with = "serde_bytes")]
1400 Vec<u8>,
1401 ),
1402 EventExists(EventId),
1404 Checkpoint {
1409 execution_state_blobs: Vec<CryptoHash>,
1411 used_blobs: Vec<BlobId>,
1416 outbox_block_hashes: Vec<CryptoHash>,
1424 inbox_cursors: Vec<(ChainId, Cursor)>,
1430 },
1431}
1432
1433impl BcsHashable<'_> for OracleResponse {}
1434
1435#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize, WitType, WitLoad, WitStore)]
1437pub struct ApplicationDescription {
1438 pub module_id: ModuleId,
1440 pub creator_chain_id: ChainId,
1442 pub block_height: BlockHeight,
1444 pub application_index: u32,
1446 #[serde(with = "serde_bytes")]
1448 #[debug(with = "hex_debug")]
1449 pub parameters: Vec<u8>,
1450 pub required_application_ids: Vec<ApplicationId>,
1452}
1453
1454impl From<&ApplicationDescription> for ApplicationId {
1455 fn from(description: &ApplicationDescription) -> Self {
1456 let mut hash = CryptoHash::new(&BlobContent::new_application_description(description));
1457 if matches!(description.module_id.vm_runtime, VmRuntime::Evm) {
1458 hash.make_evm_compatible();
1459 }
1460 ApplicationId::new(hash)
1461 }
1462}
1463
1464impl BcsHashable<'_> for ApplicationDescription {}
1465
1466impl ApplicationDescription {
1467 pub fn to_bytes(&self) -> Vec<u8> {
1469 bcs::to_bytes(self).expect("Serializing blob bytes should not fail!")
1470 }
1471
1472 pub fn contract_bytecode_blob_id(&self) -> BlobId {
1474 self.module_id.contract_bytecode_blob_id()
1475 }
1476
1477 pub fn service_bytecode_blob_id(&self) -> BlobId {
1479 self.module_id.service_bytecode_blob_id()
1480 }
1481}
1482
1483#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, WitType, WitLoad, WitStore)]
1485pub struct Bytecode {
1486 #[serde(with = "serde_bytes")]
1488 #[debug(with = "hex_debug")]
1489 pub bytes: Vec<u8>,
1490}
1491
1492impl Bytecode {
1493 pub fn new(bytes: Vec<u8>) -> Self {
1495 Bytecode { bytes }
1496 }
1497
1498 #[cfg(not(target_arch = "wasm32"))]
1500 pub async fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
1501 let path = path.as_ref();
1502 let bytes = tokio::fs::read(path).await.map_err(|error| {
1503 std::io::Error::new(error.kind(), format!("{}: {error}", path.display()))
1504 })?;
1505 Ok(Bytecode { bytes })
1506 }
1507
1508 #[cfg(not(target_arch = "wasm32"))]
1510 pub fn compress(&self) -> CompressedBytecode {
1511 #[cfg(with_metrics)]
1512 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1513 let compressed_bytes_vec = zstd::stream::encode_all(&*self.bytes, 19)
1514 .expect("Compressing bytes in memory should not fail");
1515
1516 CompressedBytecode {
1517 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1518 }
1519 }
1520
1521 #[cfg(target_arch = "wasm32")]
1523 pub fn compress(&self) -> CompressedBytecode {
1524 use ruzstd::encoding::{CompressionLevel, FrameCompressor};
1525
1526 #[cfg(with_metrics)]
1527 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1528
1529 let mut compressed_bytes_vec = Vec::new();
1530 let mut compressor = FrameCompressor::new(CompressionLevel::Fastest);
1531 compressor.set_source(&*self.bytes);
1532 compressor.set_drain(&mut compressed_bytes_vec);
1533 compressor.compress();
1534
1535 CompressedBytecode {
1536 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1537 }
1538 }
1539}
1540
1541impl AsRef<[u8]> for Bytecode {
1542 fn as_ref(&self) -> &[u8] {
1543 self.bytes.as_ref()
1544 }
1545}
1546
1547#[derive(Error, Debug)]
1549pub enum DecompressionError {
1550 #[error("Bytecode could not be decompressed: {0}")]
1552 InvalidCompressedBytecode(#[from] io::Error),
1553}
1554
1555#[serde_as]
1557#[derive(Clone, Debug, Deserialize, Hash, Serialize, WitType, WitStore)]
1558#[cfg_attr(with_testing, derive(Eq, PartialEq))]
1559pub struct CompressedBytecode {
1560 #[serde_as(as = "Arc<Bytes>")]
1562 #[debug(skip)]
1563 pub compressed_bytes: Arc<Box<[u8]>>,
1564}
1565
1566#[cfg(not(target_arch = "wasm32"))]
1567impl CompressedBytecode {
1568 pub fn decompressed_size_at_most(
1570 compressed_bytes: &[u8],
1571 limit: u64,
1572 ) -> Result<bool, DecompressionError> {
1573 let mut decoder = zstd::stream::Decoder::new(compressed_bytes)?;
1574 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1575 let mut writer = LimitedWriter::new(io::sink(), limit);
1576 match io::copy(&mut decoder, &mut writer) {
1577 Ok(_) => Ok(true),
1578 Err(error) => {
1579 error.downcast::<LimitedWriterError>()?;
1580 Ok(false)
1581 }
1582 }
1583 }
1584
1585 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1587 #[cfg(with_metrics)]
1588 let _decompression_latency = metrics::BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1589 let bytes = zstd::stream::decode_all(&**self.compressed_bytes)?;
1590
1591 #[cfg(with_metrics)]
1592 metrics::BYTECODE_DECOMPRESSED_SIZE_BYTES
1593 .with_label_values(&[])
1594 .observe(bytes.len() as f64);
1595
1596 Ok(Bytecode { bytes })
1597 }
1598}
1599
1600#[cfg(target_arch = "wasm32")]
1601impl CompressedBytecode {
1602 pub fn decompressed_size_at_most(
1604 compressed_bytes: &[u8],
1605 limit: u64,
1606 ) -> Result<bool, DecompressionError> {
1607 use ruzstd::decoding::StreamingDecoder;
1608 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1609 let mut writer = LimitedWriter::new(io::sink(), limit);
1610 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
1611
1612 match io::copy(&mut decoder, &mut writer) {
1614 Ok(_) => Ok(true),
1615 Err(error) => {
1616 error.downcast::<LimitedWriterError>()?;
1617 Ok(false)
1618 }
1619 }
1620 }
1621
1622 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1624 use ruzstd::{decoding::StreamingDecoder, io::Read};
1625
1626 #[cfg(with_metrics)]
1627 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1628
1629 let compressed_bytes = &*self.compressed_bytes;
1630 let mut bytes = Vec::new();
1631 let mut decoder = StreamingDecoder::new(&**compressed_bytes).map_err(io::Error::other)?;
1632
1633 while !decoder.get_ref().is_empty() {
1635 decoder
1636 .read_to_end(&mut bytes)
1637 .expect("Reading from a slice in memory should not result in I/O errors");
1638 }
1639
1640 #[cfg(with_metrics)]
1641 BYTECODE_DECOMPRESSED_SIZE_BYTES
1642 .with_label_values(&[])
1643 .observe(bytes.len() as f64);
1644
1645 Ok(Bytecode { bytes })
1646 }
1647}
1648
1649impl BcsHashable<'_> for BlobContent {}
1650
1651#[serde_as]
1653#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Allocative)]
1654pub struct BlobContent {
1655 blob_type: BlobType,
1657 #[debug(skip)]
1659 #[serde_as(as = "Arc<Bytes>")]
1660 bytes: Arc<Box<[u8]>>,
1661}
1662
1663impl BlobContent {
1664 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1666 let bytes = bytes.into();
1667 BlobContent {
1668 blob_type,
1669 bytes: Arc::new(bytes),
1670 }
1671 }
1672
1673 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1675 BlobContent::new(BlobType::Data, bytes)
1676 }
1677
1678 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1680 BlobContent {
1681 blob_type: BlobType::ContractBytecode,
1682 bytes: compressed_bytecode.compressed_bytes,
1683 }
1684 }
1685
1686 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1688 BlobContent {
1689 blob_type: BlobType::EvmBytecode,
1690 bytes: compressed_bytecode.compressed_bytes,
1691 }
1692 }
1693
1694 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1696 BlobContent {
1697 blob_type: BlobType::ServiceBytecode,
1698 bytes: compressed_bytecode.compressed_bytes,
1699 }
1700 }
1701
1702 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1704 let bytes = application_description.to_bytes();
1705 BlobContent::new(BlobType::ApplicationDescription, bytes)
1706 }
1707
1708 pub fn new_application_formats(bytes: impl Into<Box<[u8]>>) -> Self {
1711 BlobContent::new(BlobType::ApplicationFormats, bytes)
1712 }
1713
1714 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1716 BlobContent::new(BlobType::Committee, committee)
1717 }
1718
1719 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1721 let bytes = bcs::to_bytes(&chain_description)
1722 .expect("Serializing a ChainDescription should not fail!");
1723 BlobContent::new(BlobType::ChainDescription, bytes)
1724 }
1725
1726 pub fn bytes(&self) -> &[u8] {
1728 &self.bytes
1729 }
1730
1731 pub fn into_vec_or_clone(self) -> Vec<u8> {
1733 let bytes = Arc::unwrap_or_clone(self.bytes);
1734 bytes.into_vec()
1735 }
1736
1737 pub fn into_arc_bytes(self) -> Arc<Box<[u8]>> {
1739 self.bytes
1740 }
1741
1742 pub fn blob_type(&self) -> BlobType {
1744 self.blob_type
1745 }
1746}
1747
1748impl From<Blob> for BlobContent {
1749 fn from(blob: Blob) -> BlobContent {
1750 blob.content
1751 }
1752}
1753
1754impl From<Arc<Blob>> for BlobContent {
1755 fn from(blob: Arc<Blob>) -> BlobContent {
1756 blob.content().clone()
1757 }
1758}
1759
1760#[derive(Debug, Hash, PartialEq, Eq, Clone, Allocative)]
1762pub struct Blob {
1763 hash: CryptoHash,
1765 content: BlobContent,
1767}
1768
1769impl Blob {
1770 pub fn new(content: BlobContent) -> Self {
1772 let mut hash = CryptoHash::new(&content);
1773 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1774 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1775 .expect("to obtain an application description");
1776 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1777 hash.make_evm_compatible();
1778 }
1779 }
1780 Blob { hash, content }
1781 }
1782
1783 pub fn new_with_hash_unchecked(blob_id: BlobId, content: BlobContent) -> Self {
1785 Blob {
1786 hash: blob_id.hash,
1787 content,
1788 }
1789 }
1790
1791 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1793 let bytes = bytes.into();
1794 Blob {
1795 hash: blob_id.hash,
1796 content: BlobContent {
1797 blob_type: blob_id.blob_type,
1798 bytes: Arc::new(bytes),
1799 },
1800 }
1801 }
1802
1803 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1805 Blob::new(BlobContent::new_data(bytes))
1806 }
1807
1808 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1810 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1811 }
1812
1813 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1815 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1816 }
1817
1818 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1820 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1821 }
1822
1823 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1825 Blob::new(BlobContent::new_application_description(
1826 application_description,
1827 ))
1828 }
1829
1830 pub fn new_application_formats(bytes: impl Into<Box<[u8]>>) -> Self {
1833 Blob::new(BlobContent::new_application_formats(bytes))
1834 }
1835
1836 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1838 Blob::new(BlobContent::new_committee(committee))
1839 }
1840
1841 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1843 Blob::new(BlobContent::new_chain_description(chain_description))
1844 }
1845
1846 pub fn id(&self) -> BlobId {
1848 BlobId {
1849 hash: self.hash,
1850 blob_type: self.content.blob_type,
1851 }
1852 }
1853
1854 pub fn content(&self) -> &BlobContent {
1856 &self.content
1857 }
1858
1859 pub fn into_content(self) -> BlobContent {
1861 self.content
1862 }
1863
1864 pub fn bytes(&self) -> &[u8] {
1866 self.content.bytes()
1867 }
1868
1869 pub fn is_committee_blob(&self) -> bool {
1871 self.content().blob_type().is_committee_blob()
1872 }
1873
1874 pub fn is_checkpoint_blob(&self) -> bool {
1876 self.content().blob_type().is_checkpoint_blob()
1877 }
1878}
1879
1880impl Serialize for Blob {
1881 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1882 where
1883 S: Serializer,
1884 {
1885 if serializer.is_human_readable() {
1886 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1887 serializer.serialize_str(&hex::encode(blob_bytes))
1888 } else {
1889 BlobContent::serialize(self.content(), serializer)
1890 }
1891 }
1892}
1893
1894impl<'a> Deserialize<'a> for Blob {
1895 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1896 where
1897 D: Deserializer<'a>,
1898 {
1899 if deserializer.is_human_readable() {
1900 let s = String::deserialize(deserializer)?;
1901 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1902 let content: BlobContent =
1903 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1904
1905 Ok(Blob::new(content))
1906 } else {
1907 let content = BlobContent::deserialize(deserializer)?;
1908 Ok(Blob::new(content))
1909 }
1910 }
1911}
1912
1913impl BcsHashable<'_> for Blob {}
1914
1915#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1917pub struct Event {
1918 pub stream_id: StreamId,
1920 pub index: u32,
1922 #[debug(with = "hex_debug")]
1924 #[serde(with = "serde_bytes")]
1925 pub value: Vec<u8>,
1926}
1927
1928impl Event {
1929 pub fn id(&self, chain_id: ChainId) -> EventId {
1931 EventId {
1932 chain_id,
1933 stream_id: self.stream_id.clone(),
1934 index: self.index,
1935 }
1936 }
1937}
1938
1939#[derive(Clone, Debug, Serialize, Deserialize, WitType, WitLoad, WitStore)]
1941pub struct StreamUpdate {
1942 pub chain_id: ChainId,
1944 pub stream_id: StreamId,
1946 pub previous_index: u32,
1948 pub next_index: u32,
1950}
1951
1952impl StreamUpdate {
1953 pub fn new_indices(&self) -> impl Iterator<Item = u32> {
1955 self.previous_index..self.next_index
1956 }
1957}
1958
1959impl BcsHashable<'_> for Event {}
1960
1961#[derive(
1963 Clone,
1964 Debug,
1965 Default,
1966 PartialEq,
1967 serde::Serialize,
1968 serde::Deserialize,
1969 async_graphql::SimpleObject,
1970)]
1971pub struct MessagePolicy {
1972 pub blanket: BlanketMessagePolicy,
1974 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
1978 pub ignore_chain_ids: HashSet<ChainId>,
1980 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
1983 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
1986 pub process_events_from_application_ids: Option<HashSet<GenericApplicationId>>,
1989 pub never_reject_application_ids: HashSet<GenericApplicationId>,
1995}
1996
1997#[derive(
1999 Default,
2000 Copy,
2001 Clone,
2002 Debug,
2003 PartialEq,
2004 Eq,
2005 serde::Serialize,
2006 serde::Deserialize,
2007 async_graphql::Enum,
2008)]
2009#[cfg_attr(web, derive(tsify::Tsify), tsify(from_wasm_abi, into_wasm_abi))]
2010#[cfg_attr(any(web, not(target_arch = "wasm32")), derive(clap::ValueEnum))]
2011pub enum BlanketMessagePolicy {
2012 #[default]
2014 Accept,
2015 Reject,
2018 Ignore,
2021}
2022
2023impl MessagePolicy {
2024 #[instrument(level = "trace", skip(self))]
2026 pub fn is_ignore(&self) -> bool {
2027 matches!(self.blanket, BlanketMessagePolicy::Ignore)
2028 }
2029
2030 #[instrument(level = "trace", skip(self))]
2032 pub fn is_reject(&self) -> bool {
2033 matches!(self.blanket, BlanketMessagePolicy::Reject)
2034 }
2035
2036 #[instrument(level = "trace", skip(self))]
2040 pub fn ignores_origin(&self, origin: &ChainId) -> bool {
2041 self.is_ignore()
2042 || self.ignore_chain_ids.contains(origin)
2043 || self
2044 .restrict_chain_ids_to
2045 .as_ref()
2046 .is_some_and(|set| !set.contains(origin))
2047 }
2048}
2049
2050doc_scalar!(Bytecode, "A module bytecode (WebAssembly or EVM)");
2051doc_scalar!(Amount, "A non-negative amount of tokens.");
2052doc_scalar!(U128, "A 128-bit unsigned integer.");
2053doc_scalar!(
2054 Epoch,
2055 "A number identifying the configuration of the chain (aka the committee)"
2056);
2057doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
2058doc_scalar!(
2059 Timestamp,
2060 "A timestamp, in microseconds since the Unix epoch"
2061);
2062doc_scalar!(TimeDelta, "A duration in microseconds");
2063doc_scalar!(
2064 Round,
2065 "A number to identify successive attempts to decide a value in a consensus protocol."
2066);
2067doc_scalar!(
2068 ChainDescription,
2069 "Initial chain configuration and chain origin."
2070);
2071doc_scalar!(OracleResponse, "A record of a single oracle response.");
2072doc_scalar!(BlobContent, "A blob of binary data.");
2073doc_scalar!(
2074 Blob,
2075 "A blob of binary data, with its content-addressed blob ID."
2076);
2077doc_scalar!(ApplicationDescription, "Description of a user application");
2078
2079#[cfg(with_metrics)]
2080mod metrics {
2081 use std::sync::LazyLock;
2082
2083 use prometheus::HistogramVec;
2084
2085 use crate::prometheus_util::{
2086 exponential_bucket_interval, exponential_bucket_latencies, register_histogram_vec,
2087 };
2088
2089 pub static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
2091 register_histogram_vec(
2092 "bytecode_compression_latency",
2093 "Bytecode compression latency",
2094 &[],
2095 exponential_bucket_latencies(10.0),
2096 )
2097 });
2098
2099 pub static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
2101 register_histogram_vec(
2102 "bytecode_decompression_latency",
2103 "Bytecode decompression latency",
2104 &[],
2105 exponential_bucket_latencies(10.0),
2106 )
2107 });
2108
2109 pub static BYTECODE_DECOMPRESSED_SIZE_BYTES: LazyLock<HistogramVec> = LazyLock::new(|| {
2110 register_histogram_vec(
2111 "wasm_bytecode_decompressed_size_bytes",
2112 "Decompressed size in bytes of WASM bytecodes stored on-chain",
2113 &[],
2114 exponential_bucket_interval(10_000.0, 100_000_000.0),
2115 )
2116 });
2117}
2118
2119#[cfg(test)]
2120mod tests {
2121 use std::str::FromStr;
2122
2123 use alloy_primitives::U256;
2124
2125 use super::{Amount, ApplicationDescription, BlobContent};
2126 use crate::{
2127 crypto::CryptoHash,
2128 data_types::BlockHeight,
2129 identifiers::{BlobType, ChainId, ModuleId},
2130 vm::VmRuntime,
2131 };
2132
2133 #[test]
2134 fn non_canonical_btree_map_serializes_like_vec() {
2135 use std::collections::BTreeMap;
2136
2137 use super::NonCanonicalBTreeMap;
2138
2139 let map = NonCanonicalBTreeMap::from(BTreeMap::from([
2142 (1u32, 10u8),
2143 (256u32, 20u8),
2144 (2u32, 30u8),
2145 ]));
2146
2147 let entries = map
2150 .iter()
2151 .map(|(k, v)| (*k, *v))
2152 .collect::<Vec<(u32, u8)>>();
2153 assert_eq!(
2154 bcs::to_bytes(&map).unwrap(),
2155 bcs::to_bytes(&entries).unwrap()
2156 );
2157
2158 let canonical = map
2160 .iter()
2161 .map(|(k, v)| (*k, *v))
2162 .collect::<BTreeMap<u32, u8>>();
2163 assert_ne!(
2164 bcs::to_bytes(&map).unwrap(),
2165 bcs::to_bytes(&canonical).unwrap()
2166 );
2167
2168 let deserialized: NonCanonicalBTreeMap<u32, u8> =
2170 bcs::from_bytes(&bcs::to_bytes(&map).unwrap()).unwrap();
2171 assert_eq!(map, deserialized);
2172 }
2173
2174 #[test]
2175 fn canonical_btree_set_serializes_like_map() {
2176 use std::collections::{BTreeMap, BTreeSet};
2177
2178 use super::CanonicalBTreeSet;
2179
2180 let set = CanonicalBTreeSet::from(BTreeSet::from([1u32, 256u32, 2u32]));
2181
2182 let map = set.iter().map(|t| (*t, ())).collect::<BTreeMap<u32, ()>>();
2185 assert_eq!(bcs::to_bytes(&set).unwrap(), bcs::to_bytes(&map).unwrap());
2186
2187 let plain = set.iter().copied().collect::<BTreeSet<u32>>();
2190 assert_ne!(bcs::to_bytes(&set).unwrap(), bcs::to_bytes(&plain).unwrap());
2191
2192 let deserialized: CanonicalBTreeSet<u32> =
2194 bcs::from_bytes(&bcs::to_bytes(&set).unwrap()).unwrap();
2195 assert_eq!(set, deserialized);
2196 }
2197
2198 #[test]
2199 fn display_amount() {
2200 assert_eq!("1.", Amount::ONE.to_string());
2201 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
2202 assert_eq!(
2203 Amount(10_000_000_000_000_000_000),
2204 Amount::from_str("10").unwrap()
2205 );
2206 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
2207 assert_eq!(
2208 "1001.3",
2209 (Amount::from_str("1.1")
2210 .unwrap()
2211 .saturating_add(Amount::from_str("1_000.2").unwrap()))
2212 .to_string()
2213 );
2214 assert_eq!(
2215 " 1.00000000000000000000",
2216 format!("{:25.20}", Amount::ONE)
2217 );
2218 assert_eq!(
2219 "~+12.34~~",
2220 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
2221 );
2222 }
2223
2224 #[test]
2225 fn blob_content_serialization_deserialization() {
2226 let test_data = b"Hello, world!".as_slice();
2227 let original_blob = BlobContent::new(BlobType::Data, test_data);
2228
2229 let serialized = bcs::to_bytes(&original_blob).expect("Failed to serialize BlobContent");
2230 let deserialized: BlobContent =
2231 bcs::from_bytes(&serialized).expect("Failed to deserialize BlobContent");
2232 assert_eq!(original_blob, deserialized);
2233
2234 let serialized =
2235 serde_json::to_vec(&original_blob).expect("Failed to serialize BlobContent");
2236 let deserialized: BlobContent =
2237 serde_json::from_slice(&serialized).expect("Failed to deserialize BlobContent");
2238 assert_eq!(original_blob, deserialized);
2239 }
2240
2241 #[test]
2242 fn blob_content_hash_consistency() {
2243 let test_data = b"Hello, world!";
2244 let blob1 = BlobContent::new(BlobType::Data, test_data.as_slice());
2245 let blob2 = BlobContent::new(BlobType::Data, Vec::from(test_data.as_slice()));
2246
2247 let hash1 = crate::crypto::CryptoHash::new(&blob1);
2249 let hash2 = crate::crypto::CryptoHash::new(&blob2);
2250
2251 assert_eq!(hash1, hash2, "Hashes should be equal for same content");
2252 assert_eq!(blob1.bytes(), blob2.bytes(), "Byte content should be equal");
2253 }
2254
2255 #[test]
2256 fn test_conversion_amount_u256() {
2257 let value_amount = Amount::from_tokens(15656565652209004332);
2258 let value_u256: U256 = value_amount.into();
2259 let value_amount_rev = Amount::try_from(value_u256).expect("Failed conversion");
2260 assert_eq!(value_amount, value_amount_rev);
2261 }
2262
2263 #[test]
2272 fn application_description_serializes_module_id_as_hex_string() {
2273 let module_id = ModuleId::new(
2274 CryptoHash::test_hash("contract-bytecode"),
2275 CryptoHash::test_hash("service-bytecode"),
2276 VmRuntime::Wasm,
2277 );
2278 let description = ApplicationDescription {
2279 module_id,
2280 creator_chain_id: ChainId(CryptoHash::test_hash("chain")),
2281 block_height: BlockHeight(0),
2282 application_index: 0,
2283 parameters: Vec::new(),
2284 required_application_ids: Vec::new(),
2285 };
2286
2287 let value = serde_json::to_value(&description).unwrap();
2288 let module_id_value = value
2289 .get("module_id")
2290 .expect("`module_id` is the field name the explorer indexes into");
2291 let hex = module_id_value
2292 .as_str()
2293 .expect("`module_id` must serialize as a hex string in human-readable form");
2294 let roundtrip: ModuleId =
2295 serde_json::from_value(serde_json::Value::String(hex.to_owned())).unwrap();
2296 assert_eq!(roundtrip, module_id);
2297 }
2298}