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