1use std::{env, fmt};
5
6use crate::{
7 error::{SuiError, SuiErrorKind, SuiResult},
8 sui_serde::Readable,
9};
10use fastcrypto::encoding::{Base58, Encoding, Hex};
11use fastcrypto::hash::{Blake2b256, HashFunction};
12use once_cell::sync::{Lazy, OnceCell};
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15use serde_with::{Bytes, serde_as};
16use sui_protocol_config::Chain;
17use tracing::info;
18
19#[serde_as]
21#[derive(
22 Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
23)]
24pub struct Digest(
25 #[schemars(with = "Base58")]
26 #[serde_as(as = "Readable<Base58, Bytes>")]
27 [u8; 32],
28);
29
30impl Digest {
31 pub const ZERO: Self = Digest([0; 32]);
32
33 pub const fn new(digest: [u8; 32]) -> Self {
34 Self(digest)
35 }
36
37 pub fn generate<R: rand::RngCore + rand::CryptoRng>(mut rng: R) -> Self {
38 let mut bytes = [0; 32];
39 rng.fill_bytes(&mut bytes);
40 Self(bytes)
41 }
42
43 pub fn random() -> Self {
44 Self::generate(rand::thread_rng())
45 }
46
47 pub const fn inner(&self) -> &[u8; 32] {
48 &self.0
49 }
50
51 pub const fn into_inner(self) -> [u8; 32] {
52 self.0
53 }
54
55 pub fn next_lexicographical(&self) -> Option<Self> {
56 let mut next_digest = *self;
57 let pos = next_digest.0.iter().rposition(|&byte| byte != 255)?;
58 next_digest.0[pos] += 1;
59 next_digest
60 .0
61 .iter_mut()
62 .skip(pos + 1)
63 .for_each(|byte| *byte = 0);
64 Some(next_digest)
65 }
66}
67
68impl AsRef<[u8]> for Digest {
69 fn as_ref(&self) -> &[u8] {
70 &self.0
71 }
72}
73
74impl AsRef<[u8; 32]> for Digest {
75 fn as_ref(&self) -> &[u8; 32] {
76 &self.0
77 }
78}
79
80impl From<Digest> for [u8; 32] {
81 fn from(digest: Digest) -> Self {
82 digest.into_inner()
83 }
84}
85
86impl From<[u8; 32]> for Digest {
87 fn from(digest: [u8; 32]) -> Self {
88 Self::new(digest)
89 }
90}
91
92impl TryFrom<Vec<u8>> for Digest {
93 type Error = SuiError;
94
95 fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
96 let bytes: [u8; 32] =
97 <[u8; 32]>::try_from(&bytes[..]).map_err(|_| SuiErrorKind::InvalidDigestLength {
98 expected: 32,
99 actual: bytes.len(),
100 })?;
101
102 Ok(Self::from(bytes))
103 }
104}
105
106impl fmt::Display for Digest {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 f.write_str(&Base58::encode(self.0))
110 }
111}
112
113impl fmt::Debug for Digest {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 fmt::Display::fmt(self, f)
116 }
117}
118
119impl fmt::LowerHex for Digest {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 if f.alternate() {
122 write!(f, "0x")?;
123 }
124
125 for byte in self.0 {
126 write!(f, "{:02x}", byte)?;
127 }
128
129 Ok(())
130 }
131}
132
133impl fmt::UpperHex for Digest {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 if f.alternate() {
136 write!(f, "0x")?;
137 }
138
139 for byte in self.0 {
140 write!(f, "{:02X}", byte)?;
141 }
142
143 Ok(())
144 }
145}
146
147#[derive(
149 Clone,
150 Copy,
151 Debug,
152 Default,
153 PartialEq,
154 Eq,
155 PartialOrd,
156 Ord,
157 Hash,
158 Serialize,
159 Deserialize,
160 JsonSchema,
161)]
162pub struct ChainIdentifier(CheckpointDigest);
163
164pub const MAINNET_CHAIN_IDENTIFIER_BASE58: &str = "4btiuiMPvEENsttpZC7CZ53DruC3MAgfznDbASZ7DR6S";
165pub const TESTNET_CHAIN_IDENTIFIER_BASE58: &str = "69WiPg3DAQiwdxfncX6wYQ2siKwAe6L9BZthQea3JNMD";
166
167pub static MAINNET_CHAIN_IDENTIFIER: OnceCell<ChainIdentifier> = OnceCell::new();
168pub static TESTNET_CHAIN_IDENTIFIER: OnceCell<ChainIdentifier> = OnceCell::new();
169
170pub const SUI_PROTOCOL_CONFIG_CHAIN_OVERRIDE_ENV_VAR_NAME: &str =
173 "SUI_PROTOCOL_CONFIG_CHAIN_OVERRIDE";
174
175static SUI_PROTOCOL_CONFIG_CHAIN_OVERRIDE: Lazy<Option<Chain>> = Lazy::new(|| {
176 if let Ok(s) = env::var(SUI_PROTOCOL_CONFIG_CHAIN_OVERRIDE_ENV_VAR_NAME) {
177 info!("SUI_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 SUI_PROTOCOL_CONFIG_CHAIN_OVERRIDE: {s:?}"),
183 }
184 } else {
185 None
186 }
187});
188
189impl ChainIdentifier {
190 pub fn from_chain_short_id(short_id: &String) -> 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))
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))
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) = *SUI_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
232pub fn get_mainnet_chain_identifier() -> ChainIdentifier {
233 let digest = MAINNET_CHAIN_IDENTIFIER.get_or_init(|| {
234 let digest = CheckpointDigest::new(
235 Base58::decode(MAINNET_CHAIN_IDENTIFIER_BASE58)
236 .expect("mainnet genesis checkpoint digest literal is invalid")
237 .try_into()
238 .expect("Mainnet genesis checkpoint digest literal has incorrect length"),
239 );
240 ChainIdentifier::from(digest)
241 });
242 *digest
243}
244
245pub fn get_testnet_chain_identifier() -> ChainIdentifier {
246 let digest = TESTNET_CHAIN_IDENTIFIER.get_or_init(|| {
247 let digest = CheckpointDigest::new(
248 Base58::decode(TESTNET_CHAIN_IDENTIFIER_BASE58)
249 .expect("testnet genesis checkpoint digest literal is invalid")
250 .try_into()
251 .expect("Testnet genesis checkpoint digest literal has incorrect length"),
252 );
253 ChainIdentifier::from(digest)
254 });
255 *digest
256}
257
258impl fmt::Display for ChainIdentifier {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 for byte in self.0.0.0[0..4].iter() {
261 write!(f, "{:02x}", byte)?;
262 }
263
264 Ok(())
265 }
266}
267
268impl From<CheckpointDigest> for ChainIdentifier {
269 fn from(digest: CheckpointDigest) -> Self {
270 Self(digest)
271 }
272}
273
274#[derive(
276 Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
277)]
278pub struct CheckpointDigest(Digest);
279
280impl CheckpointDigest {
281 pub const fn new(digest: [u8; 32]) -> Self {
282 Self(Digest::new(digest))
283 }
284
285 pub fn generate<R: rand::RngCore + rand::CryptoRng>(rng: R) -> Self {
286 Self(Digest::generate(rng))
287 }
288
289 pub fn random() -> Self {
290 Self(Digest::random())
291 }
292
293 pub const fn inner(&self) -> &[u8; 32] {
294 self.0.inner()
295 }
296
297 pub const fn into_inner(self) -> [u8; 32] {
298 self.0.into_inner()
299 }
300
301 pub fn base58_encode(&self) -> String {
302 Base58::encode(self.0)
303 }
304
305 pub fn next_lexicographical(&self) -> Option<Self> {
306 self.0.next_lexicographical().map(Self)
307 }
308}
309
310impl AsRef<[u8]> for CheckpointDigest {
311 fn as_ref(&self) -> &[u8] {
312 self.0.as_ref()
313 }
314}
315
316impl AsRef<[u8; 32]> for CheckpointDigest {
317 fn as_ref(&self) -> &[u8; 32] {
318 self.0.as_ref()
319 }
320}
321
322impl From<CheckpointDigest> for [u8; 32] {
323 fn from(digest: CheckpointDigest) -> Self {
324 digest.into_inner()
325 }
326}
327
328impl From<[u8; 32]> for CheckpointDigest {
329 fn from(digest: [u8; 32]) -> Self {
330 Self::new(digest)
331 }
332}
333
334impl TryFrom<Vec<u8>> for CheckpointDigest {
335 type Error = SuiError;
336
337 fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
338 Digest::try_from(bytes).map(CheckpointDigest)
339 }
340}
341
342impl fmt::Display for CheckpointDigest {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 fmt::Display::fmt(&self.0, f)
345 }
346}
347
348impl fmt::Debug for CheckpointDigest {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 f.debug_tuple("CheckpointDigest").field(&self.0).finish()
351 }
352}
353
354impl fmt::LowerHex for CheckpointDigest {
355 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356 fmt::LowerHex::fmt(&self.0, f)
357 }
358}
359
360impl fmt::UpperHex for CheckpointDigest {
361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362 fmt::UpperHex::fmt(&self.0, f)
363 }
364}
365
366impl std::str::FromStr for CheckpointDigest {
367 type Err = anyhow::Error;
368
369 fn from_str(s: &str) -> Result<Self, Self::Err> {
370 let mut result = [0; 32];
371 let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?;
372 if buffer.len() != 32 {
373 return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes"));
374 }
375 result.copy_from_slice(&buffer);
376 Ok(CheckpointDigest::new(result))
377 }
378}
379
380#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
381pub struct CheckpointContentsDigest(Digest);
382
383impl CheckpointContentsDigest {
384 pub const fn new(digest: [u8; 32]) -> Self {
385 Self(Digest::new(digest))
386 }
387
388 pub fn generate<R: rand::RngCore + rand::CryptoRng>(rng: R) -> Self {
389 Self(Digest::generate(rng))
390 }
391
392 pub fn random() -> Self {
393 Self(Digest::random())
394 }
395
396 pub const fn inner(&self) -> &[u8; 32] {
397 self.0.inner()
398 }
399
400 pub const fn into_inner(self) -> [u8; 32] {
401 self.0.into_inner()
402 }
403
404 pub fn base58_encode(&self) -> String {
405 Base58::encode(self.0)
406 }
407
408 pub fn next_lexicographical(&self) -> Option<Self> {
409 self.0.next_lexicographical().map(Self)
410 }
411}
412
413impl AsRef<[u8]> for CheckpointContentsDigest {
414 fn as_ref(&self) -> &[u8] {
415 self.0.as_ref()
416 }
417}
418
419impl AsRef<[u8; 32]> for CheckpointContentsDigest {
420 fn as_ref(&self) -> &[u8; 32] {
421 self.0.as_ref()
422 }
423}
424
425impl TryFrom<Vec<u8>> for CheckpointContentsDigest {
426 type Error = SuiError;
427
428 fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
429 Digest::try_from(bytes).map(CheckpointContentsDigest)
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)]
487pub struct CertificateDigest(Digest);
488
489impl CertificateDigest {
490 pub const fn new(digest: [u8; 32]) -> Self {
491 Self(Digest::new(digest))
492 }
493
494 pub fn random() -> Self {
495 Self(Digest::random())
496 }
497}
498
499impl fmt::Debug for CertificateDigest {
500 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501 f.debug_tuple("CertificateDigest").field(&self.0).finish()
502 }
503}
504
505#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
507pub struct SenderSignedDataDigest(Digest);
508
509impl SenderSignedDataDigest {
510 pub const fn new(digest: [u8; 32]) -> Self {
511 Self(Digest::new(digest))
512 }
513}
514
515impl fmt::Debug for SenderSignedDataDigest {
516 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
517 f.debug_tuple("SenderSignedDataDigest")
518 .field(&self.0)
519 .finish()
520 }
521}
522
523#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
525pub struct TransactionDigest(Digest);
526
527impl Default for TransactionDigest {
528 fn default() -> Self {
529 Self::ZERO
530 }
531}
532
533impl TransactionDigest {
534 pub const ZERO: Self = Self(Digest::ZERO);
535
536 pub const fn new(digest: [u8; 32]) -> Self {
537 Self(Digest::new(digest))
538 }
539
540 pub const fn from_digest(digest: Digest) -> Self {
541 Self(digest)
542 }
543
544 pub const fn genesis_marker() -> Self {
550 Self::ZERO
551 }
552
553 pub fn generate<R: rand::RngCore + rand::CryptoRng>(rng: R) -> Self {
554 Self(Digest::generate(rng))
555 }
556
557 pub fn random() -> Self {
558 Self(Digest::random())
559 }
560
561 pub fn inner(&self) -> &[u8; 32] {
562 self.0.inner()
563 }
564
565 pub fn into_inner(self) -> [u8; 32] {
566 self.0.into_inner()
567 }
568
569 pub fn base58_encode(&self) -> String {
570 Base58::encode(self.0)
571 }
572
573 pub fn next_lexicographical(&self) -> Option<Self> {
574 self.0.next_lexicographical().map(Self)
575 }
576}
577
578impl AsRef<[u8]> for TransactionDigest {
579 fn as_ref(&self) -> &[u8] {
580 self.0.as_ref()
581 }
582}
583
584impl AsRef<[u8; 32]> for TransactionDigest {
585 fn as_ref(&self) -> &[u8; 32] {
586 self.0.as_ref()
587 }
588}
589
590impl From<TransactionDigest> for [u8; 32] {
591 fn from(digest: TransactionDigest) -> Self {
592 digest.into_inner()
593 }
594}
595
596impl From<[u8; 32]> for TransactionDigest {
597 fn from(digest: [u8; 32]) -> Self {
598 Self::new(digest)
599 }
600}
601
602impl fmt::Display for TransactionDigest {
603 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
604 fmt::Display::fmt(&self.0, f)
605 }
606}
607
608impl fmt::Debug for TransactionDigest {
609 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610 f.debug_tuple("TransactionDigest").field(&self.0).finish()
611 }
612}
613
614impl fmt::LowerHex for TransactionDigest {
615 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
616 fmt::LowerHex::fmt(&self.0, f)
617 }
618}
619
620impl fmt::UpperHex for TransactionDigest {
621 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
622 fmt::UpperHex::fmt(&self.0, f)
623 }
624}
625
626impl TryFrom<&[u8]> for TransactionDigest {
627 type Error = crate::error::SuiError;
628
629 fn try_from(bytes: &[u8]) -> Result<Self, crate::error::SuiError> {
630 let arr: [u8; 32] = bytes
631 .try_into()
632 .map_err(|_| crate::error::SuiErrorKind::InvalidTransactionDigest)?;
633 Ok(Self::new(arr))
634 }
635}
636
637impl TryFrom<Vec<u8>> for TransactionDigest {
638 type Error = crate::error::SuiError;
639
640 fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
641 Digest::try_from(bytes).map(TransactionDigest)
642 }
643}
644
645impl std::str::FromStr for TransactionDigest {
646 type Err = anyhow::Error;
647
648 fn from_str(s: &str) -> Result<Self, Self::Err> {
649 let mut result = [0; 32];
650 let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?;
651 if buffer.len() != 32 {
652 return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes"));
653 }
654 result.copy_from_slice(&buffer);
655 Ok(TransactionDigest::new(result))
656 }
657}
658
659#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
660pub struct TransactionEffectsDigest(Digest);
661
662impl TransactionEffectsDigest {
663 pub const ZERO: Self = Self(Digest::ZERO);
664
665 pub const fn new(digest: [u8; 32]) -> Self {
666 Self(Digest::new(digest))
667 }
668
669 pub fn generate<R: rand::RngCore + rand::CryptoRng>(rng: R) -> Self {
670 Self(Digest::generate(rng))
671 }
672
673 pub fn random() -> Self {
674 Self(Digest::random())
675 }
676
677 pub const fn inner(&self) -> &[u8; 32] {
678 self.0.inner()
679 }
680
681 pub const fn into_inner(self) -> [u8; 32] {
682 self.0.into_inner()
683 }
684
685 pub fn base58_encode(&self) -> String {
686 Base58::encode(self.0)
687 }
688
689 pub fn next_lexicographical(&self) -> Option<Self> {
690 self.0.next_lexicographical().map(Self)
691 }
692}
693
694impl AsRef<[u8]> for TransactionEffectsDigest {
695 fn as_ref(&self) -> &[u8] {
696 self.0.as_ref()
697 }
698}
699
700impl AsRef<[u8; 32]> for TransactionEffectsDigest {
701 fn as_ref(&self) -> &[u8; 32] {
702 self.0.as_ref()
703 }
704}
705
706impl TryFrom<Vec<u8>> for TransactionEffectsDigest {
707 type Error = SuiError;
708
709 fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
710 Digest::try_from(bytes).map(TransactionEffectsDigest)
711 }
712}
713
714impl From<TransactionEffectsDigest> for [u8; 32] {
715 fn from(digest: TransactionEffectsDigest) -> Self {
716 digest.into_inner()
717 }
718}
719
720impl From<[u8; 32]> for TransactionEffectsDigest {
721 fn from(digest: [u8; 32]) -> Self {
722 Self::new(digest)
723 }
724}
725
726impl fmt::Display for TransactionEffectsDigest {
727 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
728 fmt::Display::fmt(&self.0, f)
729 }
730}
731
732impl fmt::Debug for TransactionEffectsDigest {
733 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
734 f.debug_tuple("TransactionEffectsDigest")
735 .field(&self.0)
736 .finish()
737 }
738}
739
740impl fmt::LowerHex for TransactionEffectsDigest {
741 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
742 fmt::LowerHex::fmt(&self.0, f)
743 }
744}
745
746impl fmt::UpperHex for TransactionEffectsDigest {
747 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
748 fmt::UpperHex::fmt(&self.0, f)
749 }
750}
751
752impl std::str::FromStr for TransactionEffectsDigest {
753 type Err = anyhow::Error;
754
755 fn from_str(s: &str) -> Result<Self, Self::Err> {
756 let mut result = [0; 32];
757 let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?;
758 if buffer.len() != 32 {
759 return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes"));
760 }
761 result.copy_from_slice(&buffer);
762 Ok(TransactionEffectsDigest::new(result))
763 }
764}
765
766#[serde_as]
767#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)]
768pub struct TransactionEventsDigest(Digest);
769
770impl TransactionEventsDigest {
771 pub const ZERO: Self = Self(Digest::ZERO);
772
773 pub const fn new(digest: [u8; 32]) -> Self {
774 Self(Digest::new(digest))
775 }
776
777 pub fn random() -> Self {
778 Self(Digest::random())
779 }
780
781 pub fn next_lexicographical(&self) -> Option<Self> {
782 self.0.next_lexicographical().map(Self)
783 }
784
785 pub fn into_inner(self) -> [u8; 32] {
786 self.0.into_inner()
787 }
788
789 pub fn base58_encode(&self) -> String {
790 Base58::encode(self.0)
791 }
792}
793
794impl fmt::Display for TransactionEventsDigest {
795 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796 fmt::Display::fmt(&self.0, f)
797 }
798}
799
800impl fmt::Debug for TransactionEventsDigest {
801 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
802 f.debug_tuple("TransactionEventsDigest")
803 .field(&self.0)
804 .finish()
805 }
806}
807
808impl AsRef<[u8]> for TransactionEventsDigest {
809 fn as_ref(&self) -> &[u8] {
810 self.0.as_ref()
811 }
812}
813
814impl AsRef<[u8; 32]> for TransactionEventsDigest {
815 fn as_ref(&self) -> &[u8; 32] {
816 self.0.as_ref()
817 }
818}
819
820impl TryFrom<Vec<u8>> for TransactionEventsDigest {
821 type Error = SuiError;
822
823 fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
824 Digest::try_from(bytes).map(TransactionEventsDigest)
825 }
826}
827
828impl std::str::FromStr for TransactionEventsDigest {
829 type Err = anyhow::Error;
830
831 fn from_str(s: &str) -> Result<Self, Self::Err> {
832 let mut result = [0; 32];
833 let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?;
834 if buffer.len() != 32 {
835 return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes"));
836 }
837 result.copy_from_slice(&buffer);
838 Ok(Self::new(result))
839 }
840}
841
842#[serde_as]
843#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)]
844pub struct EffectsAuxDataDigest(Digest);
845
846impl EffectsAuxDataDigest {
847 pub const ZERO: Self = Self(Digest::ZERO);
848
849 pub const fn new(digest: [u8; 32]) -> Self {
850 Self(Digest::new(digest))
851 }
852
853 pub fn random() -> Self {
854 Self(Digest::random())
855 }
856
857 pub fn next_lexicographical(&self) -> Option<Self> {
858 self.0.next_lexicographical().map(Self)
859 }
860
861 pub fn into_inner(self) -> [u8; 32] {
862 self.0.into_inner()
863 }
864}
865
866impl fmt::Display for EffectsAuxDataDigest {
867 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
868 fmt::Display::fmt(&self.0, f)
869 }
870}
871
872impl fmt::Debug for EffectsAuxDataDigest {
873 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
874 f.debug_tuple("EffectsAuxDataDigest")
875 .field(&self.0)
876 .finish()
877 }
878}
879
880impl AsRef<[u8]> for EffectsAuxDataDigest {
881 fn as_ref(&self) -> &[u8] {
882 self.0.as_ref()
883 }
884}
885
886impl AsRef<[u8; 32]> for EffectsAuxDataDigest {
887 fn as_ref(&self) -> &[u8; 32] {
888 self.0.as_ref()
889 }
890}
891
892impl std::str::FromStr for EffectsAuxDataDigest {
893 type Err = anyhow::Error;
894
895 fn from_str(s: &str) -> Result<Self, Self::Err> {
896 let mut result = [0; 32];
897 let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?;
898 if buffer.len() != 32 {
899 return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes"));
900 }
901 result.copy_from_slice(&buffer);
902 Ok(Self::new(result))
903 }
904}
905
906#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
908pub struct ObjectDigest(Digest);
909
910impl ObjectDigest {
911 pub const MIN: ObjectDigest = Self::new([u8::MIN; 32]);
912 pub const MAX: ObjectDigest = Self::new([u8::MAX; 32]);
913 pub const OBJECT_DIGEST_DELETED_BYTE_VAL: u8 = 99;
914 pub const OBJECT_DIGEST_WRAPPED_BYTE_VAL: u8 = 88;
915 pub const OBJECT_DIGEST_CANCELLED_BYTE_VAL: u8 = 77;
916
917 pub const OBJECT_DIGEST_DELETED: ObjectDigest =
919 Self::new([Self::OBJECT_DIGEST_DELETED_BYTE_VAL; 32]);
920
921 pub const OBJECT_DIGEST_WRAPPED: ObjectDigest =
923 Self::new([Self::OBJECT_DIGEST_WRAPPED_BYTE_VAL; 32]);
924
925 pub const OBJECT_DIGEST_CANCELLED: ObjectDigest =
926 Self::new([Self::OBJECT_DIGEST_CANCELLED_BYTE_VAL; 32]);
927
928 pub const fn new(digest: [u8; 32]) -> Self {
929 Self(Digest::new(digest))
930 }
931
932 pub fn generate<R: rand::RngCore + rand::CryptoRng>(rng: R) -> Self {
933 Self(Digest::generate(rng))
934 }
935
936 pub fn random() -> Self {
937 Self(Digest::random())
938 }
939
940 pub const fn inner(&self) -> &[u8; 32] {
941 self.0.inner()
942 }
943
944 pub const fn into_inner(self) -> [u8; 32] {
945 self.0.into_inner()
946 }
947
948 pub fn is_alive(&self) -> bool {
949 *self != Self::OBJECT_DIGEST_DELETED && *self != Self::OBJECT_DIGEST_WRAPPED
950 }
951
952 pub fn is_deleted(&self) -> bool {
953 *self == Self::OBJECT_DIGEST_DELETED
954 }
955
956 pub fn is_wrapped(&self) -> bool {
957 *self == Self::OBJECT_DIGEST_WRAPPED
958 }
959
960 pub fn base58_encode(&self) -> String {
961 Base58::encode(self.0)
962 }
963}
964
965impl AsRef<[u8]> for ObjectDigest {
966 fn as_ref(&self) -> &[u8] {
967 self.0.as_ref()
968 }
969}
970
971impl AsRef<[u8; 32]> for ObjectDigest {
972 fn as_ref(&self) -> &[u8; 32] {
973 self.0.as_ref()
974 }
975}
976
977impl From<ObjectDigest> for [u8; 32] {
978 fn from(digest: ObjectDigest) -> Self {
979 digest.into_inner()
980 }
981}
982
983impl From<[u8; 32]> for ObjectDigest {
984 fn from(digest: [u8; 32]) -> Self {
985 Self::new(digest)
986 }
987}
988
989impl fmt::Display for ObjectDigest {
990 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
991 fmt::Display::fmt(&self.0, f)
992 }
993}
994
995impl fmt::Debug for ObjectDigest {
996 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
997 write!(f, "o#{}", self.0)
998 }
999}
1000
1001impl fmt::LowerHex for ObjectDigest {
1002 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1003 fmt::LowerHex::fmt(&self.0, f)
1004 }
1005}
1006
1007impl fmt::UpperHex for ObjectDigest {
1008 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1009 fmt::UpperHex::fmt(&self.0, f)
1010 }
1011}
1012
1013impl TryFrom<&[u8]> for ObjectDigest {
1014 type Error = crate::error::SuiError;
1015
1016 fn try_from(bytes: &[u8]) -> Result<Self, crate::error::SuiError> {
1017 let arr: [u8; 32] = bytes
1018 .try_into()
1019 .map_err(|_| crate::error::SuiErrorKind::InvalidTransactionDigest)?;
1020 Ok(Self::new(arr))
1021 }
1022}
1023
1024impl std::str::FromStr for ObjectDigest {
1025 type Err = anyhow::Error;
1026
1027 fn from_str(s: &str) -> Result<Self, Self::Err> {
1028 let mut result = [0; 32];
1029 let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?;
1030 if buffer.len() != 32 {
1031 return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes"));
1032 }
1033 result.copy_from_slice(&buffer);
1034 Ok(ObjectDigest::new(result))
1035 }
1036}
1037
1038#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1040pub struct ZKLoginInputsDigest(Digest);
1041
1042impl ZKLoginInputsDigest {
1043 pub const fn new(digest: [u8; 32]) -> Self {
1044 Self(Digest::new(digest))
1045 }
1046}
1047
1048#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
1049pub struct ConsensusCommitDigest(Digest);
1050
1051impl ConsensusCommitDigest {
1052 pub const ZERO: Self = Self(Digest::ZERO);
1053
1054 pub const fn new(digest: [u8; 32]) -> Self {
1055 Self(Digest::new(digest))
1056 }
1057
1058 pub const fn inner(&self) -> &[u8; 32] {
1059 self.0.inner()
1060 }
1061
1062 pub const fn into_inner(self) -> [u8; 32] {
1063 self.0.into_inner()
1064 }
1065
1066 pub fn random() -> Self {
1067 Self(Digest::random())
1068 }
1069}
1070
1071impl Default for ConsensusCommitDigest {
1072 fn default() -> Self {
1073 Self::ZERO
1074 }
1075}
1076
1077impl From<ConsensusCommitDigest> for [u8; 32] {
1078 fn from(digest: ConsensusCommitDigest) -> Self {
1079 digest.into_inner()
1080 }
1081}
1082
1083impl From<[u8; 32]> for ConsensusCommitDigest {
1084 fn from(digest: [u8; 32]) -> Self {
1085 Self::new(digest)
1086 }
1087}
1088
1089impl fmt::Display for ConsensusCommitDigest {
1090 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1091 fmt::Display::fmt(&self.0, f)
1092 }
1093}
1094
1095impl fmt::Debug for ConsensusCommitDigest {
1096 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1097 f.debug_tuple("ConsensusCommitDigest")
1098 .field(&self.0)
1099 .finish()
1100 }
1101}
1102
1103#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
1104pub struct AdditionalConsensusStateDigest(Digest);
1105
1106impl AdditionalConsensusStateDigest {
1107 pub const ZERO: Self = Self(Digest::ZERO);
1108 pub const fn new(digest: [u8; 32]) -> Self {
1109 Self(Digest::new(digest))
1110 }
1111}
1112
1113impl fmt::Display for AdditionalConsensusStateDigest {
1114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1115 fmt::Display::fmt(&self.0, f)
1116 }
1117}
1118
1119impl fmt::Debug for AdditionalConsensusStateDigest {
1120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1121 f.debug_tuple("AdditionalConsensusStateDigest")
1122 .field(&self.0)
1123 .finish()
1124 }
1125}
1126
1127#[derive(
1128 Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
1129)]
1130pub struct CheckpointArtifactsDigest(Digest);
1131
1132impl CheckpointArtifactsDigest {
1133 pub const fn new(digest: [u8; 32]) -> Self {
1134 Self(Digest::new(digest))
1135 }
1136
1137 pub const fn inner(&self) -> &[u8; 32] {
1138 self.0.inner()
1139 }
1140
1141 pub const fn into_inner(self) -> [u8; 32] {
1142 self.0.into_inner()
1143 }
1144
1145 pub fn base58_encode(&self) -> String {
1146 Base58::encode(self.0)
1147 }
1148
1149 pub fn from_artifact_digests(digests: Vec<Digest>) -> SuiResult<Self> {
1150 let bytes =
1151 bcs::to_bytes(&digests).map_err(|e| SuiError::from(format!("BCS error: {}", e)))?;
1152 Ok(Self(Digest::new(Blake2b256::digest(&bytes).into())))
1153 }
1154}
1155
1156impl From<[u8; 32]> for CheckpointArtifactsDigest {
1157 fn from(digest: [u8; 32]) -> Self {
1158 Self(Digest::new(digest))
1159 }
1160}
1161
1162impl fmt::Display for CheckpointArtifactsDigest {
1163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1164 fmt::Display::fmt(&self.0, f)
1165 }
1166}
1167
1168#[cfg(test)]
1169mod test {
1170 use crate::digests::{ChainIdentifier, SUI_PROTOCOL_CONFIG_CHAIN_OVERRIDE};
1171
1172 fn has_env_override() -> bool {
1173 SUI_PROTOCOL_CONFIG_CHAIN_OVERRIDE.is_some()
1174 }
1175
1176 #[test]
1178 fn test_chain_id_mainnet() {
1179 if has_env_override() {
1180 return;
1181 }
1182 let chain_id = ChainIdentifier::from_chain_short_id(&String::from("35834a8a"));
1183 assert_eq!(
1184 chain_id.unwrap().chain(),
1185 sui_protocol_config::Chain::Mainnet
1186 );
1187 }
1188
1189 #[test]
1190 fn test_chain_id_testnet() {
1191 if has_env_override() {
1192 return;
1193 }
1194 let chain_id = ChainIdentifier::from_chain_short_id(&String::from("4c78adac"));
1195 assert_eq!(
1196 chain_id.unwrap().chain(),
1197 sui_protocol_config::Chain::Testnet
1198 );
1199 }
1200
1201 #[test]
1202 fn test_chain_id_unknown() {
1203 if has_env_override() {
1204 return;
1205 }
1206 let chain_id = ChainIdentifier::from_chain_short_id(&String::from("unknown"));
1207 assert_eq!(chain_id, None);
1208 }
1209}