1#[cfg(with_testing)]
8use std::ops;
9use std::{
10 collections::BTreeSet,
11 fmt::{self, Display},
12 fs,
13 hash::Hash,
14 io, iter,
15 num::ParseIntError,
16 ops::{Bound, RangeBounds},
17 path::Path,
18 str::FromStr,
19};
20
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
83#[derive(
85 Eq,
86 PartialEq,
87 Ord,
88 PartialOrd,
89 Copy,
90 Clone,
91 Hash,
92 Default,
93 Debug,
94 Serialize,
95 Deserialize,
96 WitType,
97 WitLoad,
98 WitStore,
99)]
100#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
101pub struct BlockHeight(pub u64);
102
103#[derive(
105 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Serialize, Deserialize,
106)]
107pub enum Round {
108 #[default]
110 Fast,
111 MultiLeader(u32),
113 SingleLeader(u32),
115 Validator(u32),
117}
118
119#[derive(
121 Eq,
122 PartialEq,
123 Ord,
124 PartialOrd,
125 Copy,
126 Clone,
127 Hash,
128 Default,
129 Debug,
130 Serialize,
131 Deserialize,
132 WitType,
133 WitLoad,
134 WitStore,
135)]
136pub struct TimeDelta(u64);
137
138impl TimeDelta {
139 pub const fn from_micros(micros: u64) -> Self {
141 TimeDelta(micros)
142 }
143
144 pub const fn from_millis(millis: u64) -> Self {
146 TimeDelta(millis.saturating_mul(1_000))
147 }
148
149 pub const fn from_secs(secs: u64) -> Self {
151 TimeDelta(secs.saturating_mul(1_000_000))
152 }
153
154 pub fn from_duration(duration: Duration) -> Self {
157 TimeDelta::from_micros(u64::try_from(duration.as_micros()).unwrap_or(u64::MAX))
158 }
159
160 pub const fn as_micros(&self) -> u64 {
162 self.0
163 }
164
165 pub const fn as_duration(&self) -> Duration {
167 Duration::from_micros(self.as_micros())
168 }
169}
170
171#[derive(
173 Eq,
174 PartialEq,
175 Ord,
176 PartialOrd,
177 Copy,
178 Clone,
179 Hash,
180 Default,
181 Debug,
182 Serialize,
183 Deserialize,
184 WitType,
185 WitLoad,
186 WitStore,
187)]
188pub struct Timestamp(u64);
189
190impl Timestamp {
191 pub fn now() -> Timestamp {
193 Timestamp(
194 SystemTime::UNIX_EPOCH
195 .elapsed()
196 .expect("system time should be after Unix epoch")
197 .as_micros()
198 .try_into()
199 .unwrap_or(u64::MAX),
200 )
201 }
202
203 pub const fn micros(&self) -> u64 {
205 self.0
206 }
207
208 pub const fn delta_since(&self, other: Timestamp) -> TimeDelta {
211 TimeDelta::from_micros(self.0.saturating_sub(other.0))
212 }
213
214 pub const fn duration_since(&self, other: Timestamp) -> Duration {
217 Duration::from_micros(self.0.saturating_sub(other.0))
218 }
219
220 pub const fn saturating_add(&self, duration: TimeDelta) -> Timestamp {
222 Timestamp(self.0.saturating_add(duration.0))
223 }
224
225 pub const fn saturating_sub(&self, duration: TimeDelta) -> Timestamp {
227 Timestamp(self.0.saturating_sub(duration.0))
228 }
229
230 pub const fn saturating_sub_micros(&self, micros: u64) -> Timestamp {
233 Timestamp(self.0.saturating_sub(micros))
234 }
235}
236
237impl From<u64> for Timestamp {
238 fn from(t: u64) -> Timestamp {
239 Timestamp(t)
240 }
241}
242
243impl Display for Timestamp {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 if let Some(date_time) = chrono::DateTime::from_timestamp(
246 (self.0 / 1_000_000) as i64,
247 ((self.0 % 1_000_000) * 1_000) as u32,
248 ) {
249 return date_time.naive_utc().fmt(f);
250 }
251 self.0.fmt(f)
252 }
253}
254
255#[derive(
258 Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, WitLoad, WitStore, WitType,
259)]
260pub struct Resources {
261 pub wasm_fuel: u64,
263 pub evm_fuel: u64,
265 pub read_operations: u32,
267 pub write_operations: u32,
269 pub bytes_runtime: u32,
271 pub bytes_to_read: u32,
273 pub bytes_to_write: u32,
275 pub blobs_to_read: u32,
277 pub blobs_to_publish: u32,
279 pub blob_bytes_to_read: u32,
281 pub blob_bytes_to_publish: u32,
283 pub messages: u32,
285 pub message_size: u32,
288 pub storage_size_delta: u32,
290 pub service_as_oracle_queries: u32,
292 pub http_requests: u32,
294 }
297
298#[derive(Clone, Debug, Deserialize, Serialize, WitLoad, WitType)]
300#[cfg_attr(with_testing, derive(Eq, PartialEq, WitStore))]
301#[witty_specialize_with(Message = Vec<u8>)]
302pub struct SendMessageRequest<Message> {
303 pub destination: ChainId,
305 pub authenticated: bool,
307 pub is_tracked: bool,
309 pub grant: Resources,
311 pub message: Message,
313}
314
315impl<Message> SendMessageRequest<Message>
316where
317 Message: Serialize,
318{
319 pub fn into_raw(self) -> SendMessageRequest<Vec<u8>> {
321 let message = bcs::to_bytes(&self.message).expect("Failed to serialize message");
322
323 SendMessageRequest {
324 destination: self.destination,
325 authenticated: self.authenticated,
326 is_tracked: self.is_tracked,
327 grant: self.grant,
328 message,
329 }
330 }
331}
332
333#[derive(Debug, Error)]
335#[allow(missing_docs)]
336pub enum ArithmeticError {
337 #[error("Number overflow")]
338 Overflow,
339 #[error("Number underflow")]
340 Underflow,
341}
342
343macro_rules! impl_wrapped_number {
344 ($name:ident, $wrapped:ident) => {
345 impl $name {
346 pub const ZERO: Self = Self(0);
348
349 pub const MAX: Self = Self($wrapped::MAX);
351
352 pub fn try_add(self, other: Self) -> Result<Self, ArithmeticError> {
354 let val = self
355 .0
356 .checked_add(other.0)
357 .ok_or(ArithmeticError::Overflow)?;
358 Ok(Self(val))
359 }
360
361 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
363 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
364 Ok(Self(val))
365 }
366
367 pub const fn saturating_add(self, other: Self) -> Self {
369 let val = self.0.saturating_add(other.0);
370 Self(val)
371 }
372
373 pub fn try_sub(self, other: Self) -> Result<Self, ArithmeticError> {
375 let val = self
376 .0
377 .checked_sub(other.0)
378 .ok_or(ArithmeticError::Underflow)?;
379 Ok(Self(val))
380 }
381
382 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
384 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
385 Ok(Self(val))
386 }
387
388 pub const fn saturating_sub(self, other: Self) -> Self {
390 let val = self.0.saturating_sub(other.0);
391 Self(val)
392 }
393
394 pub fn try_add_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
396 self.0 = self
397 .0
398 .checked_add(other.0)
399 .ok_or(ArithmeticError::Overflow)?;
400 Ok(())
401 }
402
403 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
405 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
406 Ok(())
407 }
408
409 pub const fn saturating_add_assign(&mut self, other: Self) {
411 self.0 = self.0.saturating_add(other.0);
412 }
413
414 pub fn try_sub_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
416 self.0 = self
417 .0
418 .checked_sub(other.0)
419 .ok_or(ArithmeticError::Underflow)?;
420 Ok(())
421 }
422
423 pub const fn saturating_mul(&self, other: $wrapped) -> Self {
425 Self(self.0.saturating_mul(other))
426 }
427
428 pub fn try_mul(self, other: $wrapped) -> Result<Self, ArithmeticError> {
430 let val = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
431 Ok(Self(val))
432 }
433
434 pub fn try_mul_assign(&mut self, other: $wrapped) -> Result<(), ArithmeticError> {
436 self.0 = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
437 Ok(())
438 }
439 }
440
441 impl From<$name> for $wrapped {
442 fn from(value: $name) -> Self {
443 value.0
444 }
445 }
446
447 #[cfg(with_testing)]
449 impl From<$wrapped> for $name {
450 fn from(value: $wrapped) -> Self {
451 Self(value)
452 }
453 }
454
455 #[cfg(with_testing)]
456 impl ops::Add for $name {
457 type Output = Self;
458
459 fn add(self, other: Self) -> Self {
460 Self(self.0 + other.0)
461 }
462 }
463
464 #[cfg(with_testing)]
465 impl ops::Sub for $name {
466 type Output = Self;
467
468 fn sub(self, other: Self) -> Self {
469 Self(self.0 - other.0)
470 }
471 }
472
473 #[cfg(with_testing)]
474 impl ops::Mul<$wrapped> for $name {
475 type Output = Self;
476
477 fn mul(self, other: $wrapped) -> Self {
478 Self(self.0 * other)
479 }
480 }
481 };
482}
483
484impl TryFrom<BlockHeight> for usize {
485 type Error = ArithmeticError;
486
487 fn try_from(height: BlockHeight) -> Result<usize, ArithmeticError> {
488 usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)
489 }
490}
491
492pub trait BlockHeightRangeBounds {
494 fn to_inclusive(&self) -> Option<(BlockHeight, BlockHeight)>;
497}
498
499impl<T: RangeBounds<BlockHeight>> BlockHeightRangeBounds for T {
500 fn to_inclusive(&self) -> Option<(BlockHeight, BlockHeight)> {
501 let start = match self.start_bound() {
502 Bound::Included(height) => *height,
503 Bound::Excluded(height) => height.try_add_one().ok()?,
504 Bound::Unbounded => BlockHeight(0),
505 };
506 let end = match self.end_bound() {
507 Bound::Included(height) => *height,
508 Bound::Excluded(height) => height.try_sub_one().ok()?,
509 Bound::Unbounded => BlockHeight::MAX,
510 };
511 if start > end {
512 return None;
513 }
514 Some((start, end))
515 }
516}
517
518impl_wrapped_number!(Amount, u128);
519impl_wrapped_number!(BlockHeight, u64);
520impl_wrapped_number!(TimeDelta, u64);
521
522impl Display for Amount {
523 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
524 let places = Amount::DECIMAL_PLACES as usize;
526 let min_digits = places + 1;
527 let decimals = format!("{:0min_digits$}", self.0);
528 let integer_part = &decimals[..(decimals.len() - places)];
529 let fractional_part = decimals[(decimals.len() - places)..].trim_end_matches('0');
530
531 let precision = f.precision().unwrap_or(0).max(fractional_part.len());
533 let sign = if f.sign_plus() && self.0 > 0 { "+" } else { "" };
534 let pad_width = f.width().map_or(0, |w| {
536 w.saturating_sub(precision)
537 .saturating_sub(sign.len() + integer_part.len() + 1)
538 });
539 let left_pad = match f.align() {
540 None | Some(fmt::Alignment::Right) => pad_width,
541 Some(fmt::Alignment::Center) => pad_width / 2,
542 Some(fmt::Alignment::Left) => 0,
543 };
544
545 for _ in 0..left_pad {
546 write!(f, "{}", f.fill())?;
547 }
548 write!(f, "{sign}{integer_part}.{fractional_part:0<precision$}")?;
549 for _ in left_pad..pad_width {
550 write!(f, "{}", f.fill())?;
551 }
552 Ok(())
553 }
554}
555
556#[derive(Error, Debug)]
557#[allow(missing_docs)]
558pub enum ParseAmountError {
559 #[error("cannot parse amount")]
560 Parse,
561 #[error("cannot represent amount: number too high")]
562 TooHigh,
563 #[error("cannot represent amount: too many decimal places after the point")]
564 TooManyDigits,
565}
566
567impl FromStr for Amount {
568 type Err = ParseAmountError;
569
570 fn from_str(src: &str) -> Result<Self, Self::Err> {
571 let mut result: u128 = 0;
572 let mut decimals: Option<u8> = None;
573 let mut chars = src.trim().chars().peekable();
574 if chars.peek() == Some(&'+') {
575 chars.next();
576 }
577 for char in chars {
578 match char {
579 '_' => {}
580 '.' if decimals.is_some() => return Err(ParseAmountError::Parse),
581 '.' => decimals = Some(Amount::DECIMAL_PLACES),
582 char => {
583 let digit = u128::from(char.to_digit(10).ok_or(ParseAmountError::Parse)?);
584 if let Some(d) = &mut decimals {
585 *d = d.checked_sub(1).ok_or(ParseAmountError::TooManyDigits)?;
586 }
587 result = result
588 .checked_mul(10)
589 .and_then(|r| r.checked_add(digit))
590 .ok_or(ParseAmountError::TooHigh)?;
591 }
592 }
593 }
594 result = result
595 .checked_mul(10u128.pow(decimals.unwrap_or(Amount::DECIMAL_PLACES) as u32))
596 .ok_or(ParseAmountError::TooHigh)?;
597 Ok(Amount(result))
598 }
599}
600
601impl Display for BlockHeight {
602 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
603 self.0.fmt(f)
604 }
605}
606
607impl FromStr for BlockHeight {
608 type Err = ParseIntError;
609
610 fn from_str(src: &str) -> Result<Self, Self::Err> {
611 Ok(Self(u64::from_str(src)?))
612 }
613}
614
615impl Display for Round {
616 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
617 match self {
618 Round::Fast => write!(f, "fast round"),
619 Round::MultiLeader(r) => write!(f, "multi-leader round {}", r),
620 Round::SingleLeader(r) => write!(f, "single-leader round {}", r),
621 Round::Validator(r) => write!(f, "validator round {}", r),
622 }
623 }
624}
625
626impl Round {
627 pub fn is_multi_leader(&self) -> bool {
629 matches!(self, Round::MultiLeader(_))
630 }
631
632 pub fn multi_leader(&self) -> Option<u32> {
634 match self {
635 Round::MultiLeader(number) => Some(*number),
636 _ => None,
637 }
638 }
639
640 pub fn is_fast(&self) -> bool {
642 matches!(self, Round::Fast)
643 }
644
645 pub fn number(&self) -> u32 {
647 match self {
648 Round::Fast => 0,
649 Round::MultiLeader(r) | Round::SingleLeader(r) | Round::Validator(r) => *r,
650 }
651 }
652
653 pub fn type_name(&self) -> &'static str {
655 match self {
656 Round::Fast => "fast",
657 Round::MultiLeader(_) => "multi",
658 Round::SingleLeader(_) => "single",
659 Round::Validator(_) => "validator",
660 }
661 }
662}
663
664impl<'a> iter::Sum<&'a Amount> for Amount {
665 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
666 iter.fold(Self::ZERO, |a, b| a.saturating_add(*b))
667 }
668}
669
670impl Amount {
671 pub const DECIMAL_PLACES: u8 = 18;
673
674 pub const ONE: Amount = Amount(10u128.pow(Amount::DECIMAL_PLACES as u32));
676
677 pub const fn from_tokens(tokens: u128) -> Amount {
679 Self::ONE.saturating_mul(tokens)
680 }
681
682 pub const fn from_millis(millitokens: u128) -> Amount {
684 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 3)).saturating_mul(millitokens)
685 }
686
687 pub const fn from_micros(microtokens: u128) -> Amount {
689 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 6)).saturating_mul(microtokens)
690 }
691
692 pub const fn from_nanos(nanotokens: u128) -> Amount {
694 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 9)).saturating_mul(nanotokens)
695 }
696
697 pub const fn from_attos(attotokens: u128) -> Amount {
699 Amount(attotokens)
700 }
701
702 pub const fn upper_half(self) -> u64 {
704 (self.0 >> 64) as u64
705 }
706
707 pub const fn lower_half(self) -> u64 {
709 self.0 as u64
710 }
711
712 pub fn saturating_div(self, other: Amount) -> u128 {
714 self.0.checked_div(other.0).unwrap_or(u128::MAX)
715 }
716
717 pub fn is_zero(&self) -> bool {
719 *self == Amount::ZERO
720 }
721}
722
723#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug, Serialize, Deserialize)]
725pub enum ChainOrigin {
726 Root(u32),
728 Child {
730 parent: ChainId,
732 block_height: BlockHeight,
734 chain_index: u32,
737 },
738}
739
740impl ChainOrigin {
741 pub fn is_child(&self) -> bool {
743 matches!(self, ChainOrigin::Child { .. })
744 }
745}
746
747#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug)]
749pub struct Epoch(pub u32);
750
751impl Epoch {
752 pub const ZERO: Epoch = Epoch(0);
754}
755
756impl Serialize for Epoch {
757 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
758 where
759 S: serde::ser::Serializer,
760 {
761 if serializer.is_human_readable() {
762 serializer.serialize_str(&self.0.to_string())
763 } else {
764 serializer.serialize_newtype_struct("Epoch", &self.0)
765 }
766 }
767}
768
769impl<'de> Deserialize<'de> for Epoch {
770 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
771 where
772 D: serde::de::Deserializer<'de>,
773 {
774 if deserializer.is_human_readable() {
775 let s = String::deserialize(deserializer)?;
776 Ok(Epoch(u32::from_str(&s).map_err(serde::de::Error::custom)?))
777 } else {
778 #[derive(Deserialize)]
779 #[serde(rename = "Epoch")]
780 struct EpochDerived(u32);
781
782 let value = EpochDerived::deserialize(deserializer)?;
783 Ok(Self(value.0))
784 }
785 }
786}
787
788impl std::fmt::Display for Epoch {
789 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
790 write!(f, "{}", self.0)
791 }
792}
793
794impl std::str::FromStr for Epoch {
795 type Err = CryptoError;
796
797 fn from_str(s: &str) -> Result<Self, Self::Err> {
798 Ok(Epoch(s.parse()?))
799 }
800}
801
802impl From<u32> for Epoch {
803 fn from(value: u32) -> Self {
804 Epoch(value)
805 }
806}
807
808impl Epoch {
809 #[inline]
812 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
813 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
814 Ok(Self(val))
815 }
816
817 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
820 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
821 Ok(Self(val))
822 }
823
824 #[inline]
826 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
827 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
828 Ok(())
829 }
830}
831
832#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
834pub struct InitialChainConfig {
835 pub ownership: ChainOwnership,
837 pub epoch: Epoch,
839 pub active_epochs: BTreeSet<Epoch>,
841 pub balance: Amount,
843 pub application_permissions: ApplicationPermissions,
845}
846
847#[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize)]
849pub struct ChainDescription {
850 origin: ChainOrigin,
851 timestamp: Timestamp,
852 config: InitialChainConfig,
853}
854
855impl ChainDescription {
856 pub fn new(origin: ChainOrigin, config: InitialChainConfig, timestamp: Timestamp) -> Self {
858 Self {
859 origin,
860 config,
861 timestamp,
862 }
863 }
864
865 pub fn id(&self) -> ChainId {
867 ChainId::from(self)
868 }
869
870 pub fn origin(&self) -> ChainOrigin {
872 self.origin
873 }
874
875 pub fn config(&self) -> &InitialChainConfig {
877 &self.config
878 }
879
880 pub fn timestamp(&self) -> Timestamp {
882 self.timestamp
883 }
884
885 pub fn is_child(&self) -> bool {
887 self.origin.is_child()
888 }
889}
890
891impl BcsHashable<'_> for ChainDescription {}
892
893#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
895pub struct NetworkDescription {
896 pub name: String,
898 pub genesis_config_hash: CryptoHash,
900 pub genesis_timestamp: Timestamp,
902 pub genesis_committee_blob_hash: CryptoHash,
904 pub admin_chain_id: ChainId,
906}
907
908#[derive(
910 Default,
911 Debug,
912 PartialEq,
913 Eq,
914 PartialOrd,
915 Ord,
916 Hash,
917 Clone,
918 Serialize,
919 Deserialize,
920 WitType,
921 WitLoad,
922 WitStore,
923 InputObject,
924)]
925pub struct ApplicationPermissions {
926 #[debug(skip_if = Option::is_none)]
930 pub execute_operations: Option<Vec<ApplicationId>>,
931 #[graphql(default)]
934 #[debug(skip_if = Vec::is_empty)]
935 pub mandatory_applications: Vec<ApplicationId>,
936 #[graphql(default)]
938 #[debug(skip_if = Vec::is_empty)]
939 pub close_chain: Vec<ApplicationId>,
940 #[graphql(default)]
942 #[debug(skip_if = Vec::is_empty)]
943 pub change_application_permissions: Vec<ApplicationId>,
944 #[graphql(default)]
946 #[debug(skip_if = Option::is_none)]
947 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
948 #[graphql(default)]
950 #[debug(skip_if = Option::is_none)]
951 pub make_http_requests: Option<Vec<ApplicationId>>,
952}
953
954impl ApplicationPermissions {
955 pub fn new_single(app_id: ApplicationId) -> Self {
958 Self {
959 execute_operations: Some(vec![app_id]),
960 mandatory_applications: vec![app_id],
961 close_chain: vec![app_id],
962 change_application_permissions: vec![app_id],
963 call_service_as_oracle: Some(vec![app_id]),
964 make_http_requests: Some(vec![app_id]),
965 }
966 }
967
968 pub fn new_multiple(app_ids: Vec<ApplicationId>) -> Self {
971 Self {
972 execute_operations: Some(app_ids.clone()),
973 mandatory_applications: app_ids.clone(),
974 close_chain: app_ids.clone(),
975 change_application_permissions: app_ids.clone(),
976 call_service_as_oracle: Some(app_ids.clone()),
977 make_http_requests: Some(app_ids),
978 }
979 }
980
981 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
983 match (app_id, &self.execute_operations) {
984 (_, None) => true,
985 (GenericApplicationId::System, Some(_)) => false,
986 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
987 }
988 }
989
990 pub fn can_close_chain(&self, app_id: &ApplicationId) -> bool {
992 self.close_chain.contains(app_id)
993 }
994
995 pub fn can_change_application_permissions(&self, app_id: &ApplicationId) -> bool {
998 self.change_application_permissions.contains(app_id)
999 }
1000
1001 pub fn can_call_services(&self, app_id: &ApplicationId) -> bool {
1003 self.call_service_as_oracle
1004 .as_ref()
1005 .map(|app_ids| app_ids.contains(app_id))
1006 .unwrap_or(true)
1007 }
1008
1009 pub fn can_make_http_requests(&self, app_id: &ApplicationId) -> bool {
1011 self.make_http_requests
1012 .as_ref()
1013 .map(|app_ids| app_ids.contains(app_id))
1014 .unwrap_or(true)
1015 }
1016}
1017
1018#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1020pub enum OracleResponse {
1021 Service(
1023 #[debug(with = "hex_debug")]
1024 #[serde(with = "serde_bytes")]
1025 Vec<u8>,
1026 ),
1027 Http(http::Response),
1029 Blob(BlobId),
1031 Assert,
1033 Round(Option<u32>),
1035 Event(EventId, Vec<u8>),
1037}
1038
1039impl BcsHashable<'_> for OracleResponse {}
1040
1041#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)]
1043pub struct ApplicationDescription {
1044 pub module_id: ModuleId,
1046 pub creator_chain_id: ChainId,
1048 pub block_height: BlockHeight,
1050 pub application_index: u32,
1052 #[serde(with = "serde_bytes")]
1054 #[debug(with = "hex_debug")]
1055 pub parameters: Vec<u8>,
1056 pub required_application_ids: Vec<ApplicationId>,
1058}
1059
1060impl From<&ApplicationDescription> for ApplicationId {
1061 fn from(description: &ApplicationDescription) -> Self {
1062 let mut hash = CryptoHash::new(&BlobContent::new_application_description(description));
1063 if matches!(description.module_id.vm_runtime, VmRuntime::Evm) {
1064 hash.make_evm_compatible();
1065 }
1066 ApplicationId::new(hash)
1067 }
1068}
1069
1070impl BcsHashable<'_> for ApplicationDescription {}
1071
1072impl ApplicationDescription {
1073 pub fn to_bytes(&self) -> Vec<u8> {
1075 bcs::to_bytes(self).expect("Serializing blob bytes should not fail!")
1076 }
1077
1078 pub fn contract_bytecode_blob_id(&self) -> BlobId {
1080 self.module_id.contract_bytecode_blob_id()
1081 }
1082
1083 pub fn service_bytecode_blob_id(&self) -> BlobId {
1085 self.module_id.service_bytecode_blob_id()
1086 }
1087}
1088
1089#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
1091pub struct Bytecode {
1092 #[serde(with = "serde_bytes")]
1094 #[debug(with = "hex_debug")]
1095 pub bytes: Vec<u8>,
1096}
1097
1098impl Bytecode {
1099 pub fn new(bytes: Vec<u8>) -> Self {
1101 Bytecode { bytes }
1102 }
1103
1104 pub async fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
1106 let bytes = fs::read(path)?;
1107 Ok(Bytecode { bytes })
1108 }
1109
1110 #[cfg(not(target_arch = "wasm32"))]
1112 pub fn compress(&self) -> CompressedBytecode {
1113 #[cfg(with_metrics)]
1114 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1115 let compressed_bytes = zstd::stream::encode_all(&*self.bytes, 19)
1116 .expect("Compressing bytes in memory should not fail");
1117
1118 CompressedBytecode { compressed_bytes }
1119 }
1120}
1121
1122impl AsRef<[u8]> for Bytecode {
1123 fn as_ref(&self) -> &[u8] {
1124 self.bytes.as_ref()
1125 }
1126}
1127
1128#[derive(Error, Debug)]
1130pub enum DecompressionError {
1131 #[error("Bytecode could not be decompressed: {0}")]
1133 InvalidCompressedBytecode(#[from] io::Error),
1134}
1135
1136#[derive(Clone, Debug, Deserialize, Hash, Serialize, WitType, WitStore)]
1138#[cfg_attr(with_testing, derive(Eq, PartialEq))]
1139pub struct CompressedBytecode {
1140 #[serde(with = "serde_bytes")]
1142 #[debug(with = "hex_debug")]
1143 pub compressed_bytes: Vec<u8>,
1144}
1145
1146#[cfg(not(target_arch = "wasm32"))]
1147impl CompressedBytecode {
1148 pub fn decompressed_size_at_most(
1150 compressed_bytes: &[u8],
1151 limit: u64,
1152 ) -> Result<bool, DecompressionError> {
1153 let mut decoder = zstd::stream::Decoder::new(compressed_bytes)?;
1154 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1155 let mut writer = LimitedWriter::new(io::sink(), limit);
1156 match io::copy(&mut decoder, &mut writer) {
1157 Ok(_) => Ok(true),
1158 Err(error) => {
1159 error.downcast::<LimitedWriterError>()?;
1160 Ok(false)
1161 }
1162 }
1163 }
1164
1165 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1167 #[cfg(with_metrics)]
1168 let _decompression_latency = metrics::BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1169 let bytes = zstd::stream::decode_all(&*self.compressed_bytes)?;
1170
1171 Ok(Bytecode { bytes })
1172 }
1173}
1174
1175#[cfg(target_arch = "wasm32")]
1176impl CompressedBytecode {
1177 pub fn decompressed_size_at_most(
1179 compressed_bytes: &[u8],
1180 limit: u64,
1181 ) -> Result<bool, DecompressionError> {
1182 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1183 let mut writer = LimitedWriter::new(io::sink(), limit);
1184 let mut decoder = ruzstd::streaming_decoder::StreamingDecoder::new(compressed_bytes)
1185 .map_err(io::Error::other)?;
1186
1187 match io::copy(&mut decoder, &mut writer) {
1189 Ok(_) => Ok(true),
1190 Err(error) => {
1191 error.downcast::<LimitedWriterError>()?;
1192 Ok(false)
1193 }
1194 }
1195 }
1196
1197 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1199 use ruzstd::{io::Read, streaming_decoder::StreamingDecoder};
1200
1201 #[cfg(with_metrics)]
1202 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1203
1204 let compressed_bytes = &*self.compressed_bytes;
1205 let mut bytes = Vec::new();
1206 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
1207
1208 while !decoder.get_ref().is_empty() {
1210 decoder
1211 .read_to_end(&mut bytes)
1212 .expect("Reading from a slice in memory should not result in I/O errors");
1213 }
1214
1215 Ok(Bytecode { bytes })
1216 }
1217}
1218
1219impl BcsHashable<'_> for BlobContent {}
1220
1221#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1223pub struct BlobContent {
1224 blob_type: BlobType,
1226 #[serde(with = "serde_bytes")]
1228 #[debug(skip)]
1229 bytes: Box<[u8]>,
1230}
1231
1232impl BlobContent {
1233 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1235 let bytes = bytes.into();
1236 BlobContent { blob_type, bytes }
1237 }
1238
1239 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1241 BlobContent::new(BlobType::Data, bytes)
1242 }
1243
1244 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1246 BlobContent::new(
1247 BlobType::ContractBytecode,
1248 compressed_bytecode.compressed_bytes,
1249 )
1250 }
1251
1252 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1254 BlobContent::new(BlobType::EvmBytecode, compressed_bytecode.compressed_bytes)
1255 }
1256
1257 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1259 BlobContent::new(
1260 BlobType::ServiceBytecode,
1261 compressed_bytecode.compressed_bytes,
1262 )
1263 }
1264
1265 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1267 let bytes = application_description.to_bytes();
1268 BlobContent::new(BlobType::ApplicationDescription, bytes)
1269 }
1270
1271 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1273 BlobContent::new(BlobType::Committee, committee)
1274 }
1275
1276 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1278 let bytes = bcs::to_bytes(&chain_description)
1279 .expect("Serializing a ChainDescription should not fail!");
1280 BlobContent::new(BlobType::ChainDescription, bytes)
1281 }
1282
1283 pub fn bytes(&self) -> &[u8] {
1285 &self.bytes
1286 }
1287
1288 pub fn into_bytes(self) -> Box<[u8]> {
1290 self.bytes
1291 }
1292
1293 pub fn blob_type(&self) -> BlobType {
1295 self.blob_type
1296 }
1297}
1298
1299impl From<Blob> for BlobContent {
1300 fn from(blob: Blob) -> BlobContent {
1301 blob.content
1302 }
1303}
1304
1305#[derive(Debug, Hash, PartialEq, Eq, Clone)]
1307pub struct Blob {
1308 hash: CryptoHash,
1310 content: BlobContent,
1312}
1313
1314impl Blob {
1315 pub fn new(content: BlobContent) -> Self {
1317 let mut hash = CryptoHash::new(&content);
1318 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1319 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1320 .expect("to obtain an application description");
1321 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1322 hash.make_evm_compatible();
1323 }
1324 }
1325 Blob { hash, content }
1326 }
1327
1328 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1330 Blob {
1331 hash: blob_id.hash,
1332 content: BlobContent {
1333 blob_type: blob_id.blob_type,
1334 bytes: bytes.into(),
1335 },
1336 }
1337 }
1338
1339 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1341 Blob::new(BlobContent::new_data(bytes))
1342 }
1343
1344 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1346 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1347 }
1348
1349 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1351 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1352 }
1353
1354 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1356 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1357 }
1358
1359 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1361 Blob::new(BlobContent::new_application_description(
1362 application_description,
1363 ))
1364 }
1365
1366 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1368 Blob::new(BlobContent::new_committee(committee))
1369 }
1370
1371 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1373 Blob::new(BlobContent::new_chain_description(chain_description))
1374 }
1375
1376 pub fn id(&self) -> BlobId {
1378 BlobId {
1379 hash: self.hash,
1380 blob_type: self.content.blob_type,
1381 }
1382 }
1383
1384 pub fn content(&self) -> &BlobContent {
1386 &self.content
1387 }
1388
1389 pub fn into_content(self) -> BlobContent {
1391 self.content
1392 }
1393
1394 pub fn bytes(&self) -> &[u8] {
1396 self.content.bytes()
1397 }
1398
1399 pub fn into_bytes(self) -> Box<[u8]> {
1401 self.content.into_bytes()
1402 }
1403
1404 pub async fn load_data_blob_from_file(path: impl AsRef<Path>) -> io::Result<Self> {
1406 Ok(Self::new_data(fs::read(path)?))
1407 }
1408
1409 pub fn is_committee_blob(&self) -> bool {
1411 self.content().blob_type().is_committee_blob()
1412 }
1413}
1414
1415impl Serialize for Blob {
1416 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1417 where
1418 S: Serializer,
1419 {
1420 if serializer.is_human_readable() {
1421 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1422 serializer.serialize_str(&hex::encode(blob_bytes))
1423 } else {
1424 BlobContent::serialize(self.content(), serializer)
1425 }
1426 }
1427}
1428
1429impl<'a> Deserialize<'a> for Blob {
1430 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1431 where
1432 D: Deserializer<'a>,
1433 {
1434 if deserializer.is_human_readable() {
1435 let s = String::deserialize(deserializer)?;
1436 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1437 let content: BlobContent =
1438 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1439
1440 Ok(Blob::new(content))
1441 } else {
1442 let content = BlobContent::deserialize(deserializer)?;
1443 Ok(Blob::new(content))
1444 }
1445 }
1446}
1447
1448impl BcsHashable<'_> for Blob {}
1449
1450#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
1452pub struct Event {
1453 pub stream_id: StreamId,
1455 pub index: u32,
1457 #[debug(with = "hex_debug")]
1459 #[serde(with = "serde_bytes")]
1460 pub value: Vec<u8>,
1461}
1462
1463impl Event {
1464 pub fn id(&self, chain_id: ChainId) -> EventId {
1466 EventId {
1467 chain_id,
1468 stream_id: self.stream_id.clone(),
1469 index: self.index,
1470 }
1471 }
1472}
1473
1474#[derive(Clone, Debug, Serialize, Deserialize, WitType, WitLoad, WitStore)]
1476pub struct StreamUpdate {
1477 pub chain_id: ChainId,
1479 pub stream_id: StreamId,
1481 pub previous_index: u32,
1483 pub next_index: u32,
1485}
1486
1487impl StreamUpdate {
1488 pub fn new_indices(&self) -> impl Iterator<Item = u32> {
1490 self.previous_index..self.next_index
1491 }
1492}
1493
1494impl BcsHashable<'_> for Event {}
1495
1496doc_scalar!(Bytecode, "A WebAssembly module's bytecode");
1497doc_scalar!(Amount, "A non-negative amount of tokens.");
1498doc_scalar!(
1499 Epoch,
1500 "A number identifying the configuration of the chain (aka the committee)"
1501);
1502doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
1503doc_scalar!(
1504 Timestamp,
1505 "A timestamp, in microseconds since the Unix epoch"
1506);
1507doc_scalar!(TimeDelta, "A duration in microseconds");
1508doc_scalar!(
1509 Round,
1510 "A number to identify successive attempts to decide a value in a consensus protocol."
1511);
1512doc_scalar!(
1513 ChainDescription,
1514 "Initial chain configuration and chain origin."
1515);
1516doc_scalar!(OracleResponse, "A record of a single oracle response.");
1517doc_scalar!(BlobContent, "A blob of binary data.");
1518doc_scalar!(
1519 Blob,
1520 "A blob of binary data, with its content-addressed blob ID."
1521);
1522doc_scalar!(ApplicationDescription, "Description of a user application");
1523
1524#[cfg(with_metrics)]
1525mod metrics {
1526 use std::sync::LazyLock;
1527
1528 use prometheus::HistogramVec;
1529
1530 use crate::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
1531
1532 pub static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1534 register_histogram_vec(
1535 "bytecode_compression_latency",
1536 "Bytecode compression latency",
1537 &[],
1538 exponential_bucket_latencies(10.0),
1539 )
1540 });
1541
1542 pub static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1544 register_histogram_vec(
1545 "bytecode_decompression_latency",
1546 "Bytecode decompression latency",
1547 &[],
1548 exponential_bucket_latencies(10.0),
1549 )
1550 });
1551}
1552
1553#[cfg(test)]
1554mod tests {
1555 use std::str::FromStr;
1556
1557 use super::Amount;
1558
1559 #[test]
1560 fn display_amount() {
1561 assert_eq!("1.", Amount::ONE.to_string());
1562 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
1563 assert_eq!(
1564 Amount(10_000_000_000_000_000_000),
1565 Amount::from_str("10").unwrap()
1566 );
1567 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
1568 assert_eq!(
1569 "1001.3",
1570 (Amount::from_str("1.1")
1571 .unwrap()
1572 .saturating_add(Amount::from_str("1_000.2").unwrap()))
1573 .to_string()
1574 );
1575 assert_eq!(
1576 " 1.00000000000000000000",
1577 format!("{:25.20}", Amount::ONE)
1578 );
1579 assert_eq!(
1580 "~+12.34~~",
1581 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
1582 );
1583 }
1584}