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 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
87fn 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 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 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, <Z as ZkappApplication>::SignedAmount, <Z as ZkappApplication>::GlobalSlotSinceGenesis, >;
321
322pub type StartData<Z> = StartDataSkeleton<
323 <Z as ZkappApplication>::CallForest, <Z as ZkappApplication>::Bool, >;
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 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 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 {
426 let default_token_or_token_owner_was_caller = {
427 let account_update_token_id = &account_update.body().token_id;
428 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 {
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 {
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 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 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 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 {
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 {
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 {
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 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 {
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 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 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 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 w.exists_no_check(match verification_key {
942 SetOrKeep::Set(_) => true, 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 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 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 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 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 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 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 {
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 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 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 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 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 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 {
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 {
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}