mina_p2p_messages/v2/
hashing.rs

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