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