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
756#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug)]
758pub struct Epoch(pub u32);
759
760impl Epoch {
761 pub const ZERO: Epoch = Epoch(0);
763}
764
765impl Serialize for Epoch {
766 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
767 where
768 S: serde::ser::Serializer,
769 {
770 if serializer.is_human_readable() {
771 serializer.serialize_str(&self.0.to_string())
772 } else {
773 serializer.serialize_newtype_struct("Epoch", &self.0)
774 }
775 }
776}
777
778impl<'de> Deserialize<'de> for Epoch {
779 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
780 where
781 D: serde::de::Deserializer<'de>,
782 {
783 if deserializer.is_human_readable() {
784 let s = String::deserialize(deserializer)?;
785 Ok(Epoch(u32::from_str(&s).map_err(serde::de::Error::custom)?))
786 } else {
787 #[derive(Deserialize)]
788 #[serde(rename = "Epoch")]
789 struct EpochDerived(u32);
790
791 let value = EpochDerived::deserialize(deserializer)?;
792 Ok(Self(value.0))
793 }
794 }
795}
796
797impl std::fmt::Display for Epoch {
798 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
799 write!(f, "{}", self.0)
800 }
801}
802
803impl std::str::FromStr for Epoch {
804 type Err = CryptoError;
805
806 fn from_str(s: &str) -> Result<Self, Self::Err> {
807 Ok(Epoch(s.parse()?))
808 }
809}
810
811impl From<u32> for Epoch {
812 fn from(value: u32) -> Self {
813 Epoch(value)
814 }
815}
816
817impl Epoch {
818 #[inline]
821 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
822 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
823 Ok(Self(val))
824 }
825
826 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
829 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
830 Ok(Self(val))
831 }
832
833 #[inline]
835 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
836 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
837 Ok(())
838 }
839}
840
841#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
843pub struct InitialChainConfig {
844 pub ownership: ChainOwnership,
846 pub epoch: Epoch,
848 pub min_active_epoch: Epoch,
850 pub max_active_epoch: Epoch,
852 pub balance: Amount,
854 pub application_permissions: ApplicationPermissions,
856}
857
858#[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize)]
860pub struct ChainDescription {
861 origin: ChainOrigin,
862 timestamp: Timestamp,
863 config: InitialChainConfig,
864}
865
866impl ChainDescription {
867 pub fn new(origin: ChainOrigin, config: InitialChainConfig, timestamp: Timestamp) -> Self {
869 Self {
870 origin,
871 config,
872 timestamp,
873 }
874 }
875
876 pub fn id(&self) -> ChainId {
878 ChainId::from(self)
879 }
880
881 pub fn origin(&self) -> ChainOrigin {
883 self.origin
884 }
885
886 pub fn config(&self) -> &InitialChainConfig {
888 &self.config
889 }
890
891 pub fn timestamp(&self) -> Timestamp {
893 self.timestamp
894 }
895
896 pub fn is_child(&self) -> bool {
898 self.origin.is_child()
899 }
900}
901
902impl BcsHashable<'_> for ChainDescription {}
903
904#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
906pub struct NetworkDescription {
907 pub name: String,
909 pub genesis_config_hash: CryptoHash,
911 pub genesis_timestamp: Timestamp,
913 pub genesis_committee_blob_hash: CryptoHash,
915 pub admin_chain_id: ChainId,
917}
918
919#[derive(
921 Default,
922 Debug,
923 PartialEq,
924 Eq,
925 PartialOrd,
926 Ord,
927 Hash,
928 Clone,
929 Serialize,
930 Deserialize,
931 WitType,
932 WitLoad,
933 WitStore,
934 InputObject,
935)]
936pub struct ApplicationPermissions {
937 #[debug(skip_if = Option::is_none)]
941 pub execute_operations: Option<Vec<ApplicationId>>,
942 #[graphql(default)]
945 #[debug(skip_if = Vec::is_empty)]
946 pub mandatory_applications: Vec<ApplicationId>,
947 #[graphql(default)]
949 #[debug(skip_if = Vec::is_empty)]
950 pub close_chain: Vec<ApplicationId>,
951 #[graphql(default)]
953 #[debug(skip_if = Vec::is_empty)]
954 pub change_application_permissions: Vec<ApplicationId>,
955 #[graphql(default)]
957 #[debug(skip_if = Option::is_none)]
958 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
959 #[graphql(default)]
961 #[debug(skip_if = Option::is_none)]
962 pub make_http_requests: Option<Vec<ApplicationId>>,
963}
964
965impl ApplicationPermissions {
966 pub fn new_single(app_id: ApplicationId) -> Self {
969 Self {
970 execute_operations: Some(vec![app_id]),
971 mandatory_applications: vec![app_id],
972 close_chain: vec![app_id],
973 change_application_permissions: vec![app_id],
974 call_service_as_oracle: Some(vec![app_id]),
975 make_http_requests: Some(vec![app_id]),
976 }
977 }
978
979 pub fn new_multiple(app_ids: Vec<ApplicationId>) -> Self {
982 Self {
983 execute_operations: Some(app_ids.clone()),
984 mandatory_applications: app_ids.clone(),
985 close_chain: app_ids.clone(),
986 change_application_permissions: app_ids.clone(),
987 call_service_as_oracle: Some(app_ids.clone()),
988 make_http_requests: Some(app_ids),
989 }
990 }
991
992 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
994 match (app_id, &self.execute_operations) {
995 (_, None) => true,
996 (GenericApplicationId::System, Some(_)) => false,
997 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
998 }
999 }
1000
1001 pub fn can_close_chain(&self, app_id: &ApplicationId) -> bool {
1003 self.close_chain.contains(app_id)
1004 }
1005
1006 pub fn can_change_application_permissions(&self, app_id: &ApplicationId) -> bool {
1009 self.change_application_permissions.contains(app_id)
1010 }
1011
1012 pub fn can_call_services(&self, app_id: &ApplicationId) -> bool {
1014 self.call_service_as_oracle
1015 .as_ref()
1016 .is_none_or(|app_ids| app_ids.contains(app_id))
1017 }
1018
1019 pub fn can_make_http_requests(&self, app_id: &ApplicationId) -> bool {
1021 self.make_http_requests
1022 .as_ref()
1023 .is_none_or(|app_ids| app_ids.contains(app_id))
1024 }
1025}
1026
1027#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1029pub enum OracleResponse {
1030 Service(
1032 #[debug(with = "hex_debug")]
1033 #[serde(with = "serde_bytes")]
1034 Vec<u8>,
1035 ),
1036 Http(http::Response),
1038 Blob(BlobId),
1040 Assert,
1042 Round(Option<u32>),
1044 Event(
1046 EventId,
1047 #[debug(with = "hex_debug")]
1048 #[serde(with = "serde_bytes")]
1049 Vec<u8>,
1050 ),
1051 EventExists(EventId),
1053}
1054
1055impl BcsHashable<'_> for OracleResponse {}
1056
1057#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)]
1059pub struct ApplicationDescription {
1060 pub module_id: ModuleId,
1062 pub creator_chain_id: ChainId,
1064 pub block_height: BlockHeight,
1066 pub application_index: u32,
1068 #[serde(with = "serde_bytes")]
1070 #[debug(with = "hex_debug")]
1071 pub parameters: Vec<u8>,
1072 pub required_application_ids: Vec<ApplicationId>,
1074}
1075
1076impl From<&ApplicationDescription> for ApplicationId {
1077 fn from(description: &ApplicationDescription) -> Self {
1078 let mut hash = CryptoHash::new(&BlobContent::new_application_description(description));
1079 if matches!(description.module_id.vm_runtime, VmRuntime::Evm) {
1080 hash.make_evm_compatible();
1081 }
1082 ApplicationId::new(hash)
1083 }
1084}
1085
1086impl BcsHashable<'_> for ApplicationDescription {}
1087
1088impl ApplicationDescription {
1089 pub fn to_bytes(&self) -> Vec<u8> {
1091 bcs::to_bytes(self).expect("Serializing blob bytes should not fail!")
1092 }
1093
1094 pub fn contract_bytecode_blob_id(&self) -> BlobId {
1096 self.module_id.contract_bytecode_blob_id()
1097 }
1098
1099 pub fn service_bytecode_blob_id(&self) -> BlobId {
1101 self.module_id.service_bytecode_blob_id()
1102 }
1103}
1104
1105#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, WitType, WitLoad, WitStore)]
1107pub struct Bytecode {
1108 #[serde(with = "serde_bytes")]
1110 #[debug(with = "hex_debug")]
1111 pub bytes: Vec<u8>,
1112}
1113
1114impl Bytecode {
1115 pub fn new(bytes: Vec<u8>) -> Self {
1117 Bytecode { bytes }
1118 }
1119
1120 pub fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
1122 let bytes = fs::read(path)?;
1123 Ok(Bytecode { bytes })
1124 }
1125
1126 #[cfg(not(target_arch = "wasm32"))]
1128 pub fn compress(&self) -> CompressedBytecode {
1129 #[cfg(with_metrics)]
1130 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1131 let compressed_bytes_vec = zstd::stream::encode_all(&*self.bytes, 19)
1132 .expect("Compressing bytes in memory should not fail");
1133
1134 CompressedBytecode {
1135 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1136 }
1137 }
1138
1139 #[cfg(target_arch = "wasm32")]
1141 pub fn compress(&self) -> CompressedBytecode {
1142 use ruzstd::encoding::{CompressionLevel, FrameCompressor};
1143
1144 #[cfg(with_metrics)]
1145 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1146
1147 let mut compressed_bytes_vec = Vec::new();
1148 let mut compressor = FrameCompressor::new(CompressionLevel::Fastest);
1149 compressor.set_source(&*self.bytes);
1150 compressor.set_drain(&mut compressed_bytes_vec);
1151 compressor.compress();
1152
1153 CompressedBytecode {
1154 compressed_bytes: Arc::new(compressed_bytes_vec.into_boxed_slice()),
1155 }
1156 }
1157}
1158
1159impl AsRef<[u8]> for Bytecode {
1160 fn as_ref(&self) -> &[u8] {
1161 self.bytes.as_ref()
1162 }
1163}
1164
1165#[derive(Error, Debug)]
1167pub enum DecompressionError {
1168 #[error("Bytecode could not be decompressed: {0}")]
1170 InvalidCompressedBytecode(#[from] io::Error),
1171}
1172
1173#[serde_as]
1175#[derive(Clone, Debug, Deserialize, Hash, Serialize, WitType, WitStore)]
1176#[cfg_attr(with_testing, derive(Eq, PartialEq))]
1177pub struct CompressedBytecode {
1178 #[serde_as(as = "Arc<Bytes>")]
1180 #[debug(skip)]
1181 pub compressed_bytes: Arc<Box<[u8]>>,
1182}
1183
1184#[cfg(not(target_arch = "wasm32"))]
1185impl CompressedBytecode {
1186 pub fn decompressed_size_at_most(
1188 compressed_bytes: &[u8],
1189 limit: u64,
1190 ) -> Result<bool, DecompressionError> {
1191 let mut decoder = zstd::stream::Decoder::new(compressed_bytes)?;
1192 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1193 let mut writer = LimitedWriter::new(io::sink(), limit);
1194 match io::copy(&mut decoder, &mut writer) {
1195 Ok(_) => Ok(true),
1196 Err(error) => {
1197 error.downcast::<LimitedWriterError>()?;
1198 Ok(false)
1199 }
1200 }
1201 }
1202
1203 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1205 #[cfg(with_metrics)]
1206 let _decompression_latency = metrics::BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1207 let bytes = zstd::stream::decode_all(&**self.compressed_bytes)?;
1208
1209 Ok(Bytecode { bytes })
1210 }
1211}
1212
1213#[cfg(target_arch = "wasm32")]
1214impl CompressedBytecode {
1215 pub fn decompressed_size_at_most(
1217 compressed_bytes: &[u8],
1218 limit: u64,
1219 ) -> Result<bool, DecompressionError> {
1220 use ruzstd::decoding::StreamingDecoder;
1221 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1222 let mut writer = LimitedWriter::new(io::sink(), limit);
1223 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
1224
1225 match io::copy(&mut decoder, &mut writer) {
1227 Ok(_) => Ok(true),
1228 Err(error) => {
1229 error.downcast::<LimitedWriterError>()?;
1230 Ok(false)
1231 }
1232 }
1233 }
1234
1235 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1237 use ruzstd::{decoding::StreamingDecoder, io::Read};
1238
1239 #[cfg(with_metrics)]
1240 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1241
1242 let compressed_bytes = &*self.compressed_bytes;
1243 let mut bytes = Vec::new();
1244 let mut decoder = StreamingDecoder::new(&**compressed_bytes).map_err(io::Error::other)?;
1245
1246 while !decoder.get_ref().is_empty() {
1248 decoder
1249 .read_to_end(&mut bytes)
1250 .expect("Reading from a slice in memory should not result in I/O errors");
1251 }
1252
1253 Ok(Bytecode { bytes })
1254 }
1255}
1256
1257impl BcsHashable<'_> for BlobContent {}
1258
1259#[serde_as]
1261#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1262pub struct BlobContent {
1263 blob_type: BlobType,
1265 #[debug(skip)]
1267 #[serde_as(as = "Arc<Bytes>")]
1268 bytes: Arc<Box<[u8]>>,
1269}
1270
1271impl BlobContent {
1272 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1274 let bytes = bytes.into();
1275 BlobContent {
1276 blob_type,
1277 bytes: Arc::new(bytes),
1278 }
1279 }
1280
1281 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1283 BlobContent::new(BlobType::Data, bytes)
1284 }
1285
1286 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1288 BlobContent {
1289 blob_type: BlobType::ContractBytecode,
1290 bytes: compressed_bytecode.compressed_bytes,
1291 }
1292 }
1293
1294 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1296 BlobContent {
1297 blob_type: BlobType::EvmBytecode,
1298 bytes: compressed_bytecode.compressed_bytes,
1299 }
1300 }
1301
1302 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1304 BlobContent {
1305 blob_type: BlobType::ServiceBytecode,
1306 bytes: compressed_bytecode.compressed_bytes,
1307 }
1308 }
1309
1310 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1312 let bytes = application_description.to_bytes();
1313 BlobContent::new(BlobType::ApplicationDescription, bytes)
1314 }
1315
1316 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1318 BlobContent::new(BlobType::Committee, committee)
1319 }
1320
1321 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1323 let bytes = bcs::to_bytes(&chain_description)
1324 .expect("Serializing a ChainDescription should not fail!");
1325 BlobContent::new(BlobType::ChainDescription, bytes)
1326 }
1327
1328 pub fn bytes(&self) -> &[u8] {
1330 &self.bytes
1331 }
1332
1333 pub fn into_vec_or_clone(self) -> Vec<u8> {
1335 let bytes = Arc::unwrap_or_clone(self.bytes);
1336 bytes.into_vec()
1337 }
1338
1339 pub fn into_arc_bytes(self) -> Arc<Box<[u8]>> {
1341 self.bytes
1342 }
1343
1344 pub fn blob_type(&self) -> BlobType {
1346 self.blob_type
1347 }
1348}
1349
1350impl From<Blob> for BlobContent {
1351 fn from(blob: Blob) -> BlobContent {
1352 blob.content
1353 }
1354}
1355
1356#[derive(Debug, Hash, PartialEq, Eq, Clone)]
1358pub struct Blob {
1359 hash: CryptoHash,
1361 content: BlobContent,
1363}
1364
1365impl Blob {
1366 pub fn new(content: BlobContent) -> Self {
1368 let mut hash = CryptoHash::new(&content);
1369 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1370 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1371 .expect("to obtain an application description");
1372 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1373 hash.make_evm_compatible();
1374 }
1375 }
1376 Blob { hash, content }
1377 }
1378
1379 pub fn new_with_hash_unchecked(blob_id: BlobId, content: BlobContent) -> Self {
1381 Blob {
1382 hash: blob_id.hash,
1383 content,
1384 }
1385 }
1386
1387 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1389 let bytes = bytes.into();
1390 Blob {
1391 hash: blob_id.hash,
1392 content: BlobContent {
1393 blob_type: blob_id.blob_type,
1394 bytes: Arc::new(bytes),
1395 },
1396 }
1397 }
1398
1399 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1401 Blob::new(BlobContent::new_data(bytes))
1402 }
1403
1404 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1406 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1407 }
1408
1409 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1411 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1412 }
1413
1414 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1416 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1417 }
1418
1419 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1421 Blob::new(BlobContent::new_application_description(
1422 application_description,
1423 ))
1424 }
1425
1426 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1428 Blob::new(BlobContent::new_committee(committee))
1429 }
1430
1431 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1433 Blob::new(BlobContent::new_chain_description(chain_description))
1434 }
1435
1436 pub fn id(&self) -> BlobId {
1438 BlobId {
1439 hash: self.hash,
1440 blob_type: self.content.blob_type,
1441 }
1442 }
1443
1444 pub fn content(&self) -> &BlobContent {
1446 &self.content
1447 }
1448
1449 pub fn into_content(self) -> BlobContent {
1451 self.content
1452 }
1453
1454 pub fn bytes(&self) -> &[u8] {
1456 self.content.bytes()
1457 }
1458
1459 pub fn load_data_blob_from_file(path: impl AsRef<Path>) -> io::Result<Self> {
1461 Ok(Self::new_data(fs::read(path)?))
1462 }
1463
1464 pub fn is_committee_blob(&self) -> bool {
1466 self.content().blob_type().is_committee_blob()
1467 }
1468}
1469
1470impl Serialize for Blob {
1471 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1472 where
1473 S: Serializer,
1474 {
1475 if serializer.is_human_readable() {
1476 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1477 serializer.serialize_str(&hex::encode(blob_bytes))
1478 } else {
1479 BlobContent::serialize(self.content(), serializer)
1480 }
1481 }
1482}
1483
1484impl<'a> Deserialize<'a> for Blob {
1485 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1486 where
1487 D: Deserializer<'a>,
1488 {
1489 if deserializer.is_human_readable() {
1490 let s = String::deserialize(deserializer)?;
1491 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1492 let content: BlobContent =
1493 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1494
1495 Ok(Blob::new(content))
1496 } else {
1497 let content = BlobContent::deserialize(deserializer)?;
1498 Ok(Blob::new(content))
1499 }
1500 }
1501}
1502
1503impl BcsHashable<'_> for Blob {}
1504
1505#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
1507pub struct Event {
1508 pub stream_id: StreamId,
1510 pub index: u32,
1512 #[debug(with = "hex_debug")]
1514 #[serde(with = "serde_bytes")]
1515 pub value: Vec<u8>,
1516}
1517
1518impl Event {
1519 pub fn id(&self, chain_id: ChainId) -> EventId {
1521 EventId {
1522 chain_id,
1523 stream_id: self.stream_id.clone(),
1524 index: self.index,
1525 }
1526 }
1527}
1528
1529#[derive(Clone, Debug, Serialize, Deserialize, WitType, WitLoad, WitStore)]
1531pub struct StreamUpdate {
1532 pub chain_id: ChainId,
1534 pub stream_id: StreamId,
1536 pub previous_index: u32,
1538 pub next_index: u32,
1540}
1541
1542impl StreamUpdate {
1543 pub fn new_indices(&self) -> impl Iterator<Item = u32> {
1545 self.previous_index..self.next_index
1546 }
1547}
1548
1549impl BcsHashable<'_> for Event {}
1550
1551doc_scalar!(Bytecode, "A module bytecode (WebAssembly or EVM)");
1552doc_scalar!(Amount, "A non-negative amount of tokens.");
1553doc_scalar!(
1554 Epoch,
1555 "A number identifying the configuration of the chain (aka the committee)"
1556);
1557doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
1558doc_scalar!(
1559 Timestamp,
1560 "A timestamp, in microseconds since the Unix epoch"
1561);
1562doc_scalar!(TimeDelta, "A duration in microseconds");
1563doc_scalar!(
1564 Round,
1565 "A number to identify successive attempts to decide a value in a consensus protocol."
1566);
1567doc_scalar!(
1568 ChainDescription,
1569 "Initial chain configuration and chain origin."
1570);
1571doc_scalar!(OracleResponse, "A record of a single oracle response.");
1572doc_scalar!(BlobContent, "A blob of binary data.");
1573doc_scalar!(
1574 Blob,
1575 "A blob of binary data, with its content-addressed blob ID."
1576);
1577doc_scalar!(ApplicationDescription, "Description of a user application");
1578
1579#[cfg(with_metrics)]
1580mod metrics {
1581 use std::sync::LazyLock;
1582
1583 use prometheus::HistogramVec;
1584
1585 use crate::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
1586
1587 pub static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1589 register_histogram_vec(
1590 "bytecode_compression_latency",
1591 "Bytecode compression latency",
1592 &[],
1593 exponential_bucket_latencies(10.0),
1594 )
1595 });
1596
1597 pub static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1599 register_histogram_vec(
1600 "bytecode_decompression_latency",
1601 "Bytecode decompression latency",
1602 &[],
1603 exponential_bucket_latencies(10.0),
1604 )
1605 });
1606}
1607
1608#[cfg(test)]
1609mod tests {
1610 use std::str::FromStr;
1611
1612 use super::{Amount, BlobContent};
1613 use crate::identifiers::BlobType;
1614
1615 #[test]
1616 fn display_amount() {
1617 assert_eq!("1.", Amount::ONE.to_string());
1618 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
1619 assert_eq!(
1620 Amount(10_000_000_000_000_000_000),
1621 Amount::from_str("10").unwrap()
1622 );
1623 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
1624 assert_eq!(
1625 "1001.3",
1626 (Amount::from_str("1.1")
1627 .unwrap()
1628 .saturating_add(Amount::from_str("1_000.2").unwrap()))
1629 .to_string()
1630 );
1631 assert_eq!(
1632 " 1.00000000000000000000",
1633 format!("{:25.20}", Amount::ONE)
1634 );
1635 assert_eq!(
1636 "~+12.34~~",
1637 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
1638 );
1639 }
1640
1641 #[test]
1642 fn blob_content_serialization_deserialization() {
1643 let test_data = b"Hello, world!".as_slice();
1644 let original_blob = BlobContent::new(BlobType::Data, test_data);
1645
1646 let serialized = bcs::to_bytes(&original_blob).expect("Failed to serialize BlobContent");
1647 let deserialized: BlobContent =
1648 bcs::from_bytes(&serialized).expect("Failed to deserialize BlobContent");
1649 assert_eq!(original_blob, deserialized);
1650
1651 let serialized =
1652 serde_json::to_vec(&original_blob).expect("Failed to serialize BlobContent");
1653 let deserialized: BlobContent =
1654 serde_json::from_slice(&serialized).expect("Failed to deserialize BlobContent");
1655 assert_eq!(original_blob, deserialized);
1656 }
1657
1658 #[test]
1659 fn blob_content_hash_consistency() {
1660 let test_data = b"Hello, world!";
1661 let blob1 = BlobContent::new(BlobType::Data, test_data.as_slice());
1662 let blob2 = BlobContent::new(BlobType::Data, Vec::from(test_data.as_slice()));
1663
1664 let hash1 = crate::crypto::CryptoHash::new(&blob1);
1666 let hash2 = crate::crypto::CryptoHash::new(&blob2);
1667
1668 assert_eq!(hash1, hash2, "Hashes should be equal for same content");
1669 assert_eq!(blob1.bytes(), blob2.bytes(), "Byte content should be equal");
1670 }
1671}