mina_p2p_messages/v2/
manual.rs

1pub mod conv;
2use crate::{
3    b58::{self, Base58CheckOfBinProt, Base58CheckOfBytes},
4    b58version::USER_COMMAND_MEMO,
5    bigint::BigInt,
6    list::List,
7    number::Number,
8    string::ByteString,
9    versioned::Versioned,
10};
11use ark_ff::BigInteger256;
12use binprot::{BinProtRead, BinProtWrite};
13use binprot_derive::{BinProtRead, BinProtWrite};
14use derive_more::Deref;
15use malloc_size_of_derive::MallocSizeOf;
16use poseidon::hash::params::NO_INPUT_COINBASE_STACK;
17use serde::{de::Visitor, ser::SerializeTuple, Deserialize, Serialize, Serializer};
18use time::OffsetDateTime;
19
20use super::*;
21
22pub type TransactionSnarkScanStateStableV2TreesABase = (
23    ParallelScanWeightStableV1,
24    TransactionSnarkScanStateStableV2ScanStateTreesABaseT1,
25);
26
27pub type TransactionSnarkScanStateStableV2TreesAMerge = (
28    (ParallelScanWeightStableV1, ParallelScanWeightStableV1),
29    TransactionSnarkScanStateStableV2ScanStateTreesAMergeT1,
30);
31
32/// **OCaml name**: `Mina_base__Signed_command_memo.Make_str.Stable.V1`
33///
34/// Gid: `695`
35/// Location: [src/lib/mina_base/signed_command_memo.ml:21:6](https://github.com/MinaProtocol/mina/blob//bfd1009/src/lib/mina_base/signed_command_memo.ml#L21)
36///
37///
38/// Gid: `170`
39/// Location: [src/std_internal.ml:140:2](https://github.com/MinaProtocol/mina/blob//bfd1009/src/std_internal.ml#L140)
40///
41///
42/// Gid: `83`
43/// Location: [src/string.ml:44:6](https://github.com/MinaProtocol/mina/blob//bfd1009/src/string.ml#L44)
44#[derive(Clone, Debug, PartialEq, BinProtRead, BinProtWrite, Deref, MallocSizeOf)]
45pub struct MinaBaseSignedCommandMemoStableV1(pub crate::string::CharString);
46
47impl MinaBaseSignedCommandMemoStableV1 {
48    pub fn to_base58check(&self) -> String {
49        b58::encode(self.0.as_ref(), USER_COMMAND_MEMO)
50    }
51
52    pub fn from_base58check(s: &str) -> Self {
53        let decoded = b58::decode(s, USER_COMMAND_MEMO).unwrap();
54        MinaBaseSignedCommandMemoStableV1(decoded[1..].into())
55    }
56}
57
58impl Serialize for MinaBaseSignedCommandMemoStableV1 {
59    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
60    where
61        S: serde::ser::Serializer,
62    {
63        if serializer.is_human_readable() {
64            let base58check = b58::encode(self.0.as_ref(), USER_COMMAND_MEMO);
65            base58check.serialize(serializer)
66        } else {
67            self.0.serialize(serializer)
68        }
69    }
70}
71
72impl<'de> Deserialize<'de> for MinaBaseSignedCommandMemoStableV1 {
73    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
74    where
75        D: serde::de::Deserializer<'de>,
76    {
77        if deserializer.is_human_readable() {
78            let base58check = String::deserialize(deserializer)?;
79            let decoded = b58::decode(&base58check, USER_COMMAND_MEMO)
80                .map_err(|err| serde::de::Error::custom(format!("Base58 decode error: {}", err)))?;
81            Ok(MinaBaseSignedCommandMemoStableV1(decoded[1..].into()))
82        } else {
83            let char_string = crate::string::CharString::deserialize(deserializer)?;
84            Ok(MinaBaseSignedCommandMemoStableV1(char_string))
85        }
86    }
87}
88
89//
90//  Location: [src/lib/parallel_scan/parallel_scan.ml:247:6](https://github.com/openmina/mina/blob/da4c511501876adff40f3e1281392fedd121d607/src/lib/parallel_scan/parallel_scan.ml#L247)
91//
92//  Gid: 947
93#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
94pub enum TransactionSnarkScanStateStableV2ScanStateTreesA {
95    Leaf(Vec<TransactionSnarkScanStateStableV2TreesABase>),
96    Node {
97        depth: crate::number::Int32,
98        value: Vec<TransactionSnarkScanStateStableV2TreesAMerge>,
99        sub_tree: Box<TransactionSnarkScanStateStableV2ScanStateTreesA>,
100    },
101}
102
103#[derive(BinProtRead, BinProtWrite)]
104enum _Tree {
105    Leaf,
106    Node,
107}
108
109impl BinProtRead for TransactionSnarkScanStateStableV2ScanStateTreesA {
110    fn binprot_read<R: std::io::Read + ?Sized>(r: &mut R) -> Result<Self, binprot::Error>
111    where
112        Self: Sized,
113    {
114        let mut depth: i32 = 0;
115        let mut values: Vec<Vec<TransactionSnarkScanStateStableV2TreesAMerge>> = Vec::new();
116        loop {
117            match _Tree::binprot_read(r)? {
118                _Tree::Leaf => {
119                    let len = 1 << depth;
120                    let mut data = Vec::with_capacity(len);
121                    for _ in 0..len {
122                        data.push(TransactionSnarkScanStateStableV2TreesABase::binprot_read(
123                            r,
124                        )?)
125                    }
126                    let mut tree = Self::Leaf(data);
127                    while let Some(value) = values.pop() {
128                        depth = depth - 1;
129                        tree = Self::Node {
130                            depth: depth.into(),
131                            value,
132                            sub_tree: Box::new(tree),
133                        }
134                    }
135                    return Ok(tree);
136                }
137                _Tree::Node => {
138                    let _depth = i32::binprot_read(r)?;
139                    if _depth != depth {
140                        return Err(binprot::Error::CustomError(
141                            format!("Incorrect tree depth, expected `{depth}`, got `{_depth}`")
142                                .into(),
143                        ));
144                    }
145                    let len = 1 << depth;
146                    let mut value = Vec::with_capacity(len);
147                    for _ in 0..len {
148                        value.push(TransactionSnarkScanStateStableV2TreesAMerge::binprot_read(
149                            r,
150                        )?)
151                    }
152                    values.push(value);
153                    depth += 1;
154                }
155            }
156        }
157    }
158}
159
160impl BinProtWrite for TransactionSnarkScanStateStableV2ScanStateTreesA {
161    fn binprot_write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
162        let mut curr = self;
163        let mut curr_depth = 0;
164        loop {
165            match curr {
166                Self::Leaf(leaf) => {
167                    let len = 1 << curr_depth;
168                    if leaf.len() != len {
169                        return Err(std::io::Error::new(
170                            std::io::ErrorKind::InvalidInput,
171                            format!(
172                                "Invalid leaf data lenght, expecting {len}, got {}",
173                                leaf.len()
174                            ),
175                        ));
176                    }
177                    _Tree::Leaf.binprot_write(w)?;
178                    leaf.iter().try_for_each(|b| b.binprot_write(w))?;
179                    return Ok(());
180                }
181                Self::Node {
182                    depth,
183                    value,
184                    sub_tree,
185                } => {
186                    if &depth.0 != &curr_depth {
187                        return Err(std::io::Error::new(
188                            std::io::ErrorKind::InvalidInput,
189                            format!(
190                                "Invalid depth, expecting {curr_depth}, got {depth}",
191                                depth = depth.0
192                            ),
193                        ));
194                    }
195                    if value.len() != (1 << curr_depth) {
196                        return Err(std::io::Error::new(
197                            std::io::ErrorKind::InvalidInput,
198                            format!(
199                                "Invalid node data lenght, expecting {}, got {}",
200                                1 << curr_depth,
201                                value.len()
202                            ),
203                        ));
204                    }
205                    _Tree::Node.binprot_write(w)?;
206                    depth.binprot_write(w)?;
207                    value.iter().try_for_each(|b| b.binprot_write(w))?;
208                    curr = sub_tree;
209                    curr_depth += 1;
210                }
211            }
212        }
213    }
214}
215
216// TODO: many of these OfSexp/SexpOf implementations can be removed if rsexp-derive is forked and modified
217// to fix a big in how enums are handled, and to avoid intermediary wrapping types in the output
218
219impl rsexp::OfSexp for PicklesBaseProofsVerifiedStableV1 {
220    fn of_sexp(s: &rsexp::Sexp) -> Result<Self, rsexp::IntoSexpError>
221    where
222        Self: Sized,
223    {
224        match s.extract_enum("PicklesBaseProofsVerifiedStableV1")? {
225            (b"N0", _) => Ok(PicklesBaseProofsVerifiedStableV1::N0),
226            (b"N1", _) => Ok(PicklesBaseProofsVerifiedStableV1::N1),
227            (b"N2", _) => Ok(PicklesBaseProofsVerifiedStableV1::N2),
228            (ctor, _) => Err(rsexp::IntoSexpError::UnknownConstructorForEnum {
229                type_: "PicklesBaseProofsVerifiedStableV1",
230                constructor: String::from_utf8_lossy(ctor).to_string(),
231            }),
232        }
233    }
234}
235
236impl rsexp::OfSexp for LimbVectorConstantHex64StableV1 {
237    fn of_sexp(s: &rsexp::Sexp) -> Result<Self, rsexp::IntoSexpError>
238    where
239        Self: Sized,
240    {
241        let bytes = s.extract_atom("LimbVectorConstantHex64StableV1")?;
242        let hex_str = std::str::from_utf8(bytes).map_err(|_| {
243            rsexp::IntoSexpError::StringConversionError {
244                err: format!("Expected 16 bytes hex string, got {bytes:?}"),
245            }
246        })?;
247        if hex_str.len() != 16 {
248            return Err(rsexp::IntoSexpError::StringConversionError {
249                err: format!("Expected 16 bytes hex string, got {hex_str:?}"),
250            });
251        }
252        let n = u64::from_str_radix(hex_str, 16).map_err(|_| {
253            rsexp::IntoSexpError::StringConversionError {
254                err: format!("Expected 16 bytes hex string, got {hex_str:?}"),
255            }
256        })?;
257
258        Ok(Self(n.into()))
259    }
260}
261
262impl rsexp::SexpOf for LimbVectorConstantHex64StableV1 {
263    fn sexp_of(&self) -> rsexp::Sexp {
264        let value: u64 = self.0.as_u64();
265        let hex_str = format!("{:016x}", value);
266
267        rsexp::Sexp::Atom(format!("0x{}", hex_str).into_bytes())
268    }
269}
270
271impl rsexp::OfSexp for CompositionTypesBranchDataDomainLog2StableV1 {
272    fn of_sexp(s: &rsexp::Sexp) -> Result<Self, rsexp::IntoSexpError>
273    where
274        Self: Sized,
275    {
276        match s.extract_atom("CompositionTypesBranchDataDomainLog2StableV1")? {
277            [ch] => Ok(Self((*ch).into())),
278            bytes => Err(rsexp::IntoSexpError::StringConversionError {
279                err: format!("Expected single byte string, got {bytes:?}"),
280            }),
281        }
282    }
283}
284
285impl rsexp::SexpOf for CompositionTypesBranchDataDomainLog2StableV1 {
286    fn sexp_of(&self) -> rsexp::Sexp {
287        rsexp::Sexp::Atom(vec![self.0.as_u8()])
288    }
289}
290
291impl rsexp::OfSexp for CompositionTypesDigestConstantStableV1 {
292    fn of_sexp(s: &rsexp::Sexp) -> std::result::Result<Self, rsexp::IntoSexpError> {
293        Ok(Self(crate::pseq::PaddedSeq::<
294            LimbVectorConstantHex64StableV1,
295            4,
296        >::of_sexp(s)?))
297    }
298}
299
300impl rsexp::SexpOf for CompositionTypesDigestConstantStableV1 {
301    fn sexp_of(&self) -> rsexp::Sexp {
302        self.0.sexp_of()
303    }
304}
305
306impl rsexp::OfSexp for PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2 {
307    fn of_sexp(s: &rsexp::Sexp) -> std::result::Result<Self, rsexp::IntoSexpError> {
308        Ok(Self(crate::pseq::PaddedSeq::<
309            PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2A,
310            15,
311        >::of_sexp(s)?))
312    }
313}
314
315impl rsexp::SexpOf for PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2 {
316    fn sexp_of(&self) -> rsexp::Sexp {
317        self.0.sexp_of()
318    }
319}
320
321impl rsexp::OfSexp for TransactionSnarkProofStableV2 {
322    fn of_sexp(s: &rsexp::Sexp) -> std::result::Result<Self, rsexp::IntoSexpError> {
323        Ok(Self(PicklesProofProofsVerified2ReprStableV2::of_sexp(s)?))
324    }
325}
326
327impl rsexp::SexpOf for TransactionSnarkProofStableV2 {
328    fn sexp_of(&self) -> rsexp::Sexp {
329        self.0.sexp_of()
330    }
331}
332
333impl serde::Serialize for TransactionSnarkProofStableV2 {
334    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
335    where
336        S: serde::Serializer,
337    {
338        if serializer.is_human_readable() {
339            use base64::{engine::general_purpose::URL_SAFE, Engine as _};
340            use rsexp::SexpOf;
341
342            let sexp = self.sexp_of();
343            let sexp_bytes = sexp.to_bytes();
344            let base64_data = URL_SAFE.encode(&sexp_bytes);
345
346            base64_data.serialize(serializer)
347        } else {
348            serializer.serialize_newtype_struct("TransactionSnarkProofStableV2", &self.0)
349        }
350    }
351}
352
353impl<'de> Deserialize<'de> for TransactionSnarkProofStableV2 {
354    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
355    where
356        D: serde::de::Deserializer<'de>,
357    {
358        if deserializer.is_human_readable() {
359            use base64::{engine::general_purpose::URL_SAFE, Engine as _};
360            use rsexp::OfSexp;
361            let base64_data = String::deserialize(deserializer)?;
362            let sexp_data = URL_SAFE
363                .decode(&base64_data)
364                .map_err(serde::de::Error::custom)?;
365            let sexp = rsexp::from_slice(&sexp_data).map_err(|err| {
366                serde::de::Error::custom(format!("S-exp parsing failure: {err:?}"))
367            })?;
368            let proof = Self::of_sexp(&sexp).map_err(serde::de::Error::custom)?;
369            Ok(proof)
370        } else {
371            struct TransactionSnarkProofStableV2Visitor;
372
373            impl<'de> serde::de::Visitor<'de> for TransactionSnarkProofStableV2Visitor {
374                type Value = TransactionSnarkProofStableV2;
375
376                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
377                    formatter.write_str("a valid TransactionSnarkProofStableV2")
378                }
379
380                fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
381                where
382                    D: serde::de::Deserializer<'de>,
383                {
384                    let inner_value =
385                        PicklesProofProofsVerified2ReprStableV2::deserialize(deserializer)?;
386                    Ok(TransactionSnarkProofStableV2(inner_value))
387                }
388            }
389
390            deserializer.deserialize_newtype_struct(
391                "TransactionSnarkProofStableV2",
392                TransactionSnarkProofStableV2Visitor,
393            )
394        }
395    }
396}
397
398impl<'de> Deserialize<'de> for PicklesProofProofsVerifiedMaxStableV2 {
399    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
400    where
401        D: serde::de::Deserializer<'de>,
402    {
403        if deserializer.is_human_readable() {
404            use base64::{engine::general_purpose::URL_SAFE, Engine as _};
405            use rsexp::OfSexp;
406            let base64_data = String::deserialize(deserializer)?;
407            let sexp_data = URL_SAFE
408                .decode(&base64_data)
409                .map_err(serde::de::Error::custom)?;
410            let sexp = rsexp::from_slice(&sexp_data).map_err(|err| {
411                serde::de::Error::custom(format!("S-exp parsing failure: {err:?}"))
412            })?;
413            let proof = Self::of_sexp(&sexp).map_err(serde::de::Error::custom)?;
414            Ok(proof)
415        } else {
416            #[derive(Deserialize)]
417            struct Inner {
418                statement: PicklesProofProofsVerified2ReprStableV2Statement,
419                prev_evals: PicklesProofProofsVerified2ReprStableV2PrevEvals,
420                proof: PicklesWrapWireProofStableV1,
421            }
422
423            let inner = Inner::deserialize(deserializer)?;
424            Ok(PicklesProofProofsVerifiedMaxStableV2 {
425                statement: inner.statement,
426                prev_evals: inner.prev_evals,
427                proof: inner.proof,
428            })
429        }
430    }
431}
432
433impl serde::Serialize for PicklesProofProofsVerifiedMaxStableV2 {
434    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
435    where
436        S: serde::Serializer,
437    {
438        if serializer.is_human_readable() {
439            use base64::{engine::general_purpose::URL_SAFE, Engine as _};
440            use rsexp::SexpOf as _;
441
442            let sexp = self.sexp_of();
443            let sexp_bytes = sexp.to_bytes();
444            let base64_data = URL_SAFE.encode(&sexp_bytes);
445
446            base64_data.serialize(serializer)
447        } else {
448            use serde::ser::SerializeStruct;
449            let mut state =
450                serializer.serialize_struct("PicklesProofProofsVerifiedMaxStableV2", 3)?;
451            state.serialize_field("statement", &self.statement)?;
452            state.serialize_field("prev_evals", &self.prev_evals)?;
453            state.serialize_field("proof", &self.proof)?;
454            state.end()
455        }
456    }
457}
458
459macro_rules! base58check_of_binprot {
460    ($name:ident, versioned($ty:ty, $version:expr), $version_byte:ident) => {
461        impl From<Versioned<$ty, $version>> for $ty {
462            fn from(source: Versioned<$ty, $version>) -> Self {
463                source.into_inner()
464            }
465        }
466
467        pub type $name = Base58CheckOfBinProt<
468            $ty,
469            Versioned<$ty, $version>,
470            { $crate::b58version::$version_byte },
471        >;
472    };
473    ($name:ident, versioned $ty:ty, $version_byte:ident) => {
474        base58check_of_binprot!($name, versioned($ty, 1), $version_byte);
475    };
476    ($name:ident, $ty:ty, $version_byte:ident) => {
477        pub type $name = Base58CheckOfBinProt<$ty, $ty, { $crate::b58version::$version_byte }>;
478    };
479}
480
481macro_rules! base58check_of_bytes {
482    ($name:ident, $ty:ty, $version_byte:ident) => {
483        pub type $name = Base58CheckOfBytes<$ty, { $crate::b58version::$version_byte }>;
484    };
485}
486
487base58check_of_binprot!(LedgerHash, versioned MinaBaseLedgerHash0StableV1, LEDGER_HASH);
488base58check_of_bytes!(
489    StagedLedgerHashAuxHash,
490    crate::string::ByteString,
491    STAGED_LEDGER_HASH_AUX_HASH
492);
493base58check_of_binprot!(EpochSeed, versioned MinaBaseEpochSeedStableV1, EPOCH_SEED);
494base58check_of_bytes!(
495    StagedLedgerHashPendingCoinbaseAux,
496    crate::string::ByteString,
497    STAGED_LEDGER_HASH_PENDING_COINBASE_AUX
498);
499base58check_of_binprot!(StateHash, versioned DataHashLibStateHashStableV1, STATE_HASH);
500base58check_of_binprot!(StateBodyHash, versioned MinaBaseStateBodyHashStableV1, STATE_BODY_HASH);
501base58check_of_binprot!(
502    PendingCoinbaseHash,
503    versioned MinaBasePendingCoinbaseHashVersionedStableV1,
504    RECEIPT_CHAIN_HASH
505);
506base58check_of_binprot!(
507    ReceiptChainHash,
508    versioned MinaBaseReceiptChainHashStableV1,
509    RECEIPT_CHAIN_HASH
510);
511base58check_of_binprot!(
512    TokenIdKeyHash,
513    MinaBaseAccountIdDigestStableV1,
514    TOKEN_ID_KEY
515);
516base58check_of_binprot!(
517    CoinbaseStackData,
518    versioned MinaBasePendingCoinbaseCoinbaseStackStableV1,
519    COINBASE_STACK_DATA
520);
521base58check_of_binprot!(
522    CoinbaseStackHash,
523    versioned MinaBasePendingCoinbaseStackHashStableV1,
524    COINBASE_STACK_HASH
525);
526base58check_of_binprot!(
527    Signature,
528    versioned MinaBaseSignatureStableV1,
529    SIGNATURE
530);
531
532impl StateHash {
533    pub fn zero() -> Self {
534        DataHashLibStateHashStableV1(BigInt::zero()).into()
535    }
536}
537
538impl EpochSeed {
539    pub fn zero() -> Self {
540        MinaBaseEpochSeedStableV1(BigInt::zero()).into()
541    }
542}
543
544impl CoinbaseStackData {
545    pub fn empty() -> Self {
546        // In OCaml: https://github.com/MinaProtocol/mina/blob/68b49fdaafabed0f2cd400c4c69f91e81db681e7/src/lib/mina_base/pending_coinbase.ml#L186
547        // let empty = Random_oracle.salt "CoinbaseStack" |> Random_oracle.digest
548        let empty = poseidon::hash::hash_noinputs(&NO_INPUT_COINBASE_STACK);
549        MinaBasePendingCoinbaseCoinbaseStackStableV1(empty.into()).into()
550    }
551}
552
553impl CoinbaseStackHash {
554    pub fn zero() -> Self {
555        MinaBasePendingCoinbaseStackHashStableV1(BigInt::zero()).into()
556    }
557}
558
559impl StagedLedgerHashAuxHash {
560    pub fn zero() -> Self {
561        crate::string::ByteString::from(vec![0; 32]).into()
562    }
563}
564
565impl StagedLedgerHashPendingCoinbaseAux {
566    pub fn zero() -> Self {
567        crate::string::ByteString::from(vec![0; 32]).into()
568    }
569}
570
571impl ConsensusVrfOutputTruncatedStableV1 {
572    pub fn zero() -> Self {
573        Self(crate::string::ByteString::from(vec![0; 32]))
574    }
575}
576
577impl MinaBaseStagedLedgerHashNonSnarkStableV1 {
578    pub fn zero(genesis_ledger_hash: LedgerHash) -> Self {
579        Self {
580            ledger_hash: genesis_ledger_hash,
581            aux_hash: StagedLedgerHashAuxHash::zero(),
582            pending_coinbase_aux: StagedLedgerHashPendingCoinbaseAux::zero(),
583        }
584    }
585}
586
587impl From<mina_curves::pasta::Fp> for LedgerHash {
588    fn from(value: mina_curves::pasta::Fp) -> Self {
589        MinaBaseLedgerHash0StableV1(value.into()).into()
590    }
591}
592
593impl From<&mina_curves::pasta::Fp> for LedgerHash {
594    fn from(value: &mina_curves::pasta::Fp) -> Self {
595        MinaBaseLedgerHash0StableV1(value.into()).into()
596    }
597}
598
599impl MinaBaseStagedLedgerHashStableV1 {
600    pub fn zero(
601        genesis_ledger_hash: LedgerHash,
602        empty_pending_coinbase_hash: PendingCoinbaseHash,
603    ) -> Self {
604        Self {
605            non_snark: MinaBaseStagedLedgerHashNonSnarkStableV1::zero(genesis_ledger_hash),
606            pending_coinbase_hash: empty_pending_coinbase_hash,
607        }
608    }
609}
610
611impl MinaBasePendingCoinbaseUpdateStableV1 {
612    pub fn zero() -> Self {
613        Self {
614            action: MinaBasePendingCoinbaseUpdateActionStableV1::UpdateNone,
615            coinbase_amount: CurrencyAmountStableV1(0u64.into()),
616        }
617    }
618}
619
620impl MinaBasePendingCoinbaseStackVersionedStableV1 {
621    pub fn empty() -> Self {
622        Self {
623            data: CoinbaseStackData::empty(),
624            state: MinaBasePendingCoinbaseStateStackStableV1 {
625                init: CoinbaseStackHash::zero(),
626                curr: CoinbaseStackHash::zero(),
627            },
628        }
629    }
630}
631
632impl ConsensusProofOfStakeDataEpochDataStakingValueVersionedValueStableV1 {
633    pub fn zero(
634        ledger_hash: LedgerHash,
635        total_currency: CurrencyAmountStableV1,
636        seed: EpochSeed,
637    ) -> Self {
638        Self {
639            ledger: MinaBaseEpochLedgerValueStableV1 {
640                hash: ledger_hash,
641                total_currency,
642            },
643            seed,
644            start_checkpoint: StateHash::zero(),
645            lock_checkpoint: StateHash::zero(),
646            epoch_length: 1.into(),
647        }
648    }
649}
650
651impl ConsensusProofOfStakeDataEpochDataNextValueVersionedValueStableV1 {
652    pub fn zero(
653        ledger_hash: LedgerHash,
654        total_currency: CurrencyAmountStableV1,
655        seed: EpochSeed,
656    ) -> Self {
657        Self {
658            ledger: MinaBaseEpochLedgerValueStableV1 {
659                hash: ledger_hash,
660                total_currency,
661            },
662            seed,
663            start_checkpoint: StateHash::zero(),
664            lock_checkpoint: StateHash::zero(),
665            epoch_length: 1.into(),
666        }
667    }
668}
669
670impl AsRef<BigInteger256> for LedgerHash {
671    fn as_ref(&self) -> &BigInteger256 {
672        self.0.as_ref()
673    }
674}
675
676impl Default for TokenIdKeyHash {
677    fn default() -> Self {
678        MinaBaseAccountIdDigestStableV1(BigInt::one()).into()
679    }
680}
681
682#[derive(
683    Clone,
684    Debug,
685    PartialEq,
686    Eq,
687    PartialOrd,
688    Ord,
689    Serialize,
690    Deserialize,
691    BinProtRead,
692    BinProtWrite,
693    MallocSizeOf,
694)]
695pub struct NonZeroCurvePointWithVersions {
696    x: Versioned<crate::bigint::BigInt, 1>,
697    is_odd: bool,
698}
699
700impl From<NonZeroCurvePointUncompressedStableV1> for Versioned<NonZeroCurvePointWithVersions, 1> {
701    fn from(source: NonZeroCurvePointUncompressedStableV1) -> Self {
702        NonZeroCurvePointWithVersions {
703            x: source.x.into(),
704            is_odd: source.is_odd,
705        }
706        .into()
707    }
708}
709
710impl From<Versioned<NonZeroCurvePointWithVersions, 1>> for NonZeroCurvePointUncompressedStableV1 {
711    fn from(source: Versioned<NonZeroCurvePointWithVersions, 1>) -> Self {
712        let source = source.into_inner();
713        Self {
714            x: source.x.into_inner(),
715            is_odd: source.is_odd,
716        }
717    }
718}
719
720pub type NonZeroCurvePoint = Base58CheckOfBinProt<
721    NonZeroCurvePointUncompressedStableV1,
722    Versioned<NonZeroCurvePointWithVersions, 1>,
723    { crate::b58version::NON_ZERO_CURVE_POINT_COMPRESSED },
724>;
725
726#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
727pub enum ArchiveTransitionFrontierDiff {
728    BreadcrumbAdded {
729        block: (MinaBlockBlockStableV2, (Option<StateBodyHash>, StateHash)),
730        accounts_accessed: List<(crate::number::UInt64, MinaBaseAccountBinableArgStableV2)>,
731        accounts_created: List<(MinaBaseAccountIdStableV2, CurrencyFeeStableV1)>,
732        tokens_used: List<(MinaBaseTokenIdStableV2, Option<MinaBaseAccountIdStableV2>)>,
733        sender_receipt_chains_from_parent_ledger:
734            List<(MinaBaseAccountIdStableV2, MinaBaseReceiptChainHashStableV1)>,
735    },
736    // TODO(adonagy): I think this is legacy stuff, doublecheck
737    RootTransitioned(()),
738    BoostrapOf(()),
739}
740
741impl ArchiveTransitionFrontierDiff {
742    pub fn block(&self) -> Option<MinaBlockBlockStableV2> {
743        match self {
744            // TODO(adonagy): maybe we should use Arc here instead of cloning
745            ArchiveTransitionFrontierDiff::BreadcrumbAdded { block, .. } => Some(block.0.clone()),
746            _ => None,
747        }
748    }
749
750    pub fn accounts_accessed(
751        &self,
752    ) -> List<(crate::number::UInt64, MinaBaseAccountBinableArgStableV2)> {
753        match self {
754            ArchiveTransitionFrontierDiff::BreadcrumbAdded {
755                accounts_accessed, ..
756            } => accounts_accessed.clone(),
757            _ => List::new(),
758        }
759    }
760
761    pub fn accounts_created(&self) -> List<(MinaBaseAccountIdStableV2, CurrencyFeeStableV1)> {
762        match self {
763            ArchiveTransitionFrontierDiff::BreadcrumbAdded {
764                accounts_created, ..
765            } => accounts_created.clone(),
766            _ => List::new(),
767        }
768    }
769
770    pub fn tokens_used(
771        &self,
772    ) -> List<(MinaBaseTokenIdStableV2, Option<MinaBaseAccountIdStableV2>)> {
773        match self {
774            ArchiveTransitionFrontierDiff::BreadcrumbAdded { tokens_used, .. } => {
775                tokens_used.clone()
776            }
777            _ => List::new(),
778        }
779    }
780}
781
782#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
783pub enum ArchiveRpc {
784    SendDiff(ArchiveTransitionFrontierDiff),
785}
786
787#[derive(Clone, Debug, PartialEq, BinProtRead, BinProtWrite)]
788pub struct PrecomputedBlockProof(pub MinaBaseProofStableV2);
789
790impl Serialize for PrecomputedBlockProof {
791    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
792    where
793        S: serde::ser::Serializer,
794    {
795        use base64::{engine::general_purpose::URL_SAFE, Engine as _};
796        use binprot::BinProtWrite;
797        let mut buf = Vec::new();
798        self.0
799            .binprot_write(&mut buf)
800            .map_err(serde::ser::Error::custom)?;
801        let base64_data = URL_SAFE.encode(&buf);
802        serializer.serialize_str(&base64_data)
803    }
804}
805
806impl<'de> Deserialize<'de> for PrecomputedBlockProof {
807    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
808    where
809        D: serde::de::Deserializer<'de>,
810    {
811        use base64::{engine::general_purpose::URL_SAFE, Engine as _};
812        let base64_data = String::deserialize(deserializer)?;
813        let binprot_data = URL_SAFE
814            .decode(&base64_data)
815            .map_err(serde::de::Error::custom)?;
816        let mut read = binprot_data.as_slice();
817        let proof: MinaBaseProofStableV2 =
818            binprot::BinProtRead::binprot_read(&mut read).map_err(serde::de::Error::custom)?;
819        Ok(PrecomputedBlockProof(proof))
820    }
821}
822
823impl From<MinaBaseProofStableV2> for PrecomputedBlockProof {
824    fn from(value: MinaBaseProofStableV2) -> Self {
825        Self(value)
826    }
827}
828
829#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
830pub struct PrecomputedBlock {
831    pub scheduled_time: BlockTimeTimeStableV1,
832    pub protocol_state: MinaStateProtocolStateValueStableV2,
833    pub protocol_state_proof: PrecomputedBlockProof,
834    pub staged_ledger_diff: StagedLedgerDiffDiffStableV2,
835    // FIXME: for some reason in OCaml the base58check conversion for the JSON value
836    // uses version byte = 0x05 (ledger hash) instead of 0x10 (StateHash) and 0x11 (StateBodyHash)
837    // Note: keeping the proper types here, we should raise an issue in the ocaml repo
838    pub delta_transition_chain_proof: (
839        StateHash,           // LedgerHash,       // StateHash
840        List<StateBodyHash>, // List<LedgerHash>, // StateBodyHash
841    ),
842    pub protocol_version: ProtocolVersionStableV2,
843    #[serde(default)]
844    pub proposed_protocol_version: Option<ProtocolVersionStableV2>,
845    pub accounts_accessed: List<(crate::number::UInt64, MinaBaseAccountBinableArgStableV2)>,
846    pub accounts_created: List<(MinaBaseAccountIdStableV2, CurrencyFeeStableV1)>,
847    pub tokens_used: List<(MinaBaseTokenIdStableV2, Option<MinaBaseAccountIdStableV2>)>,
848}
849
850#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
851pub struct PrecomputedBlockData {
852    pub version: u32,
853    pub data: PrecomputedBlock,
854}
855
856impl PrecomputedBlock {
857    pub fn with_version(&self, version: u32) -> PrecomputedBlockData {
858        PrecomputedBlockData {
859            version,
860            data: self.clone(),
861        }
862    }
863}
864
865#[cfg(test)]
866mod tests {
867    use binprot::{BinProtRead, BinProtWrite};
868    use serde::{de::DeserializeOwned, Serialize};
869    use std::fmt::Debug;
870
871    use crate::v2::{
872        CoinbaseStackData, CoinbaseStackHash, EpochSeed, LedgerHash, NonZeroCurvePoint,
873        PendingCoinbaseHash, Signature, StagedLedgerHashAuxHash,
874        StagedLedgerHashPendingCoinbaseAux, StateBodyHash, StateHash, TokenIdKeyHash,
875    };
876
877    fn base58check_test<T: Serialize + DeserializeOwned + BinProtRead + BinProtWrite + Debug>(
878        b58: &str,
879        hex: &str,
880    ) {
881        let bin: T = serde_json::from_value(serde_json::json!(b58)).unwrap();
882        let json = serde_json::to_value(&bin).unwrap();
883
884        let mut binprot = Vec::new();
885        bin.binprot_write(&mut binprot).unwrap();
886
887        println!("{b58} => {}", hex::encode(&binprot));
888
889        let binprot1 = hex::decode(hex).unwrap();
890        let mut b = binprot1.as_slice();
891        let from_hex = T::binprot_read(&mut b).unwrap();
892        println!("{hex} => {}", serde_json::to_string(&from_hex).unwrap());
893
894        assert_eq!(hex::encode(&binprot), hex);
895        assert_eq!(json.as_str().unwrap(), b58);
896    }
897
898    macro_rules! b58t {
899        ($name:ident, $ty:ty, $b58:expr, $hex:expr) => {
900            #[test]
901            fn $name() {
902                base58check_test::<$ty>($b58, $hex);
903            }
904        };
905    }
906
907    b58t!(
908        ledger_hash,
909        LedgerHash,
910        "jwrPvAMUNo3EKT2puUk5Fxz6B7apRAoKNTGpAA49t3TRSfzvdrL",
911        "636f5b2d67278e17bc4343c7c23fb4991f8cf0bbbfd8558615b124d5d6254801"
912    );
913
914    b58t!(
915        staged_ledger_hash_aux_hash,
916        StagedLedgerHashAuxHash,
917        "UbhWTJLi4JM5bizFQVPvfMbjh4tCiTUTrNhedn8WdMPR1KHKJh",
918        "203294e118730ad8b7c0f2ab6d74d244eec02cfef221790bb1274fdb3b97854e50"
919    );
920
921    b58t!(
922        epoch_seed,
923        EpochSeed,
924        "2vajKi2Cxx58mByzxbJA3G6gYh1j2BoizW4zzoLcZa3kYECjhaXV",
925        "4d8802db5beb98f13e10475ddc9e718f6890613276331c062f5d71b915d6941d"
926    );
927
928    b58t!(
929        staged_ledger_hash_pending_coinbase_aux,
930        StagedLedgerHashPendingCoinbaseAux,
931        "XgkNHpgSvmF7CyRBGUzcwvCD9daBRhZUDLg3xTvohmTX1mRqHR",
932        "20c922885bfeda2c2568e32fcc595fe0ad2292dcf756be637545bc553f7f7028e8"
933    );
934
935    b58t!(
936        state_hash,
937        StateHash,
938        "3NL7AkynW6hbDrhHTAht1GLG563Fo9fdcEQk1zEyy5XedC6aZTeB",
939        "8d67aadd018581a812623915b13d5c3a6da7dfe8a195172d9bbd206810bc2329"
940    );
941
942    b58t!(
943        state_body_hash,
944        StateBodyHash,
945        "3WtsPNWF7Ua5zbvHEARuEL32KEfMM7pPYNXWVGtTStdYJRYA2rta",
946        "1b11c26e5541d2f719a50f2e5bdcd23e7995883036f3d2e5675dfd3015ec6202"
947    );
948
949    b58t!(
950        pending_coinbase_hash,
951        PendingCoinbaseHash,
952        "2n2EEn3yH1oRU8tCXTjw7dJKHQVcFTkfeDCTpBzum3sZcssPeaVM",
953        "e23a19254e600402e4474371450d498c75a9b3e28c34160d489af61c255f722c"
954    );
955
956    b58t!(
957        token_id_key,
958        TokenIdKeyHash,
959        "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf",
960        "0100000000000000000000000000000000000000000000000000000000000000"
961    );
962
963    // FIXME
964    // b58t!(
965    //     vrf_truncated_output,
966    //     ConsensusVrfOutputTruncatedStableV1,
967    //     "48H9Qk4D6RzS9kAJQX9HCDjiJ5qLiopxgxaS6xbDCWNaKQMQ9Y4C",
968    //     "20dfd73283866632d9dbfda15421eacd02800957caad91f3a9ab4cc5ccfb298e03"
969    // );
970
971    b58t!(
972        coinbase_stack_data,
973        CoinbaseStackData,
974        "4QNrZFBTDQCPfEZqBZsaPYx8qdaNFv1nebUyCUsQW9QUJqyuD3un",
975        "35b9d51e5d7c741456f86720731241a8280273cfc6c21668fd7bc6c587d0cc1d"
976    );
977
978    b58t!(
979        coinbase_stack_hash,
980        CoinbaseStackHash,
981        "4Yx5U3t3EYQycZ91yj4478bHkLwGkhDHnPbCY9TxgUk69SQityej",
982        "0000000000000000000000000000000000000000000000000000000000000000"
983    );
984
985    b58t!(
986        signature,
987        Signature,
988        "7mXS9Y91bWtTYNKuDbxTuG18wUiZLHUySy9Ms8bPyAT9KNnME1q2nctwnvowJi2Y79dnsL18iVSCuaQF1ufUKwUZZKAXHqnF",
989        "d290f924705fb714e91fedb9bed77e85bce8f5d932c3f4d692b20e4c3e5f9a3343c2baffce9ab0c2391e2f3de8ac891633338d827e6fd4f269331c248029b106"
990    );
991
992    #[test]
993    fn non_zero_curve_point() {
994        let b58 = r#""B62qkUHaJUHERZuCHQhXCQ8xsGBqyYSgjQsKnKN5HhSJecakuJ4pYyk""#;
995
996        let v = serde_json::from_str::<NonZeroCurvePoint>(&b58)
997            .unwrap()
998            .into_inner();
999        assert_eq!(v.is_odd, false);
1000        assert_eq!(
1001            &hex::encode(&v.x.to_bytes()),
1002            "3c2b5b48c22dc8b8c9d2c9d76a2ceaaf02beabb364301726c3f8e989653af513"
1003        );
1004    }
1005}
1006
1007const SHIFTED_VALUE: &str = "Shifted_value";
1008
1009impl Serialize for PicklesProofProofsVerified2ReprStableV2StatementFp {
1010    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1011    where
1012        S: serde::Serializer,
1013    {
1014        if serializer.is_human_readable() {
1015            let mut serializer = serializer.serialize_tuple(2)?;
1016            match self {
1017                PicklesProofProofsVerified2ReprStableV2StatementFp::ShiftedValue(v) => {
1018                    serializer.serialize_element(SHIFTED_VALUE)?;
1019                    serializer.serialize_element(v)?;
1020                }
1021            }
1022            serializer.end()
1023        } else {
1024            match self {
1025                PicklesProofProofsVerified2ReprStableV2StatementFp::ShiftedValue(v) => serializer
1026                    .serialize_newtype_variant(
1027                        "PicklesProofProofsVerified2ReprStableV2StatementFp",
1028                        0,
1029                        "ShiftedValue",
1030                        v,
1031                    ),
1032            }
1033        }
1034    }
1035}
1036
1037impl<'de> Deserialize<'de> for PicklesProofProofsVerified2ReprStableV2StatementFp {
1038    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1039    where
1040        D: serde::Deserializer<'de>,
1041    {
1042        struct V;
1043        impl<'de> Visitor<'de> for V {
1044            type Value = PicklesProofProofsVerified2ReprStableV2StatementFp;
1045
1046            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1047                formatter.write_str("tuple of tag and value")
1048            }
1049
1050            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1051            where
1052                A: serde::de::SeqAccess<'de>,
1053            {
1054                match seq.next_element::<String>()? {
1055                    Some(v) if &v == SHIFTED_VALUE => {}
1056                    Some(v) => {
1057                        return Err(serde::de::Error::custom(format!(
1058                            "expecting `{SHIFTED_VALUE}`, got `{v}`"
1059                        )))
1060                    }
1061                    None => return Err(serde::de::Error::custom("expecting a tag")),
1062                }
1063                match seq.next_element::<BigInt>()? {
1064                    Some(v) => {
1065                        Ok(PicklesProofProofsVerified2ReprStableV2StatementFp::ShiftedValue(v))
1066                    }
1067                    None => return Err(serde::de::Error::custom("expecting a value")),
1068                }
1069            }
1070        }
1071
1072        if deserializer.is_human_readable() {
1073            deserializer.deserialize_tuple(2, V)
1074        } else {
1075            #[derive(Deserialize)]
1076            pub enum PicklesProofProofsVerified2ReprStableV2StatementFp {
1077                ShiftedValue(crate::bigint::BigInt),
1078            }
1079            let PicklesProofProofsVerified2ReprStableV2StatementFp::ShiftedValue(v) =
1080                Deserialize::deserialize(deserializer)?;
1081            Ok(Self::ShiftedValue(v))
1082        }
1083    }
1084}
1085
1086impl Serialize for ConsensusVrfOutputTruncatedStableV1 {
1087    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1088    where
1089        S: serde::Serializer,
1090    {
1091        if serializer.is_human_readable() {
1092            // <https://github.com/MinaProtocol/mina/blob/6de36cf8851de28b667e4c1041badf62507c235d/src/lib/consensus/vrf/consensus_vrf.ml#L172>
1093            use base64::{engine::general_purpose::URL_SAFE, Engine as _};
1094            let base64_data = URL_SAFE.encode(&self.0 .0);
1095            serializer.serialize_str(&base64_data)
1096        } else {
1097            serializer.serialize_newtype_struct("ConsensusVrfOutputTruncatedStableV1", &self.0)
1098        }
1099    }
1100}
1101
1102impl<'de> Deserialize<'de> for ConsensusVrfOutputTruncatedStableV1 {
1103    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1104    where
1105        D: serde::Deserializer<'de>,
1106    {
1107        if deserializer.is_human_readable() {
1108            // <https://github.com/MinaProtocol/mina/blob/6de36cf8851de28b667e4c1041badf62507c235d/src/lib/consensus/vrf/consensus_vrf.ml#L172>
1109            use base64::{engine::general_purpose::URL_SAFE, Engine as _};
1110            let base64_data = String::deserialize(deserializer)?;
1111            URL_SAFE
1112                .decode(&base64_data)
1113                .map(|vec| ByteString::from(vec))
1114                .map_err(|e| serde::de::Error::custom(format!("Error deserializing vrf: {e}")))
1115        } else {
1116            Deserialize::deserialize(deserializer)
1117        }
1118        .map(Self)
1119    }
1120}
1121
1122impl MinaBaseVerificationKeyWireStableV1 {
1123    pub fn to_base64(&self) -> Result<String, conv::Error> {
1124        let mut buffer: Vec<u8> = Vec::new();
1125        self.binprot_write(&mut buffer)?;
1126        use base64::{engine::general_purpose::STANDARD, Engine as _};
1127
1128        let base64_data = STANDARD.encode(buffer);
1129        Ok(base64_data)
1130    }
1131
1132    pub fn from_base64(base64_data: &str) -> Result<Self, conv::Error> {
1133        use base64::{engine::general_purpose::STANDARD, Engine as _};
1134        let decoded_data = STANDARD.decode(base64_data)?;
1135        let res = MinaBaseVerificationKeyWireStableV1::binprot_read(&mut decoded_data.as_slice())?;
1136        Ok(res)
1137    }
1138}
1139
1140// TODO(adonagy): macro?
1141impl MinaBaseSignedCommandStableV2 {
1142    pub fn to_base64(&self) -> Result<String, conv::Error> {
1143        const COMMAND_VERSION_TAG: u8 = 2;
1144
1145        let mut buffer: Vec<u8> = Vec::new();
1146        COMMAND_VERSION_TAG.binprot_write(&mut buffer)?;
1147        self.binprot_write(&mut buffer)?;
1148        use base64::{engine::general_purpose::STANDARD, Engine as _};
1149
1150        let base64_data = STANDARD.encode(buffer);
1151        Ok(base64_data)
1152    }
1153
1154    pub fn from_base64(base64_data: &str) -> Result<Self, conv::Error> {
1155        use base64::{engine::general_purpose::STANDARD, Engine as _};
1156        let decoded_data = STANDARD.decode(&base64_data)?;
1157        let res = MinaBaseSignedCommandStableV2::binprot_read(&mut decoded_data[1..].as_ref())?;
1158        Ok(res)
1159    }
1160}
1161
1162/// TODO(adonagy): implement the base64 conversions similarly to the base58check ones (versioned/not versioned)
1163impl MinaBaseZkappCommandTStableV1WireStableV1 {
1164    pub fn to_base64(&self) -> Result<String, conv::Error> {
1165        const ZKAPP_VERSION_TAG: u8 = 1;
1166
1167        let mut buffer: Vec<u8> = Vec::new();
1168        ZKAPP_VERSION_TAG.binprot_write(&mut buffer)?;
1169        self.binprot_write(&mut buffer)?;
1170        use base64::{engine::general_purpose::STANDARD, Engine as _};
1171
1172        let base64_data = STANDARD.encode(buffer);
1173        Ok(base64_data)
1174    }
1175
1176    pub fn from_base64(base64_data: &str) -> Result<Self, conv::Error> {
1177        use base64::{engine::general_purpose::STANDARD, Engine as _};
1178        let decoded_data = STANDARD.decode(&base64_data)?;
1179        let res = MinaBaseZkappCommandTStableV1WireStableV1::binprot_read(
1180            &mut decoded_data[1..].as_ref(),
1181        )?;
1182        Ok(res)
1183    }
1184}
1185
1186impl Serialize for ConsensusBodyReferenceStableV1 {
1187    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1188    where
1189        S: serde::ser::Serializer,
1190    {
1191        if serializer.is_human_readable() {
1192            let hex_string = hex::encode(&self.0);
1193            serializer.serialize_str(&hex_string)
1194        } else {
1195            self.0.serialize(serializer)
1196        }
1197    }
1198}
1199
1200impl<'de> Deserialize<'de> for ConsensusBodyReferenceStableV1 {
1201    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1202    where
1203        D: serde::de::Deserializer<'de>,
1204    {
1205        if deserializer.is_human_readable() {
1206            let hex_string = String::deserialize(deserializer)?;
1207            let decoded_bytes = hex::decode(&hex_string).map_err(serde::de::Error::custom)?;
1208            Ok(ConsensusBodyReferenceStableV1(
1209                crate::string::ByteString::from(decoded_bytes),
1210            ))
1211        } else {
1212            let inner_value = crate::string::ByteString::deserialize(deserializer)?;
1213            Ok(ConsensusBodyReferenceStableV1(inner_value))
1214        }
1215    }
1216}
1217
1218// Needs to handle #[serde(untagged)] which postcard cannot deserialize
1219impl<'de> Deserialize<'de> for MinaNumbersGlobalSlotSinceGenesisMStableV1 {
1220    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1221    where
1222        D: serde::de::Deserializer<'de>,
1223    {
1224        let value = UnsignedExtendedUInt32StableV1::deserialize(deserializer)?;
1225        Ok(MinaNumbersGlobalSlotSinceGenesisMStableV1::SinceGenesis(
1226            value,
1227        ))
1228    }
1229}
1230
1231// Needs to handle #[serde(untagged)] which postcard cannot deserialize
1232impl<'de> Deserialize<'de> for MinaNumbersGlobalSlotSinceHardForkMStableV1 {
1233    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1234    where
1235        D: serde::de::Deserializer<'de>,
1236    {
1237        let value = UnsignedExtendedUInt32StableV1::deserialize(deserializer)?;
1238        Ok(MinaNumbersGlobalSlotSinceHardForkMStableV1::SinceHardFork(
1239            value,
1240        ))
1241    }
1242}
1243
1244pub type MerkleTreePath = Vec<MerkleTreeNode>;
1245
1246#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
1247#[polymorphic_variant]
1248pub enum MerkleTreeNode {
1249    Left(BigInt),
1250    Right(BigInt),
1251}
1252
1253impl ConsensusProofOfStakeDataConsensusStateValueStableV2 {
1254    pub fn global_slot(&self) -> u32 {
1255        match &self.curr_global_slot_since_hard_fork.slot_number {
1256            super::MinaNumbersGlobalSlotSinceHardForkMStableV1::SinceHardFork(s) => s.as_u32(),
1257        }
1258    }
1259}
1260
1261impl AsRef<str> for SgnStableV1 {
1262    fn as_ref(&self) -> &str {
1263        match self {
1264            SgnStableV1::Pos => "Pos",
1265            SgnStableV1::Neg => "Neg",
1266        }
1267    }
1268}
1269
1270impl Serialize for SgnStableV1 {
1271    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1272    where
1273        S: serde::Serializer,
1274    {
1275        if serializer.is_human_readable() {
1276            let mut tuple = serializer.serialize_tuple(1)?;
1277            tuple.serialize_element(self.as_ref())?;
1278            tuple.end()
1279        } else {
1280            match *self {
1281                SgnStableV1::Pos => {
1282                    Serializer::serialize_unit_variant(serializer, "SgnStableV1", 0u32, "Pos")
1283                }
1284                SgnStableV1::Neg => {
1285                    Serializer::serialize_unit_variant(serializer, "SgnStableV1", 1u32, "Neg")
1286                }
1287            }
1288        }
1289    }
1290}
1291
1292impl<'de> Deserialize<'de> for SgnStableV1 {
1293    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1294    where
1295        D: serde::Deserializer<'de>,
1296    {
1297        if deserializer.is_human_readable() {
1298            struct V;
1299
1300            impl<'de> Visitor<'de> for V {
1301                type Value = String;
1302
1303                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1304                    formatter.write_str("`Pos` or `Neg`")?;
1305                    panic!("foo")
1306                }
1307
1308                fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
1309                where
1310                    E: serde::de::Error,
1311                {
1312                    Ok(v)
1313                }
1314
1315                fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1316                where
1317                    A: serde::de::SeqAccess<'de>,
1318                {
1319                    let Some(elt) = seq.next_element()? else {
1320                        return Err(serde::de::Error::custom("No tag"));
1321                    };
1322                    Ok(elt)
1323                }
1324            }
1325            let v = deserializer.deserialize_tuple(1, V)?;
1326            match v.as_str() {
1327                "Pos" => Ok(SgnStableV1::Pos),
1328                "Neg" => Ok(SgnStableV1::Neg),
1329                _ => Err(serde::de::Error::custom(format!("Invalid tag {v}"))),
1330            }
1331        } else {
1332            #[derive(Deserialize)]
1333            enum _SgnStableV1 {
1334                Pos,
1335                Neg,
1336            }
1337
1338            let s: _SgnStableV1 = Deserialize::deserialize(deserializer)?;
1339            match s {
1340                _SgnStableV1::Pos => Ok(SgnStableV1::Pos),
1341                _SgnStableV1::Neg => Ok(SgnStableV1::Neg),
1342            }
1343        }
1344    }
1345}
1346
1347const PRECISION: usize = 9;
1348const PRECISION_EXP: u64 = 10u64.pow(PRECISION as u32);
1349
1350impl Serialize for CurrencyFeeStableV1 {
1351    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1352    where
1353        S: serde::ser::Serializer,
1354    {
1355        let amount = self.0 .0.as_u64();
1356        let whole = amount / PRECISION_EXP;
1357        let remainder = amount % PRECISION_EXP;
1358
1359        if remainder == 0 {
1360            serializer.serialize_str(&whole.to_string())
1361        } else {
1362            let tmp = format!("{:0width$}", remainder, width = PRECISION);
1363            let fractional_str = tmp.trim_end_matches('0');
1364            serializer.serialize_str(&format!("{}.{}", whole, fractional_str))
1365        }
1366    }
1367}
1368
1369impl<'de> Deserialize<'de> for CurrencyFeeStableV1 {
1370    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1371    where
1372        D: serde::de::Deserializer<'de>,
1373    {
1374        let s: String = Deserialize::deserialize(deserializer)?;
1375        let parts: Vec<&str> = s.split('.').collect();
1376        let result = match parts.as_slice() {
1377            [whole] => format!("{}{}", whole, "0".repeat(PRECISION)),
1378            [whole, decimal] => {
1379                let decimal_length = decimal.len();
1380                if decimal_length > PRECISION {
1381                    format!("{}{}", whole, &decimal[0..PRECISION])
1382                } else {
1383                    format!(
1384                        "{}{}{}",
1385                        whole,
1386                        decimal,
1387                        "0".repeat(PRECISION - decimal_length)
1388                    )
1389                }
1390            }
1391            _ => return Err(serde::de::Error::custom("Invalid currency input")),
1392        };
1393        let fee_in_nanomina: u64 = result.parse().map_err(serde::de::Error::custom)?;
1394        Ok(CurrencyFeeStableV1(
1395            UnsignedExtendedUInt64Int64ForVersionTagsStableV1(fee_in_nanomina.into()),
1396        ))
1397    }
1398}
1399
1400#[cfg(test)]
1401mod tests_currency_fee_serialization {
1402    use super::*;
1403    use serde_json;
1404
1405    #[test]
1406    fn test_correct_serialization() {
1407        let amount = 1_000_000u64;
1408        let currency = CurrencyFeeStableV1(amount.into());
1409
1410        let serialized = serde_json::to_string(&currency).unwrap();
1411        assert_eq!(serialized, "\"0.001\"", "Serialization output is incorrect");
1412    }
1413
1414    #[test]
1415    fn test_zero_remainder() {
1416        let amount = 1_000_000_000u64;
1417        let currency = CurrencyFeeStableV1(amount.into());
1418
1419        let serialized = serde_json::to_string(&currency).unwrap();
1420        assert_eq!(
1421            serialized, "\"1\"",
1422            "Serialization output is incorrect for whole numbers"
1423        );
1424    }
1425
1426    #[test]
1427    fn test_fractional_serialization() {
1428        let amount = 1_234_567_890u64;
1429        let currency = CurrencyFeeStableV1(amount.into());
1430
1431        let serialized = serde_json::to_string(&currency).unwrap();
1432        assert_eq!(
1433            serialized, "\"1.23456789\"",
1434            "Serialization output is incorrect for fractional numbers"
1435        );
1436    }
1437
1438    #[test]
1439    fn test_trailing_zeros() {
1440        let amount = 1_230_000_000u64;
1441        let currency = CurrencyFeeStableV1(amount.into());
1442
1443        let serialized = serde_json::to_string(&currency).unwrap();
1444        assert_eq!(
1445            serialized, "\"1.23\"",
1446            "Serialization output is incorrect when fractional part has trailing zeros"
1447        );
1448    }
1449
1450    #[test]
1451    fn test_deserialization_correct_serialization() {
1452        let serialized = "\"0.001\"";
1453        let currency: CurrencyFeeStableV1 = serde_json::from_str(serialized).unwrap();
1454        let expected_amount = 1_000_000u64;
1455        assert_eq!(
1456            currency,
1457            CurrencyFeeStableV1(expected_amount.into()),
1458            "Deserialization output is incorrect"
1459        );
1460    }
1461
1462    #[test]
1463    fn test_deserialization_zero_remainder() {
1464        let serialized = "\"1\"";
1465        let currency: CurrencyFeeStableV1 = serde_json::from_str(serialized).unwrap();
1466        let expected_amount = 1_000_000_000u64;
1467        assert_eq!(
1468            currency,
1469            CurrencyFeeStableV1(expected_amount.into()),
1470            "Deserialization output is incorrect for whole numbers"
1471        );
1472    }
1473
1474    #[test]
1475    fn test_deserialization_fractional() {
1476        let serialized = "\"1.23456789\"";
1477        let currency: CurrencyFeeStableV1 = serde_json::from_str(serialized).unwrap();
1478        let expected_amount = 1_234_567_890u64;
1479        assert_eq!(
1480            currency,
1481            CurrencyFeeStableV1(expected_amount.into()),
1482            "Deserialization output is incorrect for fractional numbers"
1483        );
1484    }
1485
1486    #[test]
1487    fn test_deserialization_trailing_zeros() {
1488        let serialized = "\"1.23\"";
1489        let currency: CurrencyFeeStableV1 = serde_json::from_str(serialized).unwrap();
1490        let expected_amount = 1_230_000_000u64;
1491        assert_eq!(
1492            currency,
1493            CurrencyFeeStableV1(expected_amount.into()),
1494            "Deserialization output is incorrect when fractional part has trailing zeros"
1495        );
1496    }
1497
1498    #[test]
1499    fn test_deserialization_truncates_extra_decimals() {
1500        let serialized = "\"0.1234567890123\""; // 13 decimal places
1501        let currency: CurrencyFeeStableV1 = serde_json::from_str(serialized).unwrap();
1502        let expected_amount = 123_456_789u64; // Only the first 9 decimal places are considered
1503        assert_eq!(
1504            currency,
1505            CurrencyFeeStableV1(expected_amount.into()),
1506            "Deserialization did not correctly truncate extra decimal places"
1507        );
1508    }
1509
1510    #[test]
1511    fn test_deserialization_invalid_number() {
1512        let serialized = "\"abc\"";
1513        let result: Result<CurrencyFeeStableV1, _> = serde_json::from_str(serialized);
1514        assert!(
1515            result.is_err(),
1516            "Deserialization should fail for invalid number strings"
1517        );
1518    }
1519}
1520
1521#[cfg(test)]
1522mod tests_sgn {
1523    use crate::v2::SgnStableV1;
1524
1525    #[test]
1526    fn test_json() {
1527        assert_eq!(
1528            serde_json::to_value(&SgnStableV1::Pos).unwrap(),
1529            serde_json::json!(["Pos"])
1530        );
1531        assert_eq!(
1532            serde_json::to_value(&SgnStableV1::Neg).unwrap(),
1533            serde_json::json!(["Neg"])
1534        );
1535
1536        assert_eq!(
1537            serde_json::from_value::<SgnStableV1>(serde_json::json!(["Pos"])).unwrap(),
1538            SgnStableV1::Pos
1539        );
1540        assert_eq!(
1541            serde_json::from_value::<SgnStableV1>(serde_json::json!(["Neg"])).unwrap(),
1542            SgnStableV1::Neg
1543        );
1544    }
1545}
1546
1547/// Derived name: `Mina_base__Fee_excess.Stable.V1.fee`
1548///
1549/// Gid: `602`
1550/// Location: [src/lib/currency/signed_poly.ml:6:4](https://github.com/Minaprotocol/mina/blob/b1facec/src/lib/currency/signed_poly.ml#L6)
1551/// Args: CurrencyFeeStableV1 , SgnStableV1
1552#[derive(
1553    Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite, MallocSizeOf,
1554)]
1555pub struct SignedAmount {
1556    pub magnitude: CurrencyFeeStableV1,
1557    pub sgn: SgnStableV1,
1558}
1559
1560/// **OCaml name**: `Mina_base__Fee_excess.Stable.V1`
1561///
1562/// Gid: `657`
1563/// Location: [src/lib/mina_base/fee_excess.ml:124:4](https://github.com/Minaprotocol/mina/blob/b1facec/src/lib/mina_base/fee_excess.ml#L124)
1564///
1565///
1566/// Gid: `656`
1567/// Location: [src/lib/mina_base/fee_excess.ml:54:6](https://github.com/Minaprotocol/mina/blob/b1facec/src/lib/mina_base/fee_excess.ml#L54)
1568/// Args: TokenIdKeyHash , MinaBaseFeeExcessStableV1Fee
1569#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
1570pub struct MinaBaseFeeExcessStableV1(pub TokenFeeExcess, pub TokenFeeExcess);
1571
1572#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
1573pub struct TokenFeeExcess {
1574    pub token: TokenIdKeyHash,
1575    pub amount: SignedAmount,
1576}
1577
1578impl Default for NonZeroCurvePointUncompressedStableV1 {
1579    fn default() -> Self {
1580        Self {
1581            x: Default::default(),
1582            is_odd: false,
1583        }
1584    }
1585}
1586
1587impl MinaNumbersGlobalSlotSinceGenesisMStableV1 {
1588    pub fn as_u32(&self) -> u32 {
1589        let Self::SinceGenesis(slot) = self;
1590        slot.as_u32()
1591    }
1592}
1593
1594impl MinaNumbersGlobalSlotSinceHardForkMStableV1 {
1595    pub fn as_u32(&self) -> u32 {
1596        let Self::SinceHardFork(slot) = self;
1597        slot.as_u32()
1598    }
1599}
1600
1601impl MinaNumbersGlobalSlotSpanStableV1 {
1602    pub fn as_u32(&self) -> u32 {
1603        let Self::GlobalSlotSpan(slot) = self;
1604        slot.as_u32()
1605    }
1606}
1607
1608impl From<u32> for UnsignedExtendedUInt32StableV1 {
1609    fn from(value: u32) -> Self {
1610        Self(value.into())
1611    }
1612}
1613
1614impl From<u64> for UnsignedExtendedUInt64Int64ForVersionTagsStableV1 {
1615    fn from(value: u64) -> Self {
1616        Self(value.into())
1617    }
1618}
1619
1620impl From<&PicklesProofProofsVerifiedMaxStableV2> for PicklesProofProofsVerified2ReprStableV2 {
1621    fn from(value: &PicklesProofProofsVerifiedMaxStableV2) -> Self {
1622        let PicklesProofProofsVerifiedMaxStableV2 {
1623            statement,
1624            prev_evals,
1625            proof,
1626        } = value;
1627
1628        Self {
1629            statement: statement.clone(),
1630            prev_evals: prev_evals.clone(),
1631            proof: proof.clone(),
1632        }
1633    }
1634}
1635
1636impl std::fmt::Debug for UnsignedExtendedUInt32StableV1 {
1637    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1638        let Self(inner) = self;
1639        // Avoid vertical alignment
1640        f.write_fmt(format_args!("UnsignedExtendedUInt32StableV1({:?})", inner))
1641    }
1642}
1643
1644impl std::fmt::Debug for UnsignedExtendedUInt64Int64ForVersionTagsStableV1 {
1645    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1646        let Self(inner) = self;
1647        // Avoid vertical alignment
1648        f.write_fmt(format_args!(
1649            "UnsignedExtendedUInt64Int64ForVersionTagsStableV1({:?})",
1650            inner
1651        ))
1652    }
1653}
1654
1655impl MinaBaseProtocolConstantsCheckedValueStableV1 {
1656    const fn default_constants() -> Self {
1657        const fn from_u32(v: u32) -> UnsignedExtendedUInt32StableV1 {
1658            UnsignedExtendedUInt32StableV1(Number(v))
1659        }
1660
1661        Self {
1662            k: from_u32(290),
1663            slots_per_epoch: from_u32(7140),
1664            slots_per_sub_window: from_u32(7),
1665            grace_period_slots: from_u32(2160),
1666            delta: from_u32(0),
1667            genesis_state_timestamp: BlockTimeTimeStableV1(
1668                UnsignedExtendedUInt64Int64ForVersionTagsStableV1(Number(1600251300000)), // 2020-09-16 03:15:00-07:00
1669            ),
1670        }
1671    }
1672}
1673
1674impl Default for MinaBaseProtocolConstantsCheckedValueStableV1 {
1675    fn default() -> Self {
1676        Self::default_constants()
1677    }
1678}
1679
1680pub const PROTOCOL_CONSTANTS: MinaBaseProtocolConstantsCheckedValueStableV1 =
1681    MinaBaseProtocolConstantsCheckedValueStableV1::default_constants();
1682
1683impl From<OffsetDateTime> for BlockTimeTimeStableV1 {
1684    fn from(value: OffsetDateTime) -> Self {
1685        debug_assert!(value.unix_timestamp() >= 0);
1686        BlockTimeTimeStableV1((value.unix_timestamp() as u64 * 1000).into())
1687    }
1688}
1689
1690impl AsRef<MinaBaseUserCommandStableV2> for MinaBaseUserCommandStableV2 {
1691    fn as_ref(&self) -> &MinaBaseUserCommandStableV2 {
1692        self
1693    }
1694}
1695
1696impl MinaBlockHeaderStableV2 {
1697    pub fn genesis_state_hash(&self) -> &StateHash {
1698        &self.protocol_state.body.genesis_state_hash
1699    }
1700}
1701
1702impl StagedLedgerDiffBodyStableV1 {
1703    pub fn diff(&self) -> &StagedLedgerDiffDiffDiffStableV2 {
1704        &self.staged_ledger_diff.diff
1705    }
1706    pub fn commands_iter<'a>(
1707        &'a self,
1708    ) -> Box<dyn 'a + Iterator<Item = &'a StagedLedgerDiffDiffPreDiffWithAtMostTwoCoinbaseStableV2B>>
1709    {
1710        let diff = self.diff();
1711        let iter = diff.0.commands.iter();
1712        if let Some(_1) = diff.1.as_ref() {
1713            Box::new(iter.chain(_1.commands.iter()))
1714        } else {
1715            Box::new(iter)
1716        }
1717    }
1718
1719    pub fn transactions(&self) -> impl Iterator<Item = &MinaBaseUserCommandStableV2> {
1720        self.commands_iter().map(|command| &command.data)
1721    }
1722
1723    pub fn tranasctions_with_status(
1724        &self,
1725    ) -> impl Iterator<
1726        Item = (
1727            &MinaBaseUserCommandStableV2,
1728            &MinaBaseTransactionStatusStableV2,
1729        ),
1730    > {
1731        self.commands_iter()
1732            .map(|command| (&command.data, &command.status))
1733    }
1734
1735    pub fn coinbase_fee_transfers_iter(
1736        &self,
1737    ) -> impl Iterator<Item = &StagedLedgerDiffDiffFtStableV1> {
1738        let diff = self.diff();
1739        let mut coinbases = Vec::with_capacity(4);
1740        match &diff.0.coinbase {
1741            StagedLedgerDiffDiffPreDiffWithAtMostTwoCoinbaseStableV2Coinbase::Zero => {}
1742            StagedLedgerDiffDiffPreDiffWithAtMostTwoCoinbaseStableV2Coinbase::One(v) => {
1743                coinbases.push(v.as_ref());
1744            }
1745            StagedLedgerDiffDiffPreDiffWithAtMostTwoCoinbaseStableV2Coinbase::Two(v) => {
1746                match v.as_ref() {
1747                    None => {}
1748                    Some((v1, v2)) => {
1749                        coinbases.push(Some(v1));
1750                        coinbases.push(v2.as_ref());
1751                    }
1752                }
1753            }
1754        }
1755
1756        if let Some(StagedLedgerDiffDiffPreDiffWithAtMostOneCoinbaseStableV2Coinbase::One(v)) =
1757            diff.1.as_ref().map(|v| &v.coinbase)
1758        {
1759            coinbases.push(v.as_ref());
1760        }
1761
1762        coinbases.into_iter().flatten()
1763    }
1764
1765    pub fn completed_works_iter<'a>(
1766        &'a self,
1767    ) -> Box<dyn 'a + Iterator<Item = &'a TransactionSnarkWorkTStableV2>> {
1768        let diff = self.diff();
1769        let _0 = &diff.0;
1770        if let Some(_1) = diff.1.as_ref() {
1771            Box::new(_0.completed_works.iter().chain(_1.completed_works.iter()))
1772        } else {
1773            Box::new(_0.completed_works.iter())
1774        }
1775    }
1776
1777    pub fn completed_works_count(&self) -> usize {
1778        self.diff().0.completed_works.len()
1779            + self
1780                .diff()
1781                .1
1782                .as_ref()
1783                .map_or(0, |d| d.completed_works.len())
1784    }
1785
1786    pub fn has_coinbase(&self) -> bool {
1787        let (first_pre_diff, second_pre_diff) = (
1788            self.diff().0.coinbase.clone(),
1789            self.diff().1.as_ref().map_or(
1790                StagedLedgerDiffDiffPreDiffWithAtMostOneCoinbaseStableV2Coinbase::Zero,
1791                |v| v.coinbase.clone(),
1792            ),
1793        );
1794
1795        match (first_pre_diff, second_pre_diff) {
1796            (
1797                StagedLedgerDiffDiffPreDiffWithAtMostTwoCoinbaseStableV2Coinbase::Zero,
1798                StagedLedgerDiffDiffPreDiffWithAtMostOneCoinbaseStableV2Coinbase::Zero,
1799            ) => false,
1800            _ => true,
1801        }
1802    }
1803
1804    pub fn fees_sum(&self) -> u64 {
1805        self.commands_iter()
1806            .map(|v| match &v.data {
1807                MinaBaseUserCommandStableV2::SignedCommand(v) => v.payload.common.fee.as_u64(),
1808                MinaBaseUserCommandStableV2::ZkappCommand(v) => v.fee_payer.body.fee.as_u64(),
1809            })
1810            .sum()
1811    }
1812
1813    pub fn snark_fees_sum(&self) -> u64 {
1814        self.completed_works_iter().map(|v| v.fee.as_u64()).sum()
1815    }
1816}
1817
1818// PicklesProofProofsVerifiedMaxStableV2 PicklesProofProofsVerified2ReprStableV2
1819
1820impl From<PicklesProofProofsVerifiedMaxStableV2> for PicklesProofProofsVerified2ReprStableV2 {
1821    fn from(value: PicklesProofProofsVerifiedMaxStableV2) -> Self {
1822        Self {
1823            statement: value.statement,
1824            prev_evals: value.prev_evals,
1825            proof: value.proof,
1826        }
1827    }
1828}
1829
1830impl From<PicklesProofProofsVerified2ReprStableV2> for PicklesProofProofsVerifiedMaxStableV2 {
1831    fn from(value: PicklesProofProofsVerified2ReprStableV2) -> Self {
1832        Self {
1833            statement: value.statement,
1834            prev_evals: value.prev_evals,
1835            proof: value.proof,
1836        }
1837    }
1838}
1839
1840impl std::fmt::Display for SgnStableV1 {
1841    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1842        match self {
1843            SgnStableV1::Pos => write!(f, "Positive"),
1844            SgnStableV1::Neg => write!(f, "Negative"),
1845        }
1846    }
1847}
1848
1849impl std::str::FromStr for SgnStableV1 {
1850    type Err = String;
1851
1852    fn from_str(s: &str) -> Result<Self, Self::Err> {
1853        match s {
1854            "Positive" => Ok(SgnStableV1::Pos),
1855            "Negative" => Ok(SgnStableV1::Neg),
1856            _ => Err("Invalid Sgn string, expected Positive or Negative".to_string()),
1857        }
1858    }
1859}
1860
1861#[cfg(test)]
1862mod test {
1863    use binprot::BinProtRead;
1864
1865    use crate::v2;
1866
1867    #[test]
1868    fn test_zkapp_with_sig_auth_hash() {
1869        let expexcted = "AbliNXLg4Keq0ZJyxK/QNAx8SxrJeffYytk5lbcTF9s9Af0A4fUFAP2+oQMA48vntxcABLty3SXWjvuadrLtBjcsxT1oJ3C2hwS/LDh364LKUxrLe3uF/9lr8VlW/J+ctbiI+m9I61sb9BC/AAG5YjVy4OCnqtGScsSv0DQMfEsayXn32MrZOZW3ExfbPQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAEBAQEBAQH9AJQ1dwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAAAAAePL57cXAAS7ct0l1o77mnay7QY3LMU9aCdwtocEvyw4d+uCylMay3t7hf/Za/FZVvyfnLW4iPpvSOtbG/QQvwAAAcwXZjv4NJwWwlJhFZPh2AK+o0dKOpIy1a6CXlskW7gmAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEAAQEBAQEBAf0AlDV3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQAAAAICAAAAACIBFlRlc3QgWktBcHAgdG8gUmVjZWl2ZXIAAAAAAAAAAAAA".to_string();
1870        let bytes = include_bytes!("../../../../tests/files/zkapps/with_sig_auth.bin");
1871        let zkapp =
1872            v2::MinaBaseZkappCommandTStableV1WireStableV1::binprot_read(&mut bytes.as_slice())
1873                .unwrap();
1874
1875        let zkapp_id = zkapp.to_base64().unwrap();
1876        assert_eq!(expexcted, zkapp_id);
1877    }
1878
1879    #[test]
1880    fn test_verification_key_base64_decode() {
1881        use base64::{engine::general_purpose::STANDARD, Engine as _};
1882        // let verification_key_encoded = "AACcenc1yLdGBm4xtUN1dpModROI0zovuy5rz2a94vfdBgG1C75BqviU4vw6JUYqODF8n9ivtfeU5s9PcpEGIP0htil2mfx8v2DB5RuNQ7VxJWkha0TSnJJsOl0FxhjldBbOY3tUZzZxHpPhHOKHz/ZAXRYFIsf2x+7boXC0iPurEX9VcnaJIq+YxxmnSfeYYxHkjxO9lrDBqjXzd5AHMnYyjTPC69B+5In7AOGS6R+A/g3/aR/MKDa4eDVrnsF9Oy/Ay8ahic2sSAZvtn08MdRyk/jm2cLlJbeAAad6Xyz/H9l7JrkbVwDMMPxvHVHs27tNoJCzIlrRzB7pg3ju9aQOu4h3thDr+WSgFQWKvcRPeL7f3TFjIr8WZ2457RgMcTwXwORKbqJCcyKVNOE+FlNwVkOKER+WIpC0OlgGuayPFwQQkbb91jaRlJvahfwkbF2+AJmDnavmNpop9T+/Xak1adXIrsRPeOjC+qIKxIbGimoMOoYzYlevKA80LnJ7HC0IxR+yNLvoSYxDDPNRD+OCCxk5lM2h8IDUiCNWH4FZNJ+doiigKjyZlu/xZ7jHcX7qibu/32KFTX85DPSkQM8dAEkH+vlkHmyXGLF4+xOVKeM0ihV5OEQrOABcgfTkbRsyxNInUBh0WiQyALE2ctjvkRCiE2P24bjFA8SgFmTM7gAKR89XcqLS/NP7lwCEej/L8q8R7sKGMCXmgFYluWH4JBSPDgvMxScfjFS33oBNb7po8cLnAORzohXoYTSgztklD0mKn6EegLbkLtwwr9ObsLz3m7fp/3wkNWFRkY5xzSZN1VybbQbmpyQNCpxd/kdDsvlszqlowkyC8HnKbhnvE0Mrz3ZIk4vSs/UGBSXAoESFCFCPcTq11TCOhE5rumMJErv5LusDHJgrBtQUMibLU9A1YbF7SPDAR2QZd0yx3waAC2F3xF+U682SOKF7oCZl2OICysRHqH+rZ604UfdGG0zWRuP2yg6kfGwcGQbO1ql40WrWTiFhbxxdKC7Gbz4y9Sb7q5EsPt6Z1AIn34/nXB/IWfC0gg/OgfPQTR7uxiTo2OOwjHni1f4KhT4rEmDAQn6ty6/ZRKHPWjUaAREbEw3tC36fI09hCYjjVTEmMAFTApk/tMUu0tC9Dt/vfDgXAlDJBwN5Y2Pt60qWY92skizVcWyWBxp5A8e4cVu3iToxOGUbSHzawovjubcH7qWjIZoghZJ16QB1c0ryiAfHB48OHhs2p/JZWz8Dp7kfcPkeg2Of2NbupJlNVMLIH4IGWaPAscBRkZ+F4oLqOhJ5as7fAzzU8PQdeZi0YgssGDJVmNEHP61I16KZNcxQqR0EUVwhyMmYmpVjvtfhHi/6I3TgYCmfnm6GL2sN144vMWg/gJ+p9a4GcEA0+gK3oCcKcwkq5rm+1Oxo9LWLp92Bdxq3iqfoIFmJ/ANGSbHF8StVmlVsP8zA+xuHylyiww/Lercce7cq0YA5PtYS3ge9IDYwXckBUXb5ikD3alrrv5mvMu6itB7ix2f8lbiF9Fkmc4Bk2ycIWXJDCuBN+2sTFqzUeoT6xY8XWaOcnDvqOgSm/CCSv38umiOE2jEpsKYxhRc6W70UJkrzd3hr2DiSF1I2B+krpUVK1GeOdCLC5sl7YPzk+pF8183uI9wse6UTlqiweZzB/ZVuZMnOUAmFHeq6Jb5mgW47a+FRWNXsjsA0KDFpNOoh5HYocETXS+LnAkAAADWhmIAAAADA==";
1883        let verification_key_encoded = "AACcenc1yLdGBm4xtUN1dpModROI0zovuy5rz2a94vfdBgG1C75BqviU4vw6JUYqODF8n9ivtfeU5s9PcpEGIP0htil2mfx8v2DB5RuNQ7VxJWkha0TSnJJsOl0FxhjldBbOY3tUZzZxHpPhHOKHz/ZAXRYFIsf2x+7boXC0iPurEX9VcnaJIq+YxxmnSfeYYxHkjxO9lrDBqjXzd5AHMnYyjTPC69B+5In7AOGS6R+A/g3/aR/MKDa4eDVrnsF9Oy/Ay8ahic2sSAZvtn08MdRyk/jm2cLlJbeAAad6Xyz/H9l7JrkbVwDMMPxvHVHs27tNoJCzIlrRzB7pg3ju9aQOu4h3thDr+WSgFQWKvcRPeL7f3TFjIr8WZ2457RgMcTwXwORKbqJCcyKVNOE+FlNwVkOKER+WIpC0OlgGuayPFwQQkbb91jaRlJvahfwkbF2+AJmDnavmNpop9T+/Xak1adXIrsRPeOjC+qIKxIbGimoMOoYzYlevKA80LnJ7HC0IxR+yNLvoSYxDDPNRD+OCCxk5lM2h8IDUiCNWH4FZNJ+doiigKjyZlu/xZ7jHcX7qibu/32KFTX85DPSkQM8dAEkH+vlkHmyXGLF4+xOVKeM0ihV5OEQrOABcgfTkbRsyxNInUBh0WiQyALE2ctjvkRCiE2P24bjFA8SgFmTM7gAKR89XcqLS/NP7lwCEej/L8q8R7sKGMCXmgFYluWH4JBSPDgvMxScfjFS33oBNb7po8cLnAORzohXoYTSgztklD0mKn6EegLbkLtwwr9ObsLz3m7fp/3wkNWFRkY5xzSZN1VybbQbmpyQNCpxd/kdDsvlszqlowkyC8HnKbhnvE0Mrz3ZIk4vSs/UGBSXAoESFCFCPcTq11TCOhE5rumMJErv5LusDHJgrBtQUMibLU9A1YbF7SPDAR2QZd0yx3waAC2F3xF+U682SOKF7oCZl2OICysRHqH+rZ604UfdGG0zWRuP2yg6kfGwcGQbO1ql40WrWTiFhbxxdKC7Gbz4y9Sb7q5EsPt6Z1AIn34/nXB/IWfC0gg/OgfPQTR7uxiTo2OOwjHni1f4KhT4rEmDAQn6ty6/ZRKHPWjUaAREbEw3tC36fI09hCYjjVTEmMAFTApk/tMUu0tC9Dt/vfDgXAlDJBwN5Y2Pt60qWY92skizVcWyWBxp5A8e4cVu3iToxOGUbSHzawovjubcH7qWjIZoghZJ16QB1c0ryiAfHB48OHhs2p/JZWz8Dp7kfcPkeg2Of2NbupJlNVMLIH4IGWaPAscBRkZ+F4oLqOhJ5as7fAzzU8PQdeZi0YgssGDJVmNEHP61I16KZNcxQqR0EUVwhyMmYmpVjvtfhHi/6I3TgYCmfnm6GL2sN144vMWg/gJ+p9a4GcEA0+gK3oCcKcwkq5rm+1Oxo9LWLp92Bdxq3iqfoIFmJ/ANGSbHF8StVmlVsP8zA+xuHylyiww/Lercce7cq0YA5PtYS3ge9IDYwXckBUXb5ikD3alrrv5mvMu6itB7ix2f8lbiF9Fkmc4Bk2ycIWXJDCuBN+2sTFqzUeoT6xY8XWaOcnDvqOgSm/CCSv38umiOE2jEpsKYxhRc6W70UJkrzd3hr2DiSF1I2B+krpUVK1GeOdCLC5sl7YPzk+pF8183uI9wse6UTlqIiroKqsggzLBy/IjAfxS0BxFy5zywXqp+NogFkoTEJmR5MaqOkPfap+OsD1lGScY6+X4WW/HqCWrmA3ZTqDGngQMTGXLCtl6IS/cQpihS1NRbNqOtKTaCB9COQu0oz6RivBlywuaj3MKUdmbQ2gVDj+SGQItCNaXawyPSBjB9VT+68SoJVySQsYPCuEZCb0V/40n/a7RAbyrnNjP+2HwD7p27Pl1RSzqq35xiPdnycD1UeEPLpx/ON65mYCkn+KLQZmkqPio+vA2KmJngWTx+ol4rVFimGm76VT0xCFDsu2K0YX0yoLNH4u2XfmT9NR8gGfkVRCnnNjlbgHQmEwC75+GmEJ5DjD3d+s6IXTQ60MHvxbTHHlnfmPbgKn2SAI0uVoewKC9GyK6dSaboLw3C48jl0E2kyc+7umhCk3kEeWmt//GSjRNhoq+B+mynXiOtgFs/Am2v1TBjSb+6tcijsf5tFJmeGxlCjJnTdNWBkSHpMoo6OFkkpA6/FBAUHLSM7Yv8oYyd0GtwF5cCwQ6aRTbl9oG/mUn5Q92OnDMQcUjpgEho0Dcp2OqZyyxqQSPrbIIZZQrS2HkxBgjcfcSTuSHo7ONqlRjLUpO5yS95VLGXBLLHuCiIMGT+DW6DoJRtRIS+JieVWBoX0YsWgYInXrVlWUv6gDng5AyVFkUIFwZk7/3mVAgvXO83ArVKA4S747jT60w5bgV4Jy55slDM=";
1884
1885        let decoded = STANDARD.decode(verification_key_encoded).unwrap();
1886        let verification_key =
1887            v2::MinaBaseVerificationKeyWireStableV1::binprot_read(&mut decoded.as_slice());
1888        assert!(verification_key.is_ok());
1889    }
1890
1891    #[test]
1892    fn test_archive_breadcrumb_deserialization() {
1893        let breadcrumb_bytes = include_bytes!("../../../../tests/files/archive-breadcrumb/3NK56ZbCS31qb8SvCtCCYza4beRDtKgXA2JL6s3evKouG2KkKtiy.bin");
1894        let result =
1895            v2::ArchiveTransitionFrontierDiff::binprot_read(&mut breadcrumb_bytes.as_slice());
1896
1897        assert!(result.is_ok());
1898    }
1899}