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, Hasher},
15 io, iter,
16 num::ParseIntError,
17 path::Path,
18 str::FromStr,
19};
20
21use anyhow::Context as _;
22use async_graphql::InputObject;
23use base64::engine::{general_purpose::STANDARD_NO_PAD, Engine as _};
24use custom_debug_derive::Debug;
25use linera_witty::{WitLoad, WitStore, WitType};
26#[cfg(with_metrics)]
27use prometheus::HistogramVec;
28use serde::{Deserialize, Deserializer, Serialize, Serializer};
29use thiserror::Error;
30
31#[cfg(with_metrics)]
32use crate::prometheus_util::{self, MeasureLatency};
33use crate::{
34 crypto::BcsHashable,
35 doc_scalar, hex_debug,
36 identifiers::{
37 ApplicationId, BlobId, BlobType, BytecodeId, Destination, GenericApplicationId, MessageId,
38 UserApplicationId,
39 },
40 limited_writer::{LimitedWriter, LimitedWriterError},
41 time::{Duration, SystemTime},
42};
43
44#[derive(
49 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, WitType, WitLoad, WitStore,
50)]
51pub struct Amount(u128);
52
53#[derive(Serialize, Deserialize)]
54#[serde(rename = "Amount")]
55struct AmountString(String);
56
57#[derive(Serialize, Deserialize)]
58#[serde(rename = "Amount")]
59struct AmountU128(u128);
60
61impl Serialize for Amount {
62 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
63 if serializer.is_human_readable() {
64 AmountString(self.to_string()).serialize(serializer)
65 } else {
66 AmountU128(self.0).serialize(serializer)
67 }
68 }
69}
70
71impl<'de> Deserialize<'de> for Amount {
72 fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
73 if deserializer.is_human_readable() {
74 let AmountString(s) = AmountString::deserialize(deserializer)?;
75 s.parse().map_err(serde::de::Error::custom)
76 } else {
77 Ok(Amount(AmountU128::deserialize(deserializer)?.0))
78 }
79 }
80}
81
82#[derive(
84 Eq,
85 PartialEq,
86 Ord,
87 PartialOrd,
88 Copy,
89 Clone,
90 Hash,
91 Default,
92 Debug,
93 Serialize,
94 Deserialize,
95 WitType,
96 WitLoad,
97 WitStore,
98)]
99#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
100pub struct BlockHeight(pub u64);
101
102#[derive(
104 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Serialize, Deserialize,
105)]
106pub enum Round {
107 #[default]
109 Fast,
110 MultiLeader(u32),
112 SingleLeader(u32),
114 Validator(u32),
116}
117
118#[derive(
120 Eq,
121 PartialEq,
122 Ord,
123 PartialOrd,
124 Copy,
125 Clone,
126 Hash,
127 Default,
128 Debug,
129 Serialize,
130 Deserialize,
131 WitType,
132 WitLoad,
133 WitStore,
134)]
135pub struct TimeDelta(u64);
136
137impl TimeDelta {
138 pub fn from_micros(micros: u64) -> Self {
140 TimeDelta(micros)
141 }
142
143 pub fn from_millis(millis: u64) -> Self {
145 TimeDelta(millis.saturating_mul(1_000))
146 }
147
148 pub fn from_secs(secs: u64) -> Self {
150 TimeDelta(secs.saturating_mul(1_000_000))
151 }
152
153 pub fn from_duration(duration: Duration) -> Self {
156 TimeDelta::from_micros(u64::try_from(duration.as_micros()).unwrap_or(u64::MAX))
157 }
158
159 pub fn as_micros(&self) -> u64 {
161 self.0
162 }
163
164 pub fn as_duration(&self) -> Duration {
166 Duration::from_micros(self.as_micros())
167 }
168}
169
170#[derive(
172 Eq,
173 PartialEq,
174 Ord,
175 PartialOrd,
176 Copy,
177 Clone,
178 Hash,
179 Default,
180 Debug,
181 Serialize,
182 Deserialize,
183 WitType,
184 WitLoad,
185 WitStore,
186)]
187pub struct Timestamp(u64);
188
189impl Timestamp {
190 pub fn now() -> Timestamp {
192 Timestamp(
193 SystemTime::UNIX_EPOCH
194 .elapsed()
195 .expect("system time should be after Unix epoch")
196 .as_micros()
197 .try_into()
198 .unwrap_or(u64::MAX),
199 )
200 }
201
202 pub fn micros(&self) -> u64 {
204 self.0
205 }
206
207 pub fn delta_since(&self, other: Timestamp) -> TimeDelta {
210 TimeDelta::from_micros(self.0.saturating_sub(other.0))
211 }
212
213 pub fn duration_since(&self, other: Timestamp) -> Duration {
216 Duration::from_micros(self.0.saturating_sub(other.0))
217 }
218
219 pub fn saturating_add(&self, duration: TimeDelta) -> Timestamp {
221 Timestamp(self.0.saturating_add(duration.0))
222 }
223
224 pub fn saturating_sub(&self, duration: TimeDelta) -> Timestamp {
226 Timestamp(self.0.saturating_sub(duration.0))
227 }
228
229 pub fn saturating_add_micros(&self, micros: u64) -> Timestamp {
232 Timestamp(self.0.saturating_add(micros))
233 }
234
235 pub fn saturating_sub_micros(&self, micros: u64) -> Timestamp {
238 Timestamp(self.0.saturating_sub(micros))
239 }
240}
241
242impl From<u64> for Timestamp {
243 fn from(t: u64) -> Timestamp {
244 Timestamp(t)
245 }
246}
247
248impl Display for Timestamp {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 if let Some(date_time) = chrono::DateTime::from_timestamp(
251 (self.0 / 1_000_000) as i64,
252 ((self.0 % 1_000_000) * 1_000) as u32,
253 ) {
254 return date_time.naive_utc().fmt(f);
255 }
256 self.0.fmt(f)
257 }
258}
259
260#[derive(
263 Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, WitLoad, WitStore, WitType,
264)]
265pub struct Resources {
266 pub fuel: u64,
268 pub read_operations: u32,
270 pub write_operations: u32,
272 pub bytes_to_read: u32,
274 pub bytes_to_write: u32,
276 pub messages: u32,
278 pub message_size: u32,
281 pub storage_size_delta: u32,
283 }
286
287#[derive(Clone, Debug, Deserialize, Serialize, WitLoad, WitType)]
289#[cfg_attr(with_testing, derive(Eq, PartialEq, WitStore))]
290#[witty_specialize_with(Message = Vec<u8>)]
291pub struct SendMessageRequest<Message> {
292 pub destination: Destination,
294 pub authenticated: bool,
296 pub is_tracked: bool,
298 pub grant: Resources,
300 pub message: Message,
302}
303
304impl<Message> SendMessageRequest<Message>
305where
306 Message: Serialize,
307{
308 pub fn into_raw(self) -> SendMessageRequest<Vec<u8>> {
310 let message = bcs::to_bytes(&self.message).expect("Failed to serialize message");
311
312 SendMessageRequest {
313 destination: self.destination,
314 authenticated: self.authenticated,
315 is_tracked: self.is_tracked,
316 grant: self.grant,
317 message,
318 }
319 }
320}
321
322#[derive(Debug, Error)]
324#[allow(missing_docs)]
325pub enum ArithmeticError {
326 #[error("Number overflow")]
327 Overflow,
328 #[error("Number underflow")]
329 Underflow,
330}
331
332macro_rules! impl_wrapped_number {
333 ($name:ident, $wrapped:ident) => {
334 impl $name {
335 pub const ZERO: Self = Self(0);
337
338 pub const MAX: Self = Self($wrapped::MAX);
340
341 pub fn try_add(self, other: Self) -> Result<Self, ArithmeticError> {
343 let val = self
344 .0
345 .checked_add(other.0)
346 .ok_or(ArithmeticError::Overflow)?;
347 Ok(Self(val))
348 }
349
350 pub fn try_add_one(self) -> Result<Self, ArithmeticError> {
352 let val = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
353 Ok(Self(val))
354 }
355
356 pub fn saturating_add(self, other: Self) -> Self {
358 let val = self.0.saturating_add(other.0);
359 Self(val)
360 }
361
362 pub fn try_sub(self, other: Self) -> Result<Self, ArithmeticError> {
364 let val = self
365 .0
366 .checked_sub(other.0)
367 .ok_or(ArithmeticError::Underflow)?;
368 Ok(Self(val))
369 }
370
371 pub fn try_sub_one(self) -> Result<Self, ArithmeticError> {
373 let val = self.0.checked_sub(1).ok_or(ArithmeticError::Underflow)?;
374 Ok(Self(val))
375 }
376
377 pub fn saturating_sub(self, other: Self) -> Self {
379 let val = self.0.saturating_sub(other.0);
380 Self(val)
381 }
382
383 pub fn try_add_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
385 self.0 = self
386 .0
387 .checked_add(other.0)
388 .ok_or(ArithmeticError::Overflow)?;
389 Ok(())
390 }
391
392 pub fn try_add_assign_one(&mut self) -> Result<(), ArithmeticError> {
394 self.0 = self.0.checked_add(1).ok_or(ArithmeticError::Overflow)?;
395 Ok(())
396 }
397
398 pub fn saturating_add_assign(&mut self, other: Self) {
400 self.0 = self.0.saturating_add(other.0);
401 }
402
403 pub fn try_sub_assign(&mut self, other: Self) -> Result<(), ArithmeticError> {
405 self.0 = self
406 .0
407 .checked_sub(other.0)
408 .ok_or(ArithmeticError::Underflow)?;
409 Ok(())
410 }
411
412 pub fn saturating_mul(&self, other: $wrapped) -> Self {
414 Self(self.0.saturating_mul(other))
415 }
416
417 pub fn try_mul(self, other: $wrapped) -> Result<Self, ArithmeticError> {
419 let val = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
420 Ok(Self(val))
421 }
422
423 pub fn try_mul_assign(&mut self, other: $wrapped) -> Result<(), ArithmeticError> {
425 self.0 = self.0.checked_mul(other).ok_or(ArithmeticError::Overflow)?;
426 Ok(())
427 }
428 }
429
430 impl From<$name> for $wrapped {
431 fn from(value: $name) -> Self {
432 value.0
433 }
434 }
435
436 #[cfg(with_testing)]
438 impl From<$wrapped> for $name {
439 fn from(value: $wrapped) -> Self {
440 Self(value)
441 }
442 }
443
444 #[cfg(with_testing)]
445 impl ops::Add for $name {
446 type Output = Self;
447
448 fn add(self, other: Self) -> Self {
449 Self(self.0 + other.0)
450 }
451 }
452
453 #[cfg(with_testing)]
454 impl ops::Sub for $name {
455 type Output = Self;
456
457 fn sub(self, other: Self) -> Self {
458 Self(self.0 - other.0)
459 }
460 }
461
462 #[cfg(with_testing)]
463 impl ops::Mul<$wrapped> for $name {
464 type Output = Self;
465
466 fn mul(self, other: $wrapped) -> Self {
467 Self(self.0 * other)
468 }
469 }
470 };
471}
472
473impl TryFrom<BlockHeight> for usize {
474 type Error = ArithmeticError;
475
476 fn try_from(height: BlockHeight) -> Result<usize, ArithmeticError> {
477 usize::try_from(height.0).map_err(|_| ArithmeticError::Overflow)
478 }
479}
480
481#[cfg(not(with_testing))]
482impl From<u64> for BlockHeight {
483 fn from(value: u64) -> Self {
484 Self(value)
485 }
486}
487
488impl_wrapped_number!(Amount, u128);
489impl_wrapped_number!(BlockHeight, u64);
490impl_wrapped_number!(TimeDelta, u64);
491
492impl Display for Amount {
493 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494 let places = Amount::DECIMAL_PLACES as usize;
496 let min_digits = places + 1;
497 let decimals = format!("{:0min_digits$}", self.0);
498 let integer_part = &decimals[..(decimals.len() - places)];
499 let fractional_part = decimals[(decimals.len() - places)..].trim_end_matches('0');
500
501 let precision = f.precision().unwrap_or(0).max(fractional_part.len());
503 let sign = if f.sign_plus() && self.0 > 0 { "+" } else { "" };
504 let pad_width = f.width().map_or(0, |w| {
506 w.saturating_sub(precision)
507 .saturating_sub(sign.len() + integer_part.len() + 1)
508 });
509 let left_pad = match f.align() {
510 None | Some(fmt::Alignment::Right) => pad_width,
511 Some(fmt::Alignment::Center) => pad_width / 2,
512 Some(fmt::Alignment::Left) => 0,
513 };
514
515 for _ in 0..left_pad {
516 write!(f, "{}", f.fill())?;
517 }
518 write!(f, "{sign}{integer_part}.{fractional_part:0<precision$}")?;
519 for _ in left_pad..pad_width {
520 write!(f, "{}", f.fill())?;
521 }
522 Ok(())
523 }
524}
525
526#[derive(Error, Debug)]
527#[allow(missing_docs)]
528pub enum ParseAmountError {
529 #[error("cannot parse amount")]
530 Parse,
531 #[error("cannot represent amount: number too high")]
532 TooHigh,
533 #[error("cannot represent amount: too many decimal places after the point")]
534 TooManyDigits,
535}
536
537impl FromStr for Amount {
538 type Err = ParseAmountError;
539
540 fn from_str(src: &str) -> Result<Self, Self::Err> {
541 let mut result: u128 = 0;
542 let mut decimals: Option<u8> = None;
543 let mut chars = src.trim().chars().peekable();
544 if chars.peek() == Some(&'+') {
545 chars.next();
546 }
547 for char in chars {
548 match char {
549 '_' => {}
550 '.' if decimals.is_some() => return Err(ParseAmountError::Parse),
551 '.' => decimals = Some(Amount::DECIMAL_PLACES),
552 char => {
553 let digit = u128::from(char.to_digit(10).ok_or(ParseAmountError::Parse)?);
554 if let Some(d) = &mut decimals {
555 *d = d.checked_sub(1).ok_or(ParseAmountError::TooManyDigits)?;
556 }
557 result = result
558 .checked_mul(10)
559 .and_then(|r| r.checked_add(digit))
560 .ok_or(ParseAmountError::TooHigh)?;
561 }
562 }
563 }
564 result = result
565 .checked_mul(10u128.pow(decimals.unwrap_or(Amount::DECIMAL_PLACES) as u32))
566 .ok_or(ParseAmountError::TooHigh)?;
567 Ok(Amount(result))
568 }
569}
570
571impl Display for BlockHeight {
572 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
573 self.0.fmt(f)
574 }
575}
576
577impl FromStr for BlockHeight {
578 type Err = ParseIntError;
579
580 fn from_str(src: &str) -> Result<Self, Self::Err> {
581 Ok(Self(u64::from_str(src)?))
582 }
583}
584
585impl Display for Round {
586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587 match self {
588 Round::Fast => write!(f, "fast round"),
589 Round::MultiLeader(r) => write!(f, "multi-leader round {}", r),
590 Round::SingleLeader(r) => write!(f, "single-leader round {}", r),
591 Round::Validator(r) => write!(f, "validator round {}", r),
592 }
593 }
594}
595
596impl Round {
597 pub fn is_multi_leader(&self) -> bool {
599 matches!(self, Round::MultiLeader(_))
600 }
601
602 pub fn is_fast(&self) -> bool {
604 matches!(self, Round::Fast)
605 }
606
607 pub fn number(&self) -> u32 {
609 match self {
610 Round::Fast => 0,
611 Round::MultiLeader(r) | Round::SingleLeader(r) | Round::Validator(r) => *r,
612 }
613 }
614
615 pub fn type_name(&self) -> &'static str {
617 match self {
618 Round::Fast => "fast",
619 Round::MultiLeader(_) => "multi",
620 Round::SingleLeader(_) => "single",
621 Round::Validator(_) => "validator",
622 }
623 }
624}
625
626impl<'a> iter::Sum<&'a Amount> for Amount {
627 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
628 iter.fold(Self::ZERO, |a, b| a.saturating_add(*b))
629 }
630}
631
632impl Amount {
633 pub const DECIMAL_PLACES: u8 = 18;
635
636 pub const ONE: Amount = Amount(10u128.pow(Amount::DECIMAL_PLACES as u32));
638
639 pub fn from_tokens(tokens: u128) -> Amount {
641 Self::ONE.saturating_mul(tokens)
642 }
643
644 pub fn from_millis(millitokens: u128) -> Amount {
646 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 3)).saturating_mul(millitokens)
647 }
648
649 pub fn from_micros(microtokens: u128) -> Amount {
651 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 6)).saturating_mul(microtokens)
652 }
653
654 pub fn from_nanos(nanotokens: u128) -> Amount {
656 Amount(10u128.pow(Amount::DECIMAL_PLACES as u32 - 9)).saturating_mul(nanotokens)
657 }
658
659 pub fn from_attos(attotokens: u128) -> Amount {
661 Amount(attotokens)
662 }
663
664 pub fn upper_half(self) -> u64 {
666 (self.0 >> 64) as u64
667 }
668
669 pub fn lower_half(self) -> u64 {
671 self.0 as u64
672 }
673
674 pub fn saturating_div(self, other: Amount) -> u128 {
676 self.0.checked_div(other.0).unwrap_or(u128::MAX)
677 }
678}
679
680#[derive(
682 Default,
683 Debug,
684 PartialEq,
685 Eq,
686 Hash,
687 Clone,
688 Serialize,
689 Deserialize,
690 WitType,
691 WitLoad,
692 WitStore,
693 InputObject,
694)]
695pub struct ApplicationPermissions {
696 pub execute_operations: Option<Vec<ApplicationId>>,
700 #[graphql(default)]
703 pub mandatory_applications: Vec<ApplicationId>,
704 #[graphql(default)]
706 pub close_chain: Vec<ApplicationId>,
707}
708
709impl ApplicationPermissions {
710 pub fn new_single(app_id: ApplicationId) -> Self {
713 Self {
714 execute_operations: Some(vec![app_id]),
715 mandatory_applications: vec![app_id],
716 close_chain: vec![app_id],
717 }
718 }
719
720 pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
722 match (app_id, &self.execute_operations) {
723 (_, None) => true,
724 (GenericApplicationId::System, Some(_)) => false,
725 (GenericApplicationId::User(app_id), Some(app_ids)) => app_ids.contains(app_id),
726 }
727 }
728
729 pub fn can_close_chain(&self, app_id: &ApplicationId) -> bool {
731 self.close_chain.contains(app_id)
732 }
733}
734
735#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
737pub enum OracleResponse {
738 Service(Vec<u8>),
740 Post(Vec<u8>),
742 Blob(BlobId),
744 Assert,
746}
747
748impl OracleResponse {
749 pub fn is_permitted_in_fast_blocks(&self) -> bool {
751 matches!(self, OracleResponse::Blob(_))
752 }
753}
754
755impl Display for OracleResponse {
756 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
757 match self {
758 OracleResponse::Service(bytes) => {
759 write!(f, "Service:{}", STANDARD_NO_PAD.encode(bytes))?
760 }
761 OracleResponse::Post(bytes) => write!(f, "Post:{}", STANDARD_NO_PAD.encode(bytes))?,
762 OracleResponse::Blob(blob_id) => write!(f, "Blob:{}", blob_id)?,
763 OracleResponse::Assert => write!(f, "Assert")?,
764 };
765
766 Ok(())
767 }
768}
769
770impl FromStr for OracleResponse {
771 type Err = anyhow::Error;
772
773 fn from_str(s: &str) -> Result<Self, Self::Err> {
774 if let Some(string) = s.strip_prefix("Service:") {
775 return Ok(OracleResponse::Service(
776 STANDARD_NO_PAD.decode(string).context("Invalid base64")?,
777 ));
778 }
779 if let Some(string) = s.strip_prefix("Post:") {
780 return Ok(OracleResponse::Post(
781 STANDARD_NO_PAD.decode(string).context("Invalid base64")?,
782 ));
783 }
784 if let Some(string) = s.strip_prefix("Blob:") {
785 return Ok(OracleResponse::Blob(
786 BlobId::from_str(string).context("Invalid BlobId")?,
787 ));
788 }
789 Err(anyhow::anyhow!("Invalid enum! Enum: {}", s))
790 }
791}
792
793#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)]
795pub struct UserApplicationDescription {
796 pub bytecode_id: BytecodeId,
798 pub creation: MessageId,
800 #[serde(with = "serde_bytes")]
802 #[debug(with = "hex_debug")]
803 pub parameters: Vec<u8>,
804 pub required_application_ids: Vec<UserApplicationId>,
806}
807
808impl From<&UserApplicationDescription> for UserApplicationId {
809 fn from(description: &UserApplicationDescription) -> Self {
810 UserApplicationId {
811 bytecode_id: description.bytecode_id,
812 creation: description.creation,
813 }
814 }
815}
816
817#[derive(Clone, Deserialize, Eq, Hash, PartialEq, Serialize)]
819pub struct Bytecode {
820 #[serde(with = "serde_bytes")]
822 pub bytes: Vec<u8>,
823}
824
825impl Bytecode {
826 pub fn new(bytes: Vec<u8>) -> Self {
828 Bytecode { bytes }
829 }
830
831 pub async fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
833 let bytes = fs::read(path)?;
834 Ok(Bytecode { bytes })
835 }
836
837 #[cfg(not(target_arch = "wasm32"))]
839 pub fn compress(&self) -> CompressedBytecode {
840 #[cfg(with_metrics)]
841 let _compression_latency = BYTECODE_COMPRESSION_LATENCY.measure_latency();
842 let compressed_bytes = zstd::stream::encode_all(&*self.bytes, 19)
843 .expect("Compressing bytes in memory should not fail");
844
845 CompressedBytecode { compressed_bytes }
846 }
847}
848
849impl AsRef<[u8]> for Bytecode {
850 fn as_ref(&self) -> &[u8] {
851 self.bytes.as_ref()
852 }
853}
854
855impl fmt::Debug for Bytecode {
856 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
857 f.debug_struct("Bytecode").finish_non_exhaustive()
858 }
859}
860
861#[derive(Error, Debug)]
863pub enum DecompressionError {
864 #[error("Bytecode could not be decompressed: {0}")]
866 InvalidCompressedBytecode(#[from] io::Error),
867}
868
869#[derive(Clone, Deserialize, Hash, Serialize, WitType, WitStore)]
871#[cfg_attr(with_testing, derive(Eq, PartialEq))]
872pub struct CompressedBytecode {
873 #[serde(with = "serde_bytes")]
875 pub compressed_bytes: Vec<u8>,
876}
877
878#[cfg(not(target_arch = "wasm32"))]
879impl CompressedBytecode {
880 pub fn decompressed_size_at_most(&self, limit: u64) -> Result<bool, DecompressionError> {
882 let mut decoder = zstd::stream::Decoder::new(&*self.compressed_bytes)?;
883 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
884 let mut writer = LimitedWriter::new(io::sink(), limit);
885 match io::copy(&mut decoder, &mut writer) {
886 Ok(_) => Ok(true),
887 Err(error) => {
888 error.downcast::<LimitedWriterError>()?;
889 Ok(false)
890 }
891 }
892 }
893
894 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
896 #[cfg(with_metrics)]
897 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
898 let bytes = zstd::stream::decode_all(&*self.compressed_bytes)?;
899
900 Ok(Bytecode { bytes })
901 }
902}
903
904#[cfg(target_arch = "wasm32")]
905impl CompressedBytecode {
906 pub fn decompressed_size_at_most(&self, limit: u64) -> Result<bool, DecompressionError> {
908 let compressed_bytes = &*self.compressed_bytes;
909 let limit = usize::try_from(limit).unwrap_or(usize::MAX);
910 let mut writer = LimitedWriter::new(io::sink(), limit);
911 let mut decoder = ruzstd::streaming_decoder::StreamingDecoder::new(compressed_bytes)
912 .map_err(io::Error::other)?;
913
914 match io::copy(&mut decoder, &mut writer) {
916 Ok(_) => Ok(true),
917 Err(error) => {
918 error.downcast::<LimitedWriterError>()?;
919 Ok(false)
920 }
921 }
922 }
923
924 pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
926 use ruzstd::{io::Read, streaming_decoder::StreamingDecoder};
927
928 #[cfg(with_metrics)]
929 let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();
930
931 let compressed_bytes = &*self.compressed_bytes;
932 let mut bytes = Vec::new();
933 let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
934
935 while !decoder.get_ref().is_empty() {
937 decoder
938 .read_to_end(&mut bytes)
939 .expect("Reading from a slice in memory should not result in IO errors");
940 }
941
942 Ok(Bytecode { bytes })
943 }
944}
945
946impl fmt::Debug for CompressedBytecode {
947 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
948 f.debug_struct("CompressedBytecode").finish_non_exhaustive()
949 }
950}
951
952#[derive(Clone, Serialize, Deserialize, WitType, WitStore)]
954#[cfg_attr(with_testing, derive(Eq, PartialEq))]
955#[repr(transparent)]
956pub struct BlobBytes(#[serde(with = "serde_bytes")] pub Vec<u8>);
957
958impl BcsHashable for BlobBytes {}
959
960impl Hash for BlobBytes {
961 fn hash<H: Hasher>(&self, state: &mut H) {
962 self.0.hash(state);
963 }
964}
965
966#[derive(Hash, Clone, Serialize, Deserialize, WitType, WitStore)]
968#[cfg_attr(with_testing, derive(Eq, PartialEq))]
969pub enum BlobContent {
970 Data(#[serde(with = "serde_bytes")] Vec<u8>),
972 ContractBytecode(CompressedBytecode),
974 ServiceBytecode(CompressedBytecode),
976}
977
978impl fmt::Debug for BlobContent {
979 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
980 match self {
981 BlobContent::Data(_) => write!(f, "BlobContent::Data"),
982 BlobContent::ContractBytecode(_) => write!(f, "BlobContent::ContractBytecode"),
983 BlobContent::ServiceBytecode(_) => write!(f, "BlobContent::ServiceBytecode"),
984 }
985 }
986}
987
988impl BlobContent {
989 pub fn new_with_id_unchecked(id: BlobId, bytes: Vec<u8>) -> Self {
991 match id.blob_type {
992 BlobType::Data => BlobContent::Data(bytes),
993 BlobType::ContractBytecode => BlobContent::ContractBytecode(CompressedBytecode {
994 compressed_bytes: bytes,
995 }),
996 BlobType::ServiceBytecode => BlobContent::ServiceBytecode(CompressedBytecode {
997 compressed_bytes: bytes,
998 }),
999 }
1000 }
1001
1002 pub fn new_data(bytes: Vec<u8>) -> Self {
1004 BlobContent::Data(bytes)
1005 }
1006
1007 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1009 BlobContent::ContractBytecode(compressed_bytecode)
1010 }
1011
1012 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1014 BlobContent::ServiceBytecode(compressed_bytecode)
1015 }
1016
1017 pub fn with_blob_id_unchecked(self, blob_id: BlobId) -> Blob {
1019 Blob {
1020 id: blob_id,
1021 content: self,
1022 }
1023 }
1024
1025 pub fn with_blob_id_checked(self, blob_id: BlobId) -> Option<Blob> {
1027 match blob_id.blob_type {
1028 BlobType::Data if matches!(&self, BlobContent::Data(_)) => Some(()),
1029 BlobType::ContractBytecode if matches!(&self, BlobContent::ContractBytecode(_)) => {
1030 Some(())
1031 }
1032 BlobType::ServiceBytecode if matches!(&self, BlobContent::ServiceBytecode(_)) => {
1033 Some(())
1034 }
1035 _ => None,
1036 }?;
1037
1038 let expected_blob_id = BlobId::from_content(&self);
1039
1040 if blob_id == expected_blob_id {
1041 Some(self.with_blob_id_unchecked(expected_blob_id))
1042 } else {
1043 None
1044 }
1045 }
1046
1047 pub fn inner_bytes(&self) -> Vec<u8> {
1049 match self {
1050 BlobContent::Data(bytes) => bytes,
1051 BlobContent::ContractBytecode(compressed_bytecode) => {
1052 &compressed_bytecode.compressed_bytes
1053 }
1054 BlobContent::ServiceBytecode(compressed_bytecode) => {
1055 &compressed_bytecode.compressed_bytes
1056 }
1057 }
1058 .clone()
1059 }
1060
1061 pub fn blob_bytes(&self) -> BlobBytes {
1063 BlobBytes(self.inner_bytes())
1064 }
1065
1066 pub fn size(&self) -> usize {
1068 match self {
1069 BlobContent::Data(bytes) => bytes.len(),
1070 BlobContent::ContractBytecode(compressed_bytecode)
1071 | BlobContent::ServiceBytecode(compressed_bytecode) => {
1072 compressed_bytecode.compressed_bytes.len()
1073 }
1074 }
1075 }
1076}
1077
1078impl From<Blob> for BlobContent {
1079 fn from(blob: Blob) -> BlobContent {
1080 blob.content
1081 }
1082}
1083
1084impl From<BlobContent> for Blob {
1085 fn from(content: BlobContent) -> Blob {
1086 Self {
1087 id: BlobId::from_content(&content),
1088 content,
1089 }
1090 }
1091}
1092
1093#[derive(Debug, Hash, Clone, WitType, WitStore)]
1095#[cfg_attr(with_testing, derive(Eq, PartialEq))]
1096pub struct Blob {
1097 id: BlobId,
1099 content: BlobContent,
1101}
1102
1103impl Blob {
1104 pub fn new_with_id_unchecked(id: BlobId, bytes: Vec<u8>) -> Self {
1106 BlobContent::new_with_id_unchecked(id, bytes).into()
1107 }
1108
1109 pub fn new_data(bytes: Vec<u8>) -> Self {
1111 BlobContent::new_data(bytes).into()
1112 }
1113
1114 pub fn new_contract_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1116 BlobContent::new_contract_bytecode(compressed_bytecode).into()
1117 }
1118
1119 pub fn new_service_bytecode(compressed_bytecode: CompressedBytecode) -> Self {
1121 BlobContent::new_service_bytecode(compressed_bytecode).into()
1122 }
1123
1124 pub fn id(&self) -> BlobId {
1126 self.id
1127 }
1128
1129 pub fn content(&self) -> &BlobContent {
1131 &self.content
1132 }
1133
1134 pub fn into_inner_content(self) -> BlobContent {
1136 self.content
1137 }
1138
1139 pub fn inner_bytes(&self) -> Vec<u8> {
1141 self.content.inner_bytes()
1142 }
1143
1144 pub async fn load_data_blob_from_file(path: impl AsRef<Path>) -> io::Result<Self> {
1146 Ok(Self::new_data(fs::read(path)?))
1147 }
1148}
1149
1150impl Serialize for Blob {
1151 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1152 where
1153 S: Serializer,
1154 {
1155 if serializer.is_human_readable() {
1156 let blob_bytes = bcs::to_bytes(&self.content).map_err(serde::ser::Error::custom)?;
1157 serializer.serialize_str(&hex::encode(blob_bytes))
1158 } else {
1159 BlobContent::serialize(self.content(), serializer)
1160 }
1161 }
1162}
1163
1164impl<'a> Deserialize<'a> for Blob {
1165 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1166 where
1167 D: Deserializer<'a>,
1168 {
1169 if deserializer.is_human_readable() {
1170 let s = String::deserialize(deserializer)?;
1171 let content_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
1172 let content: BlobContent =
1173 bcs::from_bytes(&content_bytes).map_err(serde::de::Error::custom)?;
1174
1175 Ok(Blob {
1176 id: BlobId::from_content(&content),
1177 content,
1178 })
1179 } else {
1180 let content = BlobContent::deserialize(deserializer)?;
1181 Ok(Blob {
1182 id: BlobId::from_content(&content),
1183 content,
1184 })
1185 }
1186 }
1187}
1188
1189doc_scalar!(Bytecode, "A WebAssembly module's bytecode");
1190doc_scalar!(Amount, "A non-negative amount of tokens.");
1191doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
1192doc_scalar!(
1193 Timestamp,
1194 "A timestamp, in microseconds since the Unix epoch"
1195);
1196doc_scalar!(TimeDelta, "A duration in microseconds");
1197doc_scalar!(
1198 Round,
1199 "A number to identify successive attempts to decide a value in a consensus protocol."
1200);
1201doc_scalar!(OracleResponse, "A record of a single oracle response.");
1202doc_scalar!(BlobContent, "A blob of binary data.");
1203doc_scalar!(
1204 Blob,
1205 "A blob of binary data, with its content-addressed blob ID."
1206);
1207doc_scalar!(
1208 UserApplicationDescription,
1209 "Description of the necessary information to run a user application"
1210);
1211
1212#[cfg(with_metrics)]
1214static BYTECODE_COMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1215 prometheus_util::register_histogram_vec(
1216 "bytecode_compression_latency",
1217 "Bytecode compression latency",
1218 &[],
1219 Some(vec![
1220 0.000_1, 0.000_25, 0.000_5, 0.001, 0.002_5, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5,
1221 1.0, 2.5, 5.0, 10.0,
1222 ]),
1223 )
1224 .expect("Histogram creation should not fail")
1225});
1226
1227#[cfg(with_metrics)]
1229static BYTECODE_DECOMPRESSION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
1230 prometheus_util::register_histogram_vec(
1231 "bytecode_decompression_latency",
1232 "Bytecode decompression latency",
1233 &[],
1234 Some(vec![
1235 0.000_1, 0.000_25, 0.000_5, 0.001, 0.002_5, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5,
1236 1.0, 2.5, 5.0, 10.0,
1237 ]),
1238 )
1239 .expect("Histogram creation should not fail")
1240});
1241
1242#[cfg(test)]
1243mod tests {
1244 use std::str::FromStr;
1245
1246 use super::Amount;
1247
1248 #[test]
1249 fn display_amount() {
1250 assert_eq!("1.", Amount::ONE.to_string());
1251 assert_eq!("1.", Amount::from_str("1.").unwrap().to_string());
1252 assert_eq!(
1253 Amount(10_000_000_000_000_000_000),
1254 Amount::from_str("10").unwrap()
1255 );
1256 assert_eq!("10.", Amount(10_000_000_000_000_000_000).to_string());
1257 assert_eq!(
1258 "1001.3",
1259 (Amount::from_str("1.1")
1260 .unwrap()
1261 .saturating_add(Amount::from_str("1_000.2").unwrap()))
1262 .to_string()
1263 );
1264 assert_eq!(
1265 " 1.00000000000000000000",
1266 format!("{:25.20}", Amount::ONE)
1267 );
1268 assert_eq!(
1269 "~+12.34~~",
1270 format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
1271 );
1272 }
1273}