1#[cfg(with_testing)]
8use std::ops;
9use std::{
10 collections::BTreeMap,
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_to_read: u32,
271 pub bytes_to_write: u32,
273 pub blobs_to_read: u32,
275 pub blobs_to_publish: u32,
277 pub blob_bytes_to_read: u32,
279 pub blob_bytes_to_publish: u32,
281 pub messages: u32,
283 pub message_size: u32,
286 pub storage_size_delta: u32,
288 pub service_as_oracle_queries: u32,
290 pub http_requests: u32,
292 }
295
296#[derive(Clone, Debug, Deserialize, Serialize, WitLoad, WitType)]
298#[cfg_attr(with_testing, derive(Eq, PartialEq, WitStore))]
299#[witty_specialize_with(Message = Vec<u8>)]
300pub struct SendMessageRequest<Message> {
301 pub destination: ChainId,
303 pub authenticated: bool,
305 pub is_tracked: bool,
307 pub grant: Resources,
309 pub message: Message,
311}
312
313impl<Message> SendMessageRequest<Message>
314where
315 Message: Serialize,
316{
317 pub fn into_raw(self) -> SendMessageRequest<Vec<u8>> {
319 let message = bcs::to_bytes(&self.message).expect("Failed to serialize message");
320
321 SendMessageRequest {
322 destination: self.destination,
323 authenticated: self.authenticated,
324 is_tracked: self.is_tracked,
325 grant: self.grant,
326 message,
327 }
328 }
329}
330
331#[derive(Debug, Error)]
333#[allow(missing_docs)]
334pub enum ArithmeticError {
335 #[error("Number overflow")]
336 Overflow,
337 #[error("Number underflow")]
338 Underflow,
339}
340
341macro_rules! impl_wrapped_number {
342 ($name:ident, $wrapped:ident) => {
343 impl $name {
344 pub const ZERO: Self = Self(0);
346
347 pub const MAX: Self = Self($wrapped::MAX);
349
350 pub fn try_add(self, other: Self) -> Result<Self, ArithmeticError> {
352 let val = self
353 .0
354 .checked_add(other.0)
355 .ok_or(ArithmeticError::Overflow)?;
356 Ok(Self(val))
357 }
358
359 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
361 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
362 Ok(Self(val))
363 }
364
365 pub const fn saturating_add(self, other: Self) -> Self {
367 let val = self.0.saturating_add(other.0);
368 Self(val)
369 }
370
371 pub fn try_sub(self, other: Self) -> Result<Self, ArithmeticError> {
373 let val = self
374 .0
375 .checked_sub(other.0)
376 .ok_or(ArithmeticError::Underflow)?;
377 Ok(Self(val))
378 }
379
380 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
382 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
383 Ok(Self(val))
384 }
385
386 pub const fn saturating_sub(self, other: Self) -> Self {
388 let val = self.0.saturating_sub(other.0);
389 Self(val)
390 }
391
392 pub fn try_add_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
394 self.0 = self
395 .0
396 .checked_add(other.0)
397 .ok_or(ArithmeticError::Overflow)?;
398 Ok(())
399 }
400
401 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
403 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
404 Ok(())
405 }
406
407 pub const fn saturating_add_assign(&mut self, other: Self) {
409 self.0 = self.0.saturating_add(other.0);
410 }
411
412 pub fn try_sub_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
414 self.0 = self
415 .0
416 .checked_sub(other.0)
417 .ok_or(ArithmeticError::Underflow)?;
418 Ok(())
419 }
420
421 pub const fn saturating_mul(&self, other: $wrapped) -> Self {
423 Self(self.0.saturating_mul(other))
424 }
425
426 pub fn try_mul(self, other: $wrapped) -> Result<Self, ArithmeticError> {
428 let val = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
429 Ok(Self(val))
430 }
431
432 pub fn try_mul_assign(&mut self, other: $wrapped) -> Result<(), ArithmeticError> {
434 self.0 = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
435 Ok(())
436 }
437 }
438
439 impl From<$name> for $wrapped {
440 fn from(value: $name) -> Self {
441 value.0
442 }
443 }
444
445 #[cfg(with_testing)]
447 impl From<$wrapped> for $name {
448 fn from(value: $wrapped) -> Self {
449 Self(value)
450 }
451 }
452
453 #[cfg(with_testing)]
454 impl ops::Add for $name {
455 type Output = Self;
456
457 fn add(self, other: Self) -> Self {
458 Self(self.0 + other.0)
459 }
460 }
461
462 #[cfg(with_testing)]
463 impl ops::Sub for $name {
464 type Output = Self;
465
466 fn sub(self, other: Self) -> Self {
467 Self(self.0 - other.0)
468 }
469 }
470
471 #[cfg(with_testing)]
472 impl ops::Mul<$wrapped> for $name {
473 type Output = Self;
474
475 fn mul(self, other: $wrapped) -> Self {
476 Self(self.0 * other)
477 }
478 }
479 };
480}
481
482impl TryFrom<BlockHeight> for usize {
483 type Error = ArithmeticError;
484
485 fn try_from(height: BlockHeight) -> Result<usize, ArithmeticError> {
486 usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)
487 }
488}
489
490pub trait BlockHeightRangeBounds {
492 fn to_inclusive(&self) -> Option<(BlockHeight, BlockHeight)>;
495}
496
497impl<T: RangeBounds<BlockHeight>> BlockHeightRangeBounds for T {
498 fn to_inclusive(&self) -> Option<(BlockHeight, BlockHeight)> {
499 let start = match self.start_bound() {
500 Bound::Included(height) => *height,
501 Bound::Excluded(height) => height.try_add_one().ok()?,
502 Bound::Unbounded => BlockHeight(0),
503 };
504 let end = match self.end_bound() {
505 Bound::Included(height) => *height,
506 Bound::Excluded(height) => height.try_sub_one().ok()?,
507 Bound::Unbounded => BlockHeight::MAX,
508 };
509 if start > end {
510 return None;
511 }
512 Some((start, end))
513 }
514}
515
516impl_wrapped_number!(Amount, u128);
517impl_wrapped_number!(BlockHeight, u64);
518impl_wrapped_number!(TimeDelta, u64);
519
520impl Display for Amount {
521 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522 let places = Amount::DECIMAL_PLACES as usize;
524 let min_digits = places + 1;
525 let decimals = format!("{:0min_digits$}", self.0);
526 let integer_part = &decimals[..(decimals.len() - places)];
527 let fractional_part = decimals[(decimals.len() - places)..].trim_end_matches('0');
528
529 let precision = f.precision().unwrap_or(0).max(fractional_part.len());
531 let sign = if f.sign_plus() && self.0 > 0 { "+" } else { "" };
532 let pad_width = f.width().map_or(0, |w| {
534 w.saturating_sub(precision)
535 .saturating_sub(sign.len() + integer_part.len() + 1)
536 });
537 let left_pad = match f.align() {
538 None | Some(fmt::Alignment::Right) => pad_width,
539 Some(fmt::Alignment::Center) => pad_width / 2,
540 Some(fmt::Alignment::Left) => 0,
541 };
542
543 for _ in 0..left_pad {
544 write!(f, "{}", f.fill())?;
545 }
546 write!(f, "{sign}{integer_part}.{fractional_part:0<precision$}")?;
547 for _ in left_pad..pad_width {
548 write!(f, "{}", f.fill())?;
549 }
550 Ok(())
551 }
552}
553
554#[derive(Error, Debug)]
555#[allow(missing_docs)]
556pub enum ParseAmountError {
557 #[error("cannot parse amount")]
558 Parse,
559 #[error("cannot represent amount: number too high")]
560 TooHigh,
561 #[error("cannot represent amount: too many decimal places after the point")]
562 TooManyDigits,
563}
564
565impl FromStr for Amount {
566 type Err = ParseAmountError;
567
568 fn from_str(src: &str) -> Result<Self, Self::Err> {
569 let mut result: u128 = 0;
570 let mut decimals: Option<u8> = None;
571 let mut chars = src.trim().chars().peekable();
572 if chars.peek() == Some(&'+') {
573 chars.next();
574 }
575 for char in chars {
576 match char {
577 '_' => {}
578 '.' if decimals.is_some() => return Err(ParseAmountError::Parse),
579 '.' => decimals = Some(Amount::DECIMAL_PLACES),
580 char => {
581 let digit = u128::from(char.to_digit(10).ok_or(ParseAmountError::Parse)?);
582 if let Some(d) = &mut decimals {
583 *d = d.checked_sub(1).ok_or(ParseAmountError::TooManyDigits)?;
584 }
585 result = result
586 .checked_mul(10)
587 .and_then(|r| r.checked_add(digit))
588 .ok_or(ParseAmountError::TooHigh)?;
589 }
590 }
591 }
592 result = result
593 .checked_mul(10u128.pow(decimals.unwrap_or(Amount::DECIMAL_PLACES) as u32))
594 .ok_or(ParseAmountError::TooHigh)?;
595 Ok(Amount(result))
596 }
597}
598
599impl Display for BlockHeight {
600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601 self.0.fmt(f)
602 }
603}
604
605impl FromStr for BlockHeight {
606 type Err = ParseIntError;
607
608 fn from_str(src: &str) -> Result<Self, Self::Err> {
609 Ok(Self(u64::from_str(src)?))
610 }
611}
612
613impl Display for Round {
614 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615 match self {
616 Round::Fast => write!(f, "fast round"),
617 Round::MultiLeader(r) => write!(f, "multi-leader round {}", r),
618 Round::SingleLeader(r) => write!(f, "single-leader round {}", r),
619 Round::Validator(r) => write!(f, "validator round {}", r),
620 }
621 }
622}
623
624impl Round {
625 pub fn is_multi_leader(&self) -> bool {
627 matches!(self, Round::MultiLeader(_))
628 }
629
630 pub fn multi_leader(&self) -> Option<u32> {
632 match self {
633 Round::MultiLeader(number) => Some(*number),
634 _ => None,
635 }
636 }
637
638 pub fn is_fast(&self) -> bool {
640 matches!(self, Round::Fast)
641 }
642
643 pub fn number(&self) -> u32 {
645 match self {
646 Round::Fast => 0,
647 Round::MultiLeader(r) | Round::SingleLeader(r) | Round::Validator(r) => *r,
648 }
649 }
650
651 pub fn type_name(&self) -> &'static str {
653 match self {
654 Round::Fast => "fast",
655 Round::MultiLeader(_) => "multi",
656 Round::SingleLeader(_) => "single",
657 Round::Validator(_) => "validator",
658 }
659 }
660}
661
662impl<'a> iter::Sum<&'a Amount> for Amount {
663 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
664 iter.fold(Self::ZERO, |a, b| a.saturating_add(*b))
665 }
666}
667
668impl Amount {
669 pub const DECIMAL_PLACES: u8 = 18;
671
672 pub const ONE: Amount = Amount(10u128.pow(Amount::DECIMAL_PLACES as u32));
674
675 pub const fn from_tokens(tokens: u128) -> Amount {
677 Self::ONE.saturating_mul(tokens)
678 }
679
680 pub const fn from_millis(millitokens: u128) -> Amount {
682 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 3)).saturating_mul(millitokens)
683 }
684
685 pub const fn from_micros(microtokens: u128) -> Amount {
687 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 6)).saturating_mul(microtokens)
688 }
689
690 pub const fn from_nanos(nanotokens: u128) -> Amount {
692 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 9)).saturating_mul(nanotokens)
693 }
694
695 pub const fn from_attos(attotokens: u128) -> Amount {
697 Amount(attotokens)
698 }
699
700 pub const fn upper_half(self) -> u64 {
702 (self.0 >> 64) as u64
703 }
704
705 pub const fn lower_half(self) -> u64 {
707 self.0 as u64
708 }
709
710 pub fn saturating_div(self, other: Amount) -> u128 {
712 self.0.checked_div(other.0).unwrap_or(u128::MAX)
713 }
714
715 pub fn is_zero(&self) -> bool {
717 *self == Amount::ZERO
718 }
719}
720
721#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug, Serialize, Deserialize)]
723pub enum ChainOrigin {
724 Root(u32),
726 Child {
728 parent: ChainId,
730 block_height: BlockHeight,
732 chain_index: u32,
735 },
736}
737
738impl ChainOrigin {
739 pub fn is_child(&self) -> bool {
741 matches!(self, ChainOrigin::Child { .. })
742 }
743}
744
745#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug)]
747pub struct Epoch(pub u32);
748
749impl Epoch {
750 pub const ZERO: Epoch = Epoch(0);
752}
753
754impl Serialize for Epoch {
755 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
756 where
757 S: serde::ser::Serializer,
758 {
759 if serializer.is_human_readable() {
760 serializer.serialize_str(&self.0.to_string())
761 } else {
762 serializer.serialize_newtype_struct("Epoch", &self.0)
763 }
764 }
765}
766
767impl<'de> Deserialize<'de> for Epoch {
768 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
769 where
770 D: serde::de::Deserializer<'de>,
771 {
772 if deserializer.is_human_readable() {
773 let s = String::deserialize(deserializer)?;
774 Ok(Epoch(u32::from_str(&s).map_err(serde::de::Error::custom)?))
775 } else {
776 #[derive(Deserialize)]
777 #[serde(rename = "Epoch")]
778 struct EpochDerived(u32);
779
780 let value = EpochDerived::deserialize(deserializer)?;
781 Ok(Self(value.0))
782 }
783 }
784}
785
786impl std::fmt::Display for Epoch {
787 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
788 write!(f, "{}", self.0)
789 }
790}
791
792impl std::str::FromStr for Epoch {
793 type Err = CryptoError;
794
795 fn from_str(s: &str) -> Result<Self, Self::Err> {
796 Ok(Epoch(s.parse()?))
797 }
798}
799
800impl From<u32> for Epoch {
801 fn from(value: u32) -> Self {
802 Epoch(value)
803 }
804}
805
806impl Epoch {
807 #[inline]
810 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
811 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
812 Ok(Self(val))
813 }
814
815 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
818 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
819 Ok(Self(val))
820 }
821
822 #[inline]
824 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
825 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
826 Ok(())
827 }
828}
829
830#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
832pub struct InitialChainConfig {
833 pub ownership: ChainOwnership,
835 pub epoch: Epoch,
837 pub committees: BTreeMap<Epoch, Vec<u8>>,
839 pub balance: Amount,
841 pub application_permissions: ApplicationPermissions,
843}
844
845#[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize)]
847pub struct ChainDescription {
848 origin: ChainOrigin,
849 timestamp: Timestamp,
850 config: InitialChainConfig,
851}
852
853impl ChainDescription {
854 pub fn new(origin: ChainOrigin, config: InitialChainConfig, timestamp: Timestamp) -> Self {
856 Self {
857 origin,
858 config,
859 timestamp,
860 }
861 }
862
863 pub fn id(&self) -> ChainId {
865 ChainId::from(self)
866 }
867
868 pub fn origin(&self) -> ChainOrigin {
870 self.origin
871 }
872
873 pub fn config(&self) -> &InitialChainConfig {
875 &self.config
876 }
877
878 pub fn timestamp(&self) -> Timestamp {
880 self.timestamp
881 }
882
883 pub fn is_child(&self) -> bool {
885 self.origin.is_child()
886 }
887}
888
889impl BcsHashable<'_> for ChainDescription {}
890
891#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
893pub struct NetworkDescription {
894 pub name: String,
896 pub genesis_config_hash: CryptoHash,
898 pub genesis_timestamp: Timestamp,
900 pub admin_chain_id: ChainId,
902}
903
904#[derive(
906 Default,
907 Debug,
908 PartialEq,
909 Eq,
910 PartialOrd,
911 Ord,
912 Hash,
913 Clone,
914 Serialize,
915 Deserialize,
916 WitType,
917 WitLoad,
918 WitStore,
919 InputObject,
920)]
921pub struct ApplicationPermissions {
922 #[debug(skip_if = Option::is_none)]
926 pub execute_operations: Option<Vec<ApplicationId>>,
927 #[graphql(default)]
930 #[debug(skip_if = Vec::is_empty)]
931 pub mandatory_applications: Vec<ApplicationId>,
932 #[graphql(default)]
934 #[debug(skip_if = Vec::is_empty)]
935 pub close_chain: Vec<ApplicationId>,
936 #[graphql(default)]
938 #[debug(skip_if = Vec::is_empty)]
939 pub change_application_permissions: Vec<ApplicationId>,
940 #[graphql(default)]
942 #[debug(skip_if = Option::is_none)]
943 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
944 #[graphql(default)]
946 #[debug(skip_if = Option::is_none)]
947 pub make_http_requests: Option<Vec<ApplicationId>>,
948}
949
950impl ApplicationPermissions {
951 pub fn new_single(app_id: ApplicationId) -> Self {
954 Self {
955 execute_operations: Some(vec![app_id]),
956 mandatory_applications: vec![app_id],
957 close_chain: vec![app_id],
958 change_application_permissions: vec![app_id],
959 call_service_as_oracle: Some(vec![app_id]),
960 make_http_requests: Some(vec![app_id]),
961 }
962 }
963
964 pub fn new_multiple(app_ids: Vec<ApplicationId>) -> Self {
967 Self {
968 execute_operations: Some(app_ids.clone()),
969 mandatory_applications: app_ids.clone(),
970 close_chain: app_ids.clone(),
971 change_application_permissions: app_ids.clone(),
972 call_service_as_oracle: Some(app_ids.clone()),
973 make_http_requests: Some(app_ids),
974 }
975 }
976
977 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
979 match (app_id, &self.execute_operations) {
980 (_, None) => true,
981 (GenericApplicationId::System, Some(_)) => false,
982 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
983 }
984 }
985
986 pub fn can_close_chain(&self, app_id: &ApplicationId) -> bool {
988 self.close_chain.contains(app_id)
989 }
990
991 pub fn can_change_application_permissions(&self, app_id: &ApplicationId) -> bool {
994 self.change_application_permissions.contains(app_id)
995 }
996
997 pub fn can_call_services(&self, app_id: &ApplicationId) -> bool {
999 self.call_service_as_oracle
1000 .as_ref()
1001 .map(|app_ids| app_ids.contains(app_id))
1002 .unwrap_or(true)
1003 }
1004
1005 pub fn can_make_http_requests(&self, app_id: &ApplicationId) -> bool {
1007 self.make_http_requests
1008 .as_ref()
1009 .map(|app_ids| app_ids.contains(app_id))
1010 .unwrap_or(true)
1011 }
1012}
1013
1014#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1016pub enum OracleResponse {
1017 Service(
1019 #[debug(with = "hex_debug")]
1020 #[serde(with = "serde_bytes")]
1021 Vec<u8>,
1022 ),
1023 Http(http::Response),
1025 Blob(BlobId),
1027 Assert,
1029 Round(Option<u32>),
1031 Event(EventId, Vec<u8>),
1033}
1034
1035impl BcsHashable<'_> for OracleResponse {}
1036
1037#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)]
1039pub struct ApplicationDescription {
1040 pub module_id: ModuleId,
1042 pub creator_chain_id: ChainId,
1044 pub block_height: BlockHeight,
1046 pub application_index: u32,
1048 #[serde(with = "serde_bytes")]
1050 #[debug(with = "hex_debug")]
1051 pub parameters: Vec<u8>,
1052 pub required_application_ids: Vec<ApplicationId>,
1054}
1055
1056impl From<&ApplicationDescription> for ApplicationId {
1057 fn from(description: &ApplicationDescription) -> Self {
1058 let mut hash = CryptoHash::new(&BlobContent::new_application_description(description));
1059 if matches!(description.module_id.vm_runtime, VmRuntime::Evm) {
1060 hash.make_evm_compatible();
1061 }
1062 ApplicationId::new(hash)
1063 }
1064}
1065
1066impl BcsHashable<'_> for ApplicationDescription {}
1067
1068impl ApplicationDescription {
1069 pub fn to_bytes(&self) -> Vec<u8> {
1071 bcs::to_bytes(self).expect("Serializing blob bytes should not fail!")
1072 }
1073
1074 pub fn contract_bytecode_blob_id(&self) -> BlobId {
1076 self.module_id.contract_bytecode_blob_id()
1077 }
1078
1079 pub fn service_bytecode_blob_id(&self) -> BlobId {
1081 self.module_id.service_bytecode_blob_id()
1082 }
1083}
1084
1085#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
1087pub struct Bytecode {
1088 #[serde(with = "serde_bytes")]
1090 #[debug(with = "hex_debug")]
1091 pub bytes: Vec<u8>,
1092}
1093
1094impl Bytecode {
1095 pub fn new(bytes: Vec<u8>) -> Self {
1097 Bytecode { bytes }
1098 }
1099
1100 pub async fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
1102 let bytes = fs::read(path)?;
1103 Ok(Bytecode { bytes })
1104 }
1105
1106 #[cfg(not(target_arch = "wasm32"))]
1108 pub fn compress(&self) -> CompressedBytecode {
1109 #[cfg(with_metrics)]
1110 let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1111 let compressed_bytes = zstd::stream::encode_all(&*self.bytes, 19)
1112 .expect("Compressing bytes in memory should not fail");
1113
1114 CompressedBytecode { compressed_bytes }
1115 }
1116}
1117
1118impl AsRef<[u8]> for Bytecode {
1119 fn as_ref(&self) -> &[u8] {
1120 self.bytes.as_ref()
1121 }
1122}
1123
1124#[derive(Error, Debug)]
1126pub enum DecompressionError {
1127 #[error("Bytecode could not be decompressed: {0}")]
1129 InvalidCompressedBytecode(#[from] io::Error),
1130}
1131
1132#[derive(Clone, Debug, Deserialize, Hash, Serialize, WitType, WitStore)]
1134#[cfg_attr(with_testing, derive(Eq, PartialEq))]
1135pub struct CompressedBytecode {
1136 #[serde(with = "serde_bytes")]
1138 #[debug(with = "hex_debug")]
1139 pub compressed_bytes: Vec<u8>,
1140}
1141
1142#[cfg(not(target_arch = "wasm32"))]
1143impl CompressedBytecode {
1144 pub fn decompressed_size_at_most(
1146 compressed_bytes: &[u8],
1147 limit: u64,
1148 ) -> Result<bool, DecompressionError> {
1149 let mut decoder = zstd::stream::Decoder::new(compressed_bytes)?;
1150 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1151 let mut writer = LimitedWriter::new(io::sink(), limit);
1152 match io::copy(&mut decoder, &mut writer) {
1153 Ok(_) => Ok(true),
1154 Err(error) => {
1155 error.downcast::<LimitedWriterError>()?;
1156 Ok(false)
1157 }
1158 }
1159 }
1160
1161 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1163 #[cfg(with_metrics)]
1164 let _decompression_latency = metrics::BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1165 let bytes = zstd::stream::decode_all(&*self.compressed_bytes)?;
1166
1167 Ok(Bytecode { bytes })
1168 }
1169}
1170
1171#[cfg(target_arch = "wasm32")]
1172impl CompressedBytecode {
1173 pub fn decompressed_size_at_most(
1175 compressed_bytes: &[u8],
1176 limit: u64,
1177 ) -> Result<bool, DecompressionError> {
1178 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
1179 let mut writer = LimitedWriter::new(io::sink(), limit);
1180 let mut decoder = ruzstd::streaming_decoder::StreamingDecoder::new(compressed_bytes)
1181 .map_err(io::Error::other)?;
1182
1183 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 use ruzstd::{io::Read, streaming_decoder::StreamingDecoder};
1196
1197 #[cfg(with_metrics)]
1198 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
1199
1200 let compressed_bytes = &*self.compressed_bytes;
1201 let mut bytes = Vec::new();
1202 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
1203
1204 while !decoder.get_ref().is_empty() {
1206 decoder
1207 .read_to_end(&mut bytes)
1208 .expect("Reading from a slice in memory should not result in I/O errors");
1209 }
1210
1211 Ok(Bytecode { bytes })
1212 }
1213}
1214
1215impl BcsHashable<'_> for BlobContent {}
1216
1217#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1219pub struct BlobContent {
1220 blob_type: BlobType,
1222 #[serde(with = "serde_bytes")]
1224 #[debug(skip)]
1225 bytes: Box<[u8]>,
1226}
1227
1228impl BlobContent {
1229 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1231 let bytes = bytes.into();
1232 BlobContent { blob_type, bytes }
1233 }
1234
1235 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1237 BlobContent::new(BlobType::Data, bytes)
1238 }
1239
1240 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1242 BlobContent::new(
1243 BlobType::ContractBytecode,
1244 compressed_bytecode.compressed_bytes,
1245 )
1246 }
1247
1248 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1250 BlobContent::new(BlobType::EvmBytecode, compressed_bytecode.compressed_bytes)
1251 }
1252
1253 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1255 BlobContent::new(
1256 BlobType::ServiceBytecode,
1257 compressed_bytecode.compressed_bytes,
1258 )
1259 }
1260
1261 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1263 let bytes = application_description.to_bytes();
1264 BlobContent::new(BlobType::ApplicationDescription, bytes)
1265 }
1266
1267 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1269 BlobContent::new(BlobType::Committee, committee)
1270 }
1271
1272 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1274 let bytes = bcs::to_bytes(&chain_description)
1275 .expect("Serializing a ChainDescription should not fail!");
1276 BlobContent::new(BlobType::ChainDescription, bytes)
1277 }
1278
1279 pub fn bytes(&self) -> &[u8] {
1281 &self.bytes
1282 }
1283
1284 pub fn into_bytes(self) -> Box<[u8]> {
1286 self.bytes
1287 }
1288
1289 pub fn blob_type(&self) -> BlobType {
1291 self.blob_type
1292 }
1293}
1294
1295impl From<Blob> for BlobContent {
1296 fn from(blob: Blob) -> BlobContent {
1297 blob.content
1298 }
1299}
1300
1301#[derive(Debug, Hash, PartialEq, Eq, Clone)]
1303pub struct Blob {
1304 hash: CryptoHash,
1306 content: BlobContent,
1308}
1309
1310impl Blob {
1311 pub fn new(content: BlobContent) -> Self {
1313 let mut hash = CryptoHash::new(&content);
1314 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1315 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1316 .expect("to obtain an application description");
1317 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1318 hash.make_evm_compatible();
1319 }
1320 }
1321 Blob { hash, content }
1322 }
1323
1324 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1326 Blob {
1327 hash: blob_id.hash,
1328 content: BlobContent {
1329 blob_type: blob_id.blob_type,
1330 bytes: bytes.into(),
1331 },
1332 }
1333 }
1334
1335 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1337 Blob::new(BlobContent::new_data(bytes))
1338 }
1339
1340 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1342 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1343 }
1344
1345 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1347 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1348 }
1349
1350 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1352 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1353 }
1354
1355 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1357 Blob::new(BlobContent::new_application_description(
1358 application_description,
1359 ))
1360 }
1361
1362 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1364 Blob::new(BlobContent::new_committee(committee))
1365 }
1366
1367 pub fn new_chain_description(chain_description: &ChainDescription) -> Self {
1369 Blob::new(BlobContent::new_chain_description(chain_description))
1370 }
1371
1372 pub fn id(&self) -> BlobId {
1374 BlobId {
1375 hash: self.hash,
1376 blob_type: self.content.blob_type,
1377 }
1378 }
1379
1380 pub fn content(&self) -> &BlobContent {
1382 &self.content
1383 }
1384
1385 pub fn into_content(self) -> BlobContent {
1387 self.content
1388 }
1389
1390 pub fn bytes(&self) -> &[u8] {
1392 self.content.bytes()
1393 }
1394
1395 pub fn into_bytes(self) -> Box<[u8]> {
1397 self.content.into_bytes()
1398 }
1399
1400 pub async fn load_data_blob_from_file(path: impl AsRef<Path>) -> io::Result<Self> {
1402 Ok(Self::new_data(fs::read(path)?))
1403 }
1404
1405 pub fn is_committee_blob(&self) -> bool {
1407 self.content().blob_type().is_committee_blob()
1408 }
1409}
1410
1411impl Serialize for Blob {
1412 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1413 where
1414 S: Serializer,
1415 {
1416 if serializer.is_human_readable() {
1417 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1418 serializer.serialize_str(&hex::encode(blob_bytes))
1419 } else {
1420 BlobContent::serialize(self.content(), serializer)
1421 }
1422 }
1423}
1424
1425impl<'a> Deserialize<'a> for Blob {
1426 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1427 where
1428 D: Deserializer<'a>,
1429 {
1430 if deserializer.is_human_readable() {
1431 let s = String::deserialize(deserializer)?;
1432 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1433 let content: BlobContent =
1434 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1435
1436 Ok(Blob::new(content))
1437 } else {
1438 let content = BlobContent::deserialize(deserializer)?;
1439 Ok(Blob::new(content))
1440 }
1441 }
1442}
1443
1444impl BcsHashable<'_> for Blob {}
1445
1446#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
1448pub struct Event {
1449 pub stream_id: StreamId,
1451 pub index: u32,
1453 #[debug(with = "hex_debug")]
1455 #[serde(with = "serde_bytes")]
1456 pub value: Vec<u8>,
1457}
1458
1459impl Event {
1460 pub fn id(&self, chain_id: ChainId) -> EventId {
1462 EventId {
1463 chain_id,
1464 stream_id: self.stream_id.clone(),
1465 index: self.index,
1466 }
1467 }
1468}
1469
1470#[derive(Clone, Debug, Serialize, Deserialize, WitType, WitLoad, WitStore)]
1472pub struct StreamUpdate {
1473 pub chain_id: ChainId,
1475 pub stream_id: StreamId,
1477 pub previous_index: u32,
1479 pub next_index: u32,
1481}
1482
1483impl StreamUpdate {
1484 pub fn new_indices(&self) -> impl Iterator<Item = u32> {
1486 self.previous_index..self.next_index
1487 }
1488}
1489
1490impl BcsHashable<'_> for Event {}
1491
1492doc_scalar!(Bytecode, "A WebAssembly module's bytecode");
1493doc_scalar!(Amount, "A non-negative amount of tokens.");
1494doc_scalar!(
1495 Epoch,
1496 "A number identifying the configuration of the chain (aka the committee)"
1497);
1498doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
1499doc_scalar!(
1500 Timestamp,
1501 "A timestamp, in microseconds since the Unix epoch"
1502);
1503doc_scalar!(TimeDelta, "A duration in microseconds");
1504doc_scalar!(
1505 Round,
1506 "A number to identify successive attempts to decide a value in a consensus protocol."
1507);
1508doc_scalar!(
1509 ChainDescription,
1510 "Initial chain configuration and chain origin."
1511);
1512doc_scalar!(OracleResponse, "A record of a single oracle response.");
1513doc_scalar!(BlobContent, "A blob of binary data.");
1514doc_scalar!(
1515 Blob,
1516 "A blob of binary data, with its content-addressed blob ID."
1517);
1518doc_scalar!(ApplicationDescription, "Description of a user application");
1519
1520#[cfg(with_metrics)]
1521mod metrics {
1522 use std::sync::LazyLock;
1523
1524 use prometheus::HistogramVec;
1525
1526 use crate::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
1527
1528 pub static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1530 register_histogram_vec(
1531 "bytecode_compression_latency",
1532 "Bytecode compression latency",
1533 &[],
1534 exponential_bucket_latencies(10.0),
1535 )
1536 });
1537
1538 pub static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1540 register_histogram_vec(
1541 "bytecode_decompression_latency",
1542 "Bytecode decompression latency",
1543 &[],
1544 exponential_bucket_latencies(10.0),
1545 )
1546 });
1547}
1548
1549#[cfg(test)]
1550mod tests {
1551 use std::str::FromStr;
1552
1553 use super::Amount;
1554
1555 #[test]
1556 fn display_amount() {
1557 assert_eq!("1.", Amount::ONE.to_string());
1558 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
1559 assert_eq!(
1560 Amount(10_000_000_000_000_000_000),
1561 Amount::from_str("10").unwrap()
1562 );
1563 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
1564 assert_eq!(
1565 "1001.3",
1566 (Amount::from_str("1.1")
1567 .unwrap()
1568 .saturating_add(Amount::from_str("1_000.2").unwrap()))
1569 .to_string()
1570 );
1571 assert_eq!(
1572 " 1.00000000000000000000",
1573 format!("{:25.20}", Amount::ONE)
1574 );
1575 assert_eq!(
1576 "~+12.34~~",
1577 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
1578 );
1579 }
1580}