1use 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#[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 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#[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
168const 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 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#[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#[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#[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#[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 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#[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 pub const OBJECT_DIGEST_DELETED: ObjectDigest =
863 Self::new([Self::OBJECT_DIGEST_DELETED_BYTE_VAL; 32]);
864
865 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#[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}