mina_tree/scan_state/transaction_logic/zkapp_command/
verifiable.rs1use mina_curves::pasta::Fp;
2use mina_p2p_messages::v2::MinaBaseZkappCommandVerifiableStableV1;
3use std::collections::HashMap;
4
5use super::{
6 AccountId, AccountUpdate, AuthorizationKind, CallForest, Control, FeePayer, Memo, SetOrKeep,
7 VerificationKeyWire,
8};
9use crate::sparse_ledger::LedgerIntf;
10
11#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
12#[serde(try_from = "MinaBaseZkappCommandVerifiableStableV1")]
13#[serde(into = "MinaBaseZkappCommandVerifiableStableV1")]
14pub struct ZkAppCommand {
15 pub fee_payer: FeePayer,
16 pub account_updates: CallForest<(AccountUpdate, Option<VerificationKeyWire>)>,
17 pub memo: Memo,
18}
19
20fn ok_if_vk_hash_expected(
21 got: VerificationKeyWire,
22 expected: Fp,
23) -> Result<VerificationKeyWire, String> {
24 if got.hash() == expected {
25 return Ok(got.clone());
26 }
27 Err(format!(
28 "Expected vk hash doesn't match hash in vk we received\
29 expected: {:?}\
30 got: {:?}",
31 expected, got
32 ))
33}
34
35pub fn find_vk_via_ledger<L>(
36 ledger: L,
37 expected_vk_hash: Fp,
38 account_id: &AccountId,
39) -> Result<VerificationKeyWire, String>
40where
41 L: LedgerIntf + Clone,
42{
43 let vk = ledger
44 .location_of_account(account_id)
45 .and_then(|location| ledger.get(&location))
46 .and_then(|account| {
47 account
48 .zkapp
49 .as_ref()
50 .and_then(|zkapp| zkapp.verification_key.clone())
51 });
52
53 match vk {
54 Some(vk) => ok_if_vk_hash_expected(vk, expected_vk_hash),
55 None => Err(format!(
56 "No verification key found for proved account update\
57 account_id: {:?}",
58 account_id
59 )),
60 }
61}
62
63fn check_authorization(p: &AccountUpdate) -> Result<(), String> {
64 use AuthorizationKind as AK;
65 use Control as C;
66
67 match (&p.authorization, &p.body.authorization_kind) {
68 (C::NoneGiven, AK::NoneGiven)
69 | (C::Proof(_), AK::Proof(_))
70 | (C::Signature(_), AK::Signature) => Ok(()),
71 _ => Err(format!(
72 "Authorization kind does not match the authorization\
73 expected={:#?}\
74 got={:#?}",
75 p.body.authorization_kind, p.authorization
76 )),
77 }
78}
79
80pub fn create(
88 zkapp: &super::ZkAppCommand,
89 is_failed: bool,
90 find_vk: impl Fn(Fp, &AccountId) -> Result<VerificationKeyWire, String>,
91) -> Result<ZkAppCommand, String> {
92 let super::ZkAppCommand {
93 fee_payer,
94 account_updates,
95 memo,
96 } = zkapp;
97
98 let mut tbl = HashMap::with_capacity(128);
99 let mut vks_overridden: HashMap<AccountId, Option<VerificationKeyWire>> =
102 HashMap::with_capacity(128);
103
104 let account_updates = account_updates.try_map_to(|p| {
105 let account_id = p.account_id();
106
107 check_authorization(p)?;
108
109 let result = match (&p.body.authorization_kind, is_failed) {
110 (AuthorizationKind::Proof(vk_hash), false) => {
111 let prioritized_vk = {
112 match vks_overridden.get(&account_id) {
117 Some(Some(vk)) => ok_if_vk_hash_expected(vk.clone(), *vk_hash)?,
118 Some(None) => {
119 return Err(format!(
121 "No verification key found for proved account \
122 update: the verification key was removed by a \
123 previous account update\
124 account_id={:?}",
125 account_id
126 ));
127 }
128 None => {
129 find_vk(*vk_hash, &account_id)?
131 }
132 }
133 };
134
135 tbl.insert(account_id, prioritized_vk.hash());
136
137 Ok((p.clone(), Some(prioritized_vk)))
138 }
139
140 _ => Ok((p.clone(), None)),
141 };
142
143 if let SetOrKeep::Set(vk_next) = &p.body.update.verification_key {
146 vks_overridden.insert(p.account_id().clone(), Some(vk_next.clone()));
147 }
148
149 result
150 })?;
151
152 Ok(ZkAppCommand {
153 fee_payer: fee_payer.clone(),
154 account_updates,
155 memo: memo.clone(),
156 })
157}