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