iota_types/
digests.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{env, fmt};
6
7use anyhow::{anyhow, bail};
8use fastcrypto::encoding::{Base58, Encoding, Hex};
9use iota_protocol_config::Chain;
10use once_cell::sync::{Lazy, OnceCell};
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13use serde_with::{Bytes, serde_as};
14use tracing::info;
15
16use crate::{error::IotaError, iota_serde::Readable};
17
18/// A representation of a 32 byte digest
19#[serde_as]
20#[derive(
21    Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
22)]
23pub struct Digest(
24    #[schemars(with = "Base58")]
25    #[serde_as(as = "Readable<Base58, Bytes>")]
26    [u8; 32],
27);
28
29impl Digest {
30    pub const ZERO: Self = Digest([0; 32]);
31
32    pub const fn new(digest: [u8; 32]) -> Self {
33        Self(digest)
34    }
35
36    pub fn generate<R: rand::RngCore + rand::CryptoRng>(mut rng: R) -> Self {
37        let mut bytes = [0; 32];
38        rng.fill_bytes(&mut bytes);
39        Self(bytes)
40    }
41
42    pub fn random() -> Self {
43        Self::generate(rand::thread_rng())
44    }
45
46    pub const fn inner(&self) -> &[u8; 32] {
47        &self.0
48    }
49
50    pub const fn into_inner(self) -> [u8; 32] {
51        self.0
52    }
53
54    pub fn next_lexicographical(&self) -> Option<Self> {
55        let mut next_digest = *self;
56        let pos = next_digest.0.iter().rposition(|&byte| byte != 255)?;
57        next_digest.0[pos] += 1;
58        next_digest
59            .0
60            .iter_mut()
61            .skip(pos + 1)
62            .for_each(|byte| *byte = 0);
63        Some(next_digest)
64    }
65}
66
67impl AsRef<[u8]> for Digest {
68    fn as_ref(&self) -> &[u8] {
69        &self.0
70    }
71}
72
73impl AsRef<[u8; 32]> for Digest {
74    fn as_ref(&self) -> &[u8; 32] {
75        &self.0
76    }
77}
78
79impl From<Digest> for [u8; 32] {
80    fn from(digest: Digest) -> Self {
81        digest.into_inner()
82    }
83}
84
85impl From<[u8; 32]> for Digest {
86    fn from(digest: [u8; 32]) -> Self {
87        Self::new(digest)
88    }
89}
90
91impl TryFrom<Vec<u8>> for Digest {
92    type Error = IotaError;
93
94    fn try_from(bytes: Vec<u8>) -> Result<Self, IotaError> {
95        let bytes: [u8; 32] =
96            <[u8; 32]>::try_from(&bytes[..]).map_err(|_| IotaError::InvalidDigestLength {
97                expected: 32,
98                actual: bytes.len(),
99            })?;
100
101        Ok(Self::from(bytes))
102    }
103}
104
105impl fmt::Display for Digest {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        // TODO avoid the allocation
108        f.write_str(&Base58::encode(self.0))
109    }
110}
111
112impl fmt::Debug for Digest {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        fmt::Display::fmt(self, f)
115    }
116}
117
118impl fmt::LowerHex for Digest {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        if f.alternate() {
121            write!(f, "0x")?;
122        }
123
124        for byte in self.0 {
125            write!(f, "{:02x}", byte)?;
126        }
127
128        Ok(())
129    }
130}
131
132impl fmt::UpperHex for Digest {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        if f.alternate() {
135            write!(f, "0x")?;
136        }
137
138        for byte in self.0 {
139            write!(f, "{:02X}", byte)?;
140        }
141
142        Ok(())
143    }
144}
145
146/// Representation of a network's identifier by the genesis checkpoint's digest
147#[derive(
148    Clone,
149    Copy,
150    Debug,
151    Default,
152    PartialEq,
153    Eq,
154    PartialOrd,
155    Ord,
156    Hash,
157    Serialize,
158    Deserialize,
159    JsonSchema,
160)]
161pub struct ChainIdentifier(pub(crate) CheckpointDigest);
162
163pub const MAINNET_CHAIN_IDENTIFIER_BASE58: &str = "7gzPnGnmjqvpmF7NXTCmtacXLqx1cMaJFV6GCmi1peqr";
164pub const TESTNET_CHAIN_IDENTIFIER_BASE58: &str = "3MhPzSaSTHGffwPSV2Ws2DaK8LR8DBGPozTd2CbiJRwe";
165
166pub static MAINNET_CHAIN_IDENTIFIER: OnceCell<ChainIdentifier> = OnceCell::new();
167pub static TESTNET_CHAIN_IDENTIFIER: OnceCell<ChainIdentifier> = OnceCell::new();
168
169/// For testing purposes or bootstrapping regenesis chain configuration, you can
170/// set this environment variable to force protocol config to use a specific
171/// Chain.
172const IOTA_PROTOCOL_CONFIG_CHAIN_OVERRIDE_ENV_VAR_NAME: &str =
173    "IOTA_PROTOCOL_CONFIG_CHAIN_OVERRIDE";
174
175static IOTA_PROTOCOL_CONFIG_CHAIN_OVERRIDE: Lazy<Option<Chain>> = Lazy::new(|| {
176    if let Ok(s) = env::var(IOTA_PROTOCOL_CONFIG_CHAIN_OVERRIDE_ENV_VAR_NAME) {
177        info!("IOTA_PROTOCOL_CONFIG_CHAIN_OVERRIDE: {:?}", s);
178        match s.as_str() {
179            "mainnet" => Some(Chain::Mainnet),
180            "testnet" => Some(Chain::Testnet),
181            "" => None,
182            _ => panic!("unrecognized IOTA_PROTOCOL_CONFIG_CHAIN_OVERRIDE: {s:?}"),
183        }
184    } else {
185        None
186    }
187});
188
189impl ChainIdentifier {
190    /// Take a short 4 byte identifier and convert it into a ChainIdentifier.
191    /// Short ids come from the JSON RPC getChainIdentifier and are encoded in
192    /// hex.
193    pub fn from_chain_short_id(short_id: impl AsRef<str>) -> Option<Self> {
194        if Hex::from_bytes(&Base58::decode(MAINNET_CHAIN_IDENTIFIER_BASE58).ok()?)
195            .encoded_with_format()
196            .starts_with(&format!("0x{}", short_id.as_ref()))
197        {
198            Some(get_mainnet_chain_identifier())
199        } else if Hex::from_bytes(&Base58::decode(TESTNET_CHAIN_IDENTIFIER_BASE58).ok()?)
200            .encoded_with_format()
201            .starts_with(&format!("0x{}", short_id.as_ref()))
202        {
203            Some(get_testnet_chain_identifier())
204        } else {
205            None
206        }
207    }
208
209    pub fn chain(&self) -> Chain {
210        let mainnet_id = get_mainnet_chain_identifier();
211        let testnet_id = get_testnet_chain_identifier();
212
213        let chain = match self {
214            id if *id == mainnet_id => Chain::Mainnet,
215            id if *id == testnet_id => Chain::Testnet,
216            _ => Chain::Unknown,
217        };
218        if let Some(override_chain) = *IOTA_PROTOCOL_CONFIG_CHAIN_OVERRIDE {
219            if chain != Chain::Unknown {
220                panic!("not allowed to override real chain {chain:?}");
221            }
222            return override_chain;
223        }
224
225        chain
226    }
227
228    pub fn as_bytes(&self) -> &[u8; 32] {
229        self.0.inner()
230    }
231
232    pub fn into_bytes(self) -> [u8; 32] {
233        self.0.into_inner()
234    }
235
236    pub fn digest(&self) -> CheckpointDigest {
237        self.0
238    }
239}
240
241pub fn get_mainnet_chain_identifier() -> ChainIdentifier {
242    let digest = MAINNET_CHAIN_IDENTIFIER.get_or_init(|| {
243        let digest = CheckpointDigest::new(
244            Base58::decode(MAINNET_CHAIN_IDENTIFIER_BASE58)
245                .expect("mainnet genesis checkpoint digest literal is invalid")
246                .try_into()
247                .expect("Mainnet genesis checkpoint digest literal has incorrect length"),
248        );
249        ChainIdentifier::from(digest)
250    });
251    *digest
252}
253
254pub fn get_testnet_chain_identifier() -> ChainIdentifier {
255    let digest = TESTNET_CHAIN_IDENTIFIER.get_or_init(|| {
256        let digest = CheckpointDigest::new(
257            Base58::decode(TESTNET_CHAIN_IDENTIFIER_BASE58)
258                .expect("testnet genesis checkpoint digest literal is invalid")
259                .try_into()
260                .expect("Testnet genesis checkpoint digest literal has incorrect length"),
261        );
262        ChainIdentifier::from(digest)
263    });
264    *digest
265}
266
267impl fmt::Display for ChainIdentifier {
268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        for byte in self.0.0.0[0..4].iter() {
270            write!(f, "{:02x}", byte)?;
271        }
272
273        Ok(())
274    }
275}
276
277impl From<CheckpointDigest> for ChainIdentifier {
278    fn from(digest: CheckpointDigest) -> Self {
279        Self(digest)
280    }
281}
282
283/// Representation of a Checkpoint's digest
284#[derive(
285    Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
286)]
287pub struct CheckpointDigest(Digest);
288
289impl CheckpointDigest {
290    pub const fn new(digest: [u8; 32]) -> Self {
291        Self(Digest::new(digest))
292    }
293
294    pub fn generate<R: rand::RngCore + rand::CryptoRng>(rng: R) -> Self {
295        Self(Digest::generate(rng))
296    }
297
298    pub fn random() -> Self {
299        Self(Digest::random())
300    }
301
302    pub const fn inner(&self) -> &[u8; 32] {
303        self.0.inner()
304    }
305
306    pub const fn into_inner(self) -> [u8; 32] {
307        self.0.into_inner()
308    }
309
310    pub fn base58_encode(&self) -> String {
311        Base58::encode(self.0)
312    }
313
314    pub fn next_lexicographical(&self) -> Option<Self> {
315        self.0.next_lexicographical().map(Self)
316    }
317}
318
319impl AsRef<[u8]> for CheckpointDigest {
320    fn as_ref(&self) -> &[u8] {
321        self.0.as_ref()
322    }
323}
324
325impl AsRef<[u8; 32]> for CheckpointDigest {
326    fn as_ref(&self) -> &[u8; 32] {
327        self.0.as_ref()
328    }
329}
330
331impl From<CheckpointDigest> for [u8; 32] {
332    fn from(digest: CheckpointDigest) -> Self {
333        digest.into_inner()
334    }
335}
336
337impl From<[u8; 32]> for CheckpointDigest {
338    fn from(digest: [u8; 32]) -> Self {
339        Self::new(digest)
340    }
341}
342
343impl TryFrom<Vec<u8>> for CheckpointDigest {
344    type Error = IotaError;
345
346    fn try_from(bytes: Vec<u8>) -> Result<Self, IotaError> {
347        Digest::try_from(bytes).map(CheckpointDigest)
348    }
349}
350
351impl fmt::Display for CheckpointDigest {
352    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353        fmt::Display::fmt(&self.0, f)
354    }
355}
356
357impl fmt::Debug for CheckpointDigest {
358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359        f.debug_tuple("CheckpointDigest").field(&self.0).finish()
360    }
361}
362
363impl fmt::LowerHex for CheckpointDigest {
364    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365        fmt::LowerHex::fmt(&self.0, f)
366    }
367}
368
369impl fmt::UpperHex for CheckpointDigest {
370    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371        fmt::UpperHex::fmt(&self.0, f)
372    }
373}
374
375impl std::str::FromStr for CheckpointDigest {
376    type Err = anyhow::Error;
377
378    fn from_str(s: &str) -> Result<Self, Self::Err> {
379        let mut result = [0; 32];
380        let buffer = Base58::decode(s).map_err(|e| anyhow!(e))?;
381        if buffer.len() != 32 {
382            bail!("Invalid digest length. Expected 32 bytes");
383        }
384        result.copy_from_slice(&buffer);
385        Ok(CheckpointDigest::new(result))
386    }
387}
388
389#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
390pub struct CheckpointContentsDigest(Digest);
391
392impl CheckpointContentsDigest {
393    pub const fn new(digest: [u8; 32]) -> Self {
394        Self(Digest::new(digest))
395    }
396
397    pub fn generate<R: rand::RngCore + rand::CryptoRng>(rng: R) -> Self {
398        Self(Digest::generate(rng))
399    }
400
401    pub fn random() -> Self {
402        Self(Digest::random())
403    }
404
405    pub const fn inner(&self) -> &[u8; 32] {
406        self.0.inner()
407    }
408
409    pub const fn into_inner(self) -> [u8; 32] {
410        self.0.into_inner()
411    }
412
413    pub fn base58_encode(&self) -> String {
414        Base58::encode(self.0)
415    }
416
417    pub fn next_lexicographical(&self) -> Option<Self> {
418        self.0.next_lexicographical().map(Self)
419    }
420}
421
422impl AsRef<[u8]> for CheckpointContentsDigest {
423    fn as_ref(&self) -> &[u8] {
424        self.0.as_ref()
425    }
426}
427
428impl AsRef<[u8; 32]> for CheckpointContentsDigest {
429    fn as_ref(&self) -> &[u8; 32] {
430        self.0.as_ref()
431    }
432}
433
434impl From<CheckpointContentsDigest> for [u8; 32] {
435    fn from(digest: CheckpointContentsDigest) -> Self {
436        digest.into_inner()
437    }
438}
439
440impl From<[u8; 32]> for CheckpointContentsDigest {
441    fn from(digest: [u8; 32]) -> Self {
442        Self::new(digest)
443    }
444}
445
446impl fmt::Display for CheckpointContentsDigest {
447    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
448        fmt::Display::fmt(&self.0, f)
449    }
450}
451
452impl fmt::Debug for CheckpointContentsDigest {
453    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454        f.debug_tuple("CheckpointContentsDigest")
455            .field(&self.0)
456            .finish()
457    }
458}
459
460impl std::str::FromStr for CheckpointContentsDigest {
461    type Err = anyhow::Error;
462
463    fn from_str(s: &str) -> Result<Self, Self::Err> {
464        let mut result = [0; 32];
465        let buffer = Base58::decode(s).map_err(|e| anyhow!(e))?;
466        if buffer.len() != 32 {
467            bail!("Invalid digest length. Expected 32 bytes");
468        }
469        result.copy_from_slice(&buffer);
470        Ok(CheckpointContentsDigest::new(result))
471    }
472}
473
474impl fmt::LowerHex for CheckpointContentsDigest {
475    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
476        fmt::LowerHex::fmt(&self.0, f)
477    }
478}
479
480impl fmt::UpperHex for CheckpointContentsDigest {
481    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
482        fmt::UpperHex::fmt(&self.0, f)
483    }
484}
485
486/// A digest of a certificate, which commits to the signatures as well as the
487/// tx.
488#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
489pub struct CertificateDigest(Digest);
490
491impl CertificateDigest {
492    pub const fn new(digest: [u8; 32]) -> Self {
493        Self(Digest::new(digest))
494    }
495
496    pub fn random() -> Self {
497        Self(Digest::random())
498    }
499}
500
501impl fmt::Debug for CertificateDigest {
502    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503        f.debug_tuple("CertificateDigest").field(&self.0).finish()
504    }
505}
506
507/// A digest of a SenderSignedData, which commits to the signatures as well as
508/// the tx.
509#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
510pub struct SenderSignedDataDigest(Digest);
511
512impl SenderSignedDataDigest {
513    pub const fn new(digest: [u8; 32]) -> Self {
514        Self(Digest::new(digest))
515    }
516}
517
518impl fmt::Debug for SenderSignedDataDigest {
519    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520        f.debug_tuple("SenderSignedDataDigest")
521            .field(&self.0)
522            .finish()
523    }
524}
525
526/// A transaction will have a (unique) digest.
527#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
528pub struct TransactionDigest(Digest);
529
530impl Default for TransactionDigest {
531    fn default() -> Self {
532        Self::ZERO
533    }
534}
535
536impl TransactionDigest {
537    pub const ZERO: Self = Self(Digest::ZERO);
538
539    pub const fn new(digest: [u8; 32]) -> Self {
540        Self(Digest::new(digest))
541    }
542
543    /// A digest we use to signify the parent transaction was the genesis,
544    /// ie. for an object there is no parent digest.
545    /// Note that this is not the same as the digest of the genesis transaction,
546    /// which cannot be known ahead of time.
547    // TODO(https://github.com/iotaledger/iota/issues/65): we can pick anything here
548    pub const fn genesis_marker() -> Self {
549        Self::ZERO
550    }
551
552    pub fn generate<R: rand::RngCore + rand::CryptoRng>(rng: R) -> Self {
553        Self(Digest::generate(rng))
554    }
555
556    pub fn random() -> Self {
557        Self(Digest::random())
558    }
559
560    pub fn inner(&self) -> &[u8; 32] {
561        self.0.inner()
562    }
563
564    pub fn into_inner(self) -> [u8; 32] {
565        self.0.into_inner()
566    }
567
568    pub fn base58_encode(&self) -> String {
569        Base58::encode(self.0)
570    }
571
572    pub fn next_lexicographical(&self) -> Option<Self> {
573        self.0.next_lexicographical().map(Self)
574    }
575}
576
577impl AsRef<[u8]> for TransactionDigest {
578    fn as_ref(&self) -> &[u8] {
579        self.0.as_ref()
580    }
581}
582
583impl AsRef<[u8; 32]> for TransactionDigest {
584    fn as_ref(&self) -> &[u8; 32] {
585        self.0.as_ref()
586    }
587}
588
589impl From<TransactionDigest> for [u8; 32] {
590    fn from(digest: TransactionDigest) -> Self {
591        digest.into_inner()
592    }
593}
594
595impl From<[u8; 32]> for TransactionDigest {
596    fn from(digest: [u8; 32]) -> Self {
597        Self::new(digest)
598    }
599}
600
601impl fmt::Display for TransactionDigest {
602    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
603        fmt::Display::fmt(&self.0, f)
604    }
605}
606
607impl fmt::Debug for TransactionDigest {
608    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
609        f.debug_tuple("TransactionDigest").field(&self.0).finish()
610    }
611}
612
613impl fmt::LowerHex for TransactionDigest {
614    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615        fmt::LowerHex::fmt(&self.0, f)
616    }
617}
618
619impl fmt::UpperHex for TransactionDigest {
620    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
621        fmt::UpperHex::fmt(&self.0, f)
622    }
623}
624
625impl TryFrom<&[u8]> for TransactionDigest {
626    type Error = crate::error::IotaError;
627
628    fn try_from(bytes: &[u8]) -> Result<Self, crate::error::IotaError> {
629        let arr: [u8; 32] = bytes
630            .try_into()
631            .map_err(|_| crate::error::IotaError::InvalidTransactionDigest)?;
632        Ok(Self::new(arr))
633    }
634}
635
636impl std::str::FromStr for TransactionDigest {
637    type Err = anyhow::Error;
638
639    fn from_str(s: &str) -> Result<Self, Self::Err> {
640        let mut result = [0; 32];
641        let buffer = Base58::decode(s).map_err(|e| anyhow!(e))?;
642        if buffer.len() != 32 {
643            bail!("Invalid digest length. Expected 32 bytes");
644        }
645        result.copy_from_slice(&buffer);
646        Ok(TransactionDigest::new(result))
647    }
648}
649
650#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
651pub struct TransactionEffectsDigest(Digest);
652
653impl TransactionEffectsDigest {
654    pub const ZERO: Self = Self(Digest::ZERO);
655
656    pub const fn new(digest: [u8; 32]) -> Self {
657        Self(Digest::new(digest))
658    }
659
660    pub fn generate<R: rand::RngCore + rand::CryptoRng>(rng: R) -> Self {
661        Self(Digest::generate(rng))
662    }
663
664    pub fn random() -> Self {
665        Self(Digest::random())
666    }
667
668    pub const fn inner(&self) -> &[u8; 32] {
669        self.0.inner()
670    }
671
672    pub const fn into_inner(self) -> [u8; 32] {
673        self.0.into_inner()
674    }
675
676    pub fn base58_encode(&self) -> String {
677        Base58::encode(self.0)
678    }
679
680    pub fn next_lexicographical(&self) -> Option<Self> {
681        self.0.next_lexicographical().map(Self)
682    }
683}
684
685impl AsRef<[u8]> for TransactionEffectsDigest {
686    fn as_ref(&self) -> &[u8] {
687        self.0.as_ref()
688    }
689}
690
691impl AsRef<[u8; 32]> for TransactionEffectsDigest {
692    fn as_ref(&self) -> &[u8; 32] {
693        self.0.as_ref()
694    }
695}
696
697impl From<TransactionEffectsDigest> for [u8; 32] {
698    fn from(digest: TransactionEffectsDigest) -> Self {
699        digest.into_inner()
700    }
701}
702
703impl From<[u8; 32]> for TransactionEffectsDigest {
704    fn from(digest: [u8; 32]) -> Self {
705        Self::new(digest)
706    }
707}
708
709impl fmt::Display for TransactionEffectsDigest {
710    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
711        fmt::Display::fmt(&self.0, f)
712    }
713}
714
715impl fmt::Debug for TransactionEffectsDigest {
716    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
717        f.debug_tuple("TransactionEffectsDigest")
718            .field(&self.0)
719            .finish()
720    }
721}
722
723impl fmt::LowerHex for TransactionEffectsDigest {
724    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
725        fmt::LowerHex::fmt(&self.0, f)
726    }
727}
728
729impl fmt::UpperHex for TransactionEffectsDigest {
730    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
731        fmt::UpperHex::fmt(&self.0, f)
732    }
733}
734
735#[serde_as]
736#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)]
737pub struct TransactionEventsDigest(Digest);
738
739impl TransactionEventsDigest {
740    pub const ZERO: Self = Self(Digest::ZERO);
741
742    pub const fn new(digest: [u8; 32]) -> Self {
743        Self(Digest::new(digest))
744    }
745
746    pub fn random() -> Self {
747        Self(Digest::random())
748    }
749
750    pub fn next_lexicographical(&self) -> Option<Self> {
751        self.0.next_lexicographical().map(Self)
752    }
753
754    pub fn into_inner(self) -> [u8; 32] {
755        self.0.into_inner()
756    }
757}
758
759impl fmt::Debug for TransactionEventsDigest {
760    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
761        f.debug_tuple("TransactionEventsDigest")
762            .field(&self.0)
763            .finish()
764    }
765}
766
767impl AsRef<[u8]> for TransactionEventsDigest {
768    fn as_ref(&self) -> &[u8] {
769        self.0.as_ref()
770    }
771}
772
773impl AsRef<[u8; 32]> for TransactionEventsDigest {
774    fn as_ref(&self) -> &[u8; 32] {
775        self.0.as_ref()
776    }
777}
778
779impl std::str::FromStr for TransactionEventsDigest {
780    type Err = anyhow::Error;
781
782    fn from_str(s: &str) -> Result<Self, Self::Err> {
783        let mut result = [0; 32];
784        let buffer = Base58::decode(s).map_err(|e| anyhow!(e))?;
785        if buffer.len() != 32 {
786            bail!("Invalid digest length. Expected 32 bytes");
787        }
788        result.copy_from_slice(&buffer);
789        Ok(Self::new(result))
790    }
791}
792
793#[serde_as]
794#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)]
795pub struct EffectsAuxDataDigest(Digest);
796
797impl EffectsAuxDataDigest {
798    pub const ZERO: Self = Self(Digest::ZERO);
799
800    pub const fn new(digest: [u8; 32]) -> Self {
801        Self(Digest::new(digest))
802    }
803
804    pub fn random() -> Self {
805        Self(Digest::random())
806    }
807
808    pub fn next_lexicographical(&self) -> Option<Self> {
809        self.0.next_lexicographical().map(Self)
810    }
811
812    pub fn into_inner(self) -> [u8; 32] {
813        self.0.into_inner()
814    }
815}
816
817impl fmt::Debug for EffectsAuxDataDigest {
818    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
819        f.debug_tuple("EffectsAuxDataDigest")
820            .field(&self.0)
821            .finish()
822    }
823}
824
825impl AsRef<[u8]> for EffectsAuxDataDigest {
826    fn as_ref(&self) -> &[u8] {
827        self.0.as_ref()
828    }
829}
830
831impl AsRef<[u8; 32]> for EffectsAuxDataDigest {
832    fn as_ref(&self) -> &[u8; 32] {
833        self.0.as_ref()
834    }
835}
836
837impl std::str::FromStr for EffectsAuxDataDigest {
838    type Err = anyhow::Error;
839
840    fn from_str(s: &str) -> Result<Self, Self::Err> {
841        let mut result = [0; 32];
842        let buffer = Base58::decode(s).map_err(|e| anyhow!(e))?;
843        if buffer.len() != 32 {
844            bail!("Invalid digest length. Expected 32 bytes");
845        }
846        result.copy_from_slice(&buffer);
847        Ok(Self::new(result))
848    }
849}
850
851// Each object has a unique digest
852#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
853pub struct ObjectDigest(Digest);
854
855impl ObjectDigest {
856    pub const MIN: ObjectDigest = Self::new([u8::MIN; 32]);
857    pub const MAX: ObjectDigest = Self::new([u8::MAX; 32]);
858    pub const OBJECT_DIGEST_DELETED_BYTE_VAL: u8 = 99;
859    pub const OBJECT_DIGEST_WRAPPED_BYTE_VAL: u8 = 88;
860    pub const OBJECT_DIGEST_CANCELLED_BYTE_VAL: u8 = 77;
861
862    /// A marker that signifies the object is deleted.
863    pub const OBJECT_DIGEST_DELETED: ObjectDigest =
864        Self::new([Self::OBJECT_DIGEST_DELETED_BYTE_VAL; 32]);
865
866    /// A marker that signifies the object is wrapped into another object.
867    pub const OBJECT_DIGEST_WRAPPED: ObjectDigest =
868        Self::new([Self::OBJECT_DIGEST_WRAPPED_BYTE_VAL; 32]);
869
870    pub const OBJECT_DIGEST_CANCELLED: ObjectDigest =
871        Self::new([Self::OBJECT_DIGEST_CANCELLED_BYTE_VAL; 32]);
872
873    pub const fn new(digest: [u8; 32]) -> Self {
874        Self(Digest::new(digest))
875    }
876
877    pub fn generate<R: rand::RngCore + rand::CryptoRng>(rng: R) -> Self {
878        Self(Digest::generate(rng))
879    }
880
881    pub fn random() -> Self {
882        Self(Digest::random())
883    }
884
885    pub const fn inner(&self) -> &[u8; 32] {
886        self.0.inner()
887    }
888
889    pub const fn into_inner(self) -> [u8; 32] {
890        self.0.into_inner()
891    }
892
893    pub fn is_alive(&self) -> bool {
894        *self != Self::OBJECT_DIGEST_DELETED && *self != Self::OBJECT_DIGEST_WRAPPED
895    }
896
897    pub fn is_deleted(&self) -> bool {
898        *self == Self::OBJECT_DIGEST_DELETED
899    }
900
901    pub fn is_wrapped(&self) -> bool {
902        *self == Self::OBJECT_DIGEST_WRAPPED
903    }
904
905    pub fn base58_encode(&self) -> String {
906        Base58::encode(self.0)
907    }
908}
909
910impl AsRef<[u8]> for ObjectDigest {
911    fn as_ref(&self) -> &[u8] {
912        self.0.as_ref()
913    }
914}
915
916impl AsRef<[u8; 32]> for ObjectDigest {
917    fn as_ref(&self) -> &[u8; 32] {
918        self.0.as_ref()
919    }
920}
921
922impl From<ObjectDigest> for [u8; 32] {
923    fn from(digest: ObjectDigest) -> Self {
924        digest.into_inner()
925    }
926}
927
928impl From<[u8; 32]> for ObjectDigest {
929    fn from(digest: [u8; 32]) -> Self {
930        Self::new(digest)
931    }
932}
933
934impl fmt::Display for ObjectDigest {
935    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
936        fmt::Display::fmt(&self.0, f)
937    }
938}
939
940impl fmt::Debug for ObjectDigest {
941    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
942        write!(f, "o#{}", self.0)
943    }
944}
945
946impl fmt::LowerHex for ObjectDigest {
947    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
948        fmt::LowerHex::fmt(&self.0, f)
949    }
950}
951
952impl fmt::UpperHex for ObjectDigest {
953    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
954        fmt::UpperHex::fmt(&self.0, f)
955    }
956}
957
958impl TryFrom<&[u8]> for ObjectDigest {
959    type Error = crate::error::IotaError;
960
961    fn try_from(bytes: &[u8]) -> Result<Self, crate::error::IotaError> {
962        let arr: [u8; 32] = bytes
963            .try_into()
964            .map_err(|_| crate::error::IotaError::InvalidTransactionDigest)?;
965        Ok(Self::new(arr))
966    }
967}
968
969impl std::str::FromStr for ObjectDigest {
970    type Err = anyhow::Error;
971
972    fn from_str(s: &str) -> Result<Self, Self::Err> {
973        let mut result = [0; 32];
974        let buffer = Base58::decode(s).map_err(|e| anyhow!(e))?;
975        if buffer.len() != 32 {
976            bail!("Invalid digest length. Expected 32 bytes");
977        }
978        result.copy_from_slice(&buffer);
979        Ok(ObjectDigest::new(result))
980    }
981}
982
983/// A digest of a ZkLoginInputs, which commits to the signatures as well as the
984/// tx.
985#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
986pub struct ZKLoginInputsDigest(Digest);
987
988impl ZKLoginInputsDigest {
989    pub const fn new(digest: [u8; 32]) -> Self {
990        Self(Digest::new(digest))
991    }
992}
993
994#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
995pub struct ConsensusCommitDigest(Digest);
996
997impl ConsensusCommitDigest {
998    pub const ZERO: Self = Self(Digest::ZERO);
999
1000    pub const fn new(digest: [u8; 32]) -> Self {
1001        Self(Digest::new(digest))
1002    }
1003
1004    pub const fn inner(&self) -> &[u8; 32] {
1005        self.0.inner()
1006    }
1007
1008    pub const fn into_inner(self) -> [u8; 32] {
1009        self.0.into_inner()
1010    }
1011
1012    pub fn random() -> Self {
1013        Self(Digest::random())
1014    }
1015}
1016
1017impl Default for ConsensusCommitDigest {
1018    fn default() -> Self {
1019        Self::ZERO
1020    }
1021}
1022
1023impl From<ConsensusCommitDigest> for [u8; 32] {
1024    fn from(digest: ConsensusCommitDigest) -> Self {
1025        digest.into_inner()
1026    }
1027}
1028
1029impl From<[u8; 32]> for ConsensusCommitDigest {
1030    fn from(digest: [u8; 32]) -> Self {
1031        Self::new(digest)
1032    }
1033}
1034
1035impl fmt::Display for ConsensusCommitDigest {
1036    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1037        fmt::Display::fmt(&self.0, f)
1038    }
1039}
1040
1041impl fmt::Debug for ConsensusCommitDigest {
1042    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1043        f.debug_tuple("ConsensusCommitDigest")
1044            .field(&self.0)
1045            .finish()
1046    }
1047}
1048
1049mod test {
1050    #[allow(unused_imports)]
1051    use crate::digests::ChainIdentifier;
1052
1053    #[test]
1054    fn test_chain_id_mainnet() {
1055        let chain_id = ChainIdentifier::from_chain_short_id("6364aad5");
1056        assert_eq!(
1057            chain_id.unwrap().chain(),
1058            iota_protocol_config::Chain::Mainnet
1059        );
1060    }
1061
1062    #[test]
1063    fn test_chain_id_testnet() {
1064        let chain_id = ChainIdentifier::from_chain_short_id("2304aa97");
1065        assert_eq!(
1066            chain_id.unwrap().chain(),
1067            iota_protocol_config::Chain::Testnet
1068        );
1069    }
1070
1071    #[test]
1072    fn test_chain_id_unknown() {
1073        let chain_id = ChainIdentifier::from_chain_short_id("unknown");
1074        assert_eq!(chain_id, None);
1075    }
1076}