1#[cfg(with_testing)]
8use std::ops;
9use std::{
10 collections::{BTreeMap, BTreeSet, HashSet},
11 fmt::{self, Display},
12 fs,
13 hash::Hash,
14 io, iter,
15 num::ParseIntError,
16 str::FromStr,
17 sync::Arc,
18};
19
20use allocative::{Allocative, Visitor};
21use alloy_primitives::U256;
22use async_graphql::{InputObject, SimpleObject};
23use custom_debug_derive::Debug;
24use linera_witty::{WitLoad, WitStore, WitType};
25use serde::{Deserialize, Deserializer, Serialize, Serializer};
26use serde_with::{serde_as, Bytes};
27use thiserror::Error;
28use tracing::instrument;
29
30#[cfg(with_metrics)]
31use crate::prometheus_util::MeasureLatency as _;
32use crate::{
33 crypto::{BcsHashable, CryptoError, CryptoHash},
34 doc_scalar, hex_debug, http,
35 identifiers::{
36 ApplicationId, BlobId, BlobType, ChainId, EventId, GenericApplicationId, 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
358impl TryFrom<U256> for Amount {
359 type Error = ArithmeticError;
360
361 fn try_from(value: U256) -> Result<Amount, ArithmeticError> {
362 let value: u128 = value.try_into().map_err(|_| ArithmeticError::Overflow)?;
363 Ok(Amount::from_attos(value))
364 }
365}
366
367#[derive(
370 Clone,
371 Copy,
372 Debug,
373 Default,
374 Eq,
375 Ord,
376 PartialEq,
377 PartialOrd,
378 Hash,
379 derive_more::Display,
380 derive_more::Deref,
381 derive_more::DerefMut,
382 derive_more::FromStr,
383)]
384pub struct U128(pub u128);
385
386impl Serialize for U128 {
387 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
388 where
389 S: Serializer,
390 {
391 if serializer.is_human_readable() {
392 serializer.serialize_str(&self.0.to_string())
393 } else {
394 self.0.serialize(serializer)
395 }
396 }
397}
398
399impl<'de> Deserialize<'de> for U128 {
400 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
401 where
402 D: Deserializer<'de>,
403 {
404 if deserializer.is_human_readable() {
405 let s = String::deserialize(deserializer)?;
406 s.parse().map(U128).map_err(serde::de::Error::custom)
407 } else {
408 u128::deserialize(deserializer).map(U128)
409 }
410 }
411}
412
413#[derive(
415 Eq,
416 PartialEq,
417 Ord,
418 PartialOrd,
419 Copy,
420 Clone,
421 Hash,
422 Default,
423 Debug,
424 Serialize,
425 Deserialize,
426 WitType,
427 WitLoad,
428 WitStore,
429 Allocative,
430)]
431#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
432pub struct BlockHeight(pub u64);
433
434#[derive(
436 Eq,
437 PartialEq,
438 Ord,
439 PartialOrd,
440 Copy,
441 Clone,
442 Hash,
443 Default,
444 Debug,
445 Serialize,
446 Deserialize,
447 Allocative,
448)]
449#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
450pub enum Round {
451 #[default]
453 Fast,
454 MultiLeader(u32),
456 SingleLeader(u32),
458 Validator(u32),
460}
461
462#[derive(
464 Eq,
465 PartialEq,
466 Ord,
467 PartialOrd,
468 Copy,
469 Clone,
470 Hash,
471 Default,
472 Debug,
473 Serialize,
474 Deserialize,
475 WitType,
476 WitLoad,
477 WitStore,
478 Allocative,
479)]
480pub struct TimeDelta(u64);
481
482impl TimeDelta {
483 pub const fn from_micros(micros: u64) -> Self {
485 TimeDelta(micros)
486 }
487
488 pub const fn from_millis(millis: u64) -> Self {
490 TimeDelta(millis.saturating_mul(1_000))
491 }
492
493 pub const fn from_secs(secs: u64) -> Self {
495 TimeDelta(secs.saturating_mul(1_000_000))
496 }
497
498 pub fn from_duration(duration: Duration) -> Self {
500 TimeDelta(u64::try_from(duration.as_micros()).unwrap_or(u64::MAX))
501 }
502
503 pub const fn as_micros(&self) -> u64 {
505 self.0
506 }
507
508 pub const fn as_duration(&self) -> Duration {
510 Duration::from_micros(self.as_micros())
511 }
512}
513
514#[derive(
516 Eq,
517 PartialEq,
518 Ord,
519 PartialOrd,
520 Copy,
521 Clone,
522 Hash,
523 Default,
524 Debug,
525 Serialize,
526 Deserialize,
527 WitType,
528 WitLoad,
529 WitStore,
530 Allocative,
531)]
532pub struct Timestamp(u64);
533
534impl Timestamp {
535 pub fn now() -> Timestamp {
537 Timestamp(
538 SystemTime::UNIX_EPOCH
539 .elapsed()
540 .expect("system time should be after Unix epoch")
541 .as_micros()
542 .try_into()
543 .unwrap_or(u64::MAX),
544 )
545 }
546
547 pub const fn micros(&self) -> u64 {
549 self.0
550 }
551
552 pub const fn delta_since(&self, other: Timestamp) -> TimeDelta {
555 TimeDelta::from_micros(self.0.saturating_sub(other.0))
556 }
557
558 pub const fn duration_since(&self, other: Timestamp) -> Duration {
561 Duration::from_micros(self.0.saturating_sub(other.0))
562 }
563
564 pub const fn saturating_add(&self, duration: TimeDelta) -> Timestamp {
566 Timestamp(self.0.saturating_add(duration.0))
567 }
568
569 pub const fn saturating_sub(&self, duration: TimeDelta) -> Timestamp {
571 Timestamp(self.0.saturating_sub(duration.0))
572 }
573
574 pub const fn saturating_sub_micros(&self, micros: u64) -> Timestamp {
577 Timestamp(self.0.saturating_sub(micros))
578 }
579}
580
581impl From<u64> for Timestamp {
582 fn from(t: u64) -> Timestamp {
583 Timestamp(t)
584 }
585}
586
587impl Display for Timestamp {
588 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
589 if let Some(date_time) = chrono::DateTime::from_timestamp(
590 (self.0 / 1_000_000) as i64,
591 ((self.0 % 1_000_000) * 1_000) as u32,
592 ) {
593 return date_time.naive_utc().fmt(f);
594 }
595 self.0.fmt(f)
596 }
597}
598
599impl FromStr for Timestamp {
600 type Err = chrono::ParseError;
601
602 fn from_str(s: &str) -> Result<Self, Self::Err> {
603 let naive = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S")
604 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S"))?;
605 let micros = naive
606 .and_utc()
607 .timestamp_micros()
608 .try_into()
609 .unwrap_or(u64::MAX);
610 Ok(Timestamp(micros))
611 }
612}
613
614#[derive(
617 Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, WitLoad, WitStore, WitType,
618)]
619pub struct Resources {
620 pub wasm_fuel: u64,
622 pub evm_fuel: u64,
624 pub read_operations: u32,
626 pub write_operations: u32,
628 pub bytes_runtime: u32,
630 pub bytes_to_read: u32,
632 pub bytes_to_write: u32,
634 pub blobs_to_read: u32,
636 pub blobs_to_publish: u32,
638 pub blob_bytes_to_read: u32,
640 pub blob_bytes_to_publish: u32,
642 pub messages: u32,
644 pub message_size: u32,
647 pub storage_size_delta: u32,
649 pub service_as_oracle_queries: u32,
651 pub http_requests: u32,
653 }
656
657#[derive(Clone, Debug, Deserialize, Serialize, WitLoad, WitType)]
659#[cfg_attr(with_testing, derive(Eq, PartialEq, WitStore))]
660#[witty_specialize_with(Message = Vec<u8>)]
661pub struct SendMessageRequest<Message> {
662 pub destination: ChainId,
664 pub authenticated: bool,
666 pub is_tracked: bool,
668 pub grant: Resources,
670 pub message: Message,
672}
673
674#[derive(Debug, Error)]
676#[allow(missing_docs)]
677pub enum ArithmeticError {
678 #[error("Number overflow")]
679 Overflow,
680 #[error("Number underflow")]
681 Underflow,
682}
683
684macro_rules! impl_wrapped_number {
685 ($name:ident, $wrapped:ident) => {
686 impl $name {
687 pub const ZERO: Self = Self(0);
689
690 pub const MAX: Self = Self($wrapped::MAX);
692
693 pub fn try_add(self, other: Self) -> Result<Self, ArithmeticError> {
695 let val = self
696 .0
697 .checked_add(other.0)
698 .ok_or(ArithmeticError::Overflow)?;
699 Ok(Self(val))
700 }
701
702 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
704 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
705 Ok(Self(val))
706 }
707
708 pub const fn saturating_add(self, other: Self) -> Self {
710 let val = self.0.saturating_add(other.0);
711 Self(val)
712 }
713
714 pub fn try_sub(self, other: Self) -> Result<Self, ArithmeticError> {
716 let val = self
717 .0
718 .checked_sub(other.0)
719 .ok_or(ArithmeticError::Underflow)?;
720 Ok(Self(val))
721 }
722
723 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
725 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
726 Ok(Self(val))
727 }
728
729 pub const fn saturating_sub(self, other: Self) -> Self {
731 let val = self.0.saturating_sub(other.0);
732 Self(val)
733 }
734
735 pub fn abs_diff(self, other: Self) -> Self {
737 Self(self.0.abs_diff(other.0))
738 }
739
740 pub fn try_add_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
742 self.0 = self
743 .0
744 .checked_add(other.0)
745 .ok_or(ArithmeticError::Overflow)?;
746 Ok(())
747 }
748
749 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
751 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
752 Ok(())
753 }
754
755 pub const fn saturating_add_assign(&mut self, other: Self) {
757 self.0 = self.0.saturating_add(other.0);
758 }
759
760 pub fn try_sub_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
762 self.0 = self
763 .0
764 .checked_sub(other.0)
765 .ok_or(ArithmeticError::Underflow)?;
766 Ok(())
767 }
768
769 pub fn saturating_div(&self, other: $wrapped) -> Self {
771 Self(self.0.checked_div(other).unwrap_or($wrapped::MAX))
772 }
773
774 pub const fn saturating_mul(&self, other: $wrapped) -> Self {
776 Self(self.0.saturating_mul(other))
777 }
778
779 pub fn try_mul(self, other: $wrapped) -> Result<Self, ArithmeticError> {
781 let val = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
782 Ok(Self(val))
783 }
784
785 pub fn try_mul_assign(&mut self, other: $wrapped) -> Result<(), ArithmeticError> {
787 self.0 = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
788 Ok(())
789 }
790 }
791
792 impl From<$name> for $wrapped {
793 fn from(value: $name) -> Self {
794 value.0
795 }
796 }
797
798 #[cfg(with_testing)]
800 impl From<$wrapped> for $name {
801 fn from(value: $wrapped) -> Self {
802 Self(value)
803 }
804 }
805
806 #[cfg(with_testing)]
807 impl ops::Add for $name {
808 type Output = Self;
809
810 fn add(self, other: Self) -> Self {
811 Self(self.0 + other.0)
812 }
813 }
814
815 #[cfg(with_testing)]
816 impl ops::Sub for $name {
817 type Output = Self;
818
819 fn sub(self, other: Self) -> Self {
820 Self(self.0 - other.0)
821 }
822 }
823
824 #[cfg(with_testing)]
825 impl ops::Mul<$wrapped> for $name {
826 type Output = Self;
827
828 fn mul(self, other: $wrapped) -> Self {
829 Self(self.0 * other)
830 }
831 }
832 };
833}
834
835impl TryFrom<BlockHeight> for usize {
836 type Error = ArithmeticError;
837
838 fn try_from(height: BlockHeight) -> Result<usize, ArithmeticError> {
839 usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)
840 }
841}
842
843impl_wrapped_number!(Amount, u128);
844impl_wrapped_number!(U128, u128);
845impl_wrapped_number!(BlockHeight, u64);
846impl_wrapped_number!(TimeDelta, u64);
847
848impl Display for Amount {
849 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
850 let places = Amount::DECIMAL_PLACES as usize;
852 let min_digits = places + 1;
853 let decimals = format!("{:0min_digits$}", self.0);
854 let integer_part = &decimals[..(decimals.len() - places)];
855 let fractional_part = decimals[(decimals.len() - places)..].trim_end_matches('0');
856
857 let precision = f.precision().unwrap_or(0).max(fractional_part.len());
859 let sign = if f.sign_plus() && self.0 > 0 { "+" } else { "" };
860 let pad_width = f.width().map_or(0, |w| {
862 w.saturating_sub(precision)
863 .saturating_sub(sign.len() + integer_part.len() + 1)
864 });
865 let left_pad = match f.align() {
866 None | Some(fmt::Alignment::Right) => pad_width,
867 Some(fmt::Alignment::Center) => pad_width / 2,
868 Some(fmt::Alignment::Left) => 0,
869 };
870
871 for _ in 0..left_pad {
872 write!(f, "{}", f.fill())?;
873 }
874 write!(f, "{sign}{integer_part}.{fractional_part:0<precision$}")?;
875 for _ in left_pad..pad_width {
876 write!(f, "{}", f.fill())?;
877 }
878 Ok(())
879 }
880}
881
882#[derive(Error, Debug)]
883#[allow(missing_docs)]
884pub enum ParseAmountError {
885 #[error("cannot parse amount")]
886 Parse,
887 #[error("cannot represent amount: number too high")]
888 TooHigh,
889 #[error("cannot represent amount: too many decimal places after the point")]
890 TooManyDigits,
891}
892
893impl FromStr for Amount {
894 type Err = ParseAmountError;
895
896 fn from_str(src: &str) -> Result<Self, Self::Err> {
897 let mut result: u128 = 0;
898 let mut decimals: Option<u8> = None;
899 let mut chars = src.trim().chars().peekable();
900 if chars.peek() == Some(&'+') {
901 chars.next();
902 }
903 for char in chars {
904 match char {
905 '_' => {}
906 '.' if decimals.is_some() => return Err(ParseAmountError::Parse),
907 '.' => decimals = Some(Amount::DECIMAL_PLACES),
908 char => {
909 let digit = u128::from(char.to_digit(10).ok_or(ParseAmountError::Parse)?);
910 if let Some(d) = &mut decimals {
911 *d = d.checked_sub(1).ok_or(ParseAmountError::TooManyDigits)?;
912 }
913 result = result
914 .checked_mul(10)
915 .and_then(|r| r.checked_add(digit))
916 .ok_or(ParseAmountError::TooHigh)?;
917 }
918 }
919 }
920 result = result
921 .checked_mul(10u128.pow(decimals.unwrap_or(Amount::DECIMAL_PLACES) as u32))
922 .ok_or(ParseAmountError::TooHigh)?;
923 Ok(Amount(result))
924 }
925}
926
927impl Display for BlockHeight {
928 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
929 self.0.fmt(f)
930 }
931}
932
933impl FromStr for BlockHeight {
934 type Err = ParseIntError;
935
936 fn from_str(src: &str) -> Result<Self, Self::Err> {
937 Ok(Self(u64::from_str(src)?))
938 }
939}
940
941impl Display for Round {
942 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
943 match self {
944 Round::Fast => write!(f, "fast round"),
945 Round::MultiLeader(r) => write!(f, "multi-leader round {r}"),
946 Round::SingleLeader(r) => write!(f, "single-leader round {r}"),
947 Round::Validator(r) => write!(f, "validator round {r}"),
948 }
949 }
950}
951
952impl Round {
953 pub fn is_multi_leader(&self) -> bool {
955 matches!(self, Round::MultiLeader(_))
956 }
957
958 pub fn multi_leader(&self) -> Option<u32> {
960 match self {
961 Round::MultiLeader(number) => Some(*number),
962 _ => None,
963 }
964 }
965
966 pub fn is_fast(&self) -> bool {
968 matches!(self, Round::Fast)
969 }
970
971 pub fn number(&self) -> u32 {
973 match self {
974 Round::Fast => 0,
975 Round::MultiLeader(r) | Round::SingleLeader(r) | Round::Validator(r) => *r,
976 }
977 }
978
979 pub fn type_name(&self) -> &'static str {
981 match self {
982 Round::Fast => "fast",
983 Round::MultiLeader(_) => "multi",
984 Round::SingleLeader(_) => "single",
985 Round::Validator(_) => "validator",
986 }
987 }
988}
989
990impl<'a> iter::Sum<&'a Amount> for Amount {
991 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
992 iter.fold(Self::ZERO, |a, b| a.saturating_add(*b))
993 }
994}
995
996impl Amount {
997 pub const DECIMAL_PLACES: u8 = 18;
999
1000 pub const ONE: Amount = Amount(10u128.pow(Amount::DECIMAL_PLACES as u32));
1002
1003 pub const fn from_tokens(tokens: u128) -> Amount {
1005 Self::ONE.saturating_mul(tokens)
1006 }
1007
1008 pub const fn from_millis(millitokens: u128) -> Amount {
1010 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 3)).saturating_mul(millitokens)
1011 }
1012
1013 pub const fn from_micros(microtokens: u128) -> Amount {
1015 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 6)).saturating_mul(microtokens)
1016 }
1017
1018 pub const fn from_nanos(nanotokens: u128) -> Amount {
1020 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 9)).saturating_mul(nanotokens)
1021 }
1022
1023 pub const fn from_attos(attotokens: u128) -> Amount {
1025 Amount(attotokens)
1026 }
1027
1028 pub const fn to_attos(self) -> u128 {
1030 self.0
1031 }
1032
1033 pub const fn upper_half(self) -> u64 {
1035 (self.0 >> 64) as u64
1036 }
1037
1038 pub const fn lower_half(self) -> u64 {
1040 self.0 as u64
1041 }
1042
1043 pub fn saturating_ratio(self, other: Amount) -> u128 {
1045 self.0.checked_div(other.0).unwrap_or(u128::MAX)
1046 }
1047
1048 pub fn is_zero(&self) -> bool {
1050 *self == Amount::ZERO
1051 }
1052}
1053
1054#[derive(
1056 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug, Serialize, Deserialize, Allocative,
1057)]
1058pub enum ChainOrigin {
1059 Root(u32),
1061 Child {
1063 parent: ChainId,
1065 block_height: BlockHeight,
1067 chain_index: u32,
1070 },
1071}
1072
1073impl ChainOrigin {
1074 pub fn root(&self) -> Option<u32> {
1076 match self {
1077 ChainOrigin::Root(i) => Some(*i),
1078 ChainOrigin::Child { .. } => None,
1079 }
1080 }
1081}
1082
1083#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Allocative)]
1085pub struct Epoch(pub u32);
1086
1087impl Epoch {
1088 pub const ZERO: Epoch = Epoch(0);
1090}
1091
1092impl Serialize for Epoch {
1093 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1094 where
1095 S: serde::ser::Serializer,
1096 {
1097 if serializer.is_human_readable() {
1098 serializer.serialize_str(&self.0.to_string())
1099 } else {
1100 serializer.serialize_newtype_struct("Epoch", &self.0)
1101 }
1102 }
1103}
1104
1105impl<'de> Deserialize<'de> for Epoch {
1106 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1107 where
1108 D: serde::de::Deserializer<'de>,
1109 {
1110 if deserializer.is_human_readable() {
1111 let s = String::deserialize(deserializer)?;
1112 Ok(Epoch(u32::from_str(&s).map_err(serde::de::Error::custom)?))
1113 } else {
1114 #[derive(Deserialize)]
1115 #[serde(rename = "Epoch")]
1116 struct EpochDerived(u32);
1117
1118 let value = EpochDerived::deserialize(deserializer)?;
1119 Ok(Self(value.0))
1120 }
1121 }
1122}
1123
1124impl std::fmt::Display for Epoch {
1125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1126 write!(f, "{}", self.0)
1127 }
1128}
1129
1130impl std::str::FromStr for Epoch {
1131 type Err = CryptoError;
1132
1133 fn from_str(s: &str) -> Result<Self, Self::Err> {
1134 Ok(Epoch(s.parse()?))
1135 }
1136}
1137
1138impl From<u32> for Epoch {
1139 fn from(value: u32) -> Self {
1140 Epoch(value)
1141 }
1142}
1143
1144impl Epoch {
1145 #[inline]
1148 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
1149 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1150 Ok(Self(val))
1151 }
1152
1153 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
1156 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
1157 Ok(Self(val))
1158 }
1159
1160 #[inline]
1162 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
1163 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1164 Ok(())
1165 }
1166}
1167
1168#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1170pub struct InitialChainConfig {
1171 pub ownership: ChainOwnership,
1173 pub epoch: Epoch,
1175 pub min_active_epoch: Epoch,
1177 pub max_active_epoch: Epoch,
1179 pub balance: Amount,
1181 pub application_permissions: ApplicationPermissions,
1183}
1184
1185#[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize, Allocative)]
1187pub struct ChainDescription {
1188 origin: ChainOrigin,
1189 timestamp: Timestamp,
1190 config: InitialChainConfig,
1191}
1192
1193impl ChainDescription {
1194 pub fn new(origin: ChainOrigin, config: InitialChainConfig, timestamp: Timestamp) -> Self {
1196 Self {
1197 origin,
1198 config,
1199 timestamp,
1200 }
1201 }
1202
1203 pub fn id(&self) -> ChainId {
1205 ChainId::from(self)
1206 }
1207
1208 pub fn origin(&self) -> ChainOrigin {
1210 self.origin
1211 }
1212
1213 pub fn config(&self) -> &InitialChainConfig {
1215 &self.config
1216 }
1217
1218 pub fn timestamp(&self) -> Timestamp {
1220 self.timestamp
1221 }
1222}
1223
1224impl BcsHashable<'_> for ChainDescription {}
1225
1226#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
1228pub struct NetworkDescription {
1229 pub name: String,
1231 pub genesis_config_hash: CryptoHash,
1233 pub genesis_timestamp: Timestamp,
1235 pub genesis_committee_blob_hash: CryptoHash,
1237 pub admin_chain_id: ChainId,
1239}
1240
1241#[derive(
1243 Default,
1244 Debug,
1245 PartialEq,
1246 Eq,
1247 PartialOrd,
1248 Ord,
1249 Hash,
1250 Clone,
1251 Serialize,
1252 Deserialize,
1253 WitType,
1254 WitLoad,
1255 WitStore,
1256 InputObject,
1257 Allocative,
1258)]
1259pub struct ApplicationPermissions {
1260 #[debug(skip_if = Option::is_none)]
1264 pub execute_operations: Option<Vec<ApplicationId>>,
1265 #[graphql(default)]
1268 #[debug(skip_if = Vec::is_empty)]
1269 pub mandatory_applications: Vec<ApplicationId>,
1270 #[graphql(default)]
1272 #[debug(skip_if = Vec::is_empty)]
1273 pub close_chain: Vec<ApplicationId>,
1274 #[graphql(default)]
1276 #[debug(skip_if = Vec::is_empty)]
1277 pub change_application_permissions: Vec<ApplicationId>,
1278 #[graphql(default)]
1280 #[debug(skip_if = Option::is_none)]
1281 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
1282 #[graphql(default)]
1284 #[debug(skip_if = Option::is_none)]
1285 pub make_http_requests: Option<Vec<ApplicationId>>,
1286}
1287
1288impl ApplicationPermissions {
1289 pub fn new_single(app_id: ApplicationId) -> Self {
1292 Self {
1293 execute_operations: Some(vec![app_id]),
1294 mandatory_applications: vec![app_id],
1295 close_chain: vec![app_id],
1296 change_application_permissions: vec![app_id],
1297 call_service_as_oracle: Some(vec![app_id]),
1298 make_http_requests: Some(vec![app_id]),
1299 }
1300 }
1301
1302 #[cfg(with_testing)]
1305 pub fn new_multiple(app_ids: Vec<ApplicationId>) -> Self {
1306 Self {
1307 execute_operations: Some(app_ids.clone()),
1308 mandatory_applications: app_ids.clone(),
1309 close_chain: app_ids.clone(),
1310 change_application_permissions: app_ids.clone(),
1311 call_service_as_oracle: Some(app_ids.clone()),
1312 make_http_requests: Some(app_ids),
1313 }
1314 }
1315
1316 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
1318 match (app_id, &self.execute_operations) {
1319 (_, None) => true,
1320 (GenericApplicationId::System, Some(_)) => false,
1321 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
1322 }
1323 }
1324
1325 pub fn can_close_chain(&self, app_id: &ApplicationId) -> bool {
1327 self.close_chain.contains(app_id)
1328 }
1329
1330 pub fn can_change_application_permissions(&self, app_id: &ApplicationId) -> bool {
1333 self.change_application_permissions.contains(app_id)
1334 }
1335
1336 pub fn can_call_services(&self, app_id: &ApplicationId) -> bool {
1338 self.call_service_as_oracle
1339 .as_ref()
1340 .is_none_or(|app_ids| app_ids.contains(app_id))
1341 }
1342
1343 pub fn can_make_http_requests(&self, app_id: &ApplicationId) -> bool {
1345 self.make_http_requests
1346 .as_ref()
1347 .is_none_or(|app_ids| app_ids.contains(app_id))
1348 }
1349}
1350
1351#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1353pub enum OracleResponse {
1354 Service(
1356 #[debug(with = "hex_debug")]
1357 #[serde(with = "serde_bytes")]
1358 Vec<u8>,
1359 ),
1360 Http(http::Response),
1362 Blob(BlobId),
1364 Assert,
1366 Round(Option<u32>),
1368 Event(
1370 EventId,
1371 #[debug(with = "hex_debug")]
1372 #[serde(with = "serde_bytes")]
1373 Vec<u8>,
1374 ),
1375 EventExists(EventId),
1377}
1378
1379impl BcsHashable<'_> for OracleResponse {}
1380
1381#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize, WitType, WitLoad, WitStore)]
1383pub struct ApplicationDescription {
1384 pub module_id: ModuleId,
1386 pub creator_chain_id: ChainId,
1388 pub block_height: BlockHeight,
1390 pub application_index: u32,
1392 #[serde(with = "serde_bytes")]
1394 #[debug(with = "hex_debug")]
1395 pub parameters: Vec<u8>,
1396 pub required_application_ids: Vec<ApplicationId>,
1398}
1399
1400impl From<&ApplicationDescription> for ApplicationId {
1401 fn from(description: &ApplicationDescription) -> Self {
1402 let mut hash = CryptoHash::new(&BlobContent::new_application_description(description));
1403 if matches!(description.module_id.vm_runtime, VmRuntime::Evm) {
1404 hash.make_evm_compatible();
1405 }
1406 ApplicationId::new(hash)
1407 }
1408}
1409
1410impl BcsHashable<'_> for ApplicationDescription {}
1411
1412impl ApplicationDescription {
1413 pub fn to_bytes(&self) -> Vec<u8> {
1415 bcs::to_bytes(self).expect("Serializing blob bytes should not fail!")
1416 }
1417
1418 pub fn contract_bytecode_blob_id(&self) -> BlobId {
1420 self.module_id.contract_bytecode_blob_id()
1421 }
1422
1423 pub fn service_bytecode_blob_id(&self) -> BlobId {
1425 self.module_id.service_bytecode_blob_id()
1426 }
1427}
1428
1429#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, WitType, WitLoad, WitStore)]
1431pub struct Bytecode {
1432 #[serde(with = "serde_bytes")]
1434 #[debug(with = "hex_debug")]
1435 pub bytes: Vec<u8>,
1436}
1437
1438impl Bytecode {
1439 pub fn new(bytes: Vec<u8>) -> Self {
1441 Bytecode { bytes }
1442 }
1443
1444 pub fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
1446 let path = path.as_ref();
1447 let bytes = fs::read(path).map_err(|error| {
1448 std::io::Error::new(error.kind(), format!("{}: {error}", path.display()))
1449 })?;
1450 Ok(Bytecode { bytes })
1451 }
1452
1453 #[cfg(not(target_arch = "wasm32"))]
1455 pub fn compress(&self) -> CompressedBytecode {
1456 #[cfg(with_metrics)]
1457 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1458 let compressed_bytes_vec = zstd::stream::encode_all(&*self.bytes, 19)
1459 .expect("Compressing bytes in memory should not fail");
1460
1461 CompressedBytecode {
1462 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1463 }
1464 }
1465
1466 #[cfg(target_arch = "wasm32")]
1468 pub fn compress(&self) -> CompressedBytecode {
1469 use ruzstd::encoding::{CompressionLevel, FrameCompressor};
1470
1471 #[cfg(with_metrics)]
1472 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1473
1474 let mut compressed_bytes_vec = Vec::new();
1475 let mut compressor = FrameCompressor::new(CompressionLevel::Fastest);
1476 compressor.set_source(&*self.bytes);
1477 compressor.set_drain(&mut compressed_bytes_vec);
1478 compressor.compress();
1479
1480 CompressedBytecode {
1481 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1482 }
1483 }
1484}
1485
1486impl AsRef<[u8]> for Bytecode {
1487 fn as_ref(&self) -> &[u8] {
1488 self.bytes.as_ref()
1489 }
1490}
1491
1492#[derive(Error, Debug)]
1494pub enum DecompressionError {
1495 #[error("Bytecode could not be decompressed: {0}")]
1497 InvalidCompressedBytecode(#[from] io::Error),
1498}
1499
1500#[serde_as]
1502#[derive(Clone, Debug, Deserialize, Hash, Serialize, WitType, WitStore)]
1503#[cfg_attr(with_testing, derive(Eq, PartialEq))]
1504pub struct CompressedBytecode {
1505 #[serde_as(as = "Arc<Bytes>")]
1507 #[debug(skip)]
1508 pub compressed_bytes: Arc<Box<[u8]>>,
1509}
1510
1511#[cfg(not(target_arch = "wasm32"))]
1512impl CompressedBytecode {
1513 pub fn decompressed_size_at_most(
1515 compressed_bytes: &[u8],
1516 limit: u64,
1517 ) -> Result<bool, DecompressionError> {
1518 let mut decoder = zstd::stream::Decoder::new(compressed_bytes)?;
1519 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1520 let mut writer = LimitedWriter::new(io::sink(), limit);
1521 match io::copy(&mut decoder, &mut writer) {
1522 Ok(_) => Ok(true),
1523 Err(error) => {
1524 error.downcast::<LimitedWriterError>()?;
1525 Ok(false)
1526 }
1527 }
1528 }
1529
1530 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1532 #[cfg(with_metrics)]
1533 let _decompression_latency = metrics::BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1534 let bytes = zstd::stream::decode_all(&**self.compressed_bytes)?;
1535
1536 #[cfg(with_metrics)]
1537 metrics::BYTECODE_DECOMPRESSED_SIZE_BYTES
1538 .with_label_values(&[])
1539 .observe(bytes.len() as f64);
1540
1541 Ok(Bytecode { bytes })
1542 }
1543}
1544
1545#[cfg(target_arch = "wasm32")]
1546impl CompressedBytecode {
1547 pub fn decompressed_size_at_most(
1549 compressed_bytes: &[u8],
1550 limit: u64,
1551 ) -> Result<bool, DecompressionError> {
1552 use ruzstd::decoding::StreamingDecoder;
1553 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1554 let mut writer = LimitedWriter::new(io::sink(), limit);
1555 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
1556
1557 match io::copy(&mut decoder, &mut writer) {
1559 Ok(_) => Ok(true),
1560 Err(error) => {
1561 error.downcast::<LimitedWriterError>()?;
1562 Ok(false)
1563 }
1564 }
1565 }
1566
1567 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1569 use ruzstd::{decoding::StreamingDecoder, io::Read};
1570
1571 #[cfg(with_metrics)]
1572 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1573
1574 let compressed_bytes = &*self.compressed_bytes;
1575 let mut bytes = Vec::new();
1576 let mut decoder = StreamingDecoder::new(&**compressed_bytes).map_err(io::Error::other)?;
1577
1578 while !decoder.get_ref().is_empty() {
1580 decoder
1581 .read_to_end(&mut bytes)
1582 .expect("Reading from a slice in memory should not result in I/O errors");
1583 }
1584
1585 #[cfg(with_metrics)]
1586 BYTECODE_DECOMPRESSED_SIZE_BYTES
1587 .with_label_values(&[])
1588 .observe(bytes.len() as f64);
1589
1590 Ok(Bytecode { bytes })
1591 }
1592}
1593
1594impl BcsHashable<'_> for BlobContent {}
1595
1596#[serde_as]
1598#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Allocative)]
1599pub struct BlobContent {
1600 blob_type: BlobType,
1602 #[debug(skip)]
1604 #[serde_as(as = "Arc<Bytes>")]
1605 bytes: Arc<Box<[u8]>>,
1606}
1607
1608impl BlobContent {
1609 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1611 let bytes = bytes.into();
1612 BlobContent {
1613 blob_type,
1614 bytes: Arc::new(bytes),
1615 }
1616 }
1617
1618 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1620 BlobContent::new(BlobType::Data, bytes)
1621 }
1622
1623 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1625 BlobContent {
1626 blob_type: BlobType::ContractBytecode,
1627 bytes: compressed_bytecode.compressed_bytes,
1628 }
1629 }
1630
1631 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1633 BlobContent {
1634 blob_type: BlobType::EvmBytecode,
1635 bytes: compressed_bytecode.compressed_bytes,
1636 }
1637 }
1638
1639 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1641 BlobContent {
1642 blob_type: BlobType::ServiceBytecode,
1643 bytes: compressed_bytecode.compressed_bytes,
1644 }
1645 }
1646
1647 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1649 let bytes = application_description.to_bytes();
1650 BlobContent::new(BlobType::ApplicationDescription, bytes)
1651 }
1652
1653 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1655 BlobContent::new(BlobType::Committee, committee)
1656 }
1657
1658 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1660 let bytes = bcs::to_bytes(&chain_description)
1661 .expect("Serializing a ChainDescription should not fail!");
1662 BlobContent::new(BlobType::ChainDescription, bytes)
1663 }
1664
1665 pub fn bytes(&self) -> &[u8] {
1667 &self.bytes
1668 }
1669
1670 pub fn into_vec_or_clone(self) -> Vec<u8> {
1672 let bytes = Arc::unwrap_or_clone(self.bytes);
1673 bytes.into_vec()
1674 }
1675
1676 pub fn into_arc_bytes(self) -> Arc<Box<[u8]>> {
1678 self.bytes
1679 }
1680
1681 pub fn blob_type(&self) -> BlobType {
1683 self.blob_type
1684 }
1685}
1686
1687impl From<Blob> for BlobContent {
1688 fn from(blob: Blob) -> BlobContent {
1689 blob.content
1690 }
1691}
1692
1693impl From<Arc<Blob>> for BlobContent {
1694 fn from(blob: Arc<Blob>) -> BlobContent {
1695 blob.content().clone()
1696 }
1697}
1698
1699#[derive(Debug, Hash, PartialEq, Eq, Clone, Allocative)]
1701pub struct Blob {
1702 hash: CryptoHash,
1704 content: BlobContent,
1706}
1707
1708impl Blob {
1709 pub fn new(content: BlobContent) -> Self {
1711 let mut hash = CryptoHash::new(&content);
1712 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1713 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1714 .expect("to obtain an application description");
1715 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1716 hash.make_evm_compatible();
1717 }
1718 }
1719 Blob { hash, content }
1720 }
1721
1722 pub fn new_with_hash_unchecked(blob_id: BlobId, content: BlobContent) -> Self {
1724 Blob {
1725 hash: blob_id.hash,
1726 content,
1727 }
1728 }
1729
1730 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1732 let bytes = bytes.into();
1733 Blob {
1734 hash: blob_id.hash,
1735 content: BlobContent {
1736 blob_type: blob_id.blob_type,
1737 bytes: Arc::new(bytes),
1738 },
1739 }
1740 }
1741
1742 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1744 Blob::new(BlobContent::new_data(bytes))
1745 }
1746
1747 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1749 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1750 }
1751
1752 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1754 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1755 }
1756
1757 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1759 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1760 }
1761
1762 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1764 Blob::new(BlobContent::new_application_description(
1765 application_description,
1766 ))
1767 }
1768
1769 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1771 Blob::new(BlobContent::new_committee(committee))
1772 }
1773
1774 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1776 Blob::new(BlobContent::new_chain_description(chain_description))
1777 }
1778
1779 pub fn id(&self) -> BlobId {
1781 BlobId {
1782 hash: self.hash,
1783 blob_type: self.content.blob_type,
1784 }
1785 }
1786
1787 pub fn content(&self) -> &BlobContent {
1789 &self.content
1790 }
1791
1792 pub fn into_content(self) -> BlobContent {
1794 self.content
1795 }
1796
1797 pub fn bytes(&self) -> &[u8] {
1799 self.content.bytes()
1800 }
1801
1802 pub fn is_committee_blob(&self) -> bool {
1804 self.content().blob_type().is_committee_blob()
1805 }
1806}
1807
1808impl Serialize for Blob {
1809 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1810 where
1811 S: Serializer,
1812 {
1813 if serializer.is_human_readable() {
1814 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1815 serializer.serialize_str(&hex::encode(blob_bytes))
1816 } else {
1817 BlobContent::serialize(self.content(), serializer)
1818 }
1819 }
1820}
1821
1822impl<'a> Deserialize<'a> for Blob {
1823 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1824 where
1825 D: Deserializer<'a>,
1826 {
1827 if deserializer.is_human_readable() {
1828 let s = String::deserialize(deserializer)?;
1829 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1830 let content: BlobContent =
1831 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1832
1833 Ok(Blob::new(content))
1834 } else {
1835 let content = BlobContent::deserialize(deserializer)?;
1836 Ok(Blob::new(content))
1837 }
1838 }
1839}
1840
1841impl BcsHashable<'_> for Blob {}
1842
1843#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1845pub struct Event {
1846 pub stream_id: StreamId,
1848 pub index: u32,
1850 #[debug(with = "hex_debug")]
1852 #[serde(with = "serde_bytes")]
1853 pub value: Vec<u8>,
1854}
1855
1856impl Event {
1857 pub fn id(&self, chain_id: ChainId) -> EventId {
1859 EventId {
1860 chain_id,
1861 stream_id: self.stream_id.clone(),
1862 index: self.index,
1863 }
1864 }
1865}
1866
1867#[derive(Clone, Debug, Serialize, Deserialize, WitType, WitLoad, WitStore)]
1869pub struct StreamUpdate {
1870 pub chain_id: ChainId,
1872 pub stream_id: StreamId,
1874 pub previous_index: u32,
1876 pub next_index: u32,
1878}
1879
1880impl StreamUpdate {
1881 pub fn new_indices(&self) -> impl Iterator<Item = u32> {
1883 self.previous_index..self.next_index
1884 }
1885}
1886
1887impl BcsHashable<'_> for Event {}
1888
1889#[derive(
1891 Clone, Debug, Default, serde::Serialize, serde::Deserialize, async_graphql::SimpleObject,
1892)]
1893pub struct MessagePolicy {
1894 pub blanket: BlanketMessagePolicy,
1896 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
1901 pub ignore_chain_ids: HashSet<ChainId>,
1903 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
1906 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
1909 pub process_events_from_application_ids: Option<HashSet<GenericApplicationId>>,
1912 pub never_reject_application_ids: HashSet<GenericApplicationId>,
1918}
1919
1920#[derive(
1922 Default,
1923 Copy,
1924 Clone,
1925 Debug,
1926 PartialEq,
1927 Eq,
1928 serde::Serialize,
1929 serde::Deserialize,
1930 async_graphql::Enum,
1931)]
1932#[cfg_attr(web, derive(tsify::Tsify), tsify(from_wasm_abi, into_wasm_abi))]
1933#[cfg_attr(any(web, not(target_arch = "wasm32")), derive(clap::ValueEnum))]
1934pub enum BlanketMessagePolicy {
1935 #[default]
1937 Accept,
1938 Reject,
1941 Ignore,
1944}
1945
1946impl MessagePolicy {
1947 #[instrument(level = "trace", skip(self))]
1949 pub fn is_ignore(&self) -> bool {
1950 matches!(self.blanket, BlanketMessagePolicy::Ignore)
1951 }
1952
1953 #[instrument(level = "trace", skip(self))]
1955 pub fn is_reject(&self) -> bool {
1956 matches!(self.blanket, BlanketMessagePolicy::Reject)
1957 }
1958
1959 #[instrument(level = "trace", skip(self))]
1963 pub fn ignores_origin(&self, origin: &ChainId) -> bool {
1964 self.is_ignore()
1965 || self.ignore_chain_ids.contains(origin)
1966 || self
1967 .restrict_chain_ids_to
1968 .as_ref()
1969 .is_some_and(|set| !set.contains(origin))
1970 }
1971
1972 #[instrument(level = "trace", skip(self))]
1977 pub fn accepts_event_stream(&self, chain_id: &ChainId, stream_id: &StreamId) -> bool {
1978 self.restrict_chain_ids_to
1979 .as_ref()
1980 .is_none_or(|chain_ids| chain_ids.contains(chain_id))
1981 && self
1982 .process_events_from_application_ids
1983 .as_ref()
1984 .is_none_or(|app_ids| app_ids.contains(&stream_id.application_id))
1985 }
1986}
1987
1988doc_scalar!(Bytecode, "A WebAssembly module's bytecode");
1989doc_scalar!(Amount, "A non-negative amount of tokens.");
1990doc_scalar!(U128, "A 128-bit unsigned integer.");
1991doc_scalar!(
1992 Epoch,
1993 "A number identifying the configuration of the chain (aka the committee)"
1994);
1995doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
1996doc_scalar!(
1997 Timestamp,
1998 "A timestamp, in microseconds since the Unix epoch"
1999);
2000doc_scalar!(TimeDelta, "A duration in microseconds");
2001doc_scalar!(
2002 Round,
2003 "A number to identify successive attempts to decide a value in a consensus protocol."
2004);
2005doc_scalar!(
2006 ChainDescription,
2007 "Initial chain configuration and chain origin."
2008);
2009doc_scalar!(OracleResponse, "A record of a single oracle response.");
2010doc_scalar!(BlobContent, "A blob of binary data.");
2011doc_scalar!(
2012 Blob,
2013 "A blob of binary data, with its content-addressed blob ID."
2014);
2015doc_scalar!(ApplicationDescription, "Description of a user application");
2016
2017#[cfg(with_metrics)]
2018mod metrics {
2019 use std::sync::LazyLock;
2020
2021 use prometheus::HistogramVec;
2022
2023 use crate::prometheus_util::{
2024 exponential_bucket_interval, exponential_bucket_latencies, register_histogram_vec,
2025 };
2026
2027 pub static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
2029 register_histogram_vec(
2030 "bytecode_compression_latency",
2031 "Bytecode compression latency",
2032 &[],
2033 exponential_bucket_latencies(10.0),
2034 )
2035 });
2036
2037 pub static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
2039 register_histogram_vec(
2040 "bytecode_decompression_latency",
2041 "Bytecode decompression latency",
2042 &[],
2043 exponential_bucket_latencies(10.0),
2044 )
2045 });
2046
2047 pub static BYTECODE_DECOMPRESSED_SIZE_BYTES: LazyLock<HistogramVec> = LazyLock::new(|| {
2048 register_histogram_vec(
2049 "wasm_bytecode_decompressed_size_bytes",
2050 "Decompressed size in bytes of WASM bytecodes stored on-chain",
2051 &[],
2052 exponential_bucket_interval(10_000.0, 100_000_000.0),
2053 )
2054 });
2055}
2056
2057#[cfg(test)]
2058mod tests {
2059 use std::str::FromStr;
2060
2061 use super::{Amount, ApplicationDescription, BlobContent};
2062 use crate::{
2063 crypto::CryptoHash,
2064 data_types::BlockHeight,
2065 identifiers::{BlobType, ChainId, ModuleId},
2066 vm::VmRuntime,
2067 };
2068
2069 #[test]
2070 fn non_canonical_btree_map_serializes_like_vec() {
2071 use std::collections::BTreeMap;
2072
2073 use super::NonCanonicalBTreeMap;
2074
2075 let map = NonCanonicalBTreeMap::from(BTreeMap::from([
2078 (1u32, 10u8),
2079 (256u32, 20u8),
2080 (2u32, 30u8),
2081 ]));
2082
2083 let entries = map
2086 .iter()
2087 .map(|(k, v)| (*k, *v))
2088 .collect::<Vec<(u32, u8)>>();
2089 assert_eq!(
2090 bcs::to_bytes(&map).unwrap(),
2091 bcs::to_bytes(&entries).unwrap()
2092 );
2093
2094 let canonical = map
2096 .iter()
2097 .map(|(k, v)| (*k, *v))
2098 .collect::<BTreeMap<u32, u8>>();
2099 assert_ne!(
2100 bcs::to_bytes(&map).unwrap(),
2101 bcs::to_bytes(&canonical).unwrap()
2102 );
2103
2104 let deserialized: NonCanonicalBTreeMap<u32, u8> =
2106 bcs::from_bytes(&bcs::to_bytes(&map).unwrap()).unwrap();
2107 assert_eq!(map, deserialized);
2108 }
2109
2110 #[test]
2111 fn canonical_btree_set_serializes_like_map() {
2112 use std::collections::{BTreeMap, BTreeSet};
2113
2114 use super::CanonicalBTreeSet;
2115
2116 let set = CanonicalBTreeSet::from(BTreeSet::from([1u32, 256u32, 2u32]));
2117
2118 let map = set.iter().map(|t| (*t, ())).collect::<BTreeMap<u32, ()>>();
2121 assert_eq!(bcs::to_bytes(&set).unwrap(), bcs::to_bytes(&map).unwrap());
2122
2123 let plain = set.iter().copied().collect::<BTreeSet<u32>>();
2126 assert_ne!(bcs::to_bytes(&set).unwrap(), bcs::to_bytes(&plain).unwrap());
2127
2128 let deserialized: CanonicalBTreeSet<u32> =
2130 bcs::from_bytes(&bcs::to_bytes(&set).unwrap()).unwrap();
2131 assert_eq!(set, deserialized);
2132 }
2133
2134 #[test]
2135 fn display_amount() {
2136 assert_eq!("1.", Amount::ONE.to_string());
2137 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
2138 assert_eq!(
2139 Amount(10_000_000_000_000_000_000),
2140 Amount::from_str("10").unwrap()
2141 );
2142 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
2143 assert_eq!(
2144 "1001.3",
2145 (Amount::from_str("1.1")
2146 .unwrap()
2147 .saturating_add(Amount::from_str("1_000.2").unwrap()))
2148 .to_string()
2149 );
2150 assert_eq!(
2151 " 1.00000000000000000000",
2152 format!("{:25.20}", Amount::ONE)
2153 );
2154 assert_eq!(
2155 "~+12.34~~",
2156 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
2157 );
2158 }
2159
2160 #[test]
2161 fn blob_content_serialization_deserialization() {
2162 let test_data = b"Hello, world!".as_slice();
2163 let original_blob = BlobContent::new(BlobType::Data, test_data);
2164
2165 let serialized = bcs::to_bytes(&original_blob).expect("Failed to serialize BlobContent");
2166 let deserialized: BlobContent =
2167 bcs::from_bytes(&serialized).expect("Failed to deserialize BlobContent");
2168 assert_eq!(original_blob, deserialized);
2169
2170 let serialized =
2171 serde_json::to_vec(&original_blob).expect("Failed to serialize BlobContent");
2172 let deserialized: BlobContent =
2173 serde_json::from_slice(&serialized).expect("Failed to deserialize BlobContent");
2174 assert_eq!(original_blob, deserialized);
2175 }
2176
2177 #[test]
2178 fn blob_content_hash_consistency() {
2179 let test_data = b"Hello, world!";
2180 let blob1 = BlobContent::new(BlobType::Data, test_data.as_slice());
2181 let blob2 = BlobContent::new(BlobType::Data, Vec::from(test_data.as_slice()));
2182
2183 let hash1 = crate::crypto::CryptoHash::new(&blob1);
2185 let hash2 = crate::crypto::CryptoHash::new(&blob2);
2186
2187 assert_eq!(hash1, hash2, "Hashes should be equal for same content");
2188 assert_eq!(blob1.bytes(), blob2.bytes(), "Byte content should be equal");
2189 }
2190
2191 #[test]
2200 fn application_description_serializes_module_id_as_hex_string() {
2201 let module_id = ModuleId::new(
2202 CryptoHash::test_hash("contract-bytecode"),
2203 CryptoHash::test_hash("service-bytecode"),
2204 VmRuntime::Wasm,
2205 );
2206 let description = ApplicationDescription {
2207 module_id,
2208 creator_chain_id: ChainId(CryptoHash::test_hash("chain")),
2209 block_height: BlockHeight(0),
2210 application_index: 0,
2211 parameters: Vec::new(),
2212 required_application_ids: Vec::new(),
2213 };
2214
2215 let value = serde_json::to_value(&description).unwrap();
2216 let module_id_value = value
2217 .get("module_id")
2218 .expect("`module_id` is the field name the explorer indexes into");
2219 let hex = module_id_value
2220 .as_str()
2221 .expect("`module_id` must serialize as a hex string in human-readable form");
2222 let roundtrip: ModuleId =
2223 serde_json::from_value(serde_json::Value::String(hex.to_owned())).unwrap();
2224 assert_eq!(roundtrip, module_id);
2225 }
2226}