mina_tree/zkapps/
zkapp_logic.rs

1use mina_curves::pasta::Fp;
2use mina_signer::CompressedPubKey;
3use openmina_core::constants::constraint_constants;
4
5use crate::{
6    scan_state::{
7        currency::{Fee, Magnitude, SlotSpan},
8        transaction_logic::{
9            protocol_state::GlobalStateSkeleton,
10            zkapp_command::{Actions, CheckAuthorizationResult, SetOrKeep},
11            TimingValidation, TransactionFailure,
12        },
13    },
14    zkapps::intefaces::*,
15    AuthRequired, MyCow, Permissions, SetVerificationKey, TokenId, VerificationKeyWire,
16};
17
18use crate::proofs::{
19    field::{Boolean, ToBoolean},
20    zkapp::StartDataSkeleton,
21};
22
23pub enum IsStart<T> {
24    Yes(T),
25    No,
26    Compute(T),
27}
28
29struct GetNextAccountUpdateResult<Z: ZkappApplication> {
30    account_update: Z::AccountUpdate,
31    caller_id: TokenId,
32    account_update_forest: Z::CallForest,
33    new_call_stack: Z::CallStack,
34    new_frame: Z::StackFrame,
35}
36
37#[derive(Clone)]
38pub enum ZkAppCommandElt {
39    ZkAppCommandCommitment(crate::ReceiptChainHash),
40}
41
42fn assert_<Z: ZkappApplication>(b: Z::Bool, s: &str) -> Result<(), String> {
43    // Used only for circuit generation (add constraints)
44    // <https://github.com/MinaProtocol/mina/blob/e44ddfe1ca54b3855e1ed336d89f6230d35aeb8c/src/lib/transaction_logic/zkapp_command_logic.ml#L929>
45
46    if let Boolean::False = b.as_boolean() {
47        return Err(s.to_string());
48    }
49
50    Ok(())
51}
52
53fn stack_frame_default<Z: ZkappApplication>() -> Z::StackFrame {
54    Z::StackFrame::make_default(StackFrameMakeParams {
55        caller: TokenId::default(),
56        caller_caller: TokenId::default(),
57        calls: &Z::CallForest::empty(),
58    })
59}
60
61fn pop_call_stack<Z: ZkappApplication>(
62    s: &Z::CallStack,
63    w: &mut Z::WitnessGenerator,
64) -> (Z::StackFrame, Z::CallStack) {
65    let res = s.pop(w);
66    let (next_frame, next_call_stack) = res.unzip();
67
68    let call_stack = w.exists_no_check(match next_call_stack.is_some {
69        Boolean::True => next_call_stack.data,
70        Boolean::False => Z::CallStack::empty(),
71    });
72
73    let on_false = Z::Branch::make(w, |_| stack_frame_default::<Z>());
74
75    let stack_frame = Z::StackFrame::on_if(
76        Z::Bool::of_boolean(next_frame.is_some),
77        BranchParam {
78            on_true: Z::Branch::make(w, |_| next_frame.data),
79            on_false,
80        },
81        w,
82    );
83
84    (stack_frame, call_stack)
85}
86
87// Different order than in `Permissions::iter_as_bits`
88fn permissions_exists<Z: ZkappApplication>(
89    perms: Permissions<AuthRequired>,
90    w: &mut Z::WitnessGenerator,
91) -> Permissions<AuthRequired> {
92    let Permissions {
93        edit_state,
94        access,
95        send,
96        receive,
97        set_delegate,
98        set_permissions,
99        set_verification_key:
100            SetVerificationKey {
101                auth: set_verification_key_auth,
102                txn_version,
103            },
104        set_zkapp_uri,
105        edit_action_state,
106        set_token_symbol,
107        increment_nonce,
108        set_voting_for,
109        set_timing,
110    } = &perms;
111
112    use crate::AuthOrVersion;
113
114    for auth in [
115        AuthOrVersion::Auth(edit_state),
116        AuthOrVersion::Auth(send),
117        AuthOrVersion::Auth(receive),
118        AuthOrVersion::Auth(set_delegate),
119        AuthOrVersion::Auth(set_permissions),
120        AuthOrVersion::Auth(set_verification_key_auth),
121        AuthOrVersion::Version(*txn_version),
122        AuthOrVersion::Auth(set_zkapp_uri),
123        AuthOrVersion::Auth(edit_action_state),
124        AuthOrVersion::Auth(set_token_symbol),
125        AuthOrVersion::Auth(increment_nonce),
126        AuthOrVersion::Auth(set_voting_for),
127        AuthOrVersion::Auth(set_timing),
128        AuthOrVersion::Auth(access),
129    ]
130    .into_iter()
131    {
132        match auth {
133            AuthOrVersion::Auth(auth) => {
134                w.exists_no_check(*auth);
135            }
136            AuthOrVersion::Version(version) => {
137                w.exists_no_check(version);
138            }
139        }
140    }
141    perms
142}
143
144fn get_next_account_update<Z: ZkappApplication>(
145    current_forest: Z::StackFrame,
146    call_stack: Z::CallStack,
147    w: &mut Z::WitnessGenerator,
148) -> GetNextAccountUpdateResult<Z> {
149    let (current_forest, call_stack) = {
150        let (next_forest, next_call_stack) = pop_call_stack::<Z>(&call_stack, w);
151        let current_is_empty = current_forest.calls().is_empty(w);
152
153        // We decompose this way because of OCaml evaluation order
154        let call_stack = w.exists_no_check(match current_is_empty.as_boolean() {
155            Boolean::True => next_call_stack,
156            Boolean::False => call_stack,
157        });
158        let stack_frame = Z::StackFrame::on_if(
159            current_is_empty,
160            BranchParam {
161                on_true: Z::Branch::make(w, |_| next_forest),
162                on_false: Z::Branch::make(w, |_| current_forest),
163            },
164            w,
165        );
166        (stack_frame, call_stack)
167    };
168
169    let ((account_update, account_update_forest), remainder_of_current_forest) =
170        current_forest.calls().pop_exn(w);
171
172    let may_use_token = &account_update.body().may_use_token;
173    let may_use_parents_own_token = may_use_token.parents_own_token().to_boolean();
174    let may_use_token_inherited_from_parent =
175        { Z::Bool::of_boolean(may_use_token.inherit_from_parent().to_boolean()) };
176
177    let on_false = Z::Branch::make(w, |w| {
178        w.exists_no_check(match may_use_parents_own_token {
179            Boolean::True => current_forest.caller(),
180            Boolean::False => TokenId::default(),
181        })
182    });
183    let on_true = Z::Branch::make(w, |_| current_forest.caller_caller());
184    let caller_id = w.on_if(
185        may_use_token_inherited_from_parent,
186        BranchParam { on_true, on_false },
187    );
188
189    let account_update_forest_empty = account_update_forest.is_empty(w);
190    let remainder_of_current_forest_empty = remainder_of_current_forest.is_empty(w);
191    let (newly_popped_frame, popped_call_stack) = pop_call_stack::<Z>(&call_stack, w);
192
193    let remainder_of_current_forest_frame = Z::StackFrame::make(StackFrameMakeParams {
194        caller: current_forest.caller(),
195        caller_caller: current_forest.caller_caller(),
196        calls: &remainder_of_current_forest,
197    });
198    let new_call_stack = {
199        let on_false = Z::Branch::make(w, |w| {
200            let on_false = Z::Branch::make(w, |w| {
201                MyCow::Own(Z::CallStack::push(
202                    remainder_of_current_forest_frame.clone(),
203                    call_stack.clone(),
204                    w,
205                ))
206            });
207            let on_true = Z::Branch::make(w, |_| MyCow::Borrow(&call_stack));
208            w.on_if(
209                remainder_of_current_forest_empty,
210                BranchParam { on_true, on_false },
211            )
212        });
213        let on_true = Z::Branch::make(w, |w| {
214            w.exists_no_check(match remainder_of_current_forest_empty.as_boolean() {
215                Boolean::True => MyCow::Borrow(&popped_call_stack),
216                Boolean::False => MyCow::Borrow(&call_stack),
217            })
218        });
219        w.on_if(
220            account_update_forest_empty,
221            BranchParam { on_true, on_false },
222        )
223    };
224    let new_frame = {
225        // We decompose this way because of OCaml evaluation order
226        let on_false = Z::Branch::make(w, |w| {
227            let caller = Z::AccountId::derive_token_id(&account_update.body().account_id(), w);
228            let caller_caller = caller_id.clone();
229            Z::StackFrame::make(StackFrameMakeParams {
230                caller,
231                caller_caller,
232                calls: &account_update_forest,
233            })
234        });
235        let on_true = Z::Branch::make(w, |w| {
236            Z::StackFrame::on_if(
237                remainder_of_current_forest_empty,
238                BranchParam {
239                    on_true: Z::Branch::make(w, |_| newly_popped_frame),
240                    on_false: Z::Branch::make(w, |_| remainder_of_current_forest_frame),
241                },
242                w,
243            )
244        });
245        Z::StackFrame::on_if(
246            account_update_forest_empty,
247            BranchParam { on_true, on_false },
248            w,
249        )
250    };
251    GetNextAccountUpdateResult {
252        account_update,
253        caller_id,
254        account_update_forest,
255        new_call_stack: new_call_stack.to_owned(),
256        new_frame,
257    }
258}
259
260pub fn update_action_state<Z: ZkappApplication>(
261    action_state: &[Fp; 5],
262    actions: &Actions,
263    txn_global_slot: Z::GlobalSlotSinceGenesis,
264    last_action_slot: Z::GlobalSlotSinceGenesis,
265    w: &mut Z::WitnessGenerator,
266) -> ([Fp; 5], <Z as ZkappApplication>::GlobalSlotSinceGenesis) {
267    let [s1, s2, s3, s4, s5] = *action_state;
268    let is_empty = Z::Actions::is_empty(actions, w);
269    let s1_updated = Z::Actions::push_events(s1, actions, w);
270    let s1_new = w.exists_no_check(match is_empty.as_boolean() {
271        Boolean::True => s1,
272        Boolean::False => s1_updated,
273    });
274    let is_this_slot = Z::GlobalSlotSinceGenesis::equal(&txn_global_slot, &last_action_slot, w);
275    let is_empty_or_this_slot = Z::Bool::or(is_empty, is_this_slot, w);
276
277    let s5 = w.exists_no_check(match is_empty_or_this_slot.as_boolean() {
278        Boolean::True => s5,
279        Boolean::False => s4,
280    });
281    let s4 = w.exists_no_check(match is_empty_or_this_slot.as_boolean() {
282        Boolean::True => s4,
283        Boolean::False => s3,
284    });
285    let s3 = w.exists_no_check(match is_empty_or_this_slot.as_boolean() {
286        Boolean::True => s3,
287        Boolean::False => s2,
288    });
289    let s2 = w.exists_no_check(match is_empty_or_this_slot.as_boolean() {
290        Boolean::True => s2,
291        Boolean::False => s1,
292    });
293    let last_action_slot = match is_empty.as_boolean() {
294        Boolean::True => last_action_slot,
295        Boolean::False => txn_global_slot,
296    }
297    .exists_no_check(w);
298    ([s1_new, s2, s3, s4, s5], last_action_slot)
299}
300
301#[derive(Debug, Clone)]
302pub struct LocalState<Z: ZkappApplication> {
303    pub stack_frame: Z::StackFrame,
304    pub call_stack: Z::CallStack,
305    pub transaction_commitment: Fp,
306    pub full_transaction_commitment: Fp,
307    pub excess: Z::SignedAmount,
308    pub supply_increase: Z::SignedAmount,
309    pub ledger: Z::Ledger,
310    pub success: Z::Bool,
311    pub account_update_index: Z::Index,
312    pub failure_status_tbl: Z::FailureStatusTable,
313    pub will_succeed: Z::Bool,
314}
315
316pub type GlobalState<Z> = GlobalStateSkeleton<
317    <Z as ZkappApplication>::Ledger,                 // ledger
318    <Z as ZkappApplication>::SignedAmount,           // fee_excess & supply_increase
319    <Z as ZkappApplication>::GlobalSlotSinceGenesis, // block_global_slot
320>;
321
322pub type StartData<Z> = StartDataSkeleton<
323    <Z as ZkappApplication>::CallForest, // account_updates
324    <Z as ZkappApplication>::Bool,       // will_succeed
325>;
326
327pub struct ApplyZkappParams<'a, Z: ZkappApplication> {
328    pub is_start: IsStart<StartData<Z>>,
329    pub global_state: &'a mut Z::GlobalState,
330    pub local_state: &'a mut LocalState<Z>,
331    pub single_data: Z::SingleData,
332}
333
334pub fn apply<Z>(params: ApplyZkappParams<'_, Z>, w: &mut Z::WitnessGenerator) -> Result<(), String>
335where
336    Z: ZkappApplication,
337{
338    let ApplyZkappParams {
339        is_start,
340        global_state,
341        local_state,
342        single_data,
343    } = params;
344
345    let is_start2 = {
346        let is_empty_call_forest = local_state.stack_frame.calls().is_empty(w);
347        match is_start {
348            IsStart::Compute(_) => (),
349            IsStart::Yes(_) => assert_::<Z>(is_empty_call_forest, "is_empty_call_forest")?,
350            IsStart::No => assert_::<Z>(is_empty_call_forest.neg(), "is_empty_call_forest.neg()")?,
351        };
352        match is_start {
353            IsStart::Yes(_) => Z::Bool::true_(),
354            IsStart::No => Z::Bool::false_(),
355            IsStart::Compute(_) => is_empty_call_forest,
356        }
357    };
358
359    let will_succeed = match &is_start {
360        IsStart::Compute(start_data) => w.exists_no_check_on_bool(
361            is_start2,
362            match is_start2.as_boolean() {
363                Boolean::True => start_data.will_succeed,
364                Boolean::False => local_state.will_succeed,
365            },
366        ),
367        IsStart::Yes(start_data) => start_data.will_succeed,
368        IsStart::No => local_state.will_succeed,
369    };
370    local_state.ledger = match is_start2.as_boolean() {
371        Boolean::True => global_state.first_pass_ledger(),
372        Boolean::False => local_state.ledger.clone(),
373    }
374    .exists_no_check_on_bool(is_start2, w);
375    local_state.will_succeed = will_succeed;
376
377    let ((account_update, remaining, call_stack), account_update_forest, (mut a, inclusion_proof)) = {
378        let (to_pop, call_stack) = {
379            match &is_start {
380                IsStart::Compute(start_data) => {
381                    // We decompose this way because of OCaml evaluation order
382                    let call_stack = w.exists_no_check(match is_start2.as_boolean() {
383                        Boolean::True => Z::CallStack::empty(),
384                        Boolean::False => local_state.call_stack.clone(),
385                    });
386                    let stack_frame = {
387                        let on_true = Z::Branch::make(w, |_| {
388                            Z::StackFrame::make(StackFrameMakeParams {
389                                caller: TokenId::default(),
390                                caller_caller: TokenId::default(),
391                                calls: &start_data.account_updates,
392                            })
393                        });
394                        let on_false = Z::Branch::make(w, |_| local_state.stack_frame.clone());
395                        Z::StackFrame::on_if(is_start2, BranchParam { on_true, on_false }, w)
396                    };
397                    (stack_frame, call_stack)
398                }
399                IsStart::Yes(start_data) => {
400                    // We decompose this way because of OCaml evaluation order
401                    let call_stack = Z::CallStack::empty();
402                    let stack_frame = Z::StackFrame::make(StackFrameMakeParams {
403                        caller: TokenId::default(),
404                        caller_caller: TokenId::default(),
405                        calls: &start_data.account_updates,
406                    });
407                    (stack_frame, call_stack)
408                }
409                IsStart::No => (
410                    local_state.stack_frame.clone(),
411                    local_state.call_stack.clone(),
412                ),
413            }
414        };
415
416        let GetNextAccountUpdateResult {
417            account_update,
418            caller_id,
419            account_update_forest,
420            new_call_stack: call_stack,
421            new_frame: remaining,
422        } = get_next_account_update::<Z>(to_pop, call_stack, w);
423
424        // local state
425        {
426            let default_token_or_token_owner_was_caller = {
427                let account_update_token_id = &account_update.body().token_id;
428                // We decompose this way because of OCaml evaluation order
429                let snd = Z::TokenId::equal(account_update_token_id, &caller_id, w);
430                let fst = Z::TokenId::equal(account_update_token_id, &TokenId::default(), w);
431                Z::Bool::or(fst, snd, w)
432            };
433            Z::LocalState::add_check(
434                local_state,
435                TransactionFailure::TokenOwnerNotCaller,
436                default_token_or_token_owner_was_caller,
437                w,
438            );
439        };
440
441        let acct = local_state.ledger.get_account(&account_update, w)?;
442        local_state.ledger.check_inclusion(&acct, w);
443
444        let (transaction_commitment, full_transaction_commitment) = match is_start {
445            IsStart::No => (
446                local_state.transaction_commitment,
447                local_state.full_transaction_commitment,
448            ),
449            IsStart::Yes(start_data) | IsStart::Compute(start_data) => {
450                let tx_commitment_on_start =
451                    Z::TransactionCommitment::commitment(remaining.calls());
452                let full_tx_commitment_on_start = Z::TransactionCommitment::full_commitment(
453                    &account_update,
454                    start_data.memo_hash,
455                    tx_commitment_on_start,
456                    w,
457                );
458                let tx_commitment = w.exists_no_check(match is_start2.as_boolean() {
459                    Boolean::True => tx_commitment_on_start,
460                    Boolean::False => local_state.transaction_commitment,
461                });
462                let full_tx_commitment = w.exists_no_check(match is_start2.as_boolean() {
463                    Boolean::True => full_tx_commitment_on_start,
464                    Boolean::False => local_state.full_transaction_commitment,
465                });
466                (tx_commitment, full_tx_commitment)
467            }
468        };
469
470        local_state.transaction_commitment = transaction_commitment;
471        local_state.full_transaction_commitment = full_transaction_commitment;
472
473        (
474            (account_update, remaining, call_stack),
475            account_update_forest,
476            acct,
477        )
478    };
479
480    local_state.stack_frame = remaining.clone();
481    local_state.call_stack = call_stack;
482    Z::LocalState::add_new_failure_status_bucket(local_state);
483
484    a.register_verification_key(&single_data, w);
485
486    let account_is_new = Z::Ledger::check_account(
487        &account_update.body().public_key,
488        &account_update.body().token_id,
489        (&a, &inclusion_proof),
490        w,
491    )?;
492
493    {
494        let self_delegate = {
495            let account_update_token_id = &account_update.body().token_id;
496            let is_default_token =
497                Z::TokenId::equal(account_update_token_id, &TokenId::default(), w);
498            Z::Bool::and(account_is_new, is_default_token, w)
499        };
500        a.set_delegate(
501            w.exists_no_check(match self_delegate.as_boolean() {
502                Boolean::True => account_update.body().public_key.clone(),
503                Boolean::False => a
504                    .get()
505                    .delegate
506                    .clone()
507                    .unwrap_or_else(CompressedPubKey::empty),
508            }),
509        )
510    };
511
512    let matching_verification_key_hashes = {
513        let is_not_proved = account_update.is_proved().neg();
514        let is_same_vk = Z::VerificationKeyHash::equal(
515            &a.verification_key_hash(),
516            &account_update.verification_key_hash(),
517            w,
518        );
519        Z::Bool::or(is_not_proved, is_same_vk, w)
520    };
521    Z::LocalState::add_check(
522        local_state,
523        TransactionFailure::UnexpectedVerificationKeyHash,
524        matching_verification_key_hashes,
525        w,
526    );
527
528    Z::Handler::check_account_precondition(&account_update, &a, account_is_new, local_state, w);
529
530    let protocol_state_precondition = &account_update.body().preconditions.network;
531    let protocol_state_predicate_satisfied =
532        Z::Handler::check_protocol_state_precondition(protocol_state_precondition, global_state, w);
533    Z::LocalState::add_check(
534        local_state,
535        TransactionFailure::ProtocolStatePreconditionUnsatisfied,
536        protocol_state_predicate_satisfied,
537        w,
538    );
539
540    // local state
541    {
542        let valid_while = &account_update.body().preconditions.valid_while;
543        let valid_while_satisfied =
544            Z::Handler::check_valid_while_precondition(valid_while, global_state, w);
545        Z::LocalState::add_check(
546            local_state,
547            TransactionFailure::ValidWhilePreconditionUnsatisfied,
548            valid_while_satisfied,
549            w,
550        );
551    };
552
553    let CheckAuthorizationResult {
554        proof_verifies,
555        signature_verifies,
556    } = {
557        let use_full_commitment = account_update.body().use_full_commitment.to_boolean();
558        let commitment = w.exists_no_check(match use_full_commitment {
559            Boolean::True => local_state.full_transaction_commitment,
560            Boolean::False => local_state.transaction_commitment,
561        });
562        account_update.check_authorization(
563            local_state.will_succeed,
564            commitment,
565            &account_update_forest,
566            &single_data,
567            w,
568        )
569    };
570    assert_::<Z>(
571        Z::Bool::equal(proof_verifies, account_update.is_proved(), w),
572        "not proved",
573    )?;
574    assert_::<Z>(
575        Z::Bool::equal(signature_verifies, account_update.is_signed(), w),
576        "not signed",
577    )?;
578
579    Z::LocalState::add_check(
580        local_state,
581        TransactionFailure::FeePayerNonceMustIncrease,
582        Z::Bool::or(account_update.increment_nonce(), is_start2.neg(), w),
583        w,
584    );
585    Z::LocalState::add_check(
586        local_state,
587        TransactionFailure::FeePayerMustBeSigned,
588        Z::Bool::or(signature_verifies, is_start2.neg(), w),
589        w,
590    );
591
592    // local state
593    {
594        let precondition_has_constant_nonce =
595            account_update.account_precondition_nonce_is_constant(w);
596        let increments_nonce_and_constrains_its_old_value = Z::Bool::and(
597            account_update.increment_nonce(),
598            precondition_has_constant_nonce,
599            w,
600        );
601        let depends_on_the_fee_payers_nonce_and_isnt_the_fee_payer =
602            Z::Bool::and(account_update.use_full_commitment(), is_start2.neg(), w);
603        let does_not_use_a_signature = signature_verifies.neg();
604        let first = Z::Bool::or(
605            increments_nonce_and_constrains_its_old_value,
606            depends_on_the_fee_payers_nonce_and_isnt_the_fee_payer,
607            w,
608        );
609        let second = Z::Bool::or(first, does_not_use_a_signature, w);
610        Z::LocalState::add_check(
611            local_state,
612            TransactionFailure::ZkappCommandReplayCheckFailed,
613            second,
614            w,
615        );
616    };
617
618    a.set_token_id(account_update.body().token_id.clone());
619
620    let account_update_token = &account_update.body().token_id;
621    let account_update_token_is_default =
622        Z::TokenId::equal(&TokenId::default(), account_update_token, w);
623    let account_is_untimed = a.is_timed().neg();
624
625    // Set account timing.
626    let (_a, _local_state) = {
627        let timing = &account_update.body().update.timing;
628        let has_permission = {
629            let set_timing = &a.get().permissions.set_timing;
630            Z::Controller::check(
631                proof_verifies,
632                signature_verifies,
633                set_timing,
634                &single_data,
635                w,
636            )?
637        };
638        let is_keep = Z::SetOrKeep::is_keep(timing);
639        let v_and = Z::Bool::and(account_is_untimed, has_permission, w);
640        Z::LocalState::add_check(
641            local_state,
642            TransactionFailure::UpdateNotPermittedTiming,
643            Z::Bool::or(is_keep, v_and, w),
644            w,
645        );
646        let timing = w.exists_no_check({
647            match timing {
648                SetOrKeep::Set(timing) => timing.clone().to_account_timing(),
649                SetOrKeep::Keep => a.get().timing.clone(),
650            }
651        });
652        assert_::<Z>(
653            Z::GlobalSlotSpan::greater_than(
654                &timing.to_record().vesting_period,
655                &SlotSpan::zero(),
656                w,
657            ),
658            "vesting_period zero",
659        )?;
660        a.get_mut().timing = timing;
661        ((), ())
662    };
663    let account_creation_fee =
664        Z::Amount::of_constant_fee(Fee::from_u64(constraint_constants().account_creation_fee));
665    let implicit_account_creation_fee = account_update.implicit_account_creation_fee();
666    Z::LocalState::add_check(
667        local_state,
668        TransactionFailure::CannotPayCreationFeeInToken,
669        Z::Bool::or(
670            implicit_account_creation_fee.neg(),
671            account_update_token_is_default,
672            w,
673        ),
674        w,
675    );
676
677    let account_update_balance_change = account_update.balance_change();
678
679    // Compute the change to the account balance.
680    let (_local_state, actual_balance_change) = {
681        let balance_change = &account_update_balance_change;
682        let neg_creation_fee = { Z::SignedAmount::of_unsigned(account_creation_fee).negate() };
683        let (balance_change_for_creation, creation_overflow) =
684            Z::SignedAmount::add_flagged(balance_change, &neg_creation_fee, w);
685        let pay_creation_fee = Z::Bool::and(account_is_new, implicit_account_creation_fee, w);
686        let creation_overflow = Z::Bool::and(pay_creation_fee, creation_overflow, w);
687        let balance_change = Z::SignedAmount::on_if(
688            pay_creation_fee,
689            SignedAmountBranchParam {
690                on_true: &balance_change_for_creation,
691                on_false: balance_change,
692            },
693            w,
694        );
695        let first = Z::Bool::or(
696            creation_overflow,
697            Z::SignedAmount::is_neg(&balance_change),
698            w,
699        );
700        Z::LocalState::add_check(
701            local_state,
702            TransactionFailure::AmountInsufficientToCreateAccount,
703            Z::Bool::and(pay_creation_fee, first, w).neg(),
704            w,
705        );
706        ((), balance_change)
707    };
708
709    // Apply balance change.
710    let (_a, _local_state) = {
711        let pay_creation_fee_from_excess =
712            Z::Bool::and(account_is_new, implicit_account_creation_fee.neg(), w);
713        let (balance, failed1) =
714            Z::Balance::add_signed_amount_flagged(&a.balance(), actual_balance_change.clone(), w);
715        Z::LocalState::add_check(local_state, TransactionFailure::Overflow, failed1.neg(), w);
716        let account_creation_fee =
717            Z::Amount::of_constant_fee(Fee::from_u64(constraint_constants().account_creation_fee));
718
719        // local state
720        {
721            let (excess_minus_creation_fee, excess_update_failed) = Z::SignedAmount::add_flagged(
722                &local_state.excess,
723                &Z::SignedAmount::of_unsigned(account_creation_fee.clone()).negate(),
724                w,
725            );
726            Z::LocalState::add_check(
727                local_state,
728                TransactionFailure::LocalExcessOverflow,
729                Z::Bool::and(pay_creation_fee_from_excess, excess_update_failed, w).neg(),
730                w,
731            );
732            local_state.excess = Z::SignedAmount::on_if(
733                pay_creation_fee_from_excess,
734                SignedAmountBranchParam {
735                    on_true: &excess_minus_creation_fee,
736                    on_false: &local_state.excess,
737                },
738                w,
739            )
740            .clone();
741        };
742
743        // local state
744        {
745            let (supply_increase_minus_creation_fee, supply_increase_update_failed) =
746                Z::SignedAmount::add_flagged(
747                    &local_state.supply_increase,
748                    &Z::SignedAmount::of_unsigned(account_creation_fee).negate(),
749                    w,
750                );
751            Z::LocalState::add_check(
752                local_state,
753                TransactionFailure::LocalSupplyIncreaseOverflow,
754                Z::Bool::and(account_is_new, supply_increase_update_failed, w).neg(),
755                w,
756            );
757            local_state.supply_increase = Z::SignedAmount::on_if(
758                account_is_new,
759                SignedAmountBranchParam {
760                    on_true: &supply_increase_minus_creation_fee,
761                    on_false: &local_state.supply_increase,
762                },
763                w,
764            )
765            .clone();
766        };
767
768        let is_receiver = actual_balance_change.is_non_neg();
769        // local state
770        {
771            let controller = {
772                let on_true = Z::Branch::make(w, |_| a.get().permissions.receive);
773                let on_false = Z::Branch::make(w, |_| a.get().permissions.send);
774                w.on_if(is_receiver, BranchParam { on_true, on_false })
775            };
776            let has_permission = Z::Controller::check(
777                proof_verifies,
778                signature_verifies,
779                &controller,
780                &single_data,
781                w,
782            )?;
783            let first = Z::SignedAmount::equal(&Z::SignedAmount::zero(), &actual_balance_change, w);
784            Z::LocalState::add_check(
785                local_state,
786                TransactionFailure::UpdateNotPermittedBalance,
787                Z::Bool::or(has_permission, first, w),
788                w,
789            );
790        };
791        Z::Account::set_balance(&mut a, balance);
792        ((), ())
793    };
794
795    let txn_global_slot = global_state.block_global_slot();
796    // Check timing with current balance
797    let (_a, _local_state) = {
798        let (invalid_timing, timing) = match Z::Account::check_timing(&a, &txn_global_slot, w) {
799            (TimingValidation::InsufficientBalance(_), _) => {
800                return Err("Did not propose a balance change at this timing check!".to_string())
801            }
802            (TimingValidation::InvalidTiming(invalid_timing), timing) => (invalid_timing, timing),
803        };
804        Z::LocalState::add_check(
805            local_state,
806            TransactionFailure::SourceMinimumBalanceViolation,
807            invalid_timing.neg(),
808            w,
809        );
810        a.get_mut().timing = timing;
811        ((), ())
812    };
813    Z::Account::make_zkapp(&mut a);
814    // Check that the account can be accessed with the given authorization.
815    // local state
816    {
817        let has_permission = {
818            let access = &a.get().permissions.access;
819            Z::Controller::check(proof_verifies, signature_verifies, access, &single_data, w)?
820        };
821        Z::LocalState::add_check(
822            local_state,
823            TransactionFailure::UpdateNotPermittedAccess,
824            has_permission,
825            w,
826        );
827    };
828
829    // Update app state.
830    let (_a, _local_state) = {
831        let app_state = &account_update.body().update.app_state;
832        let keeping_app_state = {
833            let is_all_keep: [_; 8] = app_state.each_ref().map(Z::SetOrKeep::is_keep);
834            Z::Bool::all(&is_all_keep, w)
835        };
836        let changing_entire_app_state = {
837            let is_all_set: [_; 8] = app_state.each_ref().map(Z::SetOrKeep::is_set);
838            Z::Bool::all(&is_all_set, w)
839        };
840        let proved_state = {
841            let on_false = {
842                let on_true = {
843                    w.exists_no_check(match changing_entire_app_state.as_boolean() {
844                        Boolean::True => Z::Bool::true_(),
845                        Boolean::False => a.proved_state(),
846                    })
847                };
848                w.exists_no_check_on_bool(
849                    proof_verifies,
850                    match proof_verifies.as_boolean() {
851                        Boolean::True => on_true,
852                        Boolean::False => Z::Bool::false_(),
853                    },
854                )
855            };
856            w.exists_no_check(match keeping_app_state.as_boolean() {
857                Boolean::True => a.proved_state(),
858                Boolean::False => on_false,
859            })
860        };
861        a.set_proved_state(proved_state);
862        let has_permission = {
863            let edit_state = &a.get().permissions.edit_state;
864            Z::Controller::check(
865                proof_verifies,
866                signature_verifies,
867                edit_state,
868                &single_data,
869                w,
870            )?
871        };
872        Z::LocalState::add_check(
873            local_state,
874            TransactionFailure::UpdateNotPermittedAppState,
875            Z::Bool::or(keeping_app_state, has_permission, w),
876            w,
877        );
878        // We use `rev()` and `reverse()` here to match OCaml `Pickles_types.Vector.map2`
879        let mut app_state: [Fp; 8] = app_state
880            .iter()
881            .rev()
882            .zip(a.app_state().into_iter().rev())
883            .map(|(set_or_keep, state)| {
884                w.exists_no_check(match set_or_keep {
885                    SetOrKeep::Set(s) => *s,
886                    SetOrKeep::Keep => state,
887                })
888            })
889            .collect::<Vec<_>>()
890            .try_into()
891            .unwrap();
892        app_state.reverse();
893
894        a.zkapp_mut().app_state = app_state;
895        ((), ())
896    };
897
898    // Set verification key.
899    let (_a, _local_state) = {
900        let verification_key = &account_update.body().update.verification_key;
901
902        let has_permission = {
903            let SetVerificationKey { auth, txn_version } =
904                &a.get().permissions.set_verification_key;
905
906            let older_than_current_version = Z::TxnVersion::older_than_current(*txn_version, w);
907            let original_auth = auth;
908
909            let auth = {
910                let on_true = Z::Branch::make(w, |w| {
911                    Z::Controller::verification_key_perm_fallback_to_signature_with_older_version(
912                        original_auth,
913                        w,
914                    )
915                });
916                let on_false = Z::Branch::make(w, |_| *original_auth);
917                w.on_if(
918                    older_than_current_version,
919                    BranchParam { on_true, on_false },
920                )
921            };
922
923            Z::Controller::check(proof_verifies, signature_verifies, &auth, &single_data, w)?
924        };
925        Z::LocalState::add_check(
926            local_state,
927            TransactionFailure::UpdateNotPermittedVerificationKey,
928            Z::Bool::or(Z::SetOrKeep::is_keep(verification_key), has_permission, w),
929            w,
930        );
931        let zkapp = a.zkapp();
932        w.exists_no_check(match verification_key {
933            SetOrKeep::Set(key) => key.hash(),
934            SetOrKeep::Keep => zkapp
935                .verification_key
936                .as_ref()
937                .map(VerificationKeyWire::hash)
938                .unwrap_or_else(VerificationKeyWire::dummy_hash),
939        });
940        // Made here https://github.com/MinaProtocol/mina/blob/5c92fbdbf083a74a8b9530d3d727cc7b03dcce8a/src/lib/mina_base/zkapp_basic.ml#L82
941        w.exists_no_check(match verification_key {
942            SetOrKeep::Set(_) => true, // TODO: It might be false here when `verification_key` is none ?
943            SetOrKeep::Keep => zkapp.verification_key.is_some(),
944        });
945        let verification_key = match verification_key {
946            SetOrKeep::Set(vk) => Some(vk.clone()),
947            SetOrKeep::Keep => zkapp.verification_key.clone(),
948        };
949        a.zkapp_mut().verification_key = verification_key;
950        ((), ())
951    };
952
953    // Update action state.
954    let (_a, _local_state) = {
955        let actions = &account_update.body().actions;
956        let last_action_slot = a.last_action_slot();
957        let action_state = &a.zkapp().action_state;
958        let (action_state, last_action_slot) =
959            update_action_state::<Z>(action_state, actions, txn_global_slot, last_action_slot, w);
960        let is_empty = Z::Actions::is_empty(actions, w);
961        let has_permission = {
962            let edit_action_state = &a.get().permissions.edit_action_state;
963            Z::Controller::check(
964                proof_verifies,
965                signature_verifies,
966                edit_action_state,
967                &single_data,
968                w,
969            )?
970        };
971        Z::LocalState::add_check(
972            local_state,
973            TransactionFailure::UpdateNotPermittedActionState,
974            Z::Bool::or(is_empty, has_permission, w),
975            w,
976        );
977        a.zkapp_mut().action_state = action_state;
978        Z::Account::set_last_action_slot(&mut a, last_action_slot);
979        ((), ())
980    };
981
982    // Update zkApp URI.
983    let (_a, _local_state) = {
984        let zkapp_uri = &account_update.body().update.zkapp_uri;
985        let has_permission = {
986            let set_zkapp_uri = &a.get().permissions.set_zkapp_uri;
987            Z::Controller::check(
988                proof_verifies,
989                signature_verifies,
990                set_zkapp_uri,
991                &single_data,
992                w,
993            )?
994        };
995        Z::LocalState::add_check(
996            local_state,
997            TransactionFailure::UpdateNotPermittedZkappUri,
998            Z::Bool::or(Z::SetOrKeep::is_keep(zkapp_uri), has_permission, w),
999            w,
1000        );
1001        let zkapp = a.zkapp();
1002        let zkapp_uri = w.exists_no_check(match zkapp_uri {
1003            SetOrKeep::Set(zkapp_uri) => Some(zkapp_uri),
1004            SetOrKeep::Keep => Some(&zkapp.zkapp_uri),
1005        });
1006        a.zkapp_mut().zkapp_uri = zkapp_uri.cloned().unwrap();
1007        ((), ())
1008    };
1009
1010    Z::Account::unmake_zkapp(&mut a);
1011
1012    // Update token symbol.
1013    let (_a, _local_state) = {
1014        let token_symbol = &account_update.body().update.token_symbol;
1015        let has_permission = {
1016            let set_token_symbol = &a.get().permissions.set_token_symbol;
1017            Z::Controller::check(
1018                proof_verifies,
1019                signature_verifies,
1020                set_token_symbol,
1021                &single_data,
1022                w,
1023            )?
1024        };
1025        Z::LocalState::add_check(
1026            local_state,
1027            TransactionFailure::UpdateNotPermittedTokenSymbol,
1028            Z::Bool::or(Z::SetOrKeep::is_keep(token_symbol), has_permission, w),
1029            w,
1030        );
1031        let token_symbol = w.exists_no_check({
1032            match token_symbol {
1033                SetOrKeep::Set(token_symbol) => token_symbol.clone(),
1034                SetOrKeep::Keep => a.get().token_symbol.clone(),
1035            }
1036        });
1037        a.get_mut().token_symbol = token_symbol;
1038        ((), ())
1039    };
1040
1041    // Update delegate.
1042    let (_a, _local_state) = {
1043        let delegate = &account_update.body().update.delegate;
1044        let has_permission = {
1045            let set_delegate = &a.get().permissions.set_delegate;
1046            Z::Controller::check(
1047                proof_verifies,
1048                signature_verifies,
1049                set_delegate,
1050                &single_data,
1051                w,
1052            )?
1053        };
1054        let first = Z::Bool::and(has_permission, account_update_token_is_default, w);
1055        Z::LocalState::add_check(
1056            local_state,
1057            TransactionFailure::UpdateNotPermittedDelegate,
1058            Z::Bool::or(Z::SetOrKeep::is_keep(delegate), first, w),
1059            w,
1060        );
1061        let base_delegate = a
1062            .get()
1063            .delegate
1064            .clone()
1065            .unwrap_or_else(CompressedPubKey::empty);
1066        let delegate = w.exists_no_check(match delegate {
1067            SetOrKeep::Set(delegate) => delegate.clone(),
1068            SetOrKeep::Keep => base_delegate,
1069        });
1070        a.set_delegate(delegate);
1071        ((), ())
1072    };
1073
1074    // Update nonce.
1075    let (_a, _local_state) = {
1076        let nonce = a.get().nonce;
1077        let increment_nonce = account_update.increment_nonce();
1078        let nonce = w.exists_no_check(match increment_nonce.as_boolean() {
1079            Boolean::True => nonce.succ(),
1080            Boolean::False => nonce,
1081        });
1082        let has_permission = {
1083            let increment_nonce = &a.get().permissions.increment_nonce;
1084            Z::Controller::check(
1085                proof_verifies,
1086                signature_verifies,
1087                increment_nonce,
1088                &single_data,
1089                w,
1090            )?
1091        };
1092        Z::LocalState::add_check(
1093            local_state,
1094            TransactionFailure::UpdateNotPermittedNonce,
1095            Z::Bool::or(increment_nonce.neg(), has_permission, w),
1096            w,
1097        );
1098        a.get_mut().nonce = nonce;
1099        ((), ())
1100    };
1101
1102    // Update voting-for.
1103    let (_a, _local_state) = {
1104        let voting_for = &account_update.body().update.voting_for;
1105        let has_permission = {
1106            let set_voting_for = &a.get().permissions.set_voting_for;
1107            Z::Controller::check(
1108                proof_verifies,
1109                signature_verifies,
1110                set_voting_for,
1111                &single_data,
1112                w,
1113            )?
1114        };
1115        Z::LocalState::add_check(
1116            local_state,
1117            TransactionFailure::UpdateNotPermittedVotingFor,
1118            Z::Bool::or(Z::SetOrKeep::is_keep(voting_for), has_permission, w),
1119            w,
1120        );
1121        let voting_for = w.exists_no_check(match voting_for {
1122            SetOrKeep::Set(voting_for) => voting_for.clone(),
1123            SetOrKeep::Keep => a.get().voting_for.clone(),
1124        });
1125        a.get_mut().voting_for = voting_for;
1126        ((), ())
1127    };
1128
1129    // Update receipt chain hash
1130    {
1131        let new_hash = {
1132            let old_hash = a.get().receipt_chain_hash.clone();
1133            let cond = Z::Bool::or(signature_verifies, proof_verifies, w);
1134            let on_false = Z::Branch::make(w, |_| old_hash.clone());
1135            let on_true = Z::Branch::make(w, |w| {
1136                let elt = local_state.full_transaction_commitment;
1137                Z::ReceiptChainHash::cons_zkapp_command_commitment(
1138                    local_state.account_update_index.clone(),
1139                    elt,
1140                    old_hash.clone(),
1141                    w,
1142                )
1143            });
1144            w.on_if(cond, BranchParam { on_true, on_false })
1145        };
1146        a.get_mut().receipt_chain_hash = new_hash;
1147    };
1148
1149    // update permissions.
1150    let (_a, _local_state) = {
1151        let permissions = &account_update.body().update.permissions;
1152        let has_permission = {
1153            let set_permissions = &a.get().permissions.set_permissions;
1154            Z::Controller::check(
1155                proof_verifies,
1156                signature_verifies,
1157                set_permissions,
1158                &single_data,
1159                w,
1160            )?
1161        };
1162        Z::LocalState::add_check(
1163            local_state,
1164            TransactionFailure::UpdateNotPermittedPermissions,
1165            Z::Bool::or(Z::SetOrKeep::is_keep(permissions), has_permission, w),
1166            w,
1167        );
1168        let permissions = permissions_exists::<Z>(
1169            match permissions {
1170                SetOrKeep::Set(permissions) => permissions.clone(),
1171                SetOrKeep::Keep => a.get().permissions.clone(),
1172            },
1173            w,
1174        );
1175        a.get_mut().permissions = permissions;
1176        ((), ())
1177    };
1178
1179    let a = Z::Handler::init_account(&account_update, a);
1180
1181    let local_delta = account_update_balance_change.negate();
1182
1183    let (new_local_fee_excess, overflowed) = {
1184        let first = Z::Bool::and(
1185            account_update_token_is_default,
1186            Z::SignedAmount::is_non_neg(&local_delta),
1187            w,
1188        );
1189        assert_::<Z>(Z::Bool::or(is_start2.neg(), first, w), "is_start2 or first")?;
1190        let (new_local_fee_excess, overflow) =
1191            Z::SignedAmount::add_flagged(&local_state.excess, &local_delta, w);
1192        // We decompose this way because of OCaml evaluation order
1193        let overflowed = Z::Bool::and(account_update_token_is_default, overflow, w);
1194
1195        let excess = Z::SignedAmount::on_if(
1196            account_update_token_is_default,
1197            SignedAmountBranchParam {
1198                on_true: &new_local_fee_excess,
1199                on_false: &local_state.excess,
1200            },
1201            w,
1202        )
1203        .clone();
1204        (excess, overflowed)
1205    };
1206    local_state.excess = new_local_fee_excess;
1207    Z::LocalState::add_check(
1208        local_state,
1209        TransactionFailure::LocalExcessOverflow,
1210        overflowed.neg(),
1211        w,
1212    );
1213    local_state.ledger.set_account((a, inclusion_proof), w)?;
1214
1215    let is_last_account_update = Z::CallForest::is_empty(Z::StackFrame::calls(&remaining), w);
1216    // We decompose this way because of OCaml evaluation order
1217    local_state.full_transaction_commitment =
1218        w.exists_no_check(match is_last_account_update.as_boolean() {
1219            Boolean::True => Z::TransactionCommitment::empty(),
1220            Boolean::False => local_state.full_transaction_commitment,
1221        });
1222    local_state.transaction_commitment =
1223        w.exists_no_check(match is_last_account_update.as_boolean() {
1224            Boolean::True => Z::TransactionCommitment::empty(),
1225            Boolean::False => local_state.transaction_commitment,
1226        });
1227
1228    let valid_fee_excess = {
1229        let delta_settled = Z::SignedAmount::equal(
1230            &local_state.excess,
1231            &Z::SignedAmount::of_unsigned(Z::Amount::zero()),
1232            w,
1233        );
1234        let first = Z::Bool::or(is_start2, is_last_account_update.neg(), w);
1235        Z::Bool::or(first, delta_settled, w)
1236    };
1237    Z::LocalState::add_check(
1238        local_state,
1239        TransactionFailure::InvalidFeeExcess,
1240        valid_fee_excess,
1241        w,
1242    );
1243    let is_start_or_last = Z::Bool::or(is_start2, is_last_account_update, w);
1244    let update_global_state_fee_excess = Z::Bool::and(is_start_or_last, local_state.success, w);
1245
1246    let (_global_state, global_excess_update_failed) = {
1247        let amt = global_state.fee_excess();
1248        let (res, overflow) = Z::SignedAmount::add_flagged(&amt, &local_state.excess, w);
1249        let global_excess_update_failed = Z::Bool::and(update_global_state_fee_excess, overflow, w);
1250        let new_amt = Z::SignedAmount::on_if(
1251            update_global_state_fee_excess,
1252            SignedAmountBranchParam {
1253                on_true: &res,
1254                on_false: &amt,
1255            },
1256            w,
1257        );
1258        global_state.set_fee_excess(new_amt.clone());
1259        (1, global_excess_update_failed)
1260    };
1261
1262    let signed_zero = Z::SignedAmount::of_unsigned(Z::Amount::zero());
1263    local_state.excess = Z::SignedAmount::on_if(
1264        is_start_or_last,
1265        SignedAmountBranchParam {
1266            on_true: &signed_zero,
1267            on_false: &local_state.excess,
1268        },
1269        w,
1270    )
1271    .clone();
1272    Z::LocalState::add_check(
1273        local_state,
1274        TransactionFailure::GlobalExcessOverflow,
1275        global_excess_update_failed.neg(),
1276        w,
1277    );
1278
1279    // add local supply increase in global state
1280    let (new_global_supply_increase, global_supply_increase_update_failed) = {
1281        Z::SignedAmount::add_flagged(
1282            &global_state.supply_increase(),
1283            &local_state.supply_increase,
1284            w,
1285        )
1286    };
1287    Z::LocalState::add_check(
1288        local_state,
1289        TransactionFailure::GlobalSupplyIncreaseOverflow,
1290        global_supply_increase_update_failed.neg(),
1291        w,
1292    );
1293
1294    // The first account_update must succeed.
1295    let b = Z::Bool::or(is_start2.neg(), local_state.success, w);
1296    Z::Bool::assert_with_failure_status_tbl(b, &local_state.failure_status_tbl)?;
1297
1298    let (_local_state, _global_state) = {
1299        let is_fee_payer = is_start2;
1300        global_state.set_first_pass_ledger(is_fee_payer, &local_state.ledger, w);
1301        local_state.ledger = match is_fee_payer.as_boolean() {
1302            Boolean::True => global_state.second_pass_ledger(),
1303            Boolean::False => local_state.ledger.clone(),
1304        }
1305        .exists_no_check_on_bool(is_fee_payer, w);
1306        ((), ())
1307    };
1308
1309    Z::Bool::assert_any(
1310        &[
1311            is_last_account_update.neg(),
1312            local_state.will_succeed,
1313            local_state.success.neg(),
1314        ],
1315        w,
1316    )?;
1317
1318    // global state
1319    {
1320        let is_successful_last_party = Z::Bool::and(is_last_account_update, local_state.success, w);
1321        let global_state_supply_increase = global_state.supply_increase();
1322        let supply_increase = Z::SignedAmount::on_if(
1323            is_successful_last_party,
1324            SignedAmountBranchParam {
1325                on_true: &new_global_supply_increase,
1326                on_false: &global_state_supply_increase,
1327            },
1328            w,
1329        );
1330        global_state.set_supply_increase(supply_increase.clone());
1331        global_state.set_second_pass_ledger(is_successful_last_party, &local_state.ledger, w);
1332    };
1333
1334    // local state
1335    {
1336        let will_succeed = w.exists_no_check(match is_last_account_update.as_boolean() {
1337            Boolean::True => Z::Bool::true_(),
1338            Boolean::False => local_state.will_succeed,
1339        });
1340        let account_update_index = w.exists_no_check(match is_last_account_update.as_boolean() {
1341            Boolean::True => Z::Index::zero(),
1342            Boolean::False => local_state.account_update_index.succ(),
1343        });
1344        let success = w.exists_no_check(match is_last_account_update.as_boolean() {
1345            Boolean::True => Z::Bool::true_(),
1346            Boolean::False => local_state.success,
1347        });
1348        let ledger = match is_last_account_update.as_boolean() {
1349            Boolean::True => Z::Ledger::empty(0),
1350            Boolean::False => local_state.ledger.clone(),
1351        }
1352        .exists_no_check(w);
1353        let signed_zero = Z::SignedAmount::of_unsigned(Z::Amount::zero());
1354        let supply_increase = Z::SignedAmount::on_if(
1355            is_last_account_update,
1356            SignedAmountBranchParam {
1357                on_true: &signed_zero,
1358                on_false: &local_state.supply_increase,
1359            },
1360            w,
1361        );
1362
1363        local_state.ledger = ledger;
1364        local_state.success = success;
1365        local_state.account_update_index = account_update_index;
1366        local_state.supply_increase = supply_increase.clone();
1367        local_state.will_succeed = will_succeed;
1368    };
1369
1370    Ok(())
1371}