mina_p2p_messages/v2/
hashing.rs

1use std::{fmt, io, sync::Arc};
2
3use ark_ff::fields::arithmetic::InvalidBigInt;
4use binprot::{BinProtRead, BinProtWrite};
5use generated::MinaStateBlockchainStateValueStableV2;
6use mina_curves::pasta::Fp;
7use poseidon::hash::{
8    hash_with_kimchi,
9    params::{MINA_PROTO_STATE, MINA_PROTO_STATE_BODY},
10    Inputs,
11};
12use serde::{Deserialize, Serialize};
13use sha2::{
14    digest::{generic_array::GenericArray, typenum::U32},
15    Digest, Sha256,
16};
17
18use crate::{bigint::BigInt, hash::MinaHash, hash_input::FailableToInputs};
19
20use super::{
21    generated, ConsensusBodyReferenceStableV1, ConsensusGlobalSlotStableV1,
22    ConsensusProofOfStakeDataConsensusStateValueStableV2,
23    ConsensusProofOfStakeDataEpochDataNextValueVersionedValueStableV1,
24    ConsensusProofOfStakeDataEpochDataStakingValueVersionedValueStableV1,
25    ConsensusVrfOutputTruncatedStableV1, DataHashLibStateHashStableV1, LedgerHash,
26    MinaBaseControlStableV2, MinaBaseEpochLedgerValueStableV1, MinaBaseFeeExcessStableV1,
27    MinaBaseLedgerHash0StableV1, MinaBasePendingCoinbaseHashBuilderStableV1,
28    MinaBasePendingCoinbaseHashVersionedStableV1, MinaBasePendingCoinbaseStackVersionedStableV1,
29    MinaBasePendingCoinbaseStateStackStableV1, MinaBaseProtocolConstantsCheckedValueStableV1,
30    MinaBaseStagedLedgerHashNonSnarkStableV1, MinaBaseStagedLedgerHashStableV1,
31    MinaBaseStateBodyHashStableV1, MinaBaseZkappCommandTStableV1WireStableV1AccountUpdatesAA,
32    MinaNumbersGlobalSlotSinceGenesisMStableV1, MinaNumbersGlobalSlotSinceHardForkMStableV1,
33    MinaNumbersGlobalSlotSpanStableV1, MinaStateBlockchainStateValueStableV2LedgerProofStatement,
34    MinaStateBlockchainStateValueStableV2LedgerProofStatementSource,
35    MinaStateBlockchainStateValueStableV2SignedAmount, MinaStateProtocolStateBodyValueStableV2,
36    MinaStateProtocolStateValueStableV2,
37    MinaTransactionLogicZkappCommandLogicLocalStateValueStableV1,
38    NonZeroCurvePointUncompressedStableV1, PendingCoinbaseHash, SgnStableV1, SignedAmount,
39    StateHash, TokenFeeExcess,
40};
41
42impl generated::MinaBaseStagedLedgerHashNonSnarkStableV1 {
43    pub fn sha256(&self) -> GenericArray<u8, U32> {
44        let mut ledger_hash_bytes: [u8; 32] = [0; 32];
45
46        ledger_hash_bytes.copy_from_slice(&self.ledger_hash.to_bytes()[..]);
47        ledger_hash_bytes.reverse();
48
49        let mut hasher = Sha256::new();
50        hasher.update(ledger_hash_bytes);
51        hasher.update(self.aux_hash.as_ref());
52        hasher.update(self.pending_coinbase_aux.as_ref());
53
54        hasher.finalize()
55    }
56}
57
58impl generated::ConsensusVrfOutputTruncatedStableV1 {
59    pub fn blake2b(&self) -> Vec<u8> {
60        use blake2::{
61            digest::{Update, VariableOutput},
62            Blake2bVar,
63        };
64        let mut hasher = Blake2bVar::new(32).expect("Invalid Blake2bVar output size");
65        hasher.update(&self.0);
66        hasher.finalize_boxed().to_vec()
67    }
68}
69
70#[derive(Hash, Eq, PartialEq, Ord, PartialOrd, Clone)]
71pub struct TransactionHash(Arc<[u8; 32]>);
72
73impl std::str::FromStr for TransactionHash {
74    type Err = bs58::decode::Error;
75
76    fn from_str(s: &str) -> Result<Self, Self::Err> {
77        let bytes = bs58::decode(s).with_check(Some(0x1D)).into_vec()?;
78        dbg!(bytes.len());
79        let bytes = (&bytes[2..])
80            .try_into()
81            .map_err(|_| bs58::decode::Error::BufferTooSmall)?;
82        Ok(Self(Arc::new(bytes)))
83    }
84}
85
86impl fmt::Display for TransactionHash {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        let mut bytes = [32; 33];
89        bytes[1..].copy_from_slice(&*self.0);
90        bs58::encode(bytes)
91            .with_check_version(0x1D)
92            .into_string()
93            .fmt(f)
94    }
95}
96
97impl fmt::Debug for TransactionHash {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        write!(f, "{:?}", self.0)
100        // write!(f, "TransactionHash({})", self)
101    }
102}
103
104impl From<&[u8; 32]> for TransactionHash {
105    fn from(value: &[u8; 32]) -> Self {
106        Self(Arc::new(*value))
107    }
108}
109
110impl Serialize for TransactionHash {
111    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112    where
113        S: serde::Serializer,
114    {
115        if serializer.is_human_readable() {
116            serializer.serialize_str(&self.to_string())
117        } else {
118            serde_bytes::serialize(&*self.0, serializer)
119        }
120    }
121}
122
123impl<'de> serde::Deserialize<'de> for TransactionHash {
124    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
125    where
126        D: serde::Deserializer<'de>,
127    {
128        if deserializer.is_human_readable() {
129            let b58: String = Deserialize::deserialize(deserializer)?;
130            Ok(b58.parse().map_err(|err| serde::de::Error::custom(err))?)
131        } else {
132            serde_bytes::deserialize(deserializer)
133                .map(Arc::new)
134                .map(Self)
135        }
136    }
137}
138
139impl BinProtWrite for TransactionHash {
140    fn binprot_write<W: io::Write>(&self, w: &mut W) -> std::io::Result<()> {
141        w.write_all(&*self.0)
142    }
143}
144
145impl BinProtRead for TransactionHash {
146    fn binprot_read<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, binprot::Error>
147    where
148        Self: Sized,
149    {
150        let mut bytes = [0; 32];
151        r.read_exact(&mut bytes)?;
152        Ok(Self(bytes.into()))
153    }
154}
155
156impl generated::MinaTransactionTransactionStableV2 {
157    pub fn hash(&self) -> io::Result<TransactionHash> {
158        match self {
159            Self::Command(v) => v.hash(),
160            Self::FeeTransfer(_) => Err(io::Error::new(
161                io::ErrorKind::Unsupported,
162                "fee transfer tx hashing is not yet supported",
163            )),
164            Self::Coinbase(_) => Err(io::Error::new(
165                io::ErrorKind::Unsupported,
166                "coinbase tx hashing is not yet supported",
167            )),
168        }
169    }
170}
171
172impl generated::MinaBaseUserCommandStableV2 {
173    pub fn hash(&self) -> io::Result<TransactionHash> {
174        match self {
175            Self::SignedCommand(v) => v.hash(),
176            Self::ZkappCommand(v) => v.hash(),
177        }
178    }
179}
180
181impl generated::MinaBaseSignedCommandStableV2 {
182    pub fn binprot_write_with_default_sig(&self) -> io::Result<Vec<u8>> {
183        let default_signature = generated::MinaBaseSignatureStableV1(BigInt::one(), BigInt::one());
184
185        let mut encoded = vec![];
186        self.payload.binprot_write(&mut encoded)?;
187        self.signer.binprot_write(&mut encoded)?;
188        default_signature.binprot_write(&mut encoded)?;
189        Ok(encoded)
190    }
191
192    pub fn hash(&self) -> io::Result<TransactionHash> {
193        use blake2::{
194            digest::{Update, VariableOutput},
195            Blake2bVar,
196        };
197        let mut hasher = Blake2bVar::new(32).expect("Invalid Blake2bVar output size");
198
199        hasher.update(&self.binprot_write_with_default_sig()?);
200        let mut hash = [0; 32];
201        hasher
202            .finalize_variable(&mut hash)
203            .expect("Invalid buffer size"); // Never occur
204
205        Ok(TransactionHash(hash.into()))
206    }
207}
208
209// TODO(adonagy): reduce duplication
210impl generated::MinaBaseZkappCommandTStableV1WireStableV1 {
211    fn binprot_write_with_default(&self) -> io::Result<Vec<u8>> {
212        let default_signature = generated::MinaBaseSignatureStableV1(BigInt::one(), BigInt::one());
213
214        let mut encoded = vec![];
215
216        let mut modified = self.clone();
217
218        modified.fee_payer.authorization = default_signature.clone().into();
219
220        modified.account_updates.iter_mut().for_each(|u| {
221            Self::replace_auth_recursive(&mut u.elt);
222        });
223
224        modified.binprot_write(&mut encoded)?;
225        Ok(encoded)
226    }
227
228    fn replace_auth_recursive(
229        update_elt: &mut MinaBaseZkappCommandTStableV1WireStableV1AccountUpdatesAA,
230    ) {
231        Self::replace_auth(&mut update_elt.account_update.authorization);
232        // if update_elt.calls.is_empty() {
233        //     return;
234        // }
235
236        update_elt.calls.iter_mut().for_each(|call| {
237            Self::replace_auth_recursive(&mut call.elt);
238        });
239
240        // for call in update_elt.calls.iter_mut() {
241        //     Self::replace_auth_recursive(&mut call.elt);
242        // }
243    }
244
245    pub fn replace_auth(auth: &mut MinaBaseControlStableV2) {
246        let default_signature = generated::MinaBaseSignatureStableV1(BigInt::one(), BigInt::one());
247        let default_proof = super::dummy_transaction_proof();
248        *auth = match auth {
249            MinaBaseControlStableV2::Proof(_) => {
250                MinaBaseControlStableV2::Proof(Box::new(default_proof.0.clone().into()))
251            }
252            MinaBaseControlStableV2::Signature(_) => {
253                MinaBaseControlStableV2::Signature(default_signature.clone().into())
254            }
255            MinaBaseControlStableV2::NoneGiven => MinaBaseControlStableV2::NoneGiven,
256        };
257    }
258
259    pub fn hash(&self) -> io::Result<TransactionHash> {
260        use blake2::{
261            digest::{Update, VariableOutput},
262            Blake2bVar,
263        };
264        let mut hasher = Blake2bVar::new(32).expect("Invalid Blake2bVar output size");
265
266        hasher.update(&self.binprot_write_with_default()?);
267        let mut hash = [0; 32];
268        hasher
269            .finalize_variable(&mut hash)
270            .expect("Invalid buffer size"); // Never occur
271
272        Ok(TransactionHash(hash.into()))
273    }
274}
275
276#[cfg(test)]
277mod tests {
278    use super::{super::manual, *};
279    use manual::MinaBaseSignedCommandMemoStableV1;
280
281    fn pub_key(address: &str) -> manual::NonZeroCurvePoint {
282        let key = mina_signer::PubKey::from_address(address)
283            .unwrap()
284            .into_compressed();
285        let v = generated::NonZeroCurvePointUncompressedStableV1 {
286            x: crate::bigint::BigInt::from(key.x),
287            is_odd: key.is_odd,
288        };
289        v.into()
290    }
291
292    fn tx_hash(
293        from: &str,
294        to: &str,
295        amount: u64,
296        fee: u64,
297        nonce: u32,
298        valid_until: u32,
299    ) -> String {
300        use crate::{number::Number, string::CharString};
301
302        let from = pub_key(from);
303        let to = pub_key(to);
304
305        let v = Number(fee);
306        let v = generated::UnsignedExtendedUInt64Int64ForVersionTagsStableV1(v);
307        let fee = generated::CurrencyFeeStableV1(v);
308
309        let nonce = generated::UnsignedExtendedUInt32StableV1(Number(nonce));
310
311        let valid_until = generated::UnsignedExtendedUInt32StableV1(Number(valid_until));
312
313        let memo = bs58::decode("E4Yks7aARFemZJqucP5eaARRYRthGdzaFjGfXqQRS3UeidsECRBvR")
314            .with_check(Some(0x14))
315            .into_vec()
316            .unwrap()[1..]
317            .to_vec();
318        let v = CharString::from(&memo[..]);
319        let memo = MinaBaseSignedCommandMemoStableV1(v);
320
321        let common = generated::MinaBaseSignedCommandPayloadCommonStableV2 {
322            fee,
323            fee_payer_pk: from.clone(),
324            nonce,
325            valid_until: MinaNumbersGlobalSlotSinceGenesisMStableV1::SinceGenesis(valid_until),
326            memo,
327        };
328
329        let v = Number(amount);
330        let v = generated::UnsignedExtendedUInt64Int64ForVersionTagsStableV1(v);
331        let amount = generated::CurrencyAmountStableV1(v);
332
333        let v = generated::MinaBasePaymentPayloadStableV2 {
334            receiver_pk: to.clone(),
335            amount,
336        };
337        let body = generated::MinaBaseSignedCommandPayloadBodyStableV2::Payment(v);
338
339        let payload = generated::MinaBaseSignedCommandPayloadStableV2 { common, body };
340
341        // Some random signature. hasher should ignore it and use default.
342        let signature = generated::MinaBaseSignatureStableV1(
343            BigInt::binprot_read(&mut &[122; 32][..]).unwrap(),
344            BigInt::binprot_read(&mut &[123; 32][..]).unwrap(),
345        );
346
347        let v = generated::MinaBaseSignedCommandStableV2 {
348            payload,
349            signer: from.clone(),
350            signature: signature.into(),
351        };
352        let v = generated::MinaBaseUserCommandStableV2::SignedCommand(v);
353        dbg!(v.hash().unwrap()).to_string()
354    }
355
356    // #[test]
357    // fn test_tx_hash() {
358    //     let s = "5JuSRViCY1GbnnpExLhoYLkD96vwnA97ZrbE2UFTFzBk9SPLsAyE";
359    //     let hash: TransactionHash = s.parse().unwrap();
360    //     // let bytes = bs58::decode(s).with_check(Some(0x1D)).into_vec().unwrap()[1..].to_vec();
361    //     dbg!(bs58::encode(&hash.0)
362    //         .with_check_version(0x12)
363    //         .into_string());
364    //     panic!();
365    // }
366
367    #[test]
368    #[ignore = "fix expected hash/hasing"]
369    fn test_payment_hash_1() {
370        let expected_hash = "5JthQdVqzEJRLBLALeuwPdbnGhFmCow2bVnkfHGH6vZ7R6fiMf2o";
371        let expected_tx_hash: TransactionHash = expected_hash.parse().unwrap();
372        dbg!(expected_tx_hash);
373
374        assert_eq!(
375            tx_hash(
376                "B62qp3B9VW1ir5qL1MWRwr6ecjC2NZbGr8vysGeme9vXGcFXTMNXb2t",
377                "B62qoieQNrsNKCNTZ6R4D6cib3NxVbkwZaAtRVbfS3ndrb2MkFJ1UVJ",
378                1089541195,
379                89541195,
380                26100,
381                u32::MAX,
382            ),
383            expected_hash
384        )
385    }
386}
387
388fn fp_state_hash_from_fp_hashes(previous_state_hash: Fp, body_hash: Fp) -> Fp {
389    let mut inputs = Inputs::new();
390    inputs.append_field(previous_state_hash);
391    inputs.append_field(body_hash);
392    hash_with_kimchi(&MINA_PROTO_STATE, &inputs.to_fields())
393}
394
395impl StateHash {
396    pub fn from_fp(fp: Fp) -> Self {
397        DataHashLibStateHashStableV1(fp.into()).into()
398    }
399
400    pub fn try_from_hashes(
401        pred_state_hash: &StateHash,
402        body_hash: &MinaBaseStateBodyHashStableV1,
403    ) -> Result<Self, InvalidBigInt> {
404        Ok(Self::from_fp(fp_state_hash_from_fp_hashes(
405            pred_state_hash.to_field()?,
406            body_hash.to_field()?,
407        )))
408    }
409}
410
411impl LedgerHash {
412    pub fn from_fp(fp: Fp) -> Self {
413        MinaBaseLedgerHash0StableV1(fp.into()).into()
414    }
415
416    pub fn zero() -> Self {
417        MinaBaseLedgerHash0StableV1(BigInt::zero()).into()
418    }
419}
420
421impl PendingCoinbaseHash {
422    pub fn from_fp(fp: Fp) -> Self {
423        MinaBasePendingCoinbaseHashVersionedStableV1(MinaBasePendingCoinbaseHashBuilderStableV1(
424            fp.into(),
425        ))
426        .into()
427    }
428}
429
430impl generated::MinaStateProtocolStateBodyValueStableV2 {
431    // TODO(binier): change return type to `StateBodyHash`
432    pub fn try_hash(&self) -> Result<MinaBaseStateBodyHashStableV1, InvalidBigInt> {
433        let fp = MinaHash::try_hash(self)?;
434        Ok(MinaBaseStateBodyHashStableV1(fp.into()))
435    }
436}
437
438impl generated::MinaStateProtocolStateValueStableV2 {
439    pub fn try_hash(&self) -> Result<StateHash, InvalidBigInt> {
440        Ok(StateHash::from_fp(MinaHash::try_hash(self)?))
441    }
442}
443
444impl generated::MinaBlockHeaderStableV2 {
445    pub fn try_hash(&self) -> Result<StateHash, InvalidBigInt> {
446        self.protocol_state.try_hash()
447    }
448}
449
450impl generated::MinaBlockBlockStableV2 {
451    pub fn try_hash(&self) -> Result<StateHash, InvalidBigInt> {
452        self.header.protocol_state.try_hash()
453    }
454}
455
456impl MinaHash for MinaStateProtocolStateBodyValueStableV2 {
457    fn try_hash(&self) -> Result<mina_curves::pasta::Fp, InvalidBigInt> {
458        let mut inputs = Inputs::new();
459        self.to_input(&mut inputs)?;
460        Ok(hash_with_kimchi(
461            &MINA_PROTO_STATE_BODY,
462            &inputs.to_fields(),
463        ))
464    }
465}
466
467impl MinaHash for MinaStateProtocolStateValueStableV2 {
468    fn try_hash(&self) -> Result<mina_curves::pasta::Fp, InvalidBigInt> {
469        Ok(fp_state_hash_from_fp_hashes(
470            self.previous_state_hash.to_field()?,
471            MinaHash::try_hash(&self.body)?,
472        ))
473    }
474}
475
476impl FailableToInputs for MinaStateProtocolStateBodyValueStableV2 {
477    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
478        let MinaStateProtocolStateBodyValueStableV2 {
479            genesis_state_hash,
480            blockchain_state,
481            consensus_state,
482            constants,
483        } = self;
484
485        constants.to_input(inputs)?;
486        genesis_state_hash.to_input(inputs)?;
487        blockchain_state.to_input(inputs)?;
488        consensus_state.to_input(inputs)?;
489        Ok(())
490    }
491}
492
493impl FailableToInputs for MinaBaseProtocolConstantsCheckedValueStableV1 {
494    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
495        let MinaBaseProtocolConstantsCheckedValueStableV1 {
496            k,
497            slots_per_epoch,
498            slots_per_sub_window,
499            grace_period_slots,
500            delta,
501            genesis_state_timestamp,
502        } = self;
503
504        k.to_input(inputs)?;
505        delta.to_input(inputs)?;
506        slots_per_epoch.to_input(inputs)?;
507        slots_per_sub_window.to_input(inputs)?;
508        grace_period_slots.to_input(inputs)?;
509        genesis_state_timestamp.to_input(inputs)?;
510        Ok(())
511    }
512}
513
514impl FailableToInputs for MinaStateBlockchainStateValueStableV2 {
515    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
516        let MinaStateBlockchainStateValueStableV2 {
517            staged_ledger_hash,
518            genesis_ledger_hash,
519            ledger_proof_statement,
520            timestamp,
521            body_reference,
522        } = self;
523
524        staged_ledger_hash.to_input(inputs)?;
525        genesis_ledger_hash.to_input(inputs)?;
526        ledger_proof_statement.to_input(inputs)?;
527        timestamp.to_input(inputs)?;
528        body_reference.to_input(inputs)?;
529
530        Ok(())
531    }
532}
533
534impl FailableToInputs for ConsensusProofOfStakeDataConsensusStateValueStableV2 {
535    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
536        let ConsensusProofOfStakeDataConsensusStateValueStableV2 {
537            blockchain_length,
538            epoch_count,
539            min_window_density,
540            sub_window_densities,
541            last_vrf_output,
542            total_currency,
543            curr_global_slot_since_hard_fork,
544            global_slot_since_genesis,
545            staking_epoch_data,
546            next_epoch_data,
547            has_ancestor_in_same_checkpoint_window,
548            block_stake_winner,
549            block_creator,
550            coinbase_receiver,
551            supercharge_coinbase,
552        } = self;
553        blockchain_length.to_input(inputs)?;
554        epoch_count.to_input(inputs)?;
555        min_window_density.to_input(inputs)?;
556        sub_window_densities.to_input(inputs)?;
557        last_vrf_output.to_input(inputs)?;
558        total_currency.to_input(inputs)?;
559        curr_global_slot_since_hard_fork.to_input(inputs)?;
560        global_slot_since_genesis.to_input(inputs)?;
561        has_ancestor_in_same_checkpoint_window.to_input(inputs)?;
562        supercharge_coinbase.to_input(inputs)?;
563        staking_epoch_data.to_input(inputs)?;
564        next_epoch_data.to_input(inputs)?;
565        block_stake_winner.to_input(inputs)?;
566        block_creator.to_input(inputs)?;
567        coinbase_receiver.to_input(inputs)?;
568        Ok(())
569    }
570}
571
572impl FailableToInputs for MinaBaseStagedLedgerHashStableV1 {
573    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
574        let MinaBaseStagedLedgerHashStableV1 {
575            non_snark,
576            pending_coinbase_hash,
577        } = self;
578        non_snark.to_input(inputs)?;
579        pending_coinbase_hash.to_input(inputs)?;
580        Ok(())
581    }
582}
583
584impl FailableToInputs for MinaBaseStagedLedgerHashNonSnarkStableV1 {
585    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
586        inputs.append_bytes(self.sha256().as_ref());
587        Ok(())
588    }
589}
590
591impl FailableToInputs for MinaStateBlockchainStateValueStableV2LedgerProofStatement {
592    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
593        let MinaStateBlockchainStateValueStableV2LedgerProofStatement {
594            source,
595            target,
596            connecting_ledger_left,
597            connecting_ledger_right,
598            supply_increase,
599            fee_excess,
600            sok_digest: _,
601        } = self;
602        source.to_input(inputs)?;
603        target.to_input(inputs)?;
604        connecting_ledger_left.to_input(inputs)?;
605        connecting_ledger_right.to_input(inputs)?;
606        supply_increase.to_input(inputs)?;
607        fee_excess.to_input(inputs)?;
608        Ok(())
609    }
610}
611
612impl FailableToInputs for ConsensusBodyReferenceStableV1 {
613    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
614        inputs.append_bytes(self.as_ref());
615        Ok(())
616    }
617}
618
619impl FailableToInputs for ConsensusVrfOutputTruncatedStableV1 {
620    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
621        let vrf: &[u8] = self.as_ref();
622        inputs.append_bytes(&vrf[..31]);
623        // Ignore the last 3 bits
624        let last_byte = vrf[31];
625        for bit in [1, 2, 4, 8, 16] {
626            inputs.append_bool(last_byte & bit != 0);
627        }
628        Ok(())
629    }
630}
631
632impl FailableToInputs for ConsensusGlobalSlotStableV1 {
633    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
634        let ConsensusGlobalSlotStableV1 {
635            slot_number,
636            slots_per_epoch,
637        } = self;
638        slot_number.to_input(inputs)?;
639        slots_per_epoch.to_input(inputs)?;
640        Ok(())
641    }
642}
643
644impl FailableToInputs for ConsensusProofOfStakeDataEpochDataStakingValueVersionedValueStableV1 {
645    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
646        let ConsensusProofOfStakeDataEpochDataStakingValueVersionedValueStableV1 {
647            ledger,
648            seed,
649            start_checkpoint,
650            lock_checkpoint,
651            epoch_length,
652        } = self;
653        seed.to_input(inputs)?;
654        start_checkpoint.to_input(inputs)?;
655        epoch_length.to_input(inputs)?;
656        ledger.to_input(inputs)?;
657        lock_checkpoint.to_input(inputs)?;
658        Ok(())
659    }
660}
661
662impl FailableToInputs for ConsensusProofOfStakeDataEpochDataNextValueVersionedValueStableV1 {
663    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
664        let ConsensusProofOfStakeDataEpochDataNextValueVersionedValueStableV1 {
665            ledger,
666            seed,
667            start_checkpoint,
668            lock_checkpoint,
669            epoch_length,
670        } = self;
671        seed.to_input(inputs)?;
672        start_checkpoint.to_input(inputs)?;
673        epoch_length.to_input(inputs)?;
674        ledger.to_input(inputs)?;
675        lock_checkpoint.to_input(inputs)?;
676        Ok(())
677    }
678}
679
680impl FailableToInputs for NonZeroCurvePointUncompressedStableV1 {
681    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
682        let NonZeroCurvePointUncompressedStableV1 { x, is_odd } = self;
683        x.to_input(inputs)?;
684        is_odd.to_input(inputs)?;
685        Ok(())
686    }
687}
688
689impl FailableToInputs for MinaStateBlockchainStateValueStableV2LedgerProofStatementSource {
690    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
691        let MinaStateBlockchainStateValueStableV2LedgerProofStatementSource {
692            first_pass_ledger,
693            second_pass_ledger,
694            pending_coinbase_stack,
695            local_state,
696        } = self;
697        first_pass_ledger.to_input(inputs)?;
698        second_pass_ledger.to_input(inputs)?;
699        pending_coinbase_stack.to_input(inputs)?;
700        local_state.to_input(inputs)?;
701        Ok(())
702    }
703}
704
705impl FailableToInputs for SignedAmount {
706    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
707        let SignedAmount { magnitude, sgn } = self;
708        magnitude.to_input(inputs)?;
709        sgn.to_input(inputs)?;
710        Ok(())
711    }
712}
713
714impl FailableToInputs for MinaBaseFeeExcessStableV1 {
715    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
716        let MinaBaseFeeExcessStableV1(left, right) = self;
717        left.to_input(inputs)?;
718        right.to_input(inputs)?;
719        Ok(())
720    }
721}
722
723impl FailableToInputs for TokenFeeExcess {
724    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
725        let TokenFeeExcess { token, amount } = self;
726        token.to_input(inputs)?;
727        amount.to_input(inputs)?;
728        Ok(())
729    }
730}
731
732impl FailableToInputs for MinaBaseEpochLedgerValueStableV1 {
733    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
734        let MinaBaseEpochLedgerValueStableV1 {
735            hash,
736            total_currency,
737        } = self;
738        hash.to_input(inputs)?;
739        total_currency.to_input(inputs)?;
740        Ok(())
741    }
742}
743
744impl FailableToInputs for MinaBasePendingCoinbaseStackVersionedStableV1 {
745    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
746        let MinaBasePendingCoinbaseStackVersionedStableV1 { data, state } = self;
747        data.to_input(inputs)?;
748        state.to_input(inputs)?;
749        Ok(())
750    }
751}
752
753impl FailableToInputs for MinaBasePendingCoinbaseStateStackStableV1 {
754    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
755        let MinaBasePendingCoinbaseStateStackStableV1 { init, curr } = self;
756        init.to_input(inputs)?;
757        curr.to_input(inputs)?;
758        Ok(())
759    }
760}
761
762impl FailableToInputs for MinaTransactionLogicZkappCommandLogicLocalStateValueStableV1 {
763    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
764        let MinaTransactionLogicZkappCommandLogicLocalStateValueStableV1 {
765            stack_frame,
766            call_stack,
767            transaction_commitment,
768            full_transaction_commitment,
769            excess,
770            supply_increase,
771            ledger,
772            success,
773            account_update_index,
774            failure_status_tbl: _,
775            will_succeed,
776        } = self;
777        stack_frame.to_input(inputs)?;
778        call_stack.to_input(inputs)?;
779        transaction_commitment.to_input(inputs)?;
780        full_transaction_commitment.to_input(inputs)?;
781        excess.to_input(inputs)?;
782        supply_increase.to_input(inputs)?;
783        ledger.to_input(inputs)?;
784        account_update_index.to_input(inputs)?;
785        success.to_input(inputs)?;
786        will_succeed.to_input(inputs)?;
787        Ok(())
788    }
789}
790
791impl FailableToInputs for SgnStableV1 {
792    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
793        inputs.append_bool(self == &SgnStableV1::Pos);
794        Ok(())
795    }
796}
797
798impl FailableToInputs for MinaNumbersGlobalSlotSinceGenesisMStableV1 {
799    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
800        match self {
801            MinaNumbersGlobalSlotSinceGenesisMStableV1::SinceGenesis(v) => v.to_input(inputs),
802        }
803    }
804}
805
806impl FailableToInputs for MinaStateBlockchainStateValueStableV2SignedAmount {
807    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
808        let MinaStateBlockchainStateValueStableV2SignedAmount { magnitude, sgn } = self;
809        magnitude.to_input(inputs)?;
810        sgn.to_input(inputs)?;
811        Ok(())
812    }
813}
814
815impl FailableToInputs for MinaNumbersGlobalSlotSinceHardForkMStableV1 {
816    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
817        match self {
818            MinaNumbersGlobalSlotSinceHardForkMStableV1::SinceHardFork(v) => v.to_input(inputs),
819        }
820    }
821}
822
823impl FailableToInputs for MinaNumbersGlobalSlotSpanStableV1 {
824    fn to_input(&self, inputs: &mut Inputs) -> Result<(), InvalidBigInt> {
825        match self {
826            MinaNumbersGlobalSlotSpanStableV1::GlobalSlotSpan(v) => v.to_input(inputs),
827        }
828    }
829}
830
831#[cfg(test)]
832mod hash_tests {
833    use binprot::BinProtRead;
834
835    use crate::v2::{
836        MinaBaseZkappCommandTStableV1WireStableV1, MinaStateProtocolStateValueStableV2,
837    };
838
839    #[test]
840    #[ignore = "fix expected hash/hasing"]
841    fn state_hash() {
842        const HASH: &str = "3NKpXp2SXWGC3XHnAJYjGtNcbq8tzossqj6kK4eGr6mSyJoFmpxR";
843        const JSON: &str = include_str!("../../tests/files/v2/state/617-3NKpXp2SXWGC3XHnAJYjGtNcbq8tzossqj6kK4eGr6mSyJoFmpxR.json");
844
845        let state: MinaStateProtocolStateValueStableV2 = serde_json::from_str(JSON).unwrap();
846        let hash = state.try_hash().unwrap();
847        let expected_hash = serde_json::from_value(serde_json::json!(HASH)).unwrap();
848        assert_eq!(hash, expected_hash)
849    }
850
851    #[test]
852    fn test_zkapp_with_proof_auth_hash() {
853        // expected: 5JtkEP5AugQKKQAk3YKFxxUDggWf8AiAYyCQy49t2kLHRgPqcP8o
854        // MinaBaseZkappCommandTStableV1WireStableV1
855        //
856        let expected_hash = "5JtkEP5AugQKKQAk3YKFxxUDggWf8AiAYyCQy49t2kLHRgPqcP8o".to_string();
857        let bytes = include_bytes!("../../../tests/files/zkapps/with_proof_auth.bin");
858        let zkapp =
859            MinaBaseZkappCommandTStableV1WireStableV1::binprot_read(&mut bytes.as_slice()).unwrap();
860        let hash = zkapp.hash().unwrap().to_string();
861
862        assert_eq!(expected_hash, hash);
863    }
864
865    #[test]
866
867    fn test_zkapp_with_sig_auth_hash() {
868        let expected_hash = "5JvQ6xQeGgCTe2d4KpCsJ97yK61mNRZHixJxPbKTppY1qSGgtj6t".to_string();
869        let bytes = include_bytes!("../../../tests/files/zkapps/with_sig_auth.bin");
870        let zkapp =
871            MinaBaseZkappCommandTStableV1WireStableV1::binprot_read(&mut bytes.as_slice()).unwrap();
872        let hash = zkapp.hash().unwrap().to_string();
873
874        assert_eq!(expected_hash, hash);
875    }
876}