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(any(target_arch = "wasm32", test))]
1622fn decompress_frames(
1623 mut compressed_bytes: &[u8],
1624 writer: &mut impl io::Write,
1625) -> Result<(), io::Error> {
1626 use ruzstd::decoding::{
1627 errors::{FrameDecoderError, ReadFrameHeaderError},
1628 StreamingDecoder,
1629 };
1630
1631 while !compressed_bytes.is_empty() {
1632 match StreamingDecoder::new(&mut compressed_bytes) {
1633 Ok(mut decoder) => {
1634 io::copy(&mut decoder, writer)?;
1635 }
1636 Err(FrameDecoderError::ReadFrameHeaderError(ReadFrameHeaderError::SkipFrame {
1637 length,
1638 ..
1639 })) => {
1640 compressed_bytes = compressed_bytes
1641 .get(length as usize..)
1642 .ok_or_else(|| io::Error::other("Truncated skippable frame"))?;
1643 }
1644 Err(error) => return Err(io::Error::other(error)),
1645 }
1646 }
1647
1648 Ok(())
1649}
1650
1651#[cfg(target_arch = "wasm32")]
1652impl CompressedBytecode {
1653 pub fn decompressed_size_at_most(
1655 compressed_bytes: &[u8],
1656 limit: u64,
1657 ) -> Result<bool, DecompressionError> {
1658 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1659 let mut writer = LimitedWriter::new(io::sink(), limit);
1660
1661 match decompress_frames(compressed_bytes, &mut writer) {
1662 Ok(()) => Ok(true),
1663 Err(error) => {
1664 error.downcast::<LimitedWriterError>()?;
1665 Ok(false)
1666 }
1667 }
1668 }
1669
1670 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1672 let mut bytes = Vec::new();
1673 decompress_frames(&self.compressed_bytes, &mut bytes)?;
1674
1675 Ok(Bytecode { bytes })
1676 }
1677}
1678
1679impl BcsHashable<'_> for BlobContent {}
1680
1681#[serde_as]
1683#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Allocative)]
1684pub struct BlobContent {
1685 blob_type: BlobType,
1687 #[debug(skip)]
1689 #[serde_as(as = "Arc<Bytes>")]
1690 bytes: Arc<Box<[u8]>>,
1691}
1692
1693impl BlobContent {
1694 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1696 let bytes = bytes.into();
1697 BlobContent {
1698 blob_type,
1699 bytes: Arc::new(bytes),
1700 }
1701 }
1702
1703 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1705 BlobContent::new(BlobType::Data, bytes)
1706 }
1707
1708 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1710 BlobContent {
1711 blob_type: BlobType::ContractBytecode,
1712 bytes: compressed_bytecode.compressed_bytes,
1713 }
1714 }
1715
1716 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1718 BlobContent {
1719 blob_type: BlobType::EvmBytecode,
1720 bytes: compressed_bytecode.compressed_bytes,
1721 }
1722 }
1723
1724 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1726 BlobContent {
1727 blob_type: BlobType::ServiceBytecode,
1728 bytes: compressed_bytecode.compressed_bytes,
1729 }
1730 }
1731
1732 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1734 let bytes = application_description.to_bytes();
1735 BlobContent::new(BlobType::ApplicationDescription, bytes)
1736 }
1737
1738 pub fn new_application_formats(bytes: impl Into<Box<[u8]>>) -> Self {
1741 BlobContent::new(BlobType::ApplicationFormats, bytes)
1742 }
1743
1744 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1746 BlobContent::new(BlobType::Committee, committee)
1747 }
1748
1749 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1751 let bytes = bcs::to_bytes(&chain_description)
1752 .expect("Serializing a ChainDescription should not fail!");
1753 BlobContent::new(BlobType::ChainDescription, bytes)
1754 }
1755
1756 pub fn bytes(&self) -> &[u8] {
1758 &self.bytes
1759 }
1760
1761 pub fn into_vec_or_clone(self) -> Vec<u8> {
1763 let bytes = Arc::unwrap_or_clone(self.bytes);
1764 bytes.into_vec()
1765 }
1766
1767 pub fn into_arc_bytes(self) -> Arc<Box<[u8]>> {
1769 self.bytes
1770 }
1771
1772 pub fn blob_type(&self) -> BlobType {
1774 self.blob_type
1775 }
1776}
1777
1778impl From<Blob> for BlobContent {
1779 fn from(blob: Blob) -> BlobContent {
1780 blob.content
1781 }
1782}
1783
1784impl From<Arc<Blob>> for BlobContent {
1785 fn from(blob: Arc<Blob>) -> BlobContent {
1786 blob.content().clone()
1787 }
1788}
1789
1790#[derive(Debug, Hash, PartialEq, Eq, Clone, Allocative)]
1792pub struct Blob {
1793 hash: CryptoHash,
1795 content: BlobContent,
1797}
1798
1799impl Blob {
1800 pub fn new(content: BlobContent) -> Self {
1802 let mut hash = CryptoHash::new(&content);
1803 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1804 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1805 .expect("to obtain an application description");
1806 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1807 hash.make_evm_compatible();
1808 }
1809 }
1810 Blob { hash, content }
1811 }
1812
1813 pub fn new_with_hash_unchecked(blob_id: BlobId, content: BlobContent) -> Self {
1815 Blob {
1816 hash: blob_id.hash,
1817 content,
1818 }
1819 }
1820
1821 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1823 let bytes = bytes.into();
1824 Blob {
1825 hash: blob_id.hash,
1826 content: BlobContent {
1827 blob_type: blob_id.blob_type,
1828 bytes: Arc::new(bytes),
1829 },
1830 }
1831 }
1832
1833 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1835 Blob::new(BlobContent::new_data(bytes))
1836 }
1837
1838 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1840 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1841 }
1842
1843 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1845 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1846 }
1847
1848 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1850 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1851 }
1852
1853 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1855 Blob::new(BlobContent::new_application_description(
1856 application_description,
1857 ))
1858 }
1859
1860 pub fn new_application_formats(bytes: impl Into<Box<[u8]>>) -> Self {
1863 Blob::new(BlobContent::new_application_formats(bytes))
1864 }
1865
1866 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1868 Blob::new(BlobContent::new_committee(committee))
1869 }
1870
1871 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1873 Blob::new(BlobContent::new_chain_description(chain_description))
1874 }
1875
1876 pub fn id(&self) -> BlobId {
1878 BlobId {
1879 hash: self.hash,
1880 blob_type: self.content.blob_type,
1881 }
1882 }
1883
1884 pub fn content(&self) -> &BlobContent {
1886 &self.content
1887 }
1888
1889 pub fn into_content(self) -> BlobContent {
1891 self.content
1892 }
1893
1894 pub fn bytes(&self) -> &[u8] {
1896 self.content.bytes()
1897 }
1898
1899 pub fn is_committee_blob(&self) -> bool {
1901 self.content().blob_type().is_committee_blob()
1902 }
1903
1904 pub fn is_checkpoint_blob(&self) -> bool {
1906 self.content().blob_type().is_checkpoint_blob()
1907 }
1908}
1909
1910impl Serialize for Blob {
1911 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1912 where
1913 S: Serializer,
1914 {
1915 if serializer.is_human_readable() {
1916 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1917 serializer.serialize_str(&hex::encode(blob_bytes))
1918 } else {
1919 BlobContent::serialize(self.content(), serializer)
1920 }
1921 }
1922}
1923
1924impl<'a> Deserialize<'a> for Blob {
1925 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1926 where
1927 D: Deserializer<'a>,
1928 {
1929 if deserializer.is_human_readable() {
1930 let s = String::deserialize(deserializer)?;
1931 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1932 let content: BlobContent =
1933 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1934
1935 Ok(Blob::new(content))
1936 } else {
1937 let content = BlobContent::deserialize(deserializer)?;
1938 Ok(Blob::new(content))
1939 }
1940 }
1941}
1942
1943impl BcsHashable<'_> for Blob {}
1944
1945#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1947pub struct Event {
1948 pub stream_id: StreamId,
1950 pub index: u32,
1952 #[debug(with = "hex_debug")]
1954 #[serde(with = "serde_bytes")]
1955 pub value: Vec<u8>,
1956}
1957
1958impl Event {
1959 pub fn id(&self, chain_id: ChainId) -> EventId {
1961 EventId {
1962 chain_id,
1963 stream_id: self.stream_id.clone(),
1964 index: self.index,
1965 }
1966 }
1967}
1968
1969#[derive(Clone, Debug, Serialize, Deserialize, WitType, WitLoad, WitStore)]
1971pub struct StreamUpdate {
1972 pub chain_id: ChainId,
1974 pub stream_id: StreamId,
1976 pub previous_index: u32,
1978 pub first_index: u32,
1982 pub next_index: u32,
1984}
1985
1986impl StreamUpdate {
1987 pub fn new_indices(&self) -> impl Iterator<Item = u32> {
1989 self.previous_index..self.next_index
1990 }
1991}
1992
1993impl BcsHashable<'_> for Event {}
1994
1995#[derive(
1997 Clone,
1998 Debug,
1999 Default,
2000 PartialEq,
2001 serde::Serialize,
2002 serde::Deserialize,
2003 async_graphql::SimpleObject,
2004)]
2005pub struct MessagePolicy {
2006 pub blanket: BlanketMessagePolicy,
2008 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
2012 pub ignore_chain_ids: HashSet<ChainId>,
2014 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
2017 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
2020 pub process_events_from_application_ids: Option<HashSet<GenericApplicationId>>,
2023 pub never_reject_application_ids: HashSet<GenericApplicationId>,
2029}
2030
2031#[derive(
2033 Default,
2034 Copy,
2035 Clone,
2036 Debug,
2037 PartialEq,
2038 Eq,
2039 serde::Serialize,
2040 serde::Deserialize,
2041 async_graphql::Enum,
2042)]
2043#[cfg_attr(web, derive(tsify::Tsify), tsify(from_wasm_abi, into_wasm_abi))]
2044#[cfg_attr(any(web, not(target_arch = "wasm32")), derive(clap::ValueEnum))]
2045pub enum BlanketMessagePolicy {
2046 #[default]
2048 Accept,
2049 Reject,
2052 Ignore,
2055}
2056
2057impl MessagePolicy {
2058 #[instrument(level = "trace", skip(self))]
2060 pub fn is_ignore(&self) -> bool {
2061 matches!(self.blanket, BlanketMessagePolicy::Ignore)
2062 }
2063
2064 #[instrument(level = "trace", skip(self))]
2066 pub fn is_reject(&self) -> bool {
2067 matches!(self.blanket, BlanketMessagePolicy::Reject)
2068 }
2069
2070 #[instrument(level = "trace", skip(self))]
2074 pub fn ignores_origin(&self, origin: &ChainId) -> bool {
2075 self.is_ignore()
2076 || self.ignore_chain_ids.contains(origin)
2077 || self
2078 .restrict_chain_ids_to
2079 .as_ref()
2080 .is_some_and(|set| !set.contains(origin))
2081 }
2082}
2083
2084doc_scalar!(Bytecode, "A module bytecode (WebAssembly or EVM)");
2085doc_scalar!(Amount, "A non-negative amount of tokens.");
2086doc_scalar!(U128, "A 128-bit unsigned integer.");
2087doc_scalar!(
2088 Epoch,
2089 "A number identifying the configuration of the chain (aka the committee)"
2090);
2091doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
2092doc_scalar!(
2093 Timestamp,
2094 "A timestamp, in microseconds since the Unix epoch"
2095);
2096doc_scalar!(TimeDelta, "A duration in microseconds");
2097doc_scalar!(
2098 Round,
2099 "A number to identify successive attempts to decide a value in a consensus protocol."
2100);
2101doc_scalar!(
2102 ChainDescription,
2103 "Initial chain configuration and chain origin."
2104);
2105doc_scalar!(OracleResponse, "A record of a single oracle response.");
2106doc_scalar!(BlobContent, "A blob of binary data.");
2107doc_scalar!(
2108 Blob,
2109 "A blob of binary data, with its content-addressed blob ID."
2110);
2111doc_scalar!(ApplicationDescription, "Description of a user application");
2112
2113#[cfg(with_metrics)]
2114pub(crate) mod metrics {
2115 use prometheus::HistogramVec;
2116
2117 use crate::prometheus_util::{
2118 exponential_bucket_interval, exponential_bucket_latencies, register_histogram_vec,
2119 };
2120
2121 crate::declare_metrics! {
2122 pub static BYTECODE_COMPRESSION_LATENCY: HistogramVec =
2124 register_histogram_vec(
2125 "bytecode_compression_latency",
2126 "Bytecode compression latency",
2127 &[],
2128 exponential_bucket_latencies(10.0),
2129 );
2130
2131 pub static BYTECODE_DECOMPRESSION_LATENCY: HistogramVec =
2133 register_histogram_vec(
2134 "bytecode_decompression_latency",
2135 "Bytecode decompression latency",
2136 &[],
2137 exponential_bucket_latencies(10.0),
2138 );
2139
2140 pub static BYTECODE_DECOMPRESSED_SIZE_BYTES: HistogramVec =
2141 register_histogram_vec(
2142 "wasm_bytecode_decompressed_size_bytes",
2143 "Decompressed size in bytes of WASM bytecodes stored on-chain",
2144 &[],
2145 exponential_bucket_interval(10_000.0, 100_000_000.0),
2146 );
2147 }
2148}
2149
2150#[cfg(test)]
2151mod tests {
2152 use std::str::FromStr;
2153
2154 use alloy_primitives::U256;
2155
2156 use super::{Amount, ApplicationDescription, BlobContent};
2157 use crate::{
2158 crypto::CryptoHash,
2159 data_types::BlockHeight,
2160 identifiers::{BlobType, ChainId, ModuleId},
2161 vm::VmRuntime,
2162 };
2163
2164 #[test]
2165 fn non_canonical_btree_map_serializes_like_vec() {
2166 use std::collections::BTreeMap;
2167
2168 use super::NonCanonicalBTreeMap;
2169
2170 let map = NonCanonicalBTreeMap::from(BTreeMap::from([
2173 (1u32, 10u8),
2174 (256u32, 20u8),
2175 (2u32, 30u8),
2176 ]));
2177
2178 let entries = map
2181 .iter()
2182 .map(|(k, v)| (*k, *v))
2183 .collect::<Vec<(u32, u8)>>();
2184 assert_eq!(
2185 bcs::to_bytes(&map).unwrap(),
2186 bcs::to_bytes(&entries).unwrap()
2187 );
2188
2189 let canonical = map
2191 .iter()
2192 .map(|(k, v)| (*k, *v))
2193 .collect::<BTreeMap<u32, u8>>();
2194 assert_ne!(
2195 bcs::to_bytes(&map).unwrap(),
2196 bcs::to_bytes(&canonical).unwrap()
2197 );
2198
2199 let deserialized: NonCanonicalBTreeMap<u32, u8> =
2201 bcs::from_bytes(&bcs::to_bytes(&map).unwrap()).unwrap();
2202 assert_eq!(map, deserialized);
2203 }
2204
2205 #[test]
2206 fn canonical_btree_set_serializes_like_map() {
2207 use std::collections::{BTreeMap, BTreeSet};
2208
2209 use super::CanonicalBTreeSet;
2210
2211 let set = CanonicalBTreeSet::from(BTreeSet::from([1u32, 256u32, 2u32]));
2212
2213 let map = set.iter().map(|t| (*t, ())).collect::<BTreeMap<u32, ()>>();
2216 assert_eq!(bcs::to_bytes(&set).unwrap(), bcs::to_bytes(&map).unwrap());
2217
2218 let plain = set.iter().copied().collect::<BTreeSet<u32>>();
2221 assert_ne!(bcs::to_bytes(&set).unwrap(), bcs::to_bytes(&plain).unwrap());
2222
2223 let deserialized: CanonicalBTreeSet<u32> =
2225 bcs::from_bytes(&bcs::to_bytes(&set).unwrap()).unwrap();
2226 assert_eq!(set, deserialized);
2227 }
2228
2229 #[test]
2230 fn display_amount() {
2231 assert_eq!("1.", Amount::ONE.to_string());
2232 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
2233 assert_eq!(
2234 Amount(10_000_000_000_000_000_000),
2235 Amount::from_str("10").unwrap()
2236 );
2237 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
2238 assert_eq!(
2239 "1001.3",
2240 (Amount::from_str("1.1")
2241 .unwrap()
2242 .saturating_add(Amount::from_str("1_000.2").unwrap()))
2243 .to_string()
2244 );
2245 assert_eq!(
2246 " 1.00000000000000000000",
2247 format!("{:25.20}", Amount::ONE)
2248 );
2249 assert_eq!(
2250 "~+12.34~~",
2251 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
2252 );
2253 }
2254
2255 #[test]
2256 fn blob_content_serialization_deserialization() {
2257 let test_data = b"Hello, world!".as_slice();
2258 let original_blob = BlobContent::new(BlobType::Data, test_data);
2259
2260 let serialized = bcs::to_bytes(&original_blob).expect("Failed to serialize BlobContent");
2261 let deserialized: BlobContent =
2262 bcs::from_bytes(&serialized).expect("Failed to deserialize BlobContent");
2263 assert_eq!(original_blob, deserialized);
2264
2265 let serialized =
2266 serde_json::to_vec(&original_blob).expect("Failed to serialize BlobContent");
2267 let deserialized: BlobContent =
2268 serde_json::from_slice(&serialized).expect("Failed to deserialize BlobContent");
2269 assert_eq!(original_blob, deserialized);
2270 }
2271
2272 #[test]
2273 fn blob_content_hash_consistency() {
2274 let test_data = b"Hello, world!";
2275 let blob1 = BlobContent::new(BlobType::Data, test_data.as_slice());
2276 let blob2 = BlobContent::new(BlobType::Data, Vec::from(test_data.as_slice()));
2277
2278 let hash1 = crate::crypto::CryptoHash::new(&blob1);
2280 let hash2 = crate::crypto::CryptoHash::new(&blob2);
2281
2282 assert_eq!(hash1, hash2, "Hashes should be equal for same content");
2283 assert_eq!(blob1.bytes(), blob2.bytes(), "Byte content should be equal");
2284 }
2285
2286 #[test]
2287 fn test_conversion_amount_u256() {
2288 let value_amount = Amount::from_tokens(15656565652209004332);
2289 let value_u256: U256 = value_amount.into();
2290 let value_amount_rev = Amount::try_from(value_u256).expect("Failed conversion");
2291 assert_eq!(value_amount, value_amount_rev);
2292 }
2293
2294 #[test]
2303 fn application_description_serializes_module_id_as_hex_string() {
2304 let module_id = ModuleId::new(
2305 CryptoHash::test_hash("contract-bytecode"),
2306 CryptoHash::test_hash("service-bytecode"),
2307 VmRuntime::Wasm,
2308 );
2309 let description = ApplicationDescription {
2310 module_id,
2311 creator_chain_id: ChainId(CryptoHash::test_hash("chain")),
2312 block_height: BlockHeight(0),
2313 application_index: 0,
2314 parameters: Vec::new(),
2315 required_application_ids: Vec::new(),
2316 };
2317
2318 let value = serde_json::to_value(&description).unwrap();
2319 let module_id_value = value
2320 .get("module_id")
2321 .expect("`module_id` is the field name the explorer indexes into");
2322 let hex = module_id_value
2323 .as_str()
2324 .expect("`module_id` must serialize as a hex string in human-readable form");
2325 let roundtrip: ModuleId =
2326 serde_json::from_value(serde_json::Value::String(hex.to_owned())).unwrap();
2327 assert_eq!(roundtrip, module_id);
2328 }
2329
2330 #[cfg(not(target_arch = "wasm32"))]
2334 mod compression {
2335 use std::{io, sync::Arc};
2336
2337 use super::super::{decompress_frames, Bytecode, CompressedBytecode};
2338 use crate::limited_writer::{LimitedWriter, LimitedWriterError};
2339
2340 fn multi_frame_bytecode() -> (CompressedBytecode, Vec<u8>) {
2346 let first = vec![b'a'; 100_000];
2347 let second = vec![b'b'; 50_000];
2348
2349 let mut compressed_bytes = Bytecode::new(first.clone())
2350 .compress()
2351 .compressed_bytes
2352 .to_vec();
2353 compressed_bytes
2354 .extend_from_slice(&Bytecode::new(second.clone()).compress().compressed_bytes);
2355 compressed_bytes.extend_from_slice(&[0x50, 0x2a, 0x4d, 0x18, 4, 0, 0, 0, 1, 2, 3, 4]);
2357
2358 let compressed_bytecode = CompressedBytecode {
2359 compressed_bytes: Arc::new(compressed_bytes.into_boxed_slice()),
2360 };
2361
2362 (compressed_bytecode, [first, second].concat())
2363 }
2364
2365 #[test]
2366 fn all_frames_are_decompressed() {
2367 let (compressed_bytecode, expected) = multi_frame_bytecode();
2368
2369 assert_eq!(compressed_bytecode.decompress().unwrap().bytes, expected);
2370
2371 let mut bytes = Vec::new();
2372 decompress_frames(&compressed_bytecode.compressed_bytes, &mut bytes).unwrap();
2373 assert_eq!(bytes, expected);
2374 }
2375
2376 #[test]
2377 fn all_frames_count_towards_the_size_limit() {
2378 let (compressed_bytecode, expected) = multi_frame_bytecode();
2379 let compressed_bytes = &**compressed_bytecode.compressed_bytes;
2380 let size = expected.len();
2381
2382 for limit in [size / 2, size - 1, size, size + 1] {
2383 let mut writer = LimitedWriter::new(io::sink(), limit);
2384 let within_limit = match decompress_frames(compressed_bytes, &mut writer) {
2385 Ok(()) => true,
2386 Err(error) => {
2387 error.downcast::<LimitedWriterError>().unwrap();
2388 false
2389 }
2390 };
2391
2392 assert_eq!(within_limit, limit >= size);
2393 assert_eq!(
2394 CompressedBytecode::decompressed_size_at_most(
2395 compressed_bytes,
2396 u64::try_from(limit).unwrap()
2397 )
2398 .unwrap(),
2399 within_limit
2400 );
2401 }
2402 }
2403
2404 #[test]
2405 fn trailing_garbage_is_rejected() {
2406 let (compressed_bytecode, _) = multi_frame_bytecode();
2407 let mut compressed_bytes = compressed_bytecode.compressed_bytes.to_vec();
2408 compressed_bytes.extend_from_slice(b"not a zstd frame");
2409
2410 let mut bytes = Vec::new();
2411 assert!(decompress_frames(&compressed_bytes, &mut bytes).is_err());
2412
2413 let compressed_bytecode = CompressedBytecode {
2414 compressed_bytes: Arc::new(compressed_bytes.into_boxed_slice()),
2415 };
2416 assert!(compressed_bytecode.decompress().is_err());
2417 }
2418 }
2419}