1#[cfg(with_testing)]
8use std::ops;
9use std::{
10 fmt::{self, Display},
11 fs,
12 hash::Hash,
13 io, iter,
14 num::ParseIntError,
15 ops::{Bound, RangeBounds},
16 path::Path,
17 str::FromStr,
18 sync::Arc,
19};
20
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;
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(
48 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, WitType, WitLoad, WitStore,
49)]
50#[cfg_attr(
51 all(with_testing, not(target_arch = "wasm32")),
52 derive(test_strategy::Arbitrary)
53)]
54pub struct Amount(u128);
55
56#[derive(Serialize, Deserialize)]
57#[serde(rename = "Amount")]
58struct AmountString(String);
59
60#[derive(Serialize, Deserialize)]
61#[serde(rename = "Amount")]
62struct AmountU128(u128);
63
64impl Serialize for Amount {
65 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
66 if serializer.is_human_readable() {
67 AmountString(self.to_string()).serialize(serializer)
68 } else {
69 AmountU128(self.0).serialize(serializer)
70 }
71 }
72}
73
74impl<'de> Deserialize<'de> for Amount {
75 fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
76 if deserializer.is_human_readable() {
77 let AmountString(s) = AmountString::deserialize(deserializer)?;
78 s.parse().map_err(serde::de::Error::custom)
79 } else {
80 Ok(Amount(AmountU128::deserialize(deserializer)?.0))
81 }
82 }
83}
84
85impl From<Amount> for U256 {
86 fn from(amount: Amount) -> U256 {
87 U256::from(amount.0)
88 }
89}
90
91#[derive(
93 Eq,
94 PartialEq,
95 Ord,
96 PartialOrd,
97 Copy,
98 Clone,
99 Hash,
100 Default,
101 Debug,
102 Serialize,
103 Deserialize,
104 WitType,
105 WitLoad,
106 WitStore,
107)]
108#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
109pub struct BlockHeight(pub u64);
110
111#[derive(
113 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Serialize, Deserialize,
114)]
115#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
116pub enum Round {
117 #[default]
119 Fast,
120 MultiLeader(u32),
122 SingleLeader(u32),
124 Validator(u32),
126}
127
128#[derive(
130 Eq,
131 PartialEq,
132 Ord,
133 PartialOrd,
134 Copy,
135 Clone,
136 Hash,
137 Default,
138 Debug,
139 Serialize,
140 Deserialize,
141 WitType,
142 WitLoad,
143 WitStore,
144)]
145pub struct TimeDelta(u64);
146
147impl TimeDelta {
148 pub const fn from_micros(micros: u64) -> Self {
150 TimeDelta(micros)
151 }
152
153 pub const fn from_millis(millis: u64) -> Self {
155 TimeDelta(millis.saturating_mul(1_000))
156 }
157
158 pub const fn from_secs(secs: u64) -> Self {
160 TimeDelta(secs.saturating_mul(1_000_000))
161 }
162
163 pub fn from_duration(duration: Duration) -> Self {
166 TimeDelta::from_micros(u64::try_from(duration.as_micros()).unwrap_or(u64::MAX))
167 }
168
169 pub const fn as_micros(&self) -> u64 {
171 self.0
172 }
173
174 pub const fn as_duration(&self) -> Duration {
176 Duration::from_micros(self.as_micros())
177 }
178}
179
180#[derive(
182 Eq,
183 PartialEq,
184 Ord,
185 PartialOrd,
186 Copy,
187 Clone,
188 Hash,
189 Default,
190 Debug,
191 Serialize,
192 Deserialize,
193 WitType,
194 WitLoad,
195 WitStore,
196)]
197pub struct Timestamp(u64);
198
199impl Timestamp {
200 pub fn now() -> Timestamp {
202 Timestamp(
203 SystemTime::UNIX_EPOCH
204 .elapsed()
205 .expect("system time should be after Unix epoch")
206 .as_micros()
207 .try_into()
208 .unwrap_or(u64::MAX),
209 )
210 }
211
212 pub const fn micros(&self) -> u64 {
214 self.0
215 }
216
217 pub const fn delta_since(&self, other: Timestamp) -> TimeDelta {
220 TimeDelta::from_micros(self.0.saturating_sub(other.0))
221 }
222
223 pub const fn duration_since(&self, other: Timestamp) -> Duration {
226 Duration::from_micros(self.0.saturating_sub(other.0))
227 }
228
229 pub const fn saturating_add(&self, duration: TimeDelta) -> Timestamp {
231 Timestamp(self.0.saturating_add(duration.0))
232 }
233
234 pub const fn saturating_sub(&self, duration: TimeDelta) -> Timestamp {
236 Timestamp(self.0.saturating_sub(duration.0))
237 }
238
239 pub const fn saturating_sub_micros(&self, micros: u64) -> Timestamp {
242 Timestamp(self.0.saturating_sub(micros))
243 }
244}
245
246impl From<u64> for Timestamp {
247 fn from(t: u64) -> Timestamp {
248 Timestamp(t)
249 }
250}
251
252impl Display for Timestamp {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 if let Some(date_time) = chrono::DateTime::from_timestamp(
255 (self.0 / 1_000_000) as i64,
256 ((self.0 % 1_000_000) * 1_000) as u32,
257 ) {
258 return date_time.naive_utc().fmt(f);
259 }
260 self.0.fmt(f)
261 }
262}
263
264#[derive(
267 Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, WitLoad, WitStore, WitType,
268)]
269pub struct Resources {
270 pub wasm_fuel: u64,
272 pub evm_fuel: u64,
274 pub read_operations: u32,
276 pub write_operations: u32,
278 pub bytes_runtime: u32,
280 pub bytes_to_read: u32,
282 pub bytes_to_write: u32,
284 pub blobs_to_read: u32,
286 pub blobs_to_publish: u32,
288 pub blob_bytes_to_read: u32,
290 pub blob_bytes_to_publish: u32,
292 pub messages: u32,
294 pub message_size: u32,
297 pub storage_size_delta: u32,
299 pub service_as_oracle_queries: u32,
301 pub http_requests: u32,
303 }
306
307#[derive(Clone, Debug, Deserialize, Serialize, WitLoad, WitType)]
309#[cfg_attr(with_testing, derive(Eq, PartialEq, WitStore))]
310#[witty_specialize_with(Message = Vec<u8>)]
311pub struct SendMessageRequest<Message> {
312 pub destination: ChainId,
314 pub authenticated: bool,
316 pub is_tracked: bool,
318 pub grant: Resources,
320 pub message: Message,
322}
323
324impl<Message> SendMessageRequest<Message>
325where
326 Message: Serialize,
327{
328 pub fn into_raw(self) -> SendMessageRequest<Vec<u8>> {
330 let message = bcs::to_bytes(&self.message).expect("Failed to serialize message");
331
332 SendMessageRequest {
333 destination: self.destination,
334 authenticated: self.authenticated,
335 is_tracked: self.is_tracked,
336 grant: self.grant,
337 message,
338 }
339 }
340}
341
342#[derive(Debug, Error)]
344#[allow(missing_docs)]
345pub enum ArithmeticError {
346 #[error("Number overflow")]
347 Overflow,
348 #[error("Number underflow")]
349 Underflow,
350}
351
352macro_rules! impl_wrapped_number {
353 ($name:ident, $wrapped:ident) => {
354 impl $name {
355 pub const ZERO: Self = Self(0);
357
358 pub const MAX: Self = Self($wrapped::MAX);
360
361 pub fn try_add(self, other: Self) -> Result<Self, ArithmeticError> {
363 let val = self
364 .0
365 .checked_add(other.0)
366 .ok_or(ArithmeticError::Overflow)?;
367 Ok(Self(val))
368 }
369
370 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
372 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
373 Ok(Self(val))
374 }
375
376 pub const fn saturating_add(self, other: Self) -> Self {
378 let val = self.0.saturating_add(other.0);
379 Self(val)
380 }
381
382 pub fn try_sub(self, other: Self) -> Result<Self, ArithmeticError> {
384 let val = self
385 .0
386 .checked_sub(other.0)
387 .ok_or(ArithmeticError::Underflow)?;
388 Ok(Self(val))
389 }
390
391 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
393 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
394 Ok(Self(val))
395 }
396
397 pub const fn saturating_sub(self, other: Self) -> Self {
399 let val = self.0.saturating_sub(other.0);
400 Self(val)
401 }
402
403 pub fn try_add_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
405 self.0 = self
406 .0
407 .checked_add(other.0)
408 .ok_or(ArithmeticError::Overflow)?;
409 Ok(())
410 }
411
412 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
414 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
415 Ok(())
416 }
417
418 pub const fn saturating_add_assign(&mut self, other: Self) {
420 self.0 = self.0.saturating_add(other.0);
421 }
422
423 pub fn try_sub_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
425 self.0 = self
426 .0
427 .checked_sub(other.0)
428 .ok_or(ArithmeticError::Underflow)?;
429 Ok(())
430 }
431
432 pub const fn saturating_mul(&self, other: $wrapped) -> Self {
434 Self(self.0.saturating_mul(other))
435 }
436
437 pub fn try_mul(self, other: $wrapped) -> Result<Self, ArithmeticError> {
439 let val = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
440 Ok(Self(val))
441 }
442
443 pub fn try_mul_assign(&mut self, other: $wrapped) -> Result<(), ArithmeticError> {
445 self.0 = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
446 Ok(())
447 }
448 }
449
450 impl From<$name> for $wrapped {
451 fn from(value: $name) -> Self {
452 value.0
453 }
454 }
455
456 #[cfg(with_testing)]
458 impl From<$wrapped> for $name {
459 fn from(value: $wrapped) -> Self {
460 Self(value)
461 }
462 }
463
464 #[cfg(with_testing)]
465 impl ops::Add for $name {
466 type Output = Self;
467
468 fn add(self, other: Self) -> Self {
469 Self(self.0 + other.0)
470 }
471 }
472
473 #[cfg(with_testing)]
474 impl ops::Sub for $name {
475 type Output = Self;
476
477 fn sub(self, other: Self) -> Self {
478 Self(self.0 - other.0)
479 }
480 }
481
482 #[cfg(with_testing)]
483 impl ops::Mul<$wrapped> for $name {
484 type Output = Self;
485
486 fn mul(self, other: $wrapped) -> Self {
487 Self(self.0 * other)
488 }
489 }
490 };
491}
492
493impl TryFrom<BlockHeight> for usize {
494 type Error = ArithmeticError;
495
496 fn try_from(height: BlockHeight) -> Result<usize, ArithmeticError> {
497 usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)
498 }
499}
500
501pub trait BlockHeightRangeBounds {
503 fn to_inclusive(&self) -> Option<(BlockHeight, BlockHeight)>;
506}
507
508impl<T: RangeBounds<BlockHeight>> BlockHeightRangeBounds for T {
509 fn to_inclusive(&self) -> Option<(BlockHeight, BlockHeight)> {
510 let start = match self.start_bound() {
511 Bound::Included(height) => *height,
512 Bound::Excluded(height) => height.try_add_one().ok()?,
513 Bound::Unbounded => BlockHeight(0),
514 };
515 let end = match self.end_bound() {
516 Bound::Included(height) => *height,
517 Bound::Excluded(height) => height.try_sub_one().ok()?,
518 Bound::Unbounded => BlockHeight::MAX,
519 };
520 if start > end {
521 return None;
522 }
523 Some((start, end))
524 }
525}
526
527impl_wrapped_number!(Amount, u128);
528impl_wrapped_number!(BlockHeight, u64);
529impl_wrapped_number!(TimeDelta, u64);
530
531impl Display for Amount {
532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533 let places = Amount::DECIMAL_PLACES as usize;
535 let min_digits = places + 1;
536 let decimals = format!("{:0min_digits$}", self.0);
537 let integer_part = &decimals[..(decimals.len() - places)];
538 let fractional_part = decimals[(decimals.len() - places)..].trim_end_matches('0');
539
540 let precision = f.precision().unwrap_or(0).max(fractional_part.len());
542 let sign = if f.sign_plus() && self.0 > 0 { "+" } else { "" };
543 let pad_width = f.width().map_or(0, |w| {
545 w.saturating_sub(precision)
546 .saturating_sub(sign.len() + integer_part.len() + 1)
547 });
548 let left_pad = match f.align() {
549 None | Some(fmt::Alignment::Right) => pad_width,
550 Some(fmt::Alignment::Center) => pad_width / 2,
551 Some(fmt::Alignment::Left) => 0,
552 };
553
554 for _ in 0..left_pad {
555 write!(f, "{}", f.fill())?;
556 }
557 write!(f, "{sign}{integer_part}.{fractional_part:0<precision$}")?;
558 for _ in left_pad..pad_width {
559 write!(f, "{}", f.fill())?;
560 }
561 Ok(())
562 }
563}
564
565#[derive(Error, Debug)]
566#[allow(missing_docs)]
567pub enum ParseAmountError {
568 #[error("cannot parse amount")]
569 Parse,
570 #[error("cannot represent amount: number too high")]
571 TooHigh,
572 #[error("cannot represent amount: too many decimal places after the point")]
573 TooManyDigits,
574}
575
576impl FromStr for Amount {
577 type Err = ParseAmountError;
578
579 fn from_str(src: &str) -> Result<Self, Self::Err> {
580 let mut result: u128 = 0;
581 let mut decimals: Option<u8> = None;
582 let mut chars = src.trim().chars().peekable();
583 if chars.peek() == Some(&'+') {
584 chars.next();
585 }
586 for char in chars {
587 match char {
588 '_' => {}
589 '.' if decimals.is_some() => return Err(ParseAmountError::Parse),
590 '.' => decimals = Some(Amount::DECIMAL_PLACES),
591 char => {
592 let digit = u128::from(char.to_digit(10).ok_or(ParseAmountError::Parse)?);
593 if let Some(d) = &mut decimals {
594 *d = d.checked_sub(1).ok_or(ParseAmountError::TooManyDigits)?;
595 }
596 result = result
597 .checked_mul(10)
598 .and_then(|r| r.checked_add(digit))
599 .ok_or(ParseAmountError::TooHigh)?;
600 }
601 }
602 }
603 result = result
604 .checked_mul(10u128.pow(decimals.unwrap_or(Amount::DECIMAL_PLACES) as u32))
605 .ok_or(ParseAmountError::TooHigh)?;
606 Ok(Amount(result))
607 }
608}
609
610impl Display for BlockHeight {
611 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612 self.0.fmt(f)
613 }
614}
615
616impl FromStr for BlockHeight {
617 type Err = ParseIntError;
618
619 fn from_str(src: &str) -> Result<Self, Self::Err> {
620 Ok(Self(u64::from_str(src)?))
621 }
622}
623
624impl Display for Round {
625 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
626 match self {
627 Round::Fast => write!(f, "fast round"),
628 Round::MultiLeader(r) => write!(f, "multi-leader round {}", r),
629 Round::SingleLeader(r) => write!(f, "single-leader round {}", r),
630 Round::Validator(r) => write!(f, "validator round {}", r),
631 }
632 }
633}
634
635impl Round {
636 pub fn is_multi_leader(&self) -> bool {
638 matches!(self, Round::MultiLeader(_))
639 }
640
641 pub fn multi_leader(&self) -> Option<u32> {
643 match self {
644 Round::MultiLeader(number) => Some(*number),
645 _ => None,
646 }
647 }
648
649 pub fn is_fast(&self) -> bool {
651 matches!(self, Round::Fast)
652 }
653
654 pub fn number(&self) -> u32 {
656 match self {
657 Round::Fast => 0,
658 Round::MultiLeader(r) | Round::SingleLeader(r) | Round::Validator(r) => *r,
659 }
660 }
661
662 pub fn type_name(&self) -> &'static str {
664 match self {
665 Round::Fast => "fast",
666 Round::MultiLeader(_) => "multi",
667 Round::SingleLeader(_) => "single",
668 Round::Validator(_) => "validator",
669 }
670 }
671}
672
673impl<'a> iter::Sum<&'a Amount> for Amount {
674 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
675 iter.fold(Self::ZERO, |a, b| a.saturating_add(*b))
676 }
677}
678
679impl Amount {
680 pub const DECIMAL_PLACES: u8 = 18;
682
683 pub const ONE: Amount = Amount(10u128.pow(Amount::DECIMAL_PLACES as u32));
685
686 pub const fn from_tokens(tokens: u128) -> Amount {
688 Self::ONE.saturating_mul(tokens)
689 }
690
691 pub const fn from_millis(millitokens: u128) -> Amount {
693 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 3)).saturating_mul(millitokens)
694 }
695
696 pub const fn from_micros(microtokens: u128) -> Amount {
698 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 6)).saturating_mul(microtokens)
699 }
700
701 pub const fn from_nanos(nanotokens: u128) -> Amount {
703 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 9)).saturating_mul(nanotokens)
704 }
705
706 pub const fn from_attos(attotokens: u128) -> Amount {
708 Amount(attotokens)
709 }
710
711 pub const fn upper_half(self) -> u64 {
713 (self.0 >> 64) as u64
714 }
715
716 pub const fn lower_half(self) -> u64 {
718 self.0 as u64
719 }
720
721 pub fn saturating_div(self, other: Amount) -> u128 {
723 self.0.checked_div(other.0).unwrap_or(u128::MAX)
724 }
725
726 pub fn is_zero(&self) -> bool {
728 *self == Amount::ZERO
729 }
730}
731
732#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug, Serialize, Deserialize)]
734pub enum ChainOrigin {
735 Root(u32),
737 Child {
739 parent: ChainId,
741 block_height: BlockHeight,
743 chain_index: u32,
746 },
747}
748
749impl ChainOrigin {
750 pub fn is_child(&self) -> bool {
752 matches!(self, ChainOrigin::Child { .. })
753 }
754
755 pub fn root(&self) -> Option<u32> {
757 match self {
758 ChainOrigin::Root(i) => Some(*i),
759 ChainOrigin::Child { .. } => None,
760 }
761 }
762}
763
764#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug)]
766pub struct Epoch(pub u32);
767
768impl Epoch {
769 pub const ZERO: Epoch = Epoch(0);
771}
772
773impl Serialize for Epoch {
774 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
775 where
776 S: serde::ser::Serializer,
777 {
778 if serializer.is_human_readable() {
779 serializer.serialize_str(&self.0.to_string())
780 } else {
781 serializer.serialize_newtype_struct("Epoch", &self.0)
782 }
783 }
784}
785
786impl<'de> Deserialize<'de> for Epoch {
787 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
788 where
789 D: serde::de::Deserializer<'de>,
790 {
791 if deserializer.is_human_readable() {
792 let s = String::deserialize(deserializer)?;
793 Ok(Epoch(u32::from_str(&s).map_err(serde::de::Error::custom)?))
794 } else {
795 #[derive(Deserialize)]
796 #[serde(rename = "Epoch")]
797 struct EpochDerived(u32);
798
799 let value = EpochDerived::deserialize(deserializer)?;
800 Ok(Self(value.0))
801 }
802 }
803}
804
805impl std::fmt::Display for Epoch {
806 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
807 write!(f, "{}", self.0)
808 }
809}
810
811impl std::str::FromStr for Epoch {
812 type Err = CryptoError;
813
814 fn from_str(s: &str) -> Result<Self, Self::Err> {
815 Ok(Epoch(s.parse()?))
816 }
817}
818
819impl From<u32> for Epoch {
820 fn from(value: u32) -> Self {
821 Epoch(value)
822 }
823}
824
825impl Epoch {
826 #[inline]
829 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
830 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
831 Ok(Self(val))
832 }
833
834 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
837 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
838 Ok(Self(val))
839 }
840
841 #[inline]
843 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
844 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
845 Ok(())
846 }
847}
848
849#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
851pub struct InitialChainConfig {
852 pub ownership: ChainOwnership,
854 pub epoch: Epoch,
856 pub min_active_epoch: Epoch,
858 pub max_active_epoch: Epoch,
860 pub balance: Amount,
862 pub application_permissions: ApplicationPermissions,
864}
865
866#[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize)]
868pub struct ChainDescription {
869 origin: ChainOrigin,
870 timestamp: Timestamp,
871 config: InitialChainConfig,
872}
873
874impl ChainDescription {
875 pub fn new(origin: ChainOrigin, config: InitialChainConfig, timestamp: Timestamp) -> Self {
877 Self {
878 origin,
879 config,
880 timestamp,
881 }
882 }
883
884 pub fn id(&self) -> ChainId {
886 ChainId::from(self)
887 }
888
889 pub fn origin(&self) -> ChainOrigin {
891 self.origin
892 }
893
894 pub fn config(&self) -> &InitialChainConfig {
896 &self.config
897 }
898
899 pub fn timestamp(&self) -> Timestamp {
901 self.timestamp
902 }
903
904 pub fn is_child(&self) -> bool {
906 self.origin.is_child()
907 }
908}
909
910impl BcsHashable<'_> for ChainDescription {}
911
912#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
914pub struct NetworkDescription {
915 pub name: String,
917 pub genesis_config_hash: CryptoHash,
919 pub genesis_timestamp: Timestamp,
921 pub genesis_committee_blob_hash: CryptoHash,
923 pub admin_chain_id: ChainId,
925}
926
927#[derive(
929 Default,
930 Debug,
931 PartialEq,
932 Eq,
933 PartialOrd,
934 Ord,
935 Hash,
936 Clone,
937 Serialize,
938 Deserialize,
939 WitType,
940 WitLoad,
941 WitStore,
942 InputObject,
943)]
944pub struct ApplicationPermissions {
945 #[debug(skip_if = Option::is_none)]
949 pub execute_operations: Option<Vec<ApplicationId>>,
950 #[graphql(default)]
953 #[debug(skip_if = Vec::is_empty)]
954 pub mandatory_applications: Vec<ApplicationId>,
955 #[graphql(default)]
957 #[debug(skip_if = Vec::is_empty)]
958 pub close_chain: Vec<ApplicationId>,
959 #[graphql(default)]
961 #[debug(skip_if = Vec::is_empty)]
962 pub change_application_permissions: Vec<ApplicationId>,
963 #[graphql(default)]
965 #[debug(skip_if = Option::is_none)]
966 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
967 #[graphql(default)]
969 #[debug(skip_if = Option::is_none)]
970 pub make_http_requests: Option<Vec<ApplicationId>>,
971}
972
973impl ApplicationPermissions {
974 pub fn new_single(app_id: ApplicationId) -> Self {
977 Self {
978 execute_operations: Some(vec![app_id]),
979 mandatory_applications: vec![app_id],
980 close_chain: vec![app_id],
981 change_application_permissions: vec![app_id],
982 call_service_as_oracle: Some(vec![app_id]),
983 make_http_requests: Some(vec![app_id]),
984 }
985 }
986
987 pub fn new_multiple(app_ids: Vec<ApplicationId>) -> Self {
990 Self {
991 execute_operations: Some(app_ids.clone()),
992 mandatory_applications: app_ids.clone(),
993 close_chain: app_ids.clone(),
994 change_application_permissions: app_ids.clone(),
995 call_service_as_oracle: Some(app_ids.clone()),
996 make_http_requests: Some(app_ids),
997 }
998 }
999
1000 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
1002 match (app_id, &self.execute_operations) {
1003 (_, None) => true,
1004 (GenericApplicationId::System, Some(_)) => false,
1005 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
1006 }
1007 }
1008
1009 pub fn can_close_chain(&self, app_id: &ApplicationId) -> bool {
1011 self.close_chain.contains(app_id)
1012 }
1013
1014 pub fn can_change_application_permissions(&self, app_id: &ApplicationId) -> bool {
1017 self.change_application_permissions.contains(app_id)
1018 }
1019
1020 pub fn can_call_services(&self, app_id: &ApplicationId) -> bool {
1022 self.call_service_as_oracle
1023 .as_ref()
1024 .is_none_or(|app_ids| app_ids.contains(app_id))
1025 }
1026
1027 pub fn can_make_http_requests(&self, app_id: &ApplicationId) -> bool {
1029 self.make_http_requests
1030 .as_ref()
1031 .is_none_or(|app_ids| app_ids.contains(app_id))
1032 }
1033}
1034
1035#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1037pub enum OracleResponse {
1038 Service(
1040 #[debug(with = "hex_debug")]
1041 #[serde(with = "serde_bytes")]
1042 Vec<u8>,
1043 ),
1044 Http(http::Response),
1046 Blob(BlobId),
1048 Assert,
1050 Round(Option<u32>),
1052 Event(
1054 EventId,
1055 #[debug(with = "hex_debug")]
1056 #[serde(with = "serde_bytes")]
1057 Vec<u8>,
1058 ),
1059 EventExists(EventId),
1061}
1062
1063impl BcsHashable<'_> for OracleResponse {}
1064
1065#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)]
1067pub struct ApplicationDescription {
1068 pub module_id: ModuleId,
1070 pub creator_chain_id: ChainId,
1072 pub block_height: BlockHeight,
1074 pub application_index: u32,
1076 #[serde(with = "serde_bytes")]
1078 #[debug(with = "hex_debug")]
1079 pub parameters: Vec<u8>,
1080 pub required_application_ids: Vec<ApplicationId>,
1082}
1083
1084impl From<&ApplicationDescription> for ApplicationId {
1085 fn from(description: &ApplicationDescription) -> Self {
1086 let mut hash = CryptoHash::new(&BlobContent::new_application_description(description));
1087 if matches!(description.module_id.vm_runtime, VmRuntime::Evm) {
1088 hash.make_evm_compatible();
1089 }
1090 ApplicationId::new(hash)
1091 }
1092}
1093
1094impl BcsHashable<'_> for ApplicationDescription {}
1095
1096impl ApplicationDescription {
1097 pub fn to_bytes(&self) -> Vec<u8> {
1099 bcs::to_bytes(self).expect("Serializing blob bytes should not fail!")
1100 }
1101
1102 pub fn contract_bytecode_blob_id(&self) -> BlobId {
1104 self.module_id.contract_bytecode_blob_id()
1105 }
1106
1107 pub fn service_bytecode_blob_id(&self) -> BlobId {
1109 self.module_id.service_bytecode_blob_id()
1110 }
1111}
1112
1113#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, WitType, WitLoad, WitStore)]
1115pub struct Bytecode {
1116 #[serde(with = "serde_bytes")]
1118 #[debug(with = "hex_debug")]
1119 pub bytes: Vec<u8>,
1120}
1121
1122impl Bytecode {
1123 pub fn new(bytes: Vec<u8>) -> Self {
1125 Bytecode { bytes }
1126 }
1127
1128 pub fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
1130 let bytes = fs::read(path)?;
1131 Ok(Bytecode { bytes })
1132 }
1133
1134 #[cfg(not(target_arch = "wasm32"))]
1136 pub fn compress(&self) -> CompressedBytecode {
1137 #[cfg(with_metrics)]
1138 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1139 let compressed_bytes_vec = zstd::stream::encode_all(&*self.bytes, 19)
1140 .expect("Compressing bytes in memory should not fail");
1141
1142 CompressedBytecode {
1143 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1144 }
1145 }
1146
1147 #[cfg(target_arch = "wasm32")]
1149 pub fn compress(&self) -> CompressedBytecode {
1150 use ruzstd::encoding::{CompressionLevel, FrameCompressor};
1151
1152 #[cfg(with_metrics)]
1153 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1154
1155 let mut compressed_bytes_vec = Vec::new();
1156 let mut compressor = FrameCompressor::new(CompressionLevel::Fastest);
1157 compressor.set_source(&*self.bytes);
1158 compressor.set_drain(&mut compressed_bytes_vec);
1159 compressor.compress();
1160
1161 CompressedBytecode {
1162 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1163 }
1164 }
1165}
1166
1167impl AsRef<[u8]> for Bytecode {
1168 fn as_ref(&self) -> &[u8] {
1169 self.bytes.as_ref()
1170 }
1171}
1172
1173#[derive(Error, Debug)]
1175pub enum DecompressionError {
1176 #[error("Bytecode could not be decompressed: {0}")]
1178 InvalidCompressedBytecode(#[from] io::Error),
1179}
1180
1181#[serde_as]
1183#[derive(Clone, Debug, Deserialize, Hash, Serialize, WitType, WitStore)]
1184#[cfg_attr(with_testing, derive(Eq, PartialEq))]
1185pub struct CompressedBytecode {
1186 #[serde_as(as = "Arc<Bytes>")]
1188 #[debug(skip)]
1189 pub compressed_bytes: Arc<Box<[u8]>>,
1190}
1191
1192#[cfg(not(target_arch = "wasm32"))]
1193impl CompressedBytecode {
1194 pub fn decompressed_size_at_most(
1196 compressed_bytes: &[u8],
1197 limit: u64,
1198 ) -> Result<bool, DecompressionError> {
1199 let mut decoder = zstd::stream::Decoder::new(compressed_bytes)?;
1200 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1201 let mut writer = LimitedWriter::new(io::sink(), limit);
1202 match io::copy(&mut decoder, &mut writer) {
1203 Ok(_) => Ok(true),
1204 Err(error) => {
1205 error.downcast::<LimitedWriterError>()?;
1206 Ok(false)
1207 }
1208 }
1209 }
1210
1211 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1213 #[cfg(with_metrics)]
1214 let _decompression_latency = metrics::BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1215 let bytes = zstd::stream::decode_all(&**self.compressed_bytes)?;
1216
1217 Ok(Bytecode { bytes })
1218 }
1219}
1220
1221#[cfg(target_arch = "wasm32")]
1222impl CompressedBytecode {
1223 pub fn decompressed_size_at_most(
1225 compressed_bytes: &[u8],
1226 limit: u64,
1227 ) -> Result<bool, DecompressionError> {
1228 use ruzstd::decoding::StreamingDecoder;
1229 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1230 let mut writer = LimitedWriter::new(io::sink(), limit);
1231 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
1232
1233 match io::copy(&mut decoder, &mut writer) {
1235 Ok(_) => Ok(true),
1236 Err(error) => {
1237 error.downcast::<LimitedWriterError>()?;
1238 Ok(false)
1239 }
1240 }
1241 }
1242
1243 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1245 use ruzstd::{decoding::StreamingDecoder, io::Read};
1246
1247 #[cfg(with_metrics)]
1248 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1249
1250 let compressed_bytes = &*self.compressed_bytes;
1251 let mut bytes = Vec::new();
1252 let mut decoder = StreamingDecoder::new(&**compressed_bytes).map_err(io::Error::other)?;
1253
1254 while !decoder.get_ref().is_empty() {
1256 decoder
1257 .read_to_end(&mut bytes)
1258 .expect("Reading from a slice in memory should not result in I/O errors");
1259 }
1260
1261 Ok(Bytecode { bytes })
1262 }
1263}
1264
1265impl BcsHashable<'_> for BlobContent {}
1266
1267#[serde_as]
1269#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1270pub struct BlobContent {
1271 blob_type: BlobType,
1273 #[debug(skip)]
1275 #[serde_as(as = "Arc<Bytes>")]
1276 bytes: Arc<Box<[u8]>>,
1277}
1278
1279impl BlobContent {
1280 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1282 let bytes = bytes.into();
1283 BlobContent {
1284 blob_type,
1285 bytes: Arc::new(bytes),
1286 }
1287 }
1288
1289 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1291 BlobContent::new(BlobType::Data, bytes)
1292 }
1293
1294 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1296 BlobContent {
1297 blob_type: BlobType::ContractBytecode,
1298 bytes: compressed_bytecode.compressed_bytes,
1299 }
1300 }
1301
1302 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1304 BlobContent {
1305 blob_type: BlobType::EvmBytecode,
1306 bytes: compressed_bytecode.compressed_bytes,
1307 }
1308 }
1309
1310 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1312 BlobContent {
1313 blob_type: BlobType::ServiceBytecode,
1314 bytes: compressed_bytecode.compressed_bytes,
1315 }
1316 }
1317
1318 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1320 let bytes = application_description.to_bytes();
1321 BlobContent::new(BlobType::ApplicationDescription, bytes)
1322 }
1323
1324 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1326 BlobContent::new(BlobType::Committee, committee)
1327 }
1328
1329 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1331 let bytes = bcs::to_bytes(&chain_description)
1332 .expect("Serializing a ChainDescription should not fail!");
1333 BlobContent::new(BlobType::ChainDescription, bytes)
1334 }
1335
1336 pub fn bytes(&self) -> &[u8] {
1338 &self.bytes
1339 }
1340
1341 pub fn into_vec_or_clone(self) -> Vec<u8> {
1343 let bytes = Arc::unwrap_or_clone(self.bytes);
1344 bytes.into_vec()
1345 }
1346
1347 pub fn into_arc_bytes(self) -> Arc<Box<[u8]>> {
1349 self.bytes
1350 }
1351
1352 pub fn blob_type(&self) -> BlobType {
1354 self.blob_type
1355 }
1356}
1357
1358impl From<Blob> for BlobContent {
1359 fn from(blob: Blob) -> BlobContent {
1360 blob.content
1361 }
1362}
1363
1364#[derive(Debug, Hash, PartialEq, Eq, Clone)]
1366pub struct Blob {
1367 hash: CryptoHash,
1369 content: BlobContent,
1371}
1372
1373impl Blob {
1374 pub fn new(content: BlobContent) -> Self {
1376 let mut hash = CryptoHash::new(&content);
1377 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1378 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1379 .expect("to obtain an application description");
1380 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1381 hash.make_evm_compatible();
1382 }
1383 }
1384 Blob { hash, content }
1385 }
1386
1387 pub fn new_with_hash_unchecked(blob_id: BlobId, content: BlobContent) -> Self {
1389 Blob {
1390 hash: blob_id.hash,
1391 content,
1392 }
1393 }
1394
1395 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1397 let bytes = bytes.into();
1398 Blob {
1399 hash: blob_id.hash,
1400 content: BlobContent {
1401 blob_type: blob_id.blob_type,
1402 bytes: Arc::new(bytes),
1403 },
1404 }
1405 }
1406
1407 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1409 Blob::new(BlobContent::new_data(bytes))
1410 }
1411
1412 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1414 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1415 }
1416
1417 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1419 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1420 }
1421
1422 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1424 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1425 }
1426
1427 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1429 Blob::new(BlobContent::new_application_description(
1430 application_description,
1431 ))
1432 }
1433
1434 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1436 Blob::new(BlobContent::new_committee(committee))
1437 }
1438
1439 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1441 Blob::new(BlobContent::new_chain_description(chain_description))
1442 }
1443
1444 pub fn id(&self) -> BlobId {
1446 BlobId {
1447 hash: self.hash,
1448 blob_type: self.content.blob_type,
1449 }
1450 }
1451
1452 pub fn content(&self) -> &BlobContent {
1454 &self.content
1455 }
1456
1457 pub fn into_content(self) -> BlobContent {
1459 self.content
1460 }
1461
1462 pub fn bytes(&self) -> &[u8] {
1464 self.content.bytes()
1465 }
1466
1467 pub fn load_data_blob_from_file(path: impl AsRef<Path>) -> io::Result<Self> {
1469 Ok(Self::new_data(fs::read(path)?))
1470 }
1471
1472 pub fn is_committee_blob(&self) -> bool {
1474 self.content().blob_type().is_committee_blob()
1475 }
1476}
1477
1478impl Serialize for Blob {
1479 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1480 where
1481 S: Serializer,
1482 {
1483 if serializer.is_human_readable() {
1484 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1485 serializer.serialize_str(&hex::encode(blob_bytes))
1486 } else {
1487 BlobContent::serialize(self.content(), serializer)
1488 }
1489 }
1490}
1491
1492impl<'a> Deserialize<'a> for Blob {
1493 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1494 where
1495 D: Deserializer<'a>,
1496 {
1497 if deserializer.is_human_readable() {
1498 let s = String::deserialize(deserializer)?;
1499 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1500 let content: BlobContent =
1501 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1502
1503 Ok(Blob::new(content))
1504 } else {
1505 let content = BlobContent::deserialize(deserializer)?;
1506 Ok(Blob::new(content))
1507 }
1508 }
1509}
1510
1511impl BcsHashable<'_> for Blob {}
1512
1513#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
1515pub struct Event {
1516 pub stream_id: StreamId,
1518 pub index: u32,
1520 #[debug(with = "hex_debug")]
1522 #[serde(with = "serde_bytes")]
1523 pub value: Vec<u8>,
1524}
1525
1526impl Event {
1527 pub fn id(&self, chain_id: ChainId) -> EventId {
1529 EventId {
1530 chain_id,
1531 stream_id: self.stream_id.clone(),
1532 index: self.index,
1533 }
1534 }
1535}
1536
1537#[derive(Clone, Debug, Serialize, Deserialize, WitType, WitLoad, WitStore)]
1539pub struct StreamUpdate {
1540 pub chain_id: ChainId,
1542 pub stream_id: StreamId,
1544 pub previous_index: u32,
1546 pub next_index: u32,
1548}
1549
1550impl StreamUpdate {
1551 pub fn new_indices(&self) -> impl Iterator<Item = u32> {
1553 self.previous_index..self.next_index
1554 }
1555}
1556
1557impl BcsHashable<'_> for Event {}
1558
1559doc_scalar!(Bytecode, "A module bytecode (WebAssembly or EVM)");
1560doc_scalar!(Amount, "A non-negative amount of tokens.");
1561doc_scalar!(
1562 Epoch,
1563 "A number identifying the configuration of the chain (aka the committee)"
1564);
1565doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
1566doc_scalar!(
1567 Timestamp,
1568 "A timestamp, in microseconds since the Unix epoch"
1569);
1570doc_scalar!(TimeDelta, "A duration in microseconds");
1571doc_scalar!(
1572 Round,
1573 "A number to identify successive attempts to decide a value in a consensus protocol."
1574);
1575doc_scalar!(
1576 ChainDescription,
1577 "Initial chain configuration and chain origin."
1578);
1579doc_scalar!(OracleResponse, "A record of a single oracle response.");
1580doc_scalar!(BlobContent, "A blob of binary data.");
1581doc_scalar!(
1582 Blob,
1583 "A blob of binary data, with its content-addressed blob ID."
1584);
1585doc_scalar!(ApplicationDescription, "Description of a user application");
1586
1587#[cfg(with_metrics)]
1588mod metrics {
1589 use std::sync::LazyLock;
1590
1591 use prometheus::HistogramVec;
1592
1593 use crate::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
1594
1595 pub static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1597 register_histogram_vec(
1598 "bytecode_compression_latency",
1599 "Bytecode compression latency",
1600 &[],
1601 exponential_bucket_latencies(10.0),
1602 )
1603 });
1604
1605 pub static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1607 register_histogram_vec(
1608 "bytecode_decompression_latency",
1609 "Bytecode decompression latency",
1610 &[],
1611 exponential_bucket_latencies(10.0),
1612 )
1613 });
1614}
1615
1616#[cfg(test)]
1617mod tests {
1618 use std::str::FromStr;
1619
1620 use super::{Amount, BlobContent};
1621 use crate::identifiers::BlobType;
1622
1623 #[test]
1624 fn display_amount() {
1625 assert_eq!("1.", Amount::ONE.to_string());
1626 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
1627 assert_eq!(
1628 Amount(10_000_000_000_000_000_000),
1629 Amount::from_str("10").unwrap()
1630 );
1631 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
1632 assert_eq!(
1633 "1001.3",
1634 (Amount::from_str("1.1")
1635 .unwrap()
1636 .saturating_add(Amount::from_str("1_000.2").unwrap()))
1637 .to_string()
1638 );
1639 assert_eq!(
1640 " 1.00000000000000000000",
1641 format!("{:25.20}", Amount::ONE)
1642 );
1643 assert_eq!(
1644 "~+12.34~~",
1645 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
1646 );
1647 }
1648
1649 #[test]
1650 fn blob_content_serialization_deserialization() {
1651 let test_data = b"Hello, world!".as_slice();
1652 let original_blob = BlobContent::new(BlobType::Data, test_data);
1653
1654 let serialized = bcs::to_bytes(&original_blob).expect("Failed to serialize BlobContent");
1655 let deserialized: BlobContent =
1656 bcs::from_bytes(&serialized).expect("Failed to deserialize BlobContent");
1657 assert_eq!(original_blob, deserialized);
1658
1659 let serialized =
1660 serde_json::to_vec(&original_blob).expect("Failed to serialize BlobContent");
1661 let deserialized: BlobContent =
1662 serde_json::from_slice(&serialized).expect("Failed to deserialize BlobContent");
1663 assert_eq!(original_blob, deserialized);
1664 }
1665
1666 #[test]
1667 fn blob_content_hash_consistency() {
1668 let test_data = b"Hello, world!";
1669 let blob1 = BlobContent::new(BlobType::Data, test_data.as_slice());
1670 let blob2 = BlobContent::new(BlobType::Data, Vec::from(test_data.as_slice()));
1671
1672 let hash1 = crate::crypto::CryptoHash::new(&blob1);
1674 let hash2 = crate::crypto::CryptoHash::new(&blob2);
1675
1676 assert_eq!(hash1, hash2, "Hashes should be equal for same content");
1677 assert_eq!(blob1.bytes(), blob2.bytes(), "Byte content should be equal");
1678 }
1679}