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