mina_p2p_messages/v2/
manual.rs

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