Skip to main content

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_openapi_impl {
460    ($name:ident) => {
461        #[cfg(feature = "openapi")]
462        const _: () = {
463            use utoipa::openapi::schema::{Object, SchemaType, Type};
464            impl utoipa::PartialSchema for $name {
465                fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
466                    Object::builder()
467                        .schema_type(SchemaType::Type(Type::String))
468                        .description(Some("Base58check-encoded data"))
469                        .pattern(Some("^[1-9A-HJ-NP-Za-km-z]+$"))
470                        .build()
471                        .into()
472                }
473            }
474            impl utoipa::ToSchema for $name {
475                fn name() -> std::borrow::Cow<'static, str> {
476                    std::borrow::Cow::Borrowed(stringify!($name))
477                }
478            }
479        };
480    };
481}
482
483base58check_openapi_impl!(MinaBaseSignedCommandMemoStableV1);
484
485macro_rules! base58check_of_binprot {
486    ($name:ident, versioned($ty:ty, $version:expr), $version_byte:ident) => {
487        impl From<Versioned<$ty, $version>> for $ty {
488            fn from(source: Versioned<$ty, $version>) -> Self {
489                source.into_inner()
490            }
491        }
492
493        pub type $name = Base58CheckOfBinProt<
494            $ty,
495            Versioned<$ty, $version>,
496            { $crate::b58version::$version_byte },
497        >;
498
499        base58check_openapi_impl!($name);
500    };
501    ($name:ident, versioned $ty:ty, $version_byte:ident) => {
502        base58check_of_binprot!($name, versioned($ty, 1), $version_byte);
503    };
504    ($name:ident, $ty:ty, $version_byte:ident) => {
505        pub type $name = Base58CheckOfBinProt<$ty, $ty, { $crate::b58version::$version_byte }>;
506        base58check_openapi_impl!($name);
507    };
508}
509
510macro_rules! base58check_of_bytes {
511    ($name:ident, $ty:ty, $version_byte:ident) => {
512        pub type $name = Base58CheckOfBytes<$ty, { $crate::b58version::$version_byte }>;
513        base58check_openapi_impl!($name);
514    };
515}
516
517base58check_of_binprot!(LedgerHash, versioned MinaBaseLedgerHash0StableV1, LEDGER_HASH);
518base58check_of_bytes!(
519    StagedLedgerHashAuxHash,
520    crate::string::ByteString,
521    STAGED_LEDGER_HASH_AUX_HASH
522);
523base58check_of_binprot!(EpochSeed, versioned MinaBaseEpochSeedStableV1, EPOCH_SEED);
524base58check_of_bytes!(
525    StagedLedgerHashPendingCoinbaseAux,
526    crate::string::ByteString,
527    STAGED_LEDGER_HASH_PENDING_COINBASE_AUX
528);
529base58check_of_binprot!(StateHash, versioned DataHashLibStateHashStableV1, STATE_HASH);
530base58check_of_binprot!(StateBodyHash, versioned MinaBaseStateBodyHashStableV1, STATE_BODY_HASH);
531base58check_of_binprot!(
532    PendingCoinbaseHash,
533    versioned MinaBasePendingCoinbaseHashVersionedStableV1,
534    RECEIPT_CHAIN_HASH
535);
536base58check_of_binprot!(
537    ReceiptChainHash,
538    versioned MinaBaseReceiptChainHashStableV1,
539    RECEIPT_CHAIN_HASH
540);
541base58check_of_binprot!(
542    TokenIdKeyHash,
543    MinaBaseAccountIdDigestStableV1,
544    TOKEN_ID_KEY
545);
546base58check_of_binprot!(
547    CoinbaseStackData,
548    versioned MinaBasePendingCoinbaseCoinbaseStackStableV1,
549    COINBASE_STACK_DATA
550);
551base58check_of_binprot!(
552    CoinbaseStackHash,
553    versioned MinaBasePendingCoinbaseStackHashStableV1,
554    COINBASE_STACK_HASH
555);
556base58check_of_binprot!(
557    Signature,
558    versioned MinaBaseSignatureStableV1,
559    SIGNATURE
560);
561
562impl StateHash {
563    pub fn zero() -> Self {
564        DataHashLibStateHashStableV1(BigInt::zero()).into()
565    }
566}
567
568impl EpochSeed {
569    pub fn zero() -> Self {
570        MinaBaseEpochSeedStableV1(BigInt::zero()).into()
571    }
572}
573
574impl CoinbaseStackData {
575    pub fn empty() -> Self {
576        // In OCaml: https://github.com/MinaProtocol/mina/blob/68b49fdaafabed0f2cd400c4c69f91e81db681e7/src/lib/mina_base/pending_coinbase.ml#L186
577        // let empty = Random_oracle.salt "CoinbaseStack" |> Random_oracle.digest
578        let empty = poseidon::hash::hash_noinputs(&NO_INPUT_COINBASE_STACK);
579        MinaBasePendingCoinbaseCoinbaseStackStableV1(empty.into()).into()
580    }
581}
582
583impl CoinbaseStackHash {
584    pub fn zero() -> Self {
585        MinaBasePendingCoinbaseStackHashStableV1(BigInt::zero()).into()
586    }
587}
588
589impl StagedLedgerHashAuxHash {
590    pub fn zero() -> Self {
591        crate::string::ByteString::from(vec![0; 32]).into()
592    }
593}
594
595impl StagedLedgerHashPendingCoinbaseAux {
596    pub fn zero() -> Self {
597        crate::string::ByteString::from(vec![0; 32]).into()
598    }
599}
600
601impl ConsensusVrfOutputTruncatedStableV1 {
602    pub fn zero() -> Self {
603        Self(crate::string::ByteString::from(vec![0; 32]))
604    }
605}
606
607impl MinaBaseStagedLedgerHashNonSnarkStableV1 {
608    pub fn zero(genesis_ledger_hash: LedgerHash) -> Self {
609        Self {
610            ledger_hash: genesis_ledger_hash,
611            aux_hash: StagedLedgerHashAuxHash::zero(),
612            pending_coinbase_aux: StagedLedgerHashPendingCoinbaseAux::zero(),
613        }
614    }
615}
616
617impl From<mina_curves::pasta::Fp> for LedgerHash {
618    fn from(value: mina_curves::pasta::Fp) -> Self {
619        MinaBaseLedgerHash0StableV1(value.into()).into()
620    }
621}
622
623impl From<&mina_curves::pasta::Fp> for LedgerHash {
624    fn from(value: &mina_curves::pasta::Fp) -> Self {
625        MinaBaseLedgerHash0StableV1(value.into()).into()
626    }
627}
628
629impl MinaBaseStagedLedgerHashStableV1 {
630    pub fn zero(
631        genesis_ledger_hash: LedgerHash,
632        empty_pending_coinbase_hash: PendingCoinbaseHash,
633    ) -> Self {
634        Self {
635            non_snark: MinaBaseStagedLedgerHashNonSnarkStableV1::zero(genesis_ledger_hash),
636            pending_coinbase_hash: empty_pending_coinbase_hash,
637        }
638    }
639}
640
641impl MinaBasePendingCoinbaseUpdateStableV1 {
642    pub fn zero() -> Self {
643        Self {
644            action: MinaBasePendingCoinbaseUpdateActionStableV1::UpdateNone,
645            coinbase_amount: CurrencyAmountStableV1(0u64.into()),
646        }
647    }
648}
649
650impl MinaBasePendingCoinbaseStackVersionedStableV1 {
651    pub fn empty() -> Self {
652        Self {
653            data: CoinbaseStackData::empty(),
654            state: MinaBasePendingCoinbaseStateStackStableV1 {
655                init: CoinbaseStackHash::zero(),
656                curr: CoinbaseStackHash::zero(),
657            },
658        }
659    }
660}
661
662impl ConsensusProofOfStakeDataEpochDataStakingValueVersionedValueStableV1 {
663    pub fn zero(
664        ledger_hash: LedgerHash,
665        total_currency: CurrencyAmountStableV1,
666        seed: EpochSeed,
667    ) -> Self {
668        Self {
669            ledger: MinaBaseEpochLedgerValueStableV1 {
670                hash: ledger_hash,
671                total_currency,
672            },
673            seed,
674            start_checkpoint: StateHash::zero(),
675            lock_checkpoint: StateHash::zero(),
676            epoch_length: 1.into(),
677        }
678    }
679}
680
681impl ConsensusProofOfStakeDataEpochDataNextValueVersionedValueStableV1 {
682    pub fn zero(
683        ledger_hash: LedgerHash,
684        total_currency: CurrencyAmountStableV1,
685        seed: EpochSeed,
686    ) -> Self {
687        Self {
688            ledger: MinaBaseEpochLedgerValueStableV1 {
689                hash: ledger_hash,
690                total_currency,
691            },
692            seed,
693            start_checkpoint: StateHash::zero(),
694            lock_checkpoint: StateHash::zero(),
695            epoch_length: 1.into(),
696        }
697    }
698}
699
700impl AsRef<BigInteger256> for LedgerHash {
701    fn as_ref(&self) -> &BigInteger256 {
702        self.0.as_ref()
703    }
704}
705
706impl Default for TokenIdKeyHash {
707    fn default() -> Self {
708        MinaBaseAccountIdDigestStableV1(BigInt::one()).into()
709    }
710}
711
712#[derive(
713    Clone,
714    Debug,
715    PartialEq,
716    Eq,
717    PartialOrd,
718    Ord,
719    Serialize,
720    Deserialize,
721    BinProtRead,
722    BinProtWrite,
723    MallocSizeOf,
724)]
725pub struct NonZeroCurvePointWithVersions {
726    x: Versioned<crate::bigint::BigInt, 1>,
727    is_odd: bool,
728}
729
730impl From<NonZeroCurvePointUncompressedStableV1> for Versioned<NonZeroCurvePointWithVersions, 1> {
731    fn from(source: NonZeroCurvePointUncompressedStableV1) -> Self {
732        NonZeroCurvePointWithVersions {
733            x: source.x.into(),
734            is_odd: source.is_odd,
735        }
736        .into()
737    }
738}
739
740impl From<Versioned<NonZeroCurvePointWithVersions, 1>> for NonZeroCurvePointUncompressedStableV1 {
741    fn from(source: Versioned<NonZeroCurvePointWithVersions, 1>) -> Self {
742        let source = source.into_inner();
743        Self {
744            x: source.x.into_inner(),
745            is_odd: source.is_odd,
746        }
747    }
748}
749
750pub type NonZeroCurvePoint = Base58CheckOfBinProt<
751    NonZeroCurvePointUncompressedStableV1,
752    Versioned<NonZeroCurvePointWithVersions, 1>,
753    { crate::b58version::NON_ZERO_CURVE_POINT_COMPRESSED },
754>;
755
756#[cfg(feature = "openapi")]
757const _: () = {
758    impl utoipa::PartialSchema for NonZeroCurvePoint {
759        fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
760            <String as utoipa::PartialSchema>::schema()
761        }
762    }
763    impl utoipa::ToSchema for NonZeroCurvePoint {
764        fn name() -> std::borrow::Cow<'static, str> {
765            std::borrow::Cow::Borrowed("NonZeroCurvePoint")
766        }
767    }
768};
769
770#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
771pub enum ArchiveTransitionFrontierDiff {
772    BreadcrumbAdded {
773        block: (MinaBlockBlockStableV2, (Option<StateBodyHash>, StateHash)),
774        accounts_accessed: List<(crate::number::UInt64, MinaBaseAccountBinableArgStableV2)>,
775        accounts_created: List<(MinaBaseAccountIdStableV2, CurrencyFeeStableV1)>,
776        tokens_used: List<(MinaBaseTokenIdStableV2, Option<MinaBaseAccountIdStableV2>)>,
777        sender_receipt_chains_from_parent_ledger:
778            List<(MinaBaseAccountIdStableV2, MinaBaseReceiptChainHashStableV1)>,
779    },
780    // TODO(adonagy): I think this is legacy stuff, doublecheck
781    RootTransitioned(()),
782    BoostrapOf(()),
783}
784
785impl ArchiveTransitionFrontierDiff {
786    pub fn block(&self) -> Option<MinaBlockBlockStableV2> {
787        match self {
788            // TODO(adonagy): maybe we should use Arc here instead of cloning
789            ArchiveTransitionFrontierDiff::BreadcrumbAdded { block, .. } => Some(block.0.clone()),
790            _ => None,
791        }
792    }
793
794    pub fn accounts_accessed(
795        &self,
796    ) -> List<(crate::number::UInt64, MinaBaseAccountBinableArgStableV2)> {
797        match self {
798            ArchiveTransitionFrontierDiff::BreadcrumbAdded {
799                accounts_accessed, ..
800            } => accounts_accessed.clone(),
801            _ => List::new(),
802        }
803    }
804
805    pub fn accounts_created(&self) -> List<(MinaBaseAccountIdStableV2, CurrencyFeeStableV1)> {
806        match self {
807            ArchiveTransitionFrontierDiff::BreadcrumbAdded {
808                accounts_created, ..
809            } => accounts_created.clone(),
810            _ => List::new(),
811        }
812    }
813
814    pub fn tokens_used(
815        &self,
816    ) -> List<(MinaBaseTokenIdStableV2, Option<MinaBaseAccountIdStableV2>)> {
817        match self {
818            ArchiveTransitionFrontierDiff::BreadcrumbAdded { tokens_used, .. } => {
819                tokens_used.clone()
820            }
821            _ => List::new(),
822        }
823    }
824}
825
826#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
827pub enum ArchiveRpc {
828    SendDiff(ArchiveTransitionFrontierDiff),
829}
830
831#[derive(Clone, Debug, PartialEq, BinProtRead, BinProtWrite)]
832pub struct PrecomputedBlockProof(pub MinaBaseProofStableV2);
833
834impl Serialize for PrecomputedBlockProof {
835    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
836    where
837        S: serde::ser::Serializer,
838    {
839        use base64::{engine::general_purpose::URL_SAFE, Engine as _};
840        use binprot::BinProtWrite;
841        let mut buf = Vec::new();
842        self.0
843            .binprot_write(&mut buf)
844            .map_err(serde::ser::Error::custom)?;
845        let base64_data = URL_SAFE.encode(&buf);
846        serializer.serialize_str(&base64_data)
847    }
848}
849
850impl<'de> Deserialize<'de> for PrecomputedBlockProof {
851    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
852    where
853        D: serde::de::Deserializer<'de>,
854    {
855        use base64::{engine::general_purpose::URL_SAFE, Engine as _};
856        let base64_data = String::deserialize(deserializer)?;
857        let binprot_data = URL_SAFE
858            .decode(&base64_data)
859            .map_err(serde::de::Error::custom)?;
860        let mut read = binprot_data.as_slice();
861        let proof: MinaBaseProofStableV2 =
862            binprot::BinProtRead::binprot_read(&mut read).map_err(serde::de::Error::custom)?;
863        Ok(PrecomputedBlockProof(proof))
864    }
865}
866
867impl From<MinaBaseProofStableV2> for PrecomputedBlockProof {
868    fn from(value: MinaBaseProofStableV2) -> Self {
869        Self(value)
870    }
871}
872
873#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
874pub struct PrecomputedBlock {
875    pub scheduled_time: BlockTimeTimeStableV1,
876    pub protocol_state: MinaStateProtocolStateValueStableV2,
877    pub protocol_state_proof: PrecomputedBlockProof,
878    pub staged_ledger_diff: StagedLedgerDiffDiffStableV2,
879    // FIXME: for some reason in OCaml the base58check conversion for the JSON value
880    // uses version byte = 0x05 (ledger hash) instead of 0x10 (StateHash) and 0x11 (StateBodyHash)
881    // Note: keeping the proper types here, we should raise an issue in the ocaml repo
882    pub delta_transition_chain_proof: (
883        StateHash,           // LedgerHash,       // StateHash
884        List<StateBodyHash>, // List<LedgerHash>, // StateBodyHash
885    ),
886    pub protocol_version: ProtocolVersionStableV2,
887    #[serde(default)]
888    pub proposed_protocol_version: Option<ProtocolVersionStableV2>,
889    pub accounts_accessed: List<(crate::number::UInt64, MinaBaseAccountBinableArgStableV2)>,
890    pub accounts_created: List<(MinaBaseAccountIdStableV2, CurrencyFeeStableV1)>,
891    pub tokens_used: List<(MinaBaseTokenIdStableV2, Option<MinaBaseAccountIdStableV2>)>,
892}
893
894#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
895pub struct PrecomputedBlockData {
896    pub version: u32,
897    pub data: PrecomputedBlock,
898}
899
900impl PrecomputedBlock {
901    pub fn with_version(&self, version: u32) -> PrecomputedBlockData {
902        PrecomputedBlockData {
903            version,
904            data: self.clone(),
905        }
906    }
907}
908
909#[cfg(test)]
910mod tests {
911    use binprot::{BinProtRead, BinProtWrite};
912    use serde::{de::DeserializeOwned, Serialize};
913    use std::fmt::Debug;
914
915    use crate::v2::{
916        CoinbaseStackData, CoinbaseStackHash, EpochSeed, LedgerHash, NonZeroCurvePoint,
917        PendingCoinbaseHash, Signature, StagedLedgerHashAuxHash,
918        StagedLedgerHashPendingCoinbaseAux, StateBodyHash, StateHash, TokenIdKeyHash,
919    };
920
921    fn base58check_test<T: Serialize + DeserializeOwned + BinProtRead + BinProtWrite + Debug>(
922        b58: &str,
923        hex: &str,
924    ) {
925        let bin: T = serde_json::from_value(serde_json::json!(b58)).unwrap();
926        let json = serde_json::to_value(&bin).unwrap();
927
928        let mut binprot = Vec::new();
929        bin.binprot_write(&mut binprot).unwrap();
930
931        println!("{b58} => {}", hex::encode(&binprot));
932
933        let binprot1 = hex::decode(hex).unwrap();
934        let mut b = binprot1.as_slice();
935        let from_hex = T::binprot_read(&mut b).unwrap();
936        println!("{hex} => {}", serde_json::to_string(&from_hex).unwrap());
937
938        assert_eq!(hex::encode(&binprot), hex);
939        assert_eq!(json.as_str().unwrap(), b58);
940    }
941
942    macro_rules! b58t {
943        ($name:ident, $ty:ty, $b58:expr, $hex:expr) => {
944            #[test]
945            fn $name() {
946                base58check_test::<$ty>($b58, $hex);
947            }
948        };
949    }
950
951    b58t!(
952        ledger_hash,
953        LedgerHash,
954        "jwrPvAMUNo3EKT2puUk5Fxz6B7apRAoKNTGpAA49t3TRSfzvdrL",
955        "636f5b2d67278e17bc4343c7c23fb4991f8cf0bbbfd8558615b124d5d6254801"
956    );
957
958    b58t!(
959        staged_ledger_hash_aux_hash,
960        StagedLedgerHashAuxHash,
961        "UbhWTJLi4JM5bizFQVPvfMbjh4tCiTUTrNhedn8WdMPR1KHKJh",
962        "203294e118730ad8b7c0f2ab6d74d244eec02cfef221790bb1274fdb3b97854e50"
963    );
964
965    b58t!(
966        epoch_seed,
967        EpochSeed,
968        "2vajKi2Cxx58mByzxbJA3G6gYh1j2BoizW4zzoLcZa3kYECjhaXV",
969        "4d8802db5beb98f13e10475ddc9e718f6890613276331c062f5d71b915d6941d"
970    );
971
972    b58t!(
973        staged_ledger_hash_pending_coinbase_aux,
974        StagedLedgerHashPendingCoinbaseAux,
975        "XgkNHpgSvmF7CyRBGUzcwvCD9daBRhZUDLg3xTvohmTX1mRqHR",
976        "20c922885bfeda2c2568e32fcc595fe0ad2292dcf756be637545bc553f7f7028e8"
977    );
978
979    b58t!(
980        state_hash,
981        StateHash,
982        "3NL7AkynW6hbDrhHTAht1GLG563Fo9fdcEQk1zEyy5XedC6aZTeB",
983        "8d67aadd018581a812623915b13d5c3a6da7dfe8a195172d9bbd206810bc2329"
984    );
985
986    b58t!(
987        state_body_hash,
988        StateBodyHash,
989        "3WtsPNWF7Ua5zbvHEARuEL32KEfMM7pPYNXWVGtTStdYJRYA2rta",
990        "1b11c26e5541d2f719a50f2e5bdcd23e7995883036f3d2e5675dfd3015ec6202"
991    );
992
993    b58t!(
994        pending_coinbase_hash,
995        PendingCoinbaseHash,
996        "2n2EEn3yH1oRU8tCXTjw7dJKHQVcFTkfeDCTpBzum3sZcssPeaVM",
997        "e23a19254e600402e4474371450d498c75a9b3e28c34160d489af61c255f722c"
998    );
999
1000    b58t!(
1001        token_id_key,
1002        TokenIdKeyHash,
1003        "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf",
1004        "0100000000000000000000000000000000000000000000000000000000000000"
1005    );
1006
1007    // FIXME
1008    // b58t!(
1009    //     vrf_truncated_output,
1010    //     ConsensusVrfOutputTruncatedStableV1,
1011    //     "48H9Qk4D6RzS9kAJQX9HCDjiJ5qLiopxgxaS6xbDCWNaKQMQ9Y4C",
1012    //     "20dfd73283866632d9dbfda15421eacd02800957caad91f3a9ab4cc5ccfb298e03"
1013    // );
1014
1015    b58t!(
1016        coinbase_stack_data,
1017        CoinbaseStackData,
1018        "4QNrZFBTDQCPfEZqBZsaPYx8qdaNFv1nebUyCUsQW9QUJqyuD3un",
1019        "35b9d51e5d7c741456f86720731241a8280273cfc6c21668fd7bc6c587d0cc1d"
1020    );
1021
1022    b58t!(
1023        coinbase_stack_hash,
1024        CoinbaseStackHash,
1025        "4Yx5U3t3EYQycZ91yj4478bHkLwGkhDHnPbCY9TxgUk69SQityej",
1026        "0000000000000000000000000000000000000000000000000000000000000000"
1027    );
1028
1029    b58t!(
1030        signature,
1031        Signature,
1032        "7mXS9Y91bWtTYNKuDbxTuG18wUiZLHUySy9Ms8bPyAT9KNnME1q2nctwnvowJi2Y79dnsL18iVSCuaQF1ufUKwUZZKAXHqnF",
1033        "d290f924705fb714e91fedb9bed77e85bce8f5d932c3f4d692b20e4c3e5f9a3343c2baffce9ab0c2391e2f3de8ac891633338d827e6fd4f269331c248029b106"
1034    );
1035
1036    #[test]
1037    fn non_zero_curve_point() {
1038        let b58 = r#""B62qkUHaJUHERZuCHQhXCQ8xsGBqyYSgjQsKnKN5HhSJecakuJ4pYyk""#;
1039
1040        let v = serde_json::from_str::<NonZeroCurvePoint>(&b58)
1041            .unwrap()
1042            .into_inner();
1043        assert_eq!(v.is_odd, false);
1044        assert_eq!(
1045            &hex::encode(&v.x.to_bytes()),
1046            "3c2b5b48c22dc8b8c9d2c9d76a2ceaaf02beabb364301726c3f8e989653af513"
1047        );
1048    }
1049}
1050
1051const SHIFTED_VALUE: &str = "Shifted_value";
1052
1053impl Serialize for PicklesProofProofsVerified2ReprStableV2StatementFp {
1054    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1055    where
1056        S: serde::Serializer,
1057    {
1058        if serializer.is_human_readable() {
1059            let mut serializer = serializer.serialize_tuple(2)?;
1060            match self {
1061                PicklesProofProofsVerified2ReprStableV2StatementFp::ShiftedValue(v) => {
1062                    serializer.serialize_element(SHIFTED_VALUE)?;
1063                    serializer.serialize_element(v)?;
1064                }
1065            }
1066            serializer.end()
1067        } else {
1068            match self {
1069                PicklesProofProofsVerified2ReprStableV2StatementFp::ShiftedValue(v) => serializer
1070                    .serialize_newtype_variant(
1071                        "PicklesProofProofsVerified2ReprStableV2StatementFp",
1072                        0,
1073                        "ShiftedValue",
1074                        v,
1075                    ),
1076            }
1077        }
1078    }
1079}
1080
1081impl<'de> Deserialize<'de> for PicklesProofProofsVerified2ReprStableV2StatementFp {
1082    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1083    where
1084        D: serde::Deserializer<'de>,
1085    {
1086        struct V;
1087        impl<'de> Visitor<'de> for V {
1088            type Value = PicklesProofProofsVerified2ReprStableV2StatementFp;
1089
1090            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1091                formatter.write_str("tuple of tag and value")
1092            }
1093
1094            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1095            where
1096                A: serde::de::SeqAccess<'de>,
1097            {
1098                match seq.next_element::<String>()? {
1099                    Some(v) if &v == SHIFTED_VALUE => {}
1100                    Some(v) => {
1101                        return Err(serde::de::Error::custom(format!(
1102                            "expecting `{SHIFTED_VALUE}`, got `{v}`"
1103                        )))
1104                    }
1105                    None => return Err(serde::de::Error::custom("expecting a tag")),
1106                }
1107                match seq.next_element::<BigInt>()? {
1108                    Some(v) => {
1109                        Ok(PicklesProofProofsVerified2ReprStableV2StatementFp::ShiftedValue(v))
1110                    }
1111                    None => return Err(serde::de::Error::custom("expecting a value")),
1112                }
1113            }
1114        }
1115
1116        if deserializer.is_human_readable() {
1117            deserializer.deserialize_tuple(2, V)
1118        } else {
1119            #[derive(Deserialize)]
1120            pub enum PicklesProofProofsVerified2ReprStableV2StatementFp {
1121                ShiftedValue(crate::bigint::BigInt),
1122            }
1123            let PicklesProofProofsVerified2ReprStableV2StatementFp::ShiftedValue(v) =
1124                Deserialize::deserialize(deserializer)?;
1125            Ok(Self::ShiftedValue(v))
1126        }
1127    }
1128}
1129
1130impl Serialize for ConsensusVrfOutputTruncatedStableV1 {
1131    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1132    where
1133        S: serde::Serializer,
1134    {
1135        if serializer.is_human_readable() {
1136            // <https://github.com/MinaProtocol/mina/blob/6de36cf8851de28b667e4c1041badf62507c235d/src/lib/consensus/vrf/consensus_vrf.ml#L172>
1137            use base64::{engine::general_purpose::URL_SAFE, Engine as _};
1138            let base64_data = URL_SAFE.encode(&self.0 .0);
1139            serializer.serialize_str(&base64_data)
1140        } else {
1141            serializer.serialize_newtype_struct("ConsensusVrfOutputTruncatedStableV1", &self.0)
1142        }
1143    }
1144}
1145
1146impl<'de> Deserialize<'de> for ConsensusVrfOutputTruncatedStableV1 {
1147    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1148    where
1149        D: serde::Deserializer<'de>,
1150    {
1151        if deserializer.is_human_readable() {
1152            // <https://github.com/MinaProtocol/mina/blob/6de36cf8851de28b667e4c1041badf62507c235d/src/lib/consensus/vrf/consensus_vrf.ml#L172>
1153            use base64::{engine::general_purpose::URL_SAFE, Engine as _};
1154            let base64_data = String::deserialize(deserializer)?;
1155            URL_SAFE
1156                .decode(&base64_data)
1157                .map(|vec| ByteString::from(vec))
1158                .map_err(|e| serde::de::Error::custom(format!("Error deserializing vrf: {e}")))
1159        } else {
1160            Deserialize::deserialize(deserializer)
1161        }
1162        .map(Self)
1163    }
1164}
1165
1166impl MinaBaseVerificationKeyWireStableV1 {
1167    pub fn to_base64(&self) -> Result<String, conv::Error> {
1168        let mut buffer: Vec<u8> = Vec::new();
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 = MinaBaseVerificationKeyWireStableV1::binprot_read(&mut decoded_data.as_slice())?;
1180        Ok(res)
1181    }
1182}
1183
1184// TODO(adonagy): macro?
1185impl MinaBaseSignedCommandStableV2 {
1186    pub fn to_base64(&self) -> Result<String, conv::Error> {
1187        const COMMAND_VERSION_TAG: u8 = 2;
1188
1189        let mut buffer: Vec<u8> = Vec::new();
1190        COMMAND_VERSION_TAG.binprot_write(&mut buffer)?;
1191        self.binprot_write(&mut buffer)?;
1192        use base64::{engine::general_purpose::STANDARD, Engine as _};
1193
1194        let base64_data = STANDARD.encode(buffer);
1195        Ok(base64_data)
1196    }
1197
1198    pub fn from_base64(base64_data: &str) -> Result<Self, conv::Error> {
1199        use base64::{engine::general_purpose::STANDARD, Engine as _};
1200        let decoded_data = STANDARD.decode(&base64_data)?;
1201        let res = MinaBaseSignedCommandStableV2::binprot_read(&mut decoded_data[1..].as_ref())?;
1202        Ok(res)
1203    }
1204}
1205
1206/// TODO(adonagy): implement the base64 conversions similarly to the base58check ones (versioned/not versioned)
1207impl MinaBaseZkappCommandTStableV1WireStableV1 {
1208    pub fn to_base64(&self) -> Result<String, conv::Error> {
1209        const ZKAPP_VERSION_TAG: u8 = 1;
1210
1211        let mut buffer: Vec<u8> = Vec::new();
1212        ZKAPP_VERSION_TAG.binprot_write(&mut buffer)?;
1213        self.binprot_write(&mut buffer)?;
1214        use base64::{engine::general_purpose::STANDARD, Engine as _};
1215
1216        let base64_data = STANDARD.encode(buffer);
1217        Ok(base64_data)
1218    }
1219
1220    pub fn from_base64(base64_data: &str) -> Result<Self, conv::Error> {
1221        use base64::{engine::general_purpose::STANDARD, Engine as _};
1222        let decoded_data = STANDARD.decode(&base64_data)?;
1223        let res = MinaBaseZkappCommandTStableV1WireStableV1::binprot_read(
1224            &mut decoded_data[1..].as_ref(),
1225        )?;
1226        Ok(res)
1227    }
1228}
1229
1230impl Serialize for ConsensusBodyReferenceStableV1 {
1231    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1232    where
1233        S: serde::ser::Serializer,
1234    {
1235        if serializer.is_human_readable() {
1236            let hex_string = hex::encode(&self.0);
1237            serializer.serialize_str(&hex_string)
1238        } else {
1239            self.0.serialize(serializer)
1240        }
1241    }
1242}
1243
1244impl<'de> Deserialize<'de> for ConsensusBodyReferenceStableV1 {
1245    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1246    where
1247        D: serde::de::Deserializer<'de>,
1248    {
1249        if deserializer.is_human_readable() {
1250            let hex_string = String::deserialize(deserializer)?;
1251            let decoded_bytes = hex::decode(&hex_string).map_err(serde::de::Error::custom)?;
1252            Ok(ConsensusBodyReferenceStableV1(
1253                crate::string::ByteString::from(decoded_bytes),
1254            ))
1255        } else {
1256            let inner_value = crate::string::ByteString::deserialize(deserializer)?;
1257            Ok(ConsensusBodyReferenceStableV1(inner_value))
1258        }
1259    }
1260}
1261
1262// Needs to handle #[serde(untagged)] which postcard cannot deserialize
1263impl<'de> Deserialize<'de> for MinaNumbersGlobalSlotSinceGenesisMStableV1 {
1264    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1265    where
1266        D: serde::de::Deserializer<'de>,
1267    {
1268        let value = UnsignedExtendedUInt32StableV1::deserialize(deserializer)?;
1269        Ok(MinaNumbersGlobalSlotSinceGenesisMStableV1::SinceGenesis(
1270            value,
1271        ))
1272    }
1273}
1274
1275// Needs to handle #[serde(untagged)] which postcard cannot deserialize
1276impl<'de> Deserialize<'de> for MinaNumbersGlobalSlotSinceHardForkMStableV1 {
1277    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1278    where
1279        D: serde::de::Deserializer<'de>,
1280    {
1281        let value = UnsignedExtendedUInt32StableV1::deserialize(deserializer)?;
1282        Ok(MinaNumbersGlobalSlotSinceHardForkMStableV1::SinceHardFork(
1283            value,
1284        ))
1285    }
1286}
1287
1288pub type MerkleTreePath = Vec<MerkleTreeNode>;
1289
1290#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
1291#[polymorphic_variant]
1292pub enum MerkleTreeNode {
1293    Left(BigInt),
1294    Right(BigInt),
1295}
1296
1297impl ConsensusProofOfStakeDataConsensusStateValueStableV2 {
1298    pub fn global_slot(&self) -> u32 {
1299        match &self.curr_global_slot_since_hard_fork.slot_number {
1300            super::MinaNumbersGlobalSlotSinceHardForkMStableV1::SinceHardFork(s) => s.as_u32(),
1301        }
1302    }
1303}
1304
1305impl AsRef<str> for SgnStableV1 {
1306    fn as_ref(&self) -> &str {
1307        match self {
1308            SgnStableV1::Pos => "Pos",
1309            SgnStableV1::Neg => "Neg",
1310        }
1311    }
1312}
1313
1314impl Serialize for SgnStableV1 {
1315    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1316    where
1317        S: serde::Serializer,
1318    {
1319        if serializer.is_human_readable() {
1320            let mut tuple = serializer.serialize_tuple(1)?;
1321            tuple.serialize_element(self.as_ref())?;
1322            tuple.end()
1323        } else {
1324            match *self {
1325                SgnStableV1::Pos => {
1326                    Serializer::serialize_unit_variant(serializer, "SgnStableV1", 0u32, "Pos")
1327                }
1328                SgnStableV1::Neg => {
1329                    Serializer::serialize_unit_variant(serializer, "SgnStableV1", 1u32, "Neg")
1330                }
1331            }
1332        }
1333    }
1334}
1335
1336impl<'de> Deserialize<'de> for SgnStableV1 {
1337    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1338    where
1339        D: serde::Deserializer<'de>,
1340    {
1341        if deserializer.is_human_readable() {
1342            struct V;
1343
1344            impl<'de> Visitor<'de> for V {
1345                type Value = String;
1346
1347                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1348                    formatter.write_str("`Pos` or `Neg`")?;
1349                    panic!("foo")
1350                }
1351
1352                fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
1353                where
1354                    E: serde::de::Error,
1355                {
1356                    Ok(v)
1357                }
1358
1359                fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1360                where
1361                    A: serde::de::SeqAccess<'de>,
1362                {
1363                    let Some(elt) = seq.next_element()? else {
1364                        return Err(serde::de::Error::custom("No tag"));
1365                    };
1366                    Ok(elt)
1367                }
1368            }
1369            let v = deserializer.deserialize_tuple(1, V)?;
1370            match v.as_str() {
1371                "Pos" => Ok(SgnStableV1::Pos),
1372                "Neg" => Ok(SgnStableV1::Neg),
1373                _ => Err(serde::de::Error::custom(format!("Invalid tag {v}"))),
1374            }
1375        } else {
1376            #[derive(Deserialize)]
1377            enum _SgnStableV1 {
1378                Pos,
1379                Neg,
1380            }
1381
1382            let s: _SgnStableV1 = Deserialize::deserialize(deserializer)?;
1383            match s {
1384                _SgnStableV1::Pos => Ok(SgnStableV1::Pos),
1385                _SgnStableV1::Neg => Ok(SgnStableV1::Neg),
1386            }
1387        }
1388    }
1389}
1390
1391const PRECISION: usize = 9;
1392const PRECISION_EXP: u64 = 10u64.pow(PRECISION as u32);
1393
1394impl Serialize for CurrencyFeeStableV1 {
1395    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1396    where
1397        S: serde::ser::Serializer,
1398    {
1399        let amount = self.0 .0.as_u64();
1400        let whole = amount / PRECISION_EXP;
1401        let remainder = amount % PRECISION_EXP;
1402
1403        if remainder == 0 {
1404            serializer.serialize_str(&whole.to_string())
1405        } else {
1406            let tmp = format!("{:0width$}", remainder, width = PRECISION);
1407            let fractional_str = tmp.trim_end_matches('0');
1408            serializer.serialize_str(&format!("{}.{}", whole, fractional_str))
1409        }
1410    }
1411}
1412
1413impl<'de> Deserialize<'de> for CurrencyFeeStableV1 {
1414    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1415    where
1416        D: serde::de::Deserializer<'de>,
1417    {
1418        let s: String = Deserialize::deserialize(deserializer)?;
1419        let parts: Vec<&str> = s.split('.').collect();
1420        let result = match parts.as_slice() {
1421            [whole] => format!("{}{}", whole, "0".repeat(PRECISION)),
1422            [whole, decimal] => {
1423                let decimal_length = decimal.len();
1424                if decimal_length > PRECISION {
1425                    format!("{}{}", whole, &decimal[0..PRECISION])
1426                } else {
1427                    format!(
1428                        "{}{}{}",
1429                        whole,
1430                        decimal,
1431                        "0".repeat(PRECISION - decimal_length)
1432                    )
1433                }
1434            }
1435            _ => return Err(serde::de::Error::custom("Invalid currency input")),
1436        };
1437        let fee_in_nanomina: u64 = result.parse().map_err(serde::de::Error::custom)?;
1438        Ok(CurrencyFeeStableV1(
1439            UnsignedExtendedUInt64Int64ForVersionTagsStableV1(fee_in_nanomina.into()),
1440        ))
1441    }
1442}
1443
1444#[cfg(test)]
1445mod tests_currency_fee_serialization {
1446    use super::*;
1447    use serde_json;
1448
1449    #[test]
1450    fn test_correct_serialization() {
1451        let amount = 1_000_000u64;
1452        let currency = CurrencyFeeStableV1(amount.into());
1453
1454        let serialized = serde_json::to_string(&currency).unwrap();
1455        assert_eq!(serialized, "\"0.001\"", "Serialization output is incorrect");
1456    }
1457
1458    #[test]
1459    fn test_zero_remainder() {
1460        let amount = 1_000_000_000u64;
1461        let currency = CurrencyFeeStableV1(amount.into());
1462
1463        let serialized = serde_json::to_string(&currency).unwrap();
1464        assert_eq!(
1465            serialized, "\"1\"",
1466            "Serialization output is incorrect for whole numbers"
1467        );
1468    }
1469
1470    #[test]
1471    fn test_fractional_serialization() {
1472        let amount = 1_234_567_890u64;
1473        let currency = CurrencyFeeStableV1(amount.into());
1474
1475        let serialized = serde_json::to_string(&currency).unwrap();
1476        assert_eq!(
1477            serialized, "\"1.23456789\"",
1478            "Serialization output is incorrect for fractional numbers"
1479        );
1480    }
1481
1482    #[test]
1483    fn test_trailing_zeros() {
1484        let amount = 1_230_000_000u64;
1485        let currency = CurrencyFeeStableV1(amount.into());
1486
1487        let serialized = serde_json::to_string(&currency).unwrap();
1488        assert_eq!(
1489            serialized, "\"1.23\"",
1490            "Serialization output is incorrect when fractional part has trailing zeros"
1491        );
1492    }
1493
1494    #[test]
1495    fn test_deserialization_correct_serialization() {
1496        let serialized = "\"0.001\"";
1497        let currency: CurrencyFeeStableV1 = serde_json::from_str(serialized).unwrap();
1498        let expected_amount = 1_000_000u64;
1499        assert_eq!(
1500            currency,
1501            CurrencyFeeStableV1(expected_amount.into()),
1502            "Deserialization output is incorrect"
1503        );
1504    }
1505
1506    #[test]
1507    fn test_deserialization_zero_remainder() {
1508        let serialized = "\"1\"";
1509        let currency: CurrencyFeeStableV1 = serde_json::from_str(serialized).unwrap();
1510        let expected_amount = 1_000_000_000u64;
1511        assert_eq!(
1512            currency,
1513            CurrencyFeeStableV1(expected_amount.into()),
1514            "Deserialization output is incorrect for whole numbers"
1515        );
1516    }
1517
1518    #[test]
1519    fn test_deserialization_fractional() {
1520        let serialized = "\"1.23456789\"";
1521        let currency: CurrencyFeeStableV1 = serde_json::from_str(serialized).unwrap();
1522        let expected_amount = 1_234_567_890u64;
1523        assert_eq!(
1524            currency,
1525            CurrencyFeeStableV1(expected_amount.into()),
1526            "Deserialization output is incorrect for fractional numbers"
1527        );
1528    }
1529
1530    #[test]
1531    fn test_deserialization_trailing_zeros() {
1532        let serialized = "\"1.23\"";
1533        let currency: CurrencyFeeStableV1 = serde_json::from_str(serialized).unwrap();
1534        let expected_amount = 1_230_000_000u64;
1535        assert_eq!(
1536            currency,
1537            CurrencyFeeStableV1(expected_amount.into()),
1538            "Deserialization output is incorrect when fractional part has trailing zeros"
1539        );
1540    }
1541
1542    #[test]
1543    fn test_deserialization_truncates_extra_decimals() {
1544        let serialized = "\"0.1234567890123\""; // 13 decimal places
1545        let currency: CurrencyFeeStableV1 = serde_json::from_str(serialized).unwrap();
1546        let expected_amount = 123_456_789u64; // Only the first 9 decimal places are considered
1547        assert_eq!(
1548            currency,
1549            CurrencyFeeStableV1(expected_amount.into()),
1550            "Deserialization did not correctly truncate extra decimal places"
1551        );
1552    }
1553
1554    #[test]
1555    fn test_deserialization_invalid_number() {
1556        let serialized = "\"abc\"";
1557        let result: Result<CurrencyFeeStableV1, _> = serde_json::from_str(serialized);
1558        assert!(
1559            result.is_err(),
1560            "Deserialization should fail for invalid number strings"
1561        );
1562    }
1563}
1564
1565#[cfg(test)]
1566mod tests_sgn {
1567    use crate::v2::SgnStableV1;
1568
1569    #[test]
1570    fn test_json() {
1571        assert_eq!(
1572            serde_json::to_value(&SgnStableV1::Pos).unwrap(),
1573            serde_json::json!(["Pos"])
1574        );
1575        assert_eq!(
1576            serde_json::to_value(&SgnStableV1::Neg).unwrap(),
1577            serde_json::json!(["Neg"])
1578        );
1579
1580        assert_eq!(
1581            serde_json::from_value::<SgnStableV1>(serde_json::json!(["Pos"])).unwrap(),
1582            SgnStableV1::Pos
1583        );
1584        assert_eq!(
1585            serde_json::from_value::<SgnStableV1>(serde_json::json!(["Neg"])).unwrap(),
1586            SgnStableV1::Neg
1587        );
1588    }
1589}
1590
1591/// Derived name: `Mina_base__Fee_excess.Stable.V1.fee`
1592///
1593/// Gid: `602`
1594/// Location: [src/lib/currency/signed_poly.ml:6:4](https://github.com/Minaprotocol/mina/blob/b1facec/src/lib/currency/signed_poly.ml#L6)
1595/// Args: CurrencyFeeStableV1 , SgnStableV1
1596#[derive(
1597    Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite, MallocSizeOf,
1598)]
1599pub struct SignedAmount {
1600    pub magnitude: CurrencyFeeStableV1,
1601    pub sgn: SgnStableV1,
1602}
1603
1604/// **OCaml name**: `Mina_base__Fee_excess.Stable.V1`
1605///
1606/// Gid: `657`
1607/// 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)
1608///
1609///
1610/// Gid: `656`
1611/// 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)
1612/// Args: TokenIdKeyHash , MinaBaseFeeExcessStableV1Fee
1613#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
1614pub struct MinaBaseFeeExcessStableV1(pub TokenFeeExcess, pub TokenFeeExcess);
1615
1616#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, BinProtRead, BinProtWrite)]
1617pub struct TokenFeeExcess {
1618    pub token: TokenIdKeyHash,
1619    pub amount: SignedAmount,
1620}
1621
1622impl Default for NonZeroCurvePointUncompressedStableV1 {
1623    fn default() -> Self {
1624        Self {
1625            x: Default::default(),
1626            is_odd: false,
1627        }
1628    }
1629}
1630
1631impl MinaNumbersGlobalSlotSinceGenesisMStableV1 {
1632    pub fn as_u32(&self) -> u32 {
1633        let Self::SinceGenesis(slot) = self;
1634        slot.as_u32()
1635    }
1636}
1637
1638impl MinaNumbersGlobalSlotSinceHardForkMStableV1 {
1639    pub fn as_u32(&self) -> u32 {
1640        let Self::SinceHardFork(slot) = self;
1641        slot.as_u32()
1642    }
1643}
1644
1645impl MinaNumbersGlobalSlotSpanStableV1 {
1646    pub fn as_u32(&self) -> u32 {
1647        let Self::GlobalSlotSpan(slot) = self;
1648        slot.as_u32()
1649    }
1650}
1651
1652impl From<u32> for UnsignedExtendedUInt32StableV1 {
1653    fn from(value: u32) -> Self {
1654        Self(value.into())
1655    }
1656}
1657
1658impl From<u64> for UnsignedExtendedUInt64Int64ForVersionTagsStableV1 {
1659    fn from(value: u64) -> Self {
1660        Self(value.into())
1661    }
1662}
1663
1664impl From<&PicklesProofProofsVerifiedMaxStableV2> for PicklesProofProofsVerified2ReprStableV2 {
1665    fn from(value: &PicklesProofProofsVerifiedMaxStableV2) -> Self {
1666        let PicklesProofProofsVerifiedMaxStableV2 {
1667            statement,
1668            prev_evals,
1669            proof,
1670        } = value;
1671
1672        Self {
1673            statement: statement.clone(),
1674            prev_evals: prev_evals.clone(),
1675            proof: proof.clone(),
1676        }
1677    }
1678}
1679
1680impl std::fmt::Debug for UnsignedExtendedUInt32StableV1 {
1681    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1682        let Self(inner) = self;
1683        // Avoid vertical alignment
1684        f.write_fmt(format_args!("UnsignedExtendedUInt32StableV1({:?})", inner))
1685    }
1686}
1687
1688impl std::fmt::Debug for UnsignedExtendedUInt64Int64ForVersionTagsStableV1 {
1689    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1690        let Self(inner) = self;
1691        // Avoid vertical alignment
1692        f.write_fmt(format_args!(
1693            "UnsignedExtendedUInt64Int64ForVersionTagsStableV1({:?})",
1694            inner
1695        ))
1696    }
1697}
1698
1699impl MinaBaseProtocolConstantsCheckedValueStableV1 {
1700    const fn default_constants() -> Self {
1701        const fn from_u32(v: u32) -> UnsignedExtendedUInt32StableV1 {
1702            UnsignedExtendedUInt32StableV1(Number(v))
1703        }
1704
1705        Self {
1706            k: from_u32(290),
1707            slots_per_epoch: from_u32(7140),
1708            slots_per_sub_window: from_u32(7),
1709            grace_period_slots: from_u32(2160),
1710            delta: from_u32(0),
1711            genesis_state_timestamp: BlockTimeTimeStableV1(
1712                UnsignedExtendedUInt64Int64ForVersionTagsStableV1(Number(1600251300000)), // 2020-09-16 03:15:00-07:00
1713            ),
1714        }
1715    }
1716}
1717
1718impl Default for MinaBaseProtocolConstantsCheckedValueStableV1 {
1719    fn default() -> Self {
1720        Self::default_constants()
1721    }
1722}
1723
1724pub const PROTOCOL_CONSTANTS: MinaBaseProtocolConstantsCheckedValueStableV1 =
1725    MinaBaseProtocolConstantsCheckedValueStableV1::default_constants();
1726
1727impl From<OffsetDateTime> for BlockTimeTimeStableV1 {
1728    fn from(value: OffsetDateTime) -> Self {
1729        debug_assert!(value.unix_timestamp() >= 0);
1730        BlockTimeTimeStableV1((value.unix_timestamp() as u64 * 1000).into())
1731    }
1732}
1733
1734impl AsRef<MinaBaseUserCommandStableV2> for MinaBaseUserCommandStableV2 {
1735    fn as_ref(&self) -> &MinaBaseUserCommandStableV2 {
1736        self
1737    }
1738}
1739
1740impl MinaBlockHeaderStableV2 {
1741    pub fn genesis_state_hash(&self) -> &StateHash {
1742        &self.protocol_state.body.genesis_state_hash
1743    }
1744}
1745
1746impl StagedLedgerDiffBodyStableV1 {
1747    pub fn diff(&self) -> &StagedLedgerDiffDiffDiffStableV2 {
1748        &self.staged_ledger_diff.diff
1749    }
1750    pub fn commands_iter<'a>(
1751        &'a self,
1752    ) -> Box<dyn 'a + Iterator<Item = &'a StagedLedgerDiffDiffPreDiffWithAtMostTwoCoinbaseStableV2B>>
1753    {
1754        let diff = self.diff();
1755        let iter = diff.0.commands.iter();
1756        if let Some(_1) = diff.1.as_ref() {
1757            Box::new(iter.chain(_1.commands.iter()))
1758        } else {
1759            Box::new(iter)
1760        }
1761    }
1762
1763    pub fn transactions(&self) -> impl Iterator<Item = &MinaBaseUserCommandStableV2> {
1764        self.commands_iter().map(|command| &command.data)
1765    }
1766
1767    pub fn tranasctions_with_status(
1768        &self,
1769    ) -> impl Iterator<
1770        Item = (
1771            &MinaBaseUserCommandStableV2,
1772            &MinaBaseTransactionStatusStableV2,
1773        ),
1774    > {
1775        self.commands_iter()
1776            .map(|command| (&command.data, &command.status))
1777    }
1778
1779    pub fn coinbase_fee_transfers_iter(
1780        &self,
1781    ) -> impl Iterator<Item = &StagedLedgerDiffDiffFtStableV1> {
1782        let diff = self.diff();
1783        let mut coinbases = Vec::with_capacity(4);
1784        match &diff.0.coinbase {
1785            StagedLedgerDiffDiffPreDiffWithAtMostTwoCoinbaseStableV2Coinbase::Zero => {}
1786            StagedLedgerDiffDiffPreDiffWithAtMostTwoCoinbaseStableV2Coinbase::One(v) => {
1787                coinbases.push(v.as_ref());
1788            }
1789            StagedLedgerDiffDiffPreDiffWithAtMostTwoCoinbaseStableV2Coinbase::Two(v) => {
1790                match v.as_ref() {
1791                    None => {}
1792                    Some((v1, v2)) => {
1793                        coinbases.push(Some(v1));
1794                        coinbases.push(v2.as_ref());
1795                    }
1796                }
1797            }
1798        }
1799
1800        if let Some(StagedLedgerDiffDiffPreDiffWithAtMostOneCoinbaseStableV2Coinbase::One(v)) =
1801            diff.1.as_ref().map(|v| &v.coinbase)
1802        {
1803            coinbases.push(v.as_ref());
1804        }
1805
1806        coinbases.into_iter().flatten()
1807    }
1808
1809    pub fn completed_works_iter<'a>(
1810        &'a self,
1811    ) -> Box<dyn 'a + Iterator<Item = &'a TransactionSnarkWorkTStableV2>> {
1812        let diff = self.diff();
1813        let _0 = &diff.0;
1814        if let Some(_1) = diff.1.as_ref() {
1815            Box::new(_0.completed_works.iter().chain(_1.completed_works.iter()))
1816        } else {
1817            Box::new(_0.completed_works.iter())
1818        }
1819    }
1820
1821    pub fn completed_works_count(&self) -> usize {
1822        self.diff().0.completed_works.len()
1823            + self
1824                .diff()
1825                .1
1826                .as_ref()
1827                .map_or(0, |d| d.completed_works.len())
1828    }
1829
1830    pub fn has_coinbase(&self) -> bool {
1831        let (first_pre_diff, second_pre_diff) = (
1832            self.diff().0.coinbase.clone(),
1833            self.diff().1.as_ref().map_or(
1834                StagedLedgerDiffDiffPreDiffWithAtMostOneCoinbaseStableV2Coinbase::Zero,
1835                |v| v.coinbase.clone(),
1836            ),
1837        );
1838
1839        match (first_pre_diff, second_pre_diff) {
1840            (
1841                StagedLedgerDiffDiffPreDiffWithAtMostTwoCoinbaseStableV2Coinbase::Zero,
1842                StagedLedgerDiffDiffPreDiffWithAtMostOneCoinbaseStableV2Coinbase::Zero,
1843            ) => false,
1844            _ => true,
1845        }
1846    }
1847
1848    pub fn fees_sum(&self) -> u64 {
1849        self.commands_iter()
1850            .map(|v| match &v.data {
1851                MinaBaseUserCommandStableV2::SignedCommand(v) => v.payload.common.fee.as_u64(),
1852                MinaBaseUserCommandStableV2::ZkappCommand(v) => v.fee_payer.body.fee.as_u64(),
1853            })
1854            .sum()
1855    }
1856
1857    pub fn snark_fees_sum(&self) -> u64 {
1858        self.completed_works_iter().map(|v| v.fee.as_u64()).sum()
1859    }
1860}
1861
1862// PicklesProofProofsVerifiedMaxStableV2 PicklesProofProofsVerified2ReprStableV2
1863
1864impl From<PicklesProofProofsVerifiedMaxStableV2> for PicklesProofProofsVerified2ReprStableV2 {
1865    fn from(value: PicklesProofProofsVerifiedMaxStableV2) -> Self {
1866        Self {
1867            statement: value.statement,
1868            prev_evals: value.prev_evals,
1869            proof: value.proof,
1870        }
1871    }
1872}
1873
1874impl From<PicklesProofProofsVerified2ReprStableV2> for PicklesProofProofsVerifiedMaxStableV2 {
1875    fn from(value: PicklesProofProofsVerified2ReprStableV2) -> Self {
1876        Self {
1877            statement: value.statement,
1878            prev_evals: value.prev_evals,
1879            proof: value.proof,
1880        }
1881    }
1882}
1883
1884impl std::fmt::Display for SgnStableV1 {
1885    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1886        match self {
1887            SgnStableV1::Pos => write!(f, "Positive"),
1888            SgnStableV1::Neg => write!(f, "Negative"),
1889        }
1890    }
1891}
1892
1893impl std::str::FromStr for SgnStableV1 {
1894    type Err = String;
1895
1896    fn from_str(s: &str) -> Result<Self, Self::Err> {
1897        match s {
1898            "Positive" => Ok(SgnStableV1::Pos),
1899            "Negative" => Ok(SgnStableV1::Neg),
1900            _ => Err("Invalid Sgn string, expected Positive or Negative".to_string()),
1901        }
1902    }
1903}
1904
1905#[cfg(test)]
1906mod test {
1907    use binprot::BinProtRead;
1908
1909    use crate::v2;
1910
1911    #[test]
1912    fn test_zkapp_with_sig_auth_hash() {
1913        let expexcted = "AbliNXLg4Keq0ZJyxK/QNAx8SxrJeffYytk5lbcTF9s9Af0A4fUFAP2+oQMA48vntxcABLty3SXWjvuadrLtBjcsxT1oJ3C2hwS/LDh364LKUxrLe3uF/9lr8VlW/J+ctbiI+m9I61sb9BC/AAG5YjVy4OCnqtGScsSv0DQMfEsayXn32MrZOZW3ExfbPQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAEBAQEBAQH9AJQ1dwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAAAAAePL57cXAAS7ct0l1o77mnay7QY3LMU9aCdwtocEvyw4d+uCylMay3t7hf/Za/FZVvyfnLW4iPpvSOtbG/QQvwAAAcwXZjv4NJwWwlJhFZPh2AK+o0dKOpIy1a6CXlskW7gmAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEAAQEBAQEBAf0AlDV3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQAAAAICAAAAACIBFlRlc3QgWktBcHAgdG8gUmVjZWl2ZXIAAAAAAAAAAAAA".to_string();
1914        let bytes = include_bytes!("../../../../tests/files/zkapps/with_sig_auth.bin");
1915        let zkapp =
1916            v2::MinaBaseZkappCommandTStableV1WireStableV1::binprot_read(&mut bytes.as_slice())
1917                .unwrap();
1918
1919        let zkapp_id = zkapp.to_base64().unwrap();
1920        assert_eq!(expexcted, zkapp_id);
1921    }
1922
1923    #[test]
1924    fn test_verification_key_base64_decode() {
1925        use base64::{engine::general_purpose::STANDARD, Engine as _};
1926        // 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==";
1927        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=";
1928
1929        let decoded = STANDARD.decode(verification_key_encoded).unwrap();
1930        let verification_key =
1931            v2::MinaBaseVerificationKeyWireStableV1::binprot_read(&mut decoded.as_slice());
1932        assert!(verification_key.is_ok());
1933    }
1934
1935    #[test]
1936    fn test_archive_breadcrumb_deserialization() {
1937        let breadcrumb_bytes = include_bytes!("../../../../tests/files/archive-breadcrumb/3NK56ZbCS31qb8SvCtCCYza4beRDtKgXA2JL6s3evKouG2KkKtiy.bin");
1938        let result =
1939            v2::ArchiveTransitionFrontierDiff::binprot_read(&mut breadcrumb_bytes.as_slice());
1940
1941        assert!(result.is_ok());
1942    }
1943}