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