1#[cfg(with_testing)]
8use std::ops;
9#[cfg(with_metrics)]
10use std::sync::LazyLock;
11use std::{
12 fmt::{self, Display},
13 fs,
14 hash::Hash,
15 io, iter,
16 num::ParseIntError,
17 path::Path,
18 str::FromStr,
19};
20
21use async_graphql::{InputObject, SimpleObject};
22use custom_debug_derive::Debug;
23use linera_witty::{WitLoad, WitStore, WitType};
24#[cfg(with_metrics)]
25use prometheus::HistogramVec;
26use serde::{Deserialize, Deserializer, Serialize, Serializer};
27use thiserror::Error;
28
29#[cfg(with_metrics)]
30use crate::prometheus_util::{
31 exponential_bucket_latencies, register_histogram_vec, MeasureLatency,
32};
33use crate::{
34 crypto::{BcsHashable, CryptoHash},
35 doc_scalar, hex_debug, http,
36 identifiers::{
37 ApplicationId, BlobId, BlobType, ChainId, Destination, EventId, GenericApplicationId,
38 ModuleId, StreamId,
39 },
40 limited_writer::{LimitedWriter, LimitedWriterError},
41 time::{Duration, SystemTime},
42 vm::VmRuntime,
43};
44
45#[derive(
50 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, WitType, WitLoad, WitStore,
51)]
52#[cfg_attr(
53 all(with_testing, not(target_arch = "wasm32")),
54 derive(test_strategy::Arbitrary)
55)]
56pub struct Amount(u128);
57
58#[derive(Serialize, Deserialize)]
59#[serde(rename = "Amount")]
60struct AmountString(String);
61
62#[derive(Serialize, Deserialize)]
63#[serde(rename = "Amount")]
64struct AmountU128(u128);
65
66impl Serialize for Amount {
67 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
68 if serializer.is_human_readable() {
69 AmountString(self.to_string()).serialize(serializer)
70 } else {
71 AmountU128(self.0).serialize(serializer)
72 }
73 }
74}
75
76impl<'de> Deserialize<'de> for Amount {
77 fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
78 if deserializer.is_human_readable() {
79 let AmountString(s) = AmountString::deserialize(deserializer)?;
80 s.parse().map_err(serde::de::Error::custom)
81 } else {
82 Ok(Amount(AmountU128::deserialize(deserializer)?.0))
83 }
84 }
85}
86
87#[derive(
89 Eq,
90 PartialEq,
91 Ord,
92 PartialOrd,
93 Copy,
94 Clone,
95 Hash,
96 Default,
97 Debug,
98 Serialize,
99 Deserialize,
100 WitType,
101 WitLoad,
102 WitStore,
103)]
104#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
105pub struct BlockHeight(pub u64);
106
107#[derive(
109 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Serialize, Deserialize,
110)]
111pub enum Round {
112 #[default]
114 Fast,
115 MultiLeader(u32),
117 SingleLeader(u32),
119 Validator(u32),
121}
122
123#[derive(
125 Eq,
126 PartialEq,
127 Ord,
128 PartialOrd,
129 Copy,
130 Clone,
131 Hash,
132 Default,
133 Debug,
134 Serialize,
135 Deserialize,
136 WitType,
137 WitLoad,
138 WitStore,
139)]
140pub struct TimeDelta(u64);
141
142impl TimeDelta {
143 pub fn from_micros(micros: u64) -> Self {
145 TimeDelta(micros)
146 }
147
148 pub fn from_millis(millis: u64) -> Self {
150 TimeDelta(millis.saturating_mul(1_000))
151 }
152
153 pub fn from_secs(secs: u64) -> Self {
155 TimeDelta(secs.saturating_mul(1_000_000))
156 }
157
158 pub fn from_duration(duration: Duration) -> Self {
161 TimeDelta::from_micros(u64::try_from(duration.as_micros()).unwrap_or(u64::MAX))
162 }
163
164 pub fn as_micros(&self) -> u64 {
166 self.0
167 }
168
169 pub fn as_duration(&self) -> Duration {
171 Duration::from_micros(self.as_micros())
172 }
173}
174
175#[derive(
177 Eq,
178 PartialEq,
179 Ord,
180 PartialOrd,
181 Copy,
182 Clone,
183 Hash,
184 Default,
185 Debug,
186 Serialize,
187 Deserialize,
188 WitType,
189 WitLoad,
190 WitStore,
191)]
192pub struct Timestamp(u64);
193
194impl Timestamp {
195 pub fn now() -> Timestamp {
197 Timestamp(
198 SystemTime::UNIX_EPOCH
199 .elapsed()
200 .expect("system time should be after Unix epoch")
201 .as_micros()
202 .try_into()
203 .unwrap_or(u64::MAX),
204 )
205 }
206
207 pub fn micros(&self) -> u64 {
209 self.0
210 }
211
212 pub fn delta_since(&self, other: Timestamp) -> TimeDelta {
215 TimeDelta::from_micros(self.0.saturating_sub(other.0))
216 }
217
218 pub fn duration_since(&self, other: Timestamp) -> Duration {
221 Duration::from_micros(self.0.saturating_sub(other.0))
222 }
223
224 pub fn saturating_add(&self, duration: TimeDelta) -> Timestamp {
226 Timestamp(self.0.saturating_add(duration.0))
227 }
228
229 pub fn saturating_sub(&self, duration: TimeDelta) -> Timestamp {
231 Timestamp(self.0.saturating_sub(duration.0))
232 }
233
234 pub fn saturating_add_micros(&self, micros: u64) -> Timestamp {
237 Timestamp(self.0.saturating_add(micros))
238 }
239
240 pub fn saturating_sub_micros(&self, micros: u64) -> Timestamp {
243 Timestamp(self.0.saturating_sub(micros))
244 }
245}
246
247impl From<u64> for Timestamp {
248 fn from(t: u64) -> Timestamp {
249 Timestamp(t)
250 }
251}
252
253impl Display for Timestamp {
254 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255 if let Some(date_time) = chrono::DateTime::from_timestamp(
256 (self.0 / 1_000_000) as i64,
257 ((self.0 % 1_000_000) * 1_000) as u32,
258 ) {
259 return date_time.naive_utc().fmt(f);
260 }
261 self.0.fmt(f)
262 }
263}
264
265#[derive(
268 Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, WitLoad, WitStore, WitType,
269)]
270pub struct Resources {
271 pub fuel: u64,
273 pub read_operations: u32,
275 pub write_operations: u32,
277 pub bytes_to_read: u32,
279 pub bytes_to_write: u32,
281 pub blobs_to_read: u32,
283 pub blobs_to_publish: u32,
285 pub blob_bytes_to_read: u32,
287 pub blob_bytes_to_publish: u32,
289 pub messages: u32,
291 pub message_size: u32,
294 pub storage_size_delta: u32,
296 pub service_as_oracle_queries: u32,
298 pub http_requests: u32,
300 }
303
304#[derive(Clone, Debug, Deserialize, Serialize, WitLoad, WitType)]
306#[cfg_attr(with_testing, derive(Eq, PartialEq, WitStore))]
307#[witty_specialize_with(Message = Vec<u8>)]
308pub struct SendMessageRequest<Message> {
309 pub destination: Destination,
311 pub authenticated: bool,
313 pub is_tracked: bool,
315 pub grant: Resources,
317 pub message: Message,
319}
320
321impl<Message> SendMessageRequest<Message>
322where
323 Message: Serialize,
324{
325 pub fn into_raw(self) -> SendMessageRequest<Vec<u8>> {
327 let message = bcs::to_bytes(&self.message).expect("Failed to serialize message");
328
329 SendMessageRequest {
330 destination: self.destination,
331 authenticated: self.authenticated,
332 is_tracked: self.is_tracked,
333 grant: self.grant,
334 message,
335 }
336 }
337}
338
339#[derive(Debug, Error)]
341#[allow(missing_docs)]
342pub enum ArithmeticError {
343 #[error("Number overflow")]
344 Overflow,
345 #[error("Number underflow")]
346 Underflow,
347}
348
349macro_rules! impl_wrapped_number {
350 ($name:ident, $wrapped:ident) => {
351 impl $name {
352 pub const ZERO: Self = Self(0);
354
355 pub const MAX: Self = Self($wrapped::MAX);
357
358 pub fn try_add(self, other: Self) -> Result<Self, ArithmeticError> {
360 let val = self
361 .0
362 .checked_add(other.0)
363 .ok_or(ArithmeticError::Overflow)?;
364 Ok(Self(val))
365 }
366
367 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
369 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
370 Ok(Self(val))
371 }
372
373 pub const fn saturating_add(self, other: Self) -> Self {
375 let val = self.0.saturating_add(other.0);
376 Self(val)
377 }
378
379 pub fn try_sub(self, other: Self) -> Result<Self, ArithmeticError> {
381 let val = self
382 .0
383 .checked_sub(other.0)
384 .ok_or(ArithmeticError::Underflow)?;
385 Ok(Self(val))
386 }
387
388 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
390 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
391 Ok(Self(val))
392 }
393
394 pub const fn saturating_sub(self, other: Self) -> Self {
396 let val = self.0.saturating_sub(other.0);
397 Self(val)
398 }
399
400 pub fn try_add_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
402 self.0 = self
403 .0
404 .checked_add(other.0)
405 .ok_or(ArithmeticError::Overflow)?;
406 Ok(())
407 }
408
409 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
411 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
412 Ok(())
413 }
414
415 pub const fn saturating_add_assign(&mut self, other: Self) {
417 self.0 = self.0.saturating_add(other.0);
418 }
419
420 pub fn try_sub_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
422 self.0 = self
423 .0
424 .checked_sub(other.0)
425 .ok_or(ArithmeticError::Underflow)?;
426 Ok(())
427 }
428
429 pub const fn saturating_mul(&self, other: $wrapped) -> Self {
431 Self(self.0.saturating_mul(other))
432 }
433
434 pub fn try_mul(self, other: $wrapped) -> Result<Self, ArithmeticError> {
436 let val = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
437 Ok(Self(val))
438 }
439
440 pub fn try_mul_assign(&mut self, other: $wrapped) -> Result<(), ArithmeticError> {
442 self.0 = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
443 Ok(())
444 }
445 }
446
447 impl From<$name> for $wrapped {
448 fn from(value: $name) -> Self {
449 value.0
450 }
451 }
452
453 #[cfg(with_testing)]
455 impl From<$wrapped> for $name {
456 fn from(value: $wrapped) -> Self {
457 Self(value)
458 }
459 }
460
461 #[cfg(with_testing)]
462 impl ops::Add for $name {
463 type Output = Self;
464
465 fn add(self, other: Self) -> Self {
466 Self(self.0 + other.0)
467 }
468 }
469
470 #[cfg(with_testing)]
471 impl ops::Sub for $name {
472 type Output = Self;
473
474 fn sub(self, other: Self) -> Self {
475 Self(self.0 - other.0)
476 }
477 }
478
479 #[cfg(with_testing)]
480 impl ops::Mul<$wrapped> for $name {
481 type Output = Self;
482
483 fn mul(self, other: $wrapped) -> Self {
484 Self(self.0 * other)
485 }
486 }
487 };
488}
489
490impl TryFrom<BlockHeight> for usize {
491 type Error = ArithmeticError;
492
493 fn try_from(height: BlockHeight) -> Result<usize, ArithmeticError> {
494 usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)
495 }
496}
497
498#[cfg(not(with_testing))]
499impl From<u64> for BlockHeight {
500 fn from(value: u64) -> Self {
501 Self(value)
502 }
503}
504
505impl_wrapped_number!(Amount, u128);
506impl_wrapped_number!(BlockHeight, u64);
507impl_wrapped_number!(TimeDelta, u64);
508
509impl Display for Amount {
510 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
511 let places = Amount::DECIMAL_PLACES as usize;
513 let min_digits = places + 1;
514 let decimals = format!("{:0min_digits$}", self.0);
515 let integer_part = &decimals[..(decimals.len() - places)];
516 let fractional_part = decimals[(decimals.len() - places)..].trim_end_matches('0');
517
518 let precision = f.precision().unwrap_or(0).max(fractional_part.len());
520 let sign = if f.sign_plus() && self.0 > 0 { "+" } else { "" };
521 let pad_width = f.width().map_or(0, |w| {
523 w.saturating_sub(precision)
524 .saturating_sub(sign.len() + integer_part.len() + 1)
525 });
526 let left_pad = match f.align() {
527 None | Some(fmt::Alignment::Right) => pad_width,
528 Some(fmt::Alignment::Center) => pad_width / 2,
529 Some(fmt::Alignment::Left) => 0,
530 };
531
532 for _ in 0..left_pad {
533 write!(f, "{}", f.fill())?;
534 }
535 write!(f, "{sign}{integer_part}.{fractional_part:0<precision$}")?;
536 for _ in left_pad..pad_width {
537 write!(f, "{}", f.fill())?;
538 }
539 Ok(())
540 }
541}
542
543#[derive(Error, Debug)]
544#[allow(missing_docs)]
545pub enum ParseAmountError {
546 #[error("cannot parse amount")]
547 Parse,
548 #[error("cannot represent amount: number too high")]
549 TooHigh,
550 #[error("cannot represent amount: too many decimal places after the point")]
551 TooManyDigits,
552}
553
554impl FromStr for Amount {
555 type Err = ParseAmountError;
556
557 fn from_str(src: &str) -> Result<Self, Self::Err> {
558 let mut result: u128 = 0;
559 let mut decimals: Option<u8> = None;
560 let mut chars = src.trim().chars().peekable();
561 if chars.peek() == Some(&'+') {
562 chars.next();
563 }
564 for char in chars {
565 match char {
566 '_' => {}
567 '.' if decimals.is_some() => return Err(ParseAmountError::Parse),
568 '.' => decimals = Some(Amount::DECIMAL_PLACES),
569 char => {
570 let digit = u128::from(char.to_digit(10).ok_or(ParseAmountError::Parse)?);
571 if let Some(d) = &mut decimals {
572 *d = d.checked_sub(1).ok_or(ParseAmountError::TooManyDigits)?;
573 }
574 result = result
575 .checked_mul(10)
576 .and_then(|r| r.checked_add(digit))
577 .ok_or(ParseAmountError::TooHigh)?;
578 }
579 }
580 }
581 result = result
582 .checked_mul(10u128.pow(decimals.unwrap_or(Amount::DECIMAL_PLACES) as u32))
583 .ok_or(ParseAmountError::TooHigh)?;
584 Ok(Amount(result))
585 }
586}
587
588impl Display for BlockHeight {
589 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
590 self.0.fmt(f)
591 }
592}
593
594impl FromStr for BlockHeight {
595 type Err = ParseIntError;
596
597 fn from_str(src: &str) -> Result<Self, Self::Err> {
598 Ok(Self(u64::from_str(src)?))
599 }
600}
601
602impl Display for Round {
603 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
604 match self {
605 Round::Fast => write!(f, "fast round"),
606 Round::MultiLeader(r) => write!(f, "multi-leader round {}", r),
607 Round::SingleLeader(r) => write!(f, "single-leader round {}", r),
608 Round::Validator(r) => write!(f, "validator round {}", r),
609 }
610 }
611}
612
613impl Round {
614 pub fn is_multi_leader(&self) -> bool {
616 matches!(self, Round::MultiLeader(_))
617 }
618
619 pub fn multi_leader(&self) -> Option<u32> {
621 match self {
622 Round::MultiLeader(number) => Some(*number),
623 _ => None,
624 }
625 }
626
627 pub fn is_fast(&self) -> bool {
629 matches!(self, Round::Fast)
630 }
631
632 pub fn number(&self) -> u32 {
634 match self {
635 Round::Fast => 0,
636 Round::MultiLeader(r) | Round::SingleLeader(r) | Round::Validator(r) => *r,
637 }
638 }
639
640 pub fn type_name(&self) -> &'static str {
642 match self {
643 Round::Fast => "fast",
644 Round::MultiLeader(_) => "multi",
645 Round::SingleLeader(_) => "single",
646 Round::Validator(_) => "validator",
647 }
648 }
649}
650
651impl<'a> iter::Sum<&'a Amount> for Amount {
652 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
653 iter.fold(Self::ZERO, |a, b| a.saturating_add(*b))
654 }
655}
656
657impl Amount {
658 pub const DECIMAL_PLACES: u8 = 18;
660
661 pub const ONE: Amount = Amount(10u128.pow(Amount::DECIMAL_PLACES as u32));
663
664 pub const fn from_tokens(tokens: u128) -> Amount {
666 Self::ONE.saturating_mul(tokens)
667 }
668
669 pub const fn from_millis(millitokens: u128) -> Amount {
671 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 3)).saturating_mul(millitokens)
672 }
673
674 pub const fn from_micros(microtokens: u128) -> Amount {
676 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 6)).saturating_mul(microtokens)
677 }
678
679 pub const fn from_nanos(nanotokens: u128) -> Amount {
681 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 9)).saturating_mul(nanotokens)
682 }
683
684 pub const fn from_attos(attotokens: u128) -> Amount {
686 Amount(attotokens)
687 }
688
689 pub const fn upper_half(self) -> u64 {
691 (self.0 >> 64) as u64
692 }
693
694 pub const fn lower_half(self) -> u64 {
696 self.0 as u64
697 }
698
699 pub fn saturating_div(self, other: Amount) -> u128 {
701 self.0.checked_div(other.0).unwrap_or(u128::MAX)
702 }
703
704 pub fn is_zero(&self) -> bool {
706 *self == Amount::ZERO
707 }
708}
709
710#[derive(
712 Default,
713 Debug,
714 PartialEq,
715 Eq,
716 Hash,
717 Clone,
718 Serialize,
719 Deserialize,
720 WitType,
721 WitLoad,
722 WitStore,
723 InputObject,
724)]
725pub struct ApplicationPermissions {
726 #[debug(skip_if = Option::is_none)]
730 pub execute_operations: Option<Vec<ApplicationId>>,
731 #[graphql(default)]
734 #[debug(skip_if = Vec::is_empty)]
735 pub mandatory_applications: Vec<ApplicationId>,
736 #[graphql(default)]
738 #[debug(skip_if = Vec::is_empty)]
739 pub close_chain: Vec<ApplicationId>,
740 #[graphql(default)]
742 #[debug(skip_if = Vec::is_empty)]
743 pub change_application_permissions: Vec<ApplicationId>,
744 #[graphql(default)]
746 #[debug(skip_if = Option::is_none)]
747 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
748 #[graphql(default)]
750 #[debug(skip_if = Option::is_none)]
751 pub make_http_requests: Option<Vec<ApplicationId>>,
752}
753
754impl ApplicationPermissions {
755 pub fn new_single(app_id: ApplicationId) -> Self {
758 Self {
759 execute_operations: Some(vec![app_id]),
760 mandatory_applications: vec![app_id],
761 close_chain: vec![app_id],
762 change_application_permissions: vec![app_id],
763 call_service_as_oracle: Some(vec![app_id]),
764 make_http_requests: Some(vec![app_id]),
765 }
766 }
767
768 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
770 match (app_id, &self.execute_operations) {
771 (_, None) => true,
772 (GenericApplicationId::System, Some(_)) => false,
773 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
774 }
775 }
776
777 pub fn can_close_chain(&self, app_id: &ApplicationId) -> bool {
779 self.close_chain.contains(app_id)
780 }
781
782 pub fn can_change_application_permissions(&self, app_id: &ApplicationId) -> bool {
785 self.change_application_permissions.contains(app_id)
786 }
787
788 pub fn can_call_services(&self, app_id: &ApplicationId) -> bool {
790 self.call_service_as_oracle
791 .as_ref()
792 .map(|app_ids| app_ids.contains(app_id))
793 .unwrap_or(true)
794 }
795
796 pub fn can_make_http_requests(&self, app_id: &ApplicationId) -> bool {
798 self.make_http_requests
799 .as_ref()
800 .map(|app_ids| app_ids.contains(app_id))
801 .unwrap_or(true)
802 }
803}
804
805#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
807pub enum OracleResponse {
808 Service(
810 #[debug(with = "hex_debug")]
811 #[serde(with = "serde_bytes")]
812 Vec<u8>,
813 ),
814 Http(http::Response),
816 Blob(BlobId),
818 Assert,
820 Round(Option<u32>),
822 Event(EventId, Vec<u8>),
824}
825
826impl BcsHashable<'_> for OracleResponse {}
827
828#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)]
830pub struct ApplicationDescription {
831 pub module_id: ModuleId,
833 pub creator_chain_id: ChainId,
835 pub block_height: BlockHeight,
837 pub application_index: u32,
839 #[serde(with = "serde_bytes")]
841 #[debug(with = "hex_debug")]
842 pub parameters: Vec<u8>,
843 pub required_application_ids: Vec<ApplicationId>,
845}
846
847impl From<&ApplicationDescription> for ApplicationId {
848 fn from(description: &ApplicationDescription) -> Self {
849 let mut hash = CryptoHash::new(&BlobContent::new_application_description(description));
850 if matches!(description.module_id.vm_runtime, VmRuntime::Evm) {
851 hash.make_evm_compatible();
852 }
853 ApplicationId::new(hash)
854 }
855}
856
857impl BcsHashable<'_> for ApplicationDescription {}
858
859impl ApplicationDescription {
860 pub fn to_bytes(&self) -> Vec<u8> {
862 bcs::to_bytes(self).expect("Serializing blob bytes should not fail!")
863 }
864
865 pub fn contract_bytecode_blob_id(&self) -> BlobId {
867 self.module_id.contract_bytecode_blob_id()
868 }
869
870 pub fn service_bytecode_blob_id(&self) -> BlobId {
872 self.module_id.service_bytecode_blob_id()
873 }
874}
875
876#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
878pub struct Bytecode {
879 #[serde(with = "serde_bytes")]
881 #[debug(with = "hex_debug")]
882 pub bytes: Vec<u8>,
883}
884
885impl Bytecode {
886 pub fn new(bytes: Vec<u8>) -> Self {
888 Bytecode { bytes }
889 }
890
891 pub async fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
893 let bytes = fs::read(path)?;
894 Ok(Bytecode { bytes })
895 }
896
897 #[cfg(not(target_arch = "wasm32"))]
899 pub fn compress(&self) -> CompressedBytecode {
900 #[cfg(with_metrics)]
901 let _compression_latency = BYTECODE_COMPRESSION_LATENCY.measure_latency();
902 let compressed_bytes = zstd::stream::encode_all(&*self.bytes, 19)
903 .expect("Compressing bytes in memory should not fail");
904
905 CompressedBytecode { compressed_bytes }
906 }
907}
908
909impl AsRef<[u8]> for Bytecode {
910 fn as_ref(&self) -> &[u8] {
911 self.bytes.as_ref()
912 }
913}
914
915#[derive(Error, Debug)]
917pub enum DecompressionError {
918 #[error("Bytecode could not be decompressed: {0}")]
920 InvalidCompressedBytecode(#[from] io::Error),
921}
922
923#[derive(Clone, Debug, Deserialize, Hash, Serialize, WitType, WitStore)]
925#[cfg_attr(with_testing, derive(Eq, PartialEq))]
926pub struct CompressedBytecode {
927 #[serde(with = "serde_bytes")]
929 #[debug(with = "hex_debug")]
930 pub compressed_bytes: Vec<u8>,
931}
932
933#[cfg(not(target_arch = "wasm32"))]
934impl CompressedBytecode {
935 pub fn decompressed_size_at_most(
937 compressed_bytes: &[u8],
938 limit: u64,
939 ) -> Result<bool, DecompressionError> {
940 let mut decoder = zstd::stream::Decoder::new(compressed_bytes)?;
941 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
942 let mut writer = LimitedWriter::new(io::sink(), limit);
943 match io::copy(&mut decoder, &mut writer) {
944 Ok(_) => Ok(true),
945 Err(error) => {
946 error.downcast::<LimitedWriterError>()?;
947 Ok(false)
948 }
949 }
950 }
951
952 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
954 #[cfg(with_metrics)]
955 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
956 let bytes = zstd::stream::decode_all(&*self.compressed_bytes)?;
957
958 Ok(Bytecode { bytes })
959 }
960}
961
962#[cfg(target_arch = "wasm32")]
963impl CompressedBytecode {
964 pub fn decompressed_size_at_most(
966 compressed_bytes: &[u8],
967 limit: u64,
968 ) -> Result<bool, DecompressionError> {
969 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
970 let mut writer = LimitedWriter::new(io::sink(), limit);
971 let mut decoder = ruzstd::streaming_decoder::StreamingDecoder::new(compressed_bytes)
972 .map_err(io::Error::other)?;
973
974 match io::copy(&mut decoder, &mut writer) {
976 Ok(_) => Ok(true),
977 Err(error) => {
978 error.downcast::<LimitedWriterError>()?;
979 Ok(false)
980 }
981 }
982 }
983
984 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
986 use ruzstd::{io::Read, streaming_decoder::StreamingDecoder};
987
988 #[cfg(with_metrics)]
989 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
990
991 let compressed_bytes = &*self.compressed_bytes;
992 let mut bytes = Vec::new();
993 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
994
995 while !decoder.get_ref().is_empty() {
997 decoder
998 .read_to_end(&mut bytes)
999 .expect("Reading from a slice in memory should not result in I/O errors");
1000 }
1001
1002 Ok(Bytecode { bytes })
1003 }
1004}
1005
1006impl BcsHashable<'_> for BlobContent {}
1007
1008#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1010pub struct BlobContent {
1011 blob_type: BlobType,
1013 #[serde(with = "serde_bytes")]
1015 #[debug(skip)]
1016 bytes: Box<[u8]>,
1017}
1018
1019impl BlobContent {
1020 pub fn new(blob_type: BlobType, bytes: impl Into<Box<[u8]>>) -> Self {
1022 let bytes = bytes.into();
1023 BlobContent { blob_type, bytes }
1024 }
1025
1026 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1028 BlobContent::new(BlobType::Data, bytes)
1029 }
1030
1031 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1033 BlobContent::new(
1034 BlobType::ContractBytecode,
1035 compressed_bytecode.compressed_bytes,
1036 )
1037 }
1038
1039 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1041 BlobContent::new(BlobType::EvmBytecode, compressed_bytecode.compressed_bytes)
1042 }
1043
1044 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1046 BlobContent::new(
1047 BlobType::ServiceBytecode,
1048 compressed_bytecode.compressed_bytes,
1049 )
1050 }
1051
1052 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1054 let bytes = application_description.to_bytes();
1055 BlobContent::new(BlobType::ApplicationDescription, bytes)
1056 }
1057
1058 pub fn new_committee(committee: impl Into<Box<[u8]>>) -> Self {
1060 BlobContent::new(BlobType::Committee, committee)
1061 }
1062
1063 pub fn bytes(&self) -> &[u8] {
1065 &self.bytes
1066 }
1067
1068 pub fn into_bytes(self) -> Box<[u8]> {
1070 self.bytes
1071 }
1072
1073 pub fn blob_type(&self) -> BlobType {
1075 self.blob_type
1076 }
1077}
1078
1079impl From<Blob> for BlobContent {
1080 fn from(blob: Blob) -> BlobContent {
1081 blob.content
1082 }
1083}
1084
1085#[derive(Debug, Hash, PartialEq, Eq, Clone)]
1087pub struct Blob {
1088 hash: CryptoHash,
1090 content: BlobContent,
1092}
1093
1094impl Blob {
1095 pub fn new(content: BlobContent) -> Self {
1097 let mut hash = CryptoHash::new(&content);
1098 if matches!(content.blob_type, BlobType::ApplicationDescription) {
1099 let application_description = bcs::from_bytes::<ApplicationDescription>(&content.bytes)
1100 .expect("to obtain an application description");
1101 if matches!(application_description.module_id.vm_runtime, VmRuntime::Evm) {
1102 hash.make_evm_compatible();
1103 }
1104 }
1105 Blob { hash, content }
1106 }
1107
1108 pub fn new_with_id_unchecked(blob_id: BlobId, bytes: impl Into<Box<[u8]>>) -> Self {
1110 Blob {
1111 hash: blob_id.hash,
1112 content: BlobContent {
1113 blob_type: blob_id.blob_type,
1114 bytes: bytes.into(),
1115 },
1116 }
1117 }
1118
1119 pub fn new_data(bytes: impl Into<Box<[u8]>>) -> Self {
1121 Blob::new(BlobContent::new_data(bytes))
1122 }
1123
1124 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1126 Blob::new(BlobContent::new_contract_bytecode(compressed_bytecode))
1127 }
1128
1129 pub fn new_evm_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1131 Blob::new(BlobContent::new_evm_bytecode(compressed_bytecode))
1132 }
1133
1134 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1136 Blob::new(BlobContent::new_service_bytecode(compressed_bytecode))
1137 }
1138
1139 pub fn new_application_description(application_description: &ApplicationDescription) -> Self {
1142 Blob::new(BlobContent::new_application_description(
1143 application_description,
1144 ))
1145 }
1146
1147 pub fn id(&self) -> BlobId {
1149 BlobId {
1150 hash: self.hash,
1151 blob_type: self.content.blob_type,
1152 }
1153 }
1154
1155 pub fn content(&self) -> &BlobContent {
1157 &self.content
1158 }
1159
1160 pub fn into_content(self) -> BlobContent {
1162 self.content
1163 }
1164
1165 pub fn bytes(&self) -> &[u8] {
1167 self.content.bytes()
1168 }
1169
1170 pub fn into_bytes(self) -> Box<[u8]> {
1172 self.content.into_bytes()
1173 }
1174
1175 pub async fn load_data_blob_from_file(path: impl AsRef<Path>) -> io::Result<Self> {
1177 Ok(Self::new_data(fs::read(path)?))
1178 }
1179}
1180
1181impl Serialize for Blob {
1182 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1183 where
1184 S: Serializer,
1185 {
1186 if serializer.is_human_readable() {
1187 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1188 serializer.serialize_str(&hex::encode(blob_bytes))
1189 } else {
1190 BlobContent::serialize(self.content(), serializer)
1191 }
1192 }
1193}
1194
1195impl<'a> Deserialize<'a> for Blob {
1196 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1197 where
1198 D: Deserializer<'a>,
1199 {
1200 if deserializer.is_human_readable() {
1201 let s = String::deserialize(deserializer)?;
1202 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1203 let content: BlobContent =
1204 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1205
1206 Ok(Blob::new(content))
1207 } else {
1208 let content = BlobContent::deserialize(deserializer)?;
1209 Ok(Blob::new(content))
1210 }
1211 }
1212}
1213
1214impl BcsHashable<'_> for Blob {}
1215
1216#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
1218pub struct Event {
1219 pub stream_id: StreamId,
1221 pub index: u32,
1223 #[debug(with = "hex_debug")]
1225 #[serde(with = "serde_bytes")]
1226 pub value: Vec<u8>,
1227}
1228
1229impl Event {
1230 pub fn id(&self, chain_id: ChainId) -> EventId {
1232 EventId {
1233 chain_id,
1234 stream_id: self.stream_id.clone(),
1235 index: self.index,
1236 }
1237 }
1238}
1239
1240impl BcsHashable<'_> for Event {}
1241
1242doc_scalar!(Bytecode, "A WebAssembly module's bytecode");
1243doc_scalar!(Amount, "A non-negative amount of tokens.");
1244doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
1245doc_scalar!(
1246 Timestamp,
1247 "A timestamp, in microseconds since the Unix epoch"
1248);
1249doc_scalar!(TimeDelta, "A duration in microseconds");
1250doc_scalar!(
1251 Round,
1252 "A number to identify successive attempts to decide a value in a consensus protocol."
1253);
1254doc_scalar!(OracleResponse, "A record of a single oracle response.");
1255doc_scalar!(BlobContent, "A blob of binary data.");
1256doc_scalar!(
1257 Blob,
1258 "A blob of binary data, with its content-addressed blob ID."
1259);
1260doc_scalar!(ApplicationDescription, "Description of a user application");
1261
1262#[cfg(with_metrics)]
1264static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1265 register_histogram_vec(
1266 "bytecode_compression_latency",
1267 "Bytecode compression latency",
1268 &[],
1269 exponential_bucket_latencies(10.0),
1270 )
1271});
1272
1273#[cfg(with_metrics)]
1275static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1276 register_histogram_vec(
1277 "bytecode_decompression_latency",
1278 "Bytecode decompression latency",
1279 &[],
1280 exponential_bucket_latencies(10.0),
1281 )
1282});
1283
1284#[cfg(test)]
1285mod tests {
1286 use std::str::FromStr;
1287
1288 use super::Amount;
1289
1290 #[test]
1291 fn display_amount() {
1292 assert_eq!("1.", Amount::ONE.to_string());
1293 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
1294 assert_eq!(
1295 Amount(10_000_000_000_000_000_000),
1296 Amount::from_str("10").unwrap()
1297 );
1298 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
1299 assert_eq!(
1300 "1001.3",
1301 (Amount::from_str("1.1")
1302 .unwrap()
1303 .saturating_add(Amount::from_str("1_000.2").unwrap()))
1304 .to_string()
1305 );
1306 assert_eq!(
1307 " 1.00000000000000000000",
1308 format!("{:25.20}", Amount::ONE)
1309 );
1310 assert_eq!(
1311 "~+12.34~~",
1312 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
1313 );
1314 }
1315}