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};
19
20use alloy_primitives::U256;
21use async_graphql::{InputObject, SimpleObject};
22use custom_debug_derive::Debug;
23use linera_witty::{WitLoad, WitStore, WitType};
24use serde::{Deserialize, Deserializer, Serialize, Serializer};
25use thiserror::Error;
26
27#[cfg(with_metrics)]
28use crate::prometheus_util::MeasureLatency as _;
29use crate::{
30 crypto::{BcsHashable, CryptoError, CryptoHash},
31 doc_scalar, hex_debug, http,
32 identifiers::{
33 ApplicationId, BlobId, BlobType, ChainId, EventId, GenericApplicationId, ModuleId, StreamId,
34 },
35 limited_writer::{LimitedWriter, LimitedWriterError},
36 ownership::ChainOwnership,
37 time::{Duration, SystemTime},
38 vm::VmRuntime,
39};
40
41#[derive(
46 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, WitType, WitLoad, WitStore,
47)]
48#[cfg_attr(
49 all(with_testing, not(target_arch = "wasm32")),
50 derive(test_strategy::Arbitrary)
51)]
52pub struct Amount(u128);
53
54#[derive(Serialize, Deserialize)]
55#[serde(rename = "Amount")]
56struct AmountString(String);
57
58#[derive(Serialize, Deserialize)]
59#[serde(rename = "Amount")]
60struct AmountU128(u128);
61
62impl Serialize for Amount {
63 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
64 if serializer.is_human_readable() {
65 AmountString(self.to_string()).serialize(serializer)
66 } else {
67 AmountU128(self.0).serialize(serializer)
68 }
69 }
70}
71
72impl<'de> Deserialize<'de> for Amount {
73 fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
74 if deserializer.is_human_readable() {
75 let AmountString(s) = AmountString::deserialize(deserializer)?;
76 s.parse().map_err(serde::de::Error::custom)
77 } else {
78 Ok(Amount(AmountU128::deserialize(deserializer)?.0))
79 }
80 }
81}
82
83impl From<Amount> for U256 {
84 fn from(amount: Amount) -> U256 {
85 U256::from(amount.0)
86 }
87}
88
89#[derive(
91 Eq,
92 PartialEq,
93 Ord,
94 PartialOrd,
95 Copy,
96 Clone,
97 Hash,
98 Default,
99 Debug,
100 Serialize,
101 Deserialize,
102 WitType,
103 WitLoad,
104 WitStore,
105)]
106#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
107pub struct BlockHeight(pub u64);
108
109#[derive(
111 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Serialize, Deserialize,
112)]
113#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
114pub enum Round {
115 #[default]
117 Fast,
118 MultiLeader(u32),
120 SingleLeader(u32),
122 Validator(u32),
124}
125
126#[derive(
128 Eq,
129 PartialEq,
130 Ord,
131 PartialOrd,
132 Copy,
133 Clone,
134 Hash,
135 Default,
136 Debug,
137 Serialize,
138 Deserialize,
139 WitType,
140 WitLoad,
141 WitStore,
142)]
143pub struct TimeDelta(u64);
144
145impl TimeDelta {
146 pub const fn from_micros(micros: u64) -> Self {
148 TimeDelta(micros)
149 }
150
151 pub const fn from_millis(millis: u64) -> Self {
153 TimeDelta(millis.saturating_mul(1_000))
154 }
155
156 pub const fn from_secs(secs: u64) -> Self {
158 TimeDelta(secs.saturating_mul(1_000_000))
159 }
160
161 pub fn from_duration(duration: Duration) -> Self {
164 TimeDelta::from_micros(u64::try_from(duration.as_micros()).unwrap_or(u64::MAX))
165 }
166
167 pub const fn as_micros(&self) -> u64 {
169 self.0
170 }
171
172 pub const fn as_duration(&self) -> Duration {
174 Duration::from_micros(self.as_micros())
175 }
176}
177
178#[derive(
180 Eq,
181 PartialEq,
182 Ord,
183 PartialOrd,
184 Copy,
185 Clone,
186 Hash,
187 Default,
188 Debug,
189 Serialize,
190 Deserialize,
191 WitType,
192 WitLoad,
193 WitStore,
194)]
195pub struct Timestamp(u64);
196
197impl Timestamp {
198 pub fn now() -> Timestamp {
200 Timestamp(
201 SystemTime::UNIX_EPOCH
202 .elapsed()
203 .expect("system time should be after Unix epoch")
204 .as_micros()
205 .try_into()
206 .unwrap_or(u64::MAX),
207 )
208 }
209
210 pub const fn micros(&self) -> u64 {
212 self.0
213 }
214
215 pub const fn delta_since(&self, other: Timestamp) -> TimeDelta {
218 TimeDelta::from_micros(self.0.saturating_sub(other.0))
219 }
220
221 pub const fn duration_since(&self, other: Timestamp) -> Duration {
224 Duration::from_micros(self.0.saturating_sub(other.0))
225 }
226
227 pub const fn saturating_add(&self, duration: TimeDelta) -> Timestamp {
229 Timestamp(self.0.saturating_add(duration.0))
230 }
231
232 pub const fn saturating_sub(&self, duration: TimeDelta) -> Timestamp {
234 Timestamp(self.0.saturating_sub(duration.0))
235 }
236
237 pub const fn saturating_sub_micros(&self, micros: u64) -> Timestamp {
240 Timestamp(self.0.saturating_sub(micros))
241 }
242}
243
244impl From<u64> for Timestamp {
245 fn from(t: u64) -> Timestamp {
246 Timestamp(t)
247 }
248}
249
250impl Display for Timestamp {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 if let Some(date_time) = chrono::DateTime::from_timestamp(
253 (self.0 / 1_000_000) as i64,
254 ((self.0 % 1_000_000) * 1_000) as u32,
255 ) {
256 return date_time.naive_utc().fmt(f);
257 }
258 self.0.fmt(f)
259 }
260}
261
262#[derive(
265 Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, WitLoad, WitStore, WitType,
266)]
267pub struct Resources {
268 pub wasm_fuel: u64,
270 pub evm_fuel: u64,
272 pub read_operations: u32,
274 pub write_operations: u32,
276 pub bytes_runtime: u32,
278 pub bytes_to_read: u32,
280 pub bytes_to_write: u32,
282 pub blobs_to_read: u32,
284 pub blobs_to_publish: u32,
286 pub blob_bytes_to_read: u32,
288 pub blob_bytes_to_publish: u32,
290 pub messages: u32,
292 pub message_size: u32,
295 pub storage_size_delta: u32,
297 pub service_as_oracle_queries: u32,
299 pub http_requests: u32,
301 }
304
305#[derive(Clone, Debug, Deserialize, Serialize, WitLoad, WitType)]
307#[cfg_attr(with_testing, derive(Eq, PartialEq, WitStore))]
308#[witty_specialize_with(Message = Vec<u8>)]
309pub struct SendMessageRequest<Message> {
310 pub destination: ChainId,
312 pub authenticated: bool,
314 pub is_tracked: bool,
316 pub grant: Resources,
318 pub message: Message,
320}
321
322impl<Message> SendMessageRequest<Message>
323where
324 Message: Serialize,
325{
326 pub fn into_raw(self) -> SendMessageRequest<Vec<u8>> {
328 let message = bcs::to_bytes(&self.message).expect("Failed to serialize message");
329
330 SendMessageRequest {
331 destination: self.destination,
332 authenticated: self.authenticated,
333 is_tracked: self.is_tracked,
334 grant: self.grant,
335 message,
336 }
337 }
338}
339
340#[derive(Debug, Error)]
342#[allow(missing_docs)]
343pub enum ArithmeticError {
344 #[error("Number overflow")]
345 Overflow,
346 #[error("Number underflow")]
347 Underflow,
348}
349
350macro_rules! impl_wrapped_number {
351 ($name:ident, $wrapped:ident) => {
352 impl $name {
353 pub const ZERO: Self = Self(0);
355
356 pub const MAX: Self = Self($wrapped::MAX);
358
359 pub fn try_add(self, other: Self) -> Result<Self, ArithmeticError> {
361 let val = self
362 .0
363 .checked_add(other.0)
364 .ok_or(ArithmeticError::Overflow)?;
365 Ok(Self(val))
366 }
367
368 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
370 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
371 Ok(Self(val))
372 }
373
374 pub const fn saturating_add(self, other: Self) -> Self {
376 let val = self.0.saturating_add(other.0);
377 Self(val)
378 }
379
380 pub fn try_sub(self, other: Self) -> Result<Self, ArithmeticError> {
382 let val = self
383 .0
384 .checked_sub(other.0)
385 .ok_or(ArithmeticError::Underflow)?;
386 Ok(Self(val))
387 }
388
389 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
391 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
392 Ok(Self(val))
393 }
394
395 pub const fn saturating_sub(self, other: Self) -> Self {
397 let val = self.0.saturating_sub(other.0);
398 Self(val)
399 }
400
401 pub fn try_add_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
403 self.0 = self
404 .0
405 .checked_add(other.0)
406 .ok_or(ArithmeticError::Overflow)?;
407 Ok(())
408 }
409
410 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
412 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
413 Ok(())
414 }
415
416 pub const fn saturating_add_assign(&mut self, other: Self) {
418 self.0 = self.0.saturating_add(other.0);
419 }
420
421 pub fn try_sub_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
423 self.0 = self
424 .0
425 .checked_sub(other.0)
426 .ok_or(ArithmeticError::Underflow)?;
427 Ok(())
428 }
429
430 pub const fn saturating_mul(&self, other: $wrapped) -> Self {
432 Self(self.0.saturating_mul(other))
433 }
434
435 pub fn try_mul(self, other: $wrapped) -> Result<Self, ArithmeticError> {
437 let val = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
438 Ok(Self(val))
439 }
440
441 pub fn try_mul_assign(&mut self, other: $wrapped) -> Result<(), ArithmeticError> {
443 self.0 = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
444 Ok(())
445 }
446 }
447
448 impl From<$name> for $wrapped {
449 fn from(value: $name) -> Self {
450 value.0
451 }
452 }
453
454 #[cfg(with_testing)]
456 impl From<$wrapped> for $name {
457 fn from(value: $wrapped) -> Self {
458 Self(value)
459 }
460 }
461
462 #[cfg(with_testing)]
463 impl ops::Add for $name {
464 type Output = Self;
465
466 fn add(self, other: Self) -> Self {
467 Self(self.0 + other.0)
468 }
469 }
470
471 #[cfg(with_testing)]
472 impl ops::Sub for $name {
473 type Output = Self;
474
475 fn sub(self, other: Self) -> Self {
476 Self(self.0 - other.0)
477 }
478 }
479
480 #[cfg(with_testing)]
481 impl ops::Mul<$wrapped> for $name {
482 type Output = Self;
483
484 fn mul(self, other: $wrapped) -> Self {
485 Self(self.0 * other)
486 }
487 }
488 };
489}
490
491impl TryFrom<BlockHeight> for usize {
492 type Error = ArithmeticError;
493
494 fn try_from(height: BlockHeight) -> Result<usize, ArithmeticError> {
495 usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)
496 }
497}
498
499pub trait BlockHeightRangeBounds {
501 fn to_inclusive(&self) -> Option<(BlockHeight, BlockHeight)>;
504}
505
506impl<T: RangeBounds<BlockHeight>> BlockHeightRangeBounds for T {
507 fn to_inclusive(&self) -> Option<(BlockHeight, BlockHeight)> {
508 let start = match self.start_bound() {
509 Bound::Included(height) => *height,
510 Bound::Excluded(height) => height.try_add_one().ok()?,
511 Bound::Unbounded => BlockHeight(0),
512 };
513 let end = match self.end_bound() {
514 Bound::Included(height) => *height,
515 Bound::Excluded(height) => height.try_sub_one().ok()?,
516 Bound::Unbounded => BlockHeight::MAX,
517 };
518 if start > end {
519 return None;
520 }
521 Some((start, end))
522 }
523}
524
525impl_wrapped_number!(Amount, u128);
526impl_wrapped_number!(BlockHeight, u64);
527impl_wrapped_number!(TimeDelta, u64);
528
529impl Display for Amount {
530 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531 let places = Amount::DECIMAL_PLACES as usize;
533 let min_digits = places + 1;
534 let decimals = format!("{:0min_digits$}", self.0);
535 let integer_part = &decimals[..(decimals.len() - places)];
536 let fractional_part = decimals[(decimals.len() - places)..].trim_end_matches('0');
537
538 let precision = f.precision().unwrap_or(0).max(fractional_part.len());
540 let sign = if f.sign_plus() && self.0 > 0 { "+" } else { "" };
541 let pad_width = f.width().map_or(0, |w| {
543 w.saturating_sub(precision)
544 .saturating_sub(sign.len() + integer_part.len() + 1)
545 });
546 let left_pad = match f.align() {
547 None | Some(fmt::Alignment::Right) => pad_width,
548 Some(fmt::Alignment::Center) => pad_width / 2,
549 Some(fmt::Alignment::Left) => 0,
550 };
551
552 for _ in 0..left_pad {
553 write!(f, "{}", f.fill())?;
554 }
555 write!(f, "{sign}{integer_part}.{fractional_part:0<precision$}")?;
556 for _ in left_pad..pad_width {
557 write!(f, "{}", f.fill())?;
558 }
559 Ok(())
560 }
561}
562
563#[derive(Error, Debug)]
564#[allow(missing_docs)]
565pub enum ParseAmountError {
566 #[error("cannot parse amount")]
567 Parse,
568 #[error("cannot represent amount: number too high")]
569 TooHigh,
570 #[error("cannot represent amount: too many decimal places after the point")]
571 TooManyDigits,
572}
573
574impl FromStr for Amount {
575 type Err = ParseAmountError;
576
577 fn from_str(src: &str) -> Result<Self, Self::Err> {
578 let mut result: u128 = 0;
579 let mut decimals: Option<u8> = None;
580 let mut chars = src.trim().chars().peekable();
581 if chars.peek() == Some(&'+') {
582 chars.next();
583 }
584 for char in chars {
585 match char {
586 '_' => {}
587 '.' if decimals.is_some() => return Err(ParseAmountError::Parse),
588 '.' => decimals = Some(Amount::DECIMAL_PLACES),
589 char => {
590 let digit = u128::from(char.to_digit(10).ok_or(ParseAmountError::Parse)?);
591 if let Some(d) = &mut decimals {
592 *d = d.checked_sub(1).ok_or(ParseAmountError::TooManyDigits)?;
593 }
594 result = result
595 .checked_mul(10)
596 .and_then(|r| r.checked_add(digit))
597 .ok_or(ParseAmountError::TooHigh)?;
598 }
599 }
600 }
601 result = result
602 .checked_mul(10u128.pow(decimals.unwrap_or(Amount::DECIMAL_PLACES) as u32))
603 .ok_or(ParseAmountError::TooHigh)?;
604 Ok(Amount(result))
605 }
606}
607
608impl Display for BlockHeight {
609 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610 self.0.fmt(f)
611 }
612}
613
614impl FromStr for BlockHeight {
615 type Err = ParseIntError;
616
617 fn from_str(src: &str) -> Result<Self, Self::Err> {
618 Ok(Self(u64::from_str(src)?))
619 }
620}
621
622impl Display for Round {
623 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
624 match self {
625 Round::Fast => write!(f, "fast round"),
626 Round::MultiLeader(r) => write!(f, "multi-leader round {}", r),
627 Round::SingleLeader(r) => write!(f, "single-leader round {}", r),
628 Round::Validator(r) => write!(f, "validator round {}", r),
629 }
630 }
631}
632
633impl Round {
634 pub fn is_multi_leader(&self) -> bool {
636 matches!(self, Round::MultiLeader(_))
637 }
638
639 pub fn multi_leader(&self) -> Option<u32> {
641 match self {
642 Round::MultiLeader(number) => Some(*number),
643 _ => None,
644 }
645 }
646
647 pub fn is_fast(&self) -> bool {
649 matches!(self, Round::Fast)
650 }
651
652 pub fn number(&self) -> u32 {
654 match self {
655 Round::Fast => 0,
656 Round::MultiLeader(r) | Round::SingleLeader(r) | Round::Validator(r) => *r,
657 }
658 }
659
660 pub fn type_name(&self) -> &'static str {
662 match self {
663 Round::Fast => "fast",
664 Round::MultiLeader(_) => "multi",
665 Round::SingleLeader(_) => "single",
666 Round::Validator(_) => "validator",
667 }
668 }
669}
670
671impl<'a> iter::Sum<&'a Amount> for Amount {
672 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
673 iter.fold(Self::ZERO, |a, b| a.saturating_add(*b))
674 }
675}
676
677impl Amount {
678 pub const DECIMAL_PLACES: u8 = 18;
680
681 pub const ONE: Amount = Amount(10u128.pow(Amount::DECIMAL_PLACES as u32));
683
684 pub const fn from_tokens(tokens: u128) -> Amount {
686 Self::ONE.saturating_mul(tokens)
687 }
688
689 pub const fn from_millis(millitokens: u128) -> Amount {
691 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 3)).saturating_mul(millitokens)
692 }
693
694 pub const fn from_micros(microtokens: u128) -> Amount {
696 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 6)).saturating_mul(microtokens)
697 }
698
699 pub const fn from_nanos(nanotokens: u128) -> Amount {
701 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 9)).saturating_mul(nanotokens)
702 }
703
704 pub const fn from_attos(attotokens: u128) -> Amount {
706 Amount(attotokens)
707 }
708
709 pub const fn upper_half(self) -> u64 {
711 (self.0 >> 64) as u64
712 }
713
714 pub const fn lower_half(self) -> u64 {
716 self.0 as u64
717 }
718
719 pub fn saturating_div(self, other: Amount) -> u128 {
721 self.0.checked_div(other.0).unwrap_or(u128::MAX)
722 }
723
724 pub fn is_zero(&self) -> bool {
726 *self == Amount::ZERO
727 }
728}
729
730#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug, Serialize, Deserialize)]
732pub enum ChainOrigin {
733 Root(u32),
735 Child {
737 parent: ChainId,
739 block_height: BlockHeight,
741 chain_index: u32,
744 },
745}
746
747impl ChainOrigin {
748 pub fn is_child(&self) -> bool {
750 matches!(self, ChainOrigin::Child { .. })
751 }
752}
753
754#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug)]
756pub struct Epoch(pub u32);
757
758impl Epoch {
759 pub const ZERO: Epoch = Epoch(0);
761}
762
763impl Serialize for Epoch {
764 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
765 where
766 S: serde::ser::Serializer,
767 {
768 if serializer.is_human_readable() {
769 serializer.serialize_str(&self.0.to_string())
770 } else {
771 serializer.serialize_newtype_struct("Epoch", &self.0)
772 }
773 }
774}
775
776impl<'de> Deserialize<'de> for Epoch {
777 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
778 where
779 D: serde::de::Deserializer<'de>,
780 {
781 if deserializer.is_human_readable() {
782 let s = String::deserialize(deserializer)?;
783 Ok(Epoch(u32::from_str(&s).map_err(serde::de::Error::custom)?))
784 } else {
785 #[derive(Deserialize)]
786 #[serde(rename = "Epoch")]
787 struct EpochDerived(u32);
788
789 let value = EpochDerived::deserialize(deserializer)?;
790 Ok(Self(value.0))
791 }
792 }
793}
794
795impl std::fmt::Display for Epoch {
796 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
797 write!(f, "{}", self.0)
798 }
799}
800
801impl std::str::FromStr for Epoch {
802 type Err = CryptoError;
803
804 fn from_str(s: &str) -> Result<Self, Self::Err> {
805 Ok(Epoch(s.parse()?))
806 }
807}
808
809impl From<u32> for Epoch {
810 fn from(value: u32) -> Self {
811 Epoch(value)
812 }
813}
814
815impl Epoch {
816 #[inline]
819 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
820 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
821 Ok(Self(val))
822 }
823
824 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
827 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
828 Ok(Self(val))
829 }
830
831 #[inline]
833 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
834 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
835 Ok(())
836 }
837}
838
839#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
841pub struct InitialChainConfig {
842 pub ownership: ChainOwnership,
844 pub epoch: Epoch,
846 pub min_active_epoch: Epoch,
848 pub max_active_epoch: Epoch,
850 pub balance: Amount,
852 pub application_permissions: ApplicationPermissions,
854}
855
856#[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize)]
858pub struct ChainDescription {
859 origin: ChainOrigin,
860 timestamp: Timestamp,
861 config: InitialChainConfig,
862}
863
864impl ChainDescription {
865 pub fn new(origin: ChainOrigin, config: InitialChainConfig, timestamp: Timestamp) -> Self {
867 Self {
868 origin,
869 config,
870 timestamp,
871 }
872 }
873
874 pub fn id(&self) -> ChainId {
876 ChainId::from(self)
877 }
878
879 pub fn origin(&self) -> ChainOrigin {
881 self.origin
882 }
883
884 pub fn config(&self) -> &InitialChainConfig {
886 &self.config
887 }
888
889 pub fn timestamp(&self) -> Timestamp {
891 self.timestamp
892 }
893
894 pub fn is_child(&self) -> bool {
896 self.origin.is_child()
897 }
898}
899
900impl BcsHashable<'_> for ChainDescription {}
901
902#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
904pub struct NetworkDescription {
905 pub name: String,
907 pub genesis_config_hash: CryptoHash,
909 pub genesis_timestamp: Timestamp,
911 pub genesis_committee_blob_hash: CryptoHash,
913 pub admin_chain_id: ChainId,
915}
916
917#[derive(
919 Default,
920 Debug,
921 PartialEq,
922 Eq,
923 PartialOrd,
924 Ord,
925 Hash,
926 Clone,
927 Serialize,
928 Deserialize,
929 WitType,
930 WitLoad,
931 WitStore,
932 InputObject,
933)]
934pub struct ApplicationPermissions {
935 #[debug(skip_if = Option::is_none)]
939 pub execute_operations: Option<Vec<ApplicationId>>,
940 #[graphql(default)]
943 #[debug(skip_if = Vec::is_empty)]
944 pub mandatory_applications: Vec<ApplicationId>,
945 #[graphql(default)]
947 #[debug(skip_if = Vec::is_empty)]
948 pub close_chain: Vec<ApplicationId>,
949 #[graphql(default)]
951 #[debug(skip_if = Vec::is_empty)]
952 pub change_application_permissions: Vec<ApplicationId>,
953 #[graphql(default)]
955 #[debug(skip_if = Option::is_none)]
956 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
957 #[graphql(default)]
959 #[debug(skip_if = Option::is_none)]
960 pub make_http_requests: Option<Vec<ApplicationId>>,
961}
962
963impl ApplicationPermissions {
964 pub fn new_single(app_id: ApplicationId) -> Self {
967 Self {
968 execute_operations: Some(vec![app_id]),
969 mandatory_applications: vec![app_id],
970 close_chain: vec![app_id],
971 change_application_permissions: vec![app_id],
972 call_service_as_oracle: Some(vec![app_id]),
973 make_http_requests: Some(vec![app_id]),
974 }
975 }
976
977 pub fn new_multiple(app_ids: Vec<ApplicationId>) -> Self {
980 Self {
981 execute_operations: Some(app_ids.clone()),
982 mandatory_applications: app_ids.clone(),
983 close_chain: app_ids.clone(),
984 change_application_permissions: app_ids.clone(),
985 call_service_as_oracle: Some(app_ids.clone()),
986 make_http_requests: Some(app_ids),
987 }
988 }
989
990 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
992 match (app_id, &self.execute_operations) {
993 (_, None) => true,
994 (GenericApplicationId::System, Some(_)) => false,
995 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
996 }
997 }
998
999 pub fn can_close_chain(&self, app_id: &ApplicationId) -> bool {
1001 self.close_chain.contains(app_id)
1002 }
1003
1004 pub fn can_change_application_permissions(&self, app_id: &ApplicationId) -> bool {
1007 self.change_application_permissions.contains(app_id)
1008 }
1009
1010 pub fn can_call_services(&self, app_id: &ApplicationId) -> bool {
1012 self.call_service_as_oracle
1013 .as_ref()
1014 .map(|app_ids| app_ids.contains(app_id))
1015 .unwrap_or(true)
1016 }
1017
1018 pub fn can_make_http_requests(&self, app_id: &ApplicationId) -> bool {
1020 self.make_http_requests
1021 .as_ref()
1022 .map(|app_ids| app_ids.contains(app_id))
1023 .unwrap_or(true)
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(EventId, Vec<u8>),
1046 EventExists(EventId),
1048}
1049
1050impl BcsHashable<'_> for OracleResponse {}
1051
1052#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)]
1054pub struct ApplicationDescription {
1055 pub module_id: ModuleId,
1057 pub creator_chain_id: ChainId,
1059 pub block_height: BlockHeight,
1061 pub application_index: u32,
1063 #[serde(with = "serde_bytes")]
1065 #[debug(with = "hex_debug")]
1066 pub parameters: Vec<u8>,
1067 pub required_application_ids: Vec<ApplicationId>,
1069}
1070
1071impl From<&ApplicationDescription> for ApplicationId {
1072 fn from(description: &ApplicationDescription) -> Self {
1073 let mut hash = CryptoHash::new(&BlobContent::new_application_description(description));
1074 if matches!(description.module_id.vm_runtime, VmRuntime::Evm) {
1075 hash.make_evm_compatible();
1076 }
1077 ApplicationId::new(hash)
1078 }
1079}
1080
1081impl BcsHashable<'_> for ApplicationDescription {}
1082
1083impl ApplicationDescription {
1084 pub fn to_bytes(&self) -> Vec<u8> {
1086 bcs::to_bytes(self).expect("Serializing blob bytes should not fail!")
1087 }
1088
1089 pub fn contract_bytecode_blob_id(&self) -> BlobId {
1091 self.module_id.contract_bytecode_blob_id()
1092 }
1093
1094 pub fn service_bytecode_blob_id(&self) -> BlobId {
1096 self.module_id.service_bytecode_blob_id()
1097 }
1098}
1099
1100#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, WitType, WitLoad, WitStore)]
1102pub struct Bytecode {
1103 #[serde(with = "serde_bytes")]
1105 #[debug(with = "hex_debug")]
1106 pub bytes: Vec<u8>,
1107}
1108
1109impl Bytecode {
1110 pub fn new(bytes: Vec<u8>) -> Self {
1112 Bytecode { bytes }
1113 }
1114
1115 pub async fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
1117 let bytes = fs::read(path)?;
1118 Ok(Bytecode { bytes })
1119 }
1120
1121 #[cfg(not(target_arch = "wasm32"))]
1123 pub fn compress(&self) -> CompressedBytecode {
1124 #[cfg(with_metrics)]
1125 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1126 let compressed_bytes = zstd::stream::encode_all(&*self.bytes, 19)
1127 .expect("Compressing bytes in memory should not fail");
1128
1129 CompressedBytecode { compressed_bytes }
1130 }
1131
1132 #[cfg(target_arch = "wasm32")]
1134 pub fn compress(&self) -> CompressedBytecode {
1135 use ruzstd::encoding::{CompressionLevel, FrameCompressor};
1136
1137 #[cfg(with_metrics)]
1138 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1139
1140 let mut compressed_bytes = Vec::new();
1141 let mut compressor = FrameCompressor::new(CompressionLevel::Fastest);
1142 compressor.set_source(&*self.bytes);
1143 compressor.set_drain(&mut compressed_bytes);
1144 compressor.compress();
1145
1146 CompressedBytecode { compressed_bytes }
1147 }
1148}
1149
1150impl AsRef<[u8]> for Bytecode {
1151 fn as_ref(&self) -> &[u8] {
1152 self.bytes.as_ref()
1153 }
1154}
1155
1156#[derive(Error, Debug)]
1158pub enum DecompressionError {
1159 #[error("Bytecode could not be decompressed: {0}")]
1161 InvalidCompressedBytecode(#[from] io::Error),
1162}
1163
1164#[derive(Clone, Debug, Deserialize, Hash, Serialize, WitType, WitStore)]
1166#[cfg_attr(with_testing, derive(Eq, PartialEq))]
1167pub struct CompressedBytecode {
1168 #[serde(with = "serde_bytes")]
1170 #[debug(with = "hex_debug")]
1171 pub compressed_bytes: Vec<u8>,
1172}
1173
1174#[cfg(not(target_arch = "wasm32"))]
1175impl CompressedBytecode {
1176 pub fn decompressed_size_at_most(
1178 compressed_bytes: &[u8],
1179 limit: u64,
1180 ) -> Result<bool, DecompressionError> {
1181 let mut decoder = zstd::stream::Decoder::new(compressed_bytes)?;
1182 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1183 let mut writer = LimitedWriter::new(io::sink(), limit);
1184 match io::copy(&mut decoder, &mut writer) {
1185 Ok(_) => Ok(true),
1186 Err(error) => {
1187 error.downcast::<LimitedWriterError>()?;
1188 Ok(false)
1189 }
1190 }
1191 }
1192
1193 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1195 #[cfg(with_metrics)]
1196 let _decompression_latency = metrics::BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1197 let bytes = zstd::stream::decode_all(&*self.compressed_bytes)?;
1198
1199 Ok(Bytecode { bytes })
1200 }
1201}
1202
1203#[cfg(target_arch = "wasm32")]
1204impl CompressedBytecode {
1205 pub fn decompressed_size_at_most(
1207 compressed_bytes: &[u8],
1208 limit: u64,
1209 ) -> Result<bool, DecompressionError> {
1210 use ruzstd::decoding::StreamingDecoder;
1211 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1212 let mut writer = LimitedWriter::new(io::sink(), limit);
1213 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
1214
1215 match io::copy(&mut decoder, &mut writer) {
1217 Ok(_) => Ok(true),
1218 Err(error) => {
1219 error.downcast::<LimitedWriterError>()?;
1220 Ok(false)
1221 }
1222 }
1223 }
1224
1225 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1227 use ruzstd::{decoding::StreamingDecoder, io::Read};
1228
1229 #[cfg(with_metrics)]
1230 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1231
1232 let compressed_bytes = &*self.compressed_bytes;
1233 let mut bytes = Vec::new();
1234 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
1235
1236 while !decoder.get_ref().is_empty() {
1238 decoder
1239 .read_to_end(&mut bytes)
1240 .expect("Reading from a slice in memory should not result in I/O errors");
1241 }
1242
1243 Ok(Bytecode { bytes })
1244 }
1245}
1246
1247impl BcsHashable<'_> for BlobContent {}
1248
1249#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1251pub struct BlobContent {
1252 blob_type: BlobType,
1254 #[serde(with = "serde_bytes")]
1256 #[debug(skip)]
1257 bytes: Box<[u8]>,
1258}
1259
1260impl BlobContent {
1261 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1263 let bytes = bytes.into();
1264 BlobContent { blob_type, bytes }
1265 }
1266
1267 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1269 BlobContent::new(BlobType::Data, bytes)
1270 }
1271
1272 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1274 BlobContent::new(
1275 BlobType::ContractBytecode,
1276 compressed_bytecode.compressed_bytes,
1277 )
1278 }
1279
1280 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1282 BlobContent::new(BlobType::EvmBytecode, compressed_bytecode.compressed_bytes)
1283 }
1284
1285 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1287 BlobContent::new(
1288 BlobType::ServiceBytecode,
1289 compressed_bytecode.compressed_bytes,
1290 )
1291 }
1292
1293 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1295 let bytes = application_description.to_bytes();
1296 BlobContent::new(BlobType::ApplicationDescription, bytes)
1297 }
1298
1299 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1301 BlobContent::new(BlobType::Committee, committee)
1302 }
1303
1304 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1306 let bytes = bcs::to_bytes(&chain_description)
1307 .expect("Serializing a ChainDescription should not fail!");
1308 BlobContent::new(BlobType::ChainDescription, bytes)
1309 }
1310
1311 pub fn bytes(&self) -> &[u8] {
1313 &self.bytes
1314 }
1315
1316 pub fn into_bytes(self) -> Box<[u8]> {
1318 self.bytes
1319 }
1320
1321 pub fn blob_type(&self) -> BlobType {
1323 self.blob_type
1324 }
1325}
1326
1327impl From<Blob> for BlobContent {
1328 fn from(blob: Blob) -> BlobContent {
1329 blob.content
1330 }
1331}
1332
1333#[derive(Debug, Hash, PartialEq, Eq, Clone)]
1335pub struct Blob {
1336 hash: CryptoHash,
1338 content: BlobContent,
1340}
1341
1342impl Blob {
1343 pub fn new(content: BlobContent) -> Self {
1345 let mut hash = CryptoHash::new(&content);
1346 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1347 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1348 .expect("to obtain an application description");
1349 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1350 hash.make_evm_compatible();
1351 }
1352 }
1353 Blob { hash, content }
1354 }
1355
1356 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1358 Blob {
1359 hash: blob_id.hash,
1360 content: BlobContent {
1361 blob_type: blob_id.blob_type,
1362 bytes: bytes.into(),
1363 },
1364 }
1365 }
1366
1367 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1369 Blob::new(BlobContent::new_data(bytes))
1370 }
1371
1372 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1374 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1375 }
1376
1377 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1379 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1380 }
1381
1382 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1384 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1385 }
1386
1387 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1389 Blob::new(BlobContent::new_application_description(
1390 application_description,
1391 ))
1392 }
1393
1394 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1396 Blob::new(BlobContent::new_committee(committee))
1397 }
1398
1399 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1401 Blob::new(BlobContent::new_chain_description(chain_description))
1402 }
1403
1404 pub fn id(&self) -> BlobId {
1406 BlobId {
1407 hash: self.hash,
1408 blob_type: self.content.blob_type,
1409 }
1410 }
1411
1412 pub fn content(&self) -> &BlobContent {
1414 &self.content
1415 }
1416
1417 pub fn into_content(self) -> BlobContent {
1419 self.content
1420 }
1421
1422 pub fn bytes(&self) -> &[u8] {
1424 self.content.bytes()
1425 }
1426
1427 pub fn into_bytes(self) -> Box<[u8]> {
1429 self.content.into_bytes()
1430 }
1431
1432 pub async fn load_data_blob_from_file(path: impl AsRef<Path>) -> io::Result<Self> {
1434 Ok(Self::new_data(fs::read(path)?))
1435 }
1436
1437 pub fn is_committee_blob(&self) -> bool {
1439 self.content().blob_type().is_committee_blob()
1440 }
1441}
1442
1443impl Serialize for Blob {
1444 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1445 where
1446 S: Serializer,
1447 {
1448 if serializer.is_human_readable() {
1449 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1450 serializer.serialize_str(&hex::encode(blob_bytes))
1451 } else {
1452 BlobContent::serialize(self.content(), serializer)
1453 }
1454 }
1455}
1456
1457impl<'a> Deserialize<'a> for Blob {
1458 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1459 where
1460 D: Deserializer<'a>,
1461 {
1462 if deserializer.is_human_readable() {
1463 let s = String::deserialize(deserializer)?;
1464 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1465 let content: BlobContent =
1466 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1467
1468 Ok(Blob::new(content))
1469 } else {
1470 let content = BlobContent::deserialize(deserializer)?;
1471 Ok(Blob::new(content))
1472 }
1473 }
1474}
1475
1476impl BcsHashable<'_> for Blob {}
1477
1478#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
1480pub struct Event {
1481 pub stream_id: StreamId,
1483 pub index: u32,
1485 #[debug(with = "hex_debug")]
1487 #[serde(with = "serde_bytes")]
1488 pub value: Vec<u8>,
1489}
1490
1491impl Event {
1492 pub fn id(&self, chain_id: ChainId) -> EventId {
1494 EventId {
1495 chain_id,
1496 stream_id: self.stream_id.clone(),
1497 index: self.index,
1498 }
1499 }
1500}
1501
1502#[derive(Clone, Debug, Serialize, Deserialize, WitType, WitLoad, WitStore)]
1504pub struct StreamUpdate {
1505 pub chain_id: ChainId,
1507 pub stream_id: StreamId,
1509 pub previous_index: u32,
1511 pub next_index: u32,
1513}
1514
1515impl StreamUpdate {
1516 pub fn new_indices(&self) -> impl Iterator<Item = u32> {
1518 self.previous_index..self.next_index
1519 }
1520}
1521
1522impl BcsHashable<'_> for Event {}
1523
1524doc_scalar!(Bytecode, "A WebAssembly module's bytecode");
1525doc_scalar!(Amount, "A non-negative amount of tokens.");
1526doc_scalar!(
1527 Epoch,
1528 "A number identifying the configuration of the chain (aka the committee)"
1529);
1530doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
1531doc_scalar!(
1532 Timestamp,
1533 "A timestamp, in microseconds since the Unix epoch"
1534);
1535doc_scalar!(TimeDelta, "A duration in microseconds");
1536doc_scalar!(
1537 Round,
1538 "A number to identify successive attempts to decide a value in a consensus protocol."
1539);
1540doc_scalar!(
1541 ChainDescription,
1542 "Initial chain configuration and chain origin."
1543);
1544doc_scalar!(OracleResponse, "A record of a single oracle response.");
1545doc_scalar!(BlobContent, "A blob of binary data.");
1546doc_scalar!(
1547 Blob,
1548 "A blob of binary data, with its content-addressed blob ID."
1549);
1550doc_scalar!(ApplicationDescription, "Description of a user application");
1551
1552#[cfg(with_metrics)]
1553mod metrics {
1554 use std::sync::LazyLock;
1555
1556 use prometheus::HistogramVec;
1557
1558 use crate::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
1559
1560 pub static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1562 register_histogram_vec(
1563 "bytecode_compression_latency",
1564 "Bytecode compression latency",
1565 &[],
1566 exponential_bucket_latencies(10.0),
1567 )
1568 });
1569
1570 pub static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1572 register_histogram_vec(
1573 "bytecode_decompression_latency",
1574 "Bytecode decompression latency",
1575 &[],
1576 exponential_bucket_latencies(10.0),
1577 )
1578 });
1579}
1580
1581#[cfg(test)]
1582mod tests {
1583 use std::str::FromStr;
1584
1585 use super::Amount;
1586
1587 #[test]
1588 fn display_amount() {
1589 assert_eq!("1.", Amount::ONE.to_string());
1590 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
1591 assert_eq!(
1592 Amount(10_000_000_000_000_000_000),
1593 Amount::from_str("10").unwrap()
1594 );
1595 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
1596 assert_eq!(
1597 "1001.3",
1598 (Amount::from_str("1.1")
1599 .unwrap()
1600 .saturating_add(Amount::from_str("1_000.2").unwrap()))
1601 .to_string()
1602 );
1603 assert_eq!(
1604 " 1.00000000000000000000",
1605 format!("{:25.20}", Amount::ONE)
1606 );
1607 assert_eq!(
1608 "~+12.34~~",
1609 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
1610 );
1611 }
1612}