• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MinaProtocol / mina / 411

24 Jul 2025 03:14PM UTC coverage: 33.188% (-27.7%) from 60.871%
411

push

buildkite

web-flow
Merge pull request #17541 from MinaProtocol/brian/merge-compatible-into-develop

Merge compatible into develop

164 of 702 new or added lines in 96 files covered. (23.36%)

18243 existing lines in 393 files now uncovered.

23983 of 72264 relevant lines covered (33.19%)

24667.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.77
/src/lib/mina_generators/zkapp_command_generators.ml
1
(* zkapp_command_generators -- Quickcheck generators for zkApp transactions *)
2

77✔
3
open Core_kernel
4
open Mina_base
5
module Ledger = Mina_ledger.Ledger
6

7
type balance_change_range_t =
8
  { min_balance_change : Currency.Amount.t
9
  ; max_balance_change : Currency.Amount.t
10
  ; min_new_zkapp_balance : Currency.Amount.t
11
  ; max_new_zkapp_balance : Currency.Amount.t
12
  }
13

14
type failure =
×
15
  | Invalid_account_precondition
×
16
  | Invalid_protocol_state_precondition
×
17
  | Update_not_permitted of
×
18
      [ `Delegate
×
19
      | `App_state
20
      | `Voting_for
21
      | `Verification_key
22
      | `Zkapp_uri
23
      | `Token_symbol
24
      | `Send
25
      | `Receive ]
×
26
[@@deriving yojson]
×
27

28
type role =
29
  [ `Fee_payer | `New_account | `Ordinary_participant | `New_token_account ]
30

31
let gen_account_precondition_from_account ?failure
UNCOV
32
    ?(ignore_sequence_events_precond = false) ?(no_account_precondition = false)
×
33
    ?(is_nonce_precondition = false) ~first_use_of_account account =
250✔
34
  let open Quickcheck.Let_syntax in
250✔
UNCOV
35
  if no_account_precondition then return Zkapp_precondition.Account.accept
×
36
  else
37
    let { Account.balance; nonce; delegate; receipt_chain_hash; zkapp; _ } =
250✔
38
      account
39
    in
40
    (* choose constructor *)
41
    let%bind b =
UNCOV
42
      if is_nonce_precondition then return false else Quickcheck.Generator.bool
×
43
    in
44
    if b then
250✔
45
      (* Full *)
46
      let open Zkapp_basic in
120✔
47
      let%bind (predicate_account : Zkapp_precondition.Account.t) =
48
        let%bind balance =
49
          let%bind balance_change_int = Int.gen_uniform_incl 1 10_000_000 in
120✔
50
          let balance_change =
120✔
51
            Currency.Amount.of_nanomina_int_exn balance_change_int
52
          in
53
          let lower =
120✔
54
            match Currency.Balance.sub_amount balance balance_change with
55
            | None ->
29✔
56
                Currency.Balance.zero
57
            | Some bal ->
91✔
58
                bal
59
          in
60
          let upper =
61
            match Currency.Balance.add_amount balance balance_change with
62
            | None ->
×
63
                Currency.Balance.max_int
64
            | Some bal ->
120✔
65
                bal
66
          in
67
          Or_ignore.gen
68
            (return { Zkapp_precondition.Closed_interval.lower; upper })
120✔
69
        in
70
        let%bind nonce =
71
          let%bind nonce_change_int = Int.gen_uniform_incl 1 100 in
120✔
72
          let nonce_change = Account.Nonce.of_int nonce_change_int in
120✔
73
          let lower =
120✔
74
            match Account.Nonce.sub nonce nonce_change with
75
            | None ->
119✔
76
                Account.Nonce.zero
77
            | Some nonce ->
1✔
78
                nonce
79
          in
80
          let upper =
81
            (* Nonce.add doesn't check for overflow, so check here *)
82
            match Account.Nonce.(sub max_value) nonce_change with
120✔
83
            | None ->
×
84
                (* unreachable *)
85
                failwith
86
                  "gen_account_precondition_from: nonce subtraction failed \
87
                   unexpectedly"
88
            | Some n ->
120✔
89
                if Account.Nonce.( < ) n nonce then Account.Nonce.max_value
×
90
                else Account.Nonce.add nonce nonce_change
120✔
91
          in
92
          Or_ignore.gen
93
            (return { Zkapp_precondition.Closed_interval.lower; upper })
120✔
94
        in
95
        let receipt_chain_hash =
120✔
96
          if first_use_of_account then Or_ignore.Check receipt_chain_hash
80✔
97
          else Or_ignore.Ignore
40✔
98
        in
99
        let%bind delegate =
100
          match delegate with
101
          | None ->
15✔
102
              return Or_ignore.Ignore
15✔
103
          | Some pk ->
105✔
104
              Or_ignore.gen (return pk)
105✔
105
        in
106
        let%bind state, action_state, proved_state, is_new =
107
          match zkapp with
108
          | None ->
77✔
109
              let len = Pickles_types.Nat.to_int Zkapp_state.Max_state_size.n in
110
              (* won't raise, correct length given *)
111
              let state =
77✔
112
                Zkapp_state.V.of_list_exn
113
                  (List.init len ~f:(fun _ -> Or_ignore.Ignore))
77✔
114
              in
115
              let action_state = Or_ignore.Ignore in
77✔
116
              let proved_state = Or_ignore.Ignore in
117
              let is_new = Or_ignore.Ignore in
118
              return (state, action_state, proved_state, is_new)
77✔
119
          | Some { Zkapp_account.app_state; action_state; proved_state; _ } ->
43✔
120
              let state =
121
                Zkapp_state.V.map app_state ~f:(fun field ->
122
                    Quickcheck.random_value (Or_ignore.gen (return field)) )
344✔
123
              in
124
              let%bind action_state =
125
                if ignore_sequence_events_precond then return Or_ignore.Ignore
×
126
                else
127
                  (* choose a value from account action state *)
128
                  let fields =
43✔
129
                    Pickles_types.Vector.Vector_5.to_list action_state
130
                  in
131
                  let%bind ndx =
132
                    Int.gen_uniform_incl 0 (List.length fields - 1)
43✔
133
                  in
134
                  return (Or_ignore.Check (List.nth_exn fields ndx))
43✔
135
              in
136
              let proved_state = Or_ignore.Check proved_state in
43✔
137
              let is_new =
138
                (* when we apply the generated Zkapp_command.t, the account is always in the ledger
139
              *)
140
                Or_ignore.Check false
141
              in
142
              return (state, action_state, proved_state, is_new)
143
        in
144
        return
120✔
145
          { Zkapp_precondition.Account.balance
146
          ; nonce
147
          ; receipt_chain_hash
148
          ; delegate
149
          ; state
150
          ; action_state
151
          ; proved_state
152
          ; is_new
153
          }
154
      in
155
      match failure with
120✔
156
      | Some Invalid_account_precondition ->
×
157
          let module Tamperable = struct
158
            type t =
159
              | Balance
160
              | Nonce
161
              | Receipt_chain_hash
162
              | Delegate
163
              | State
164
              | Sequence_state
165
              | Proved_state
166
          end in
167
          let%bind faulty_predicate_account =
168
            (* tamper with account using randomly chosen item *)
169
            let tamperable : Tamperable.t list =
170
              [ Balance
171
              ; Nonce
172
              ; Receipt_chain_hash
173
              ; Delegate
174
              ; State
175
              ; Sequence_state
176
              ; Proved_state
177
              ]
178
            in
179
            match%bind Quickcheck.Generator.of_list tamperable with
×
180
            | Balance ->
×
181
                let new_balance =
182
                  if Currency.Balance.equal balance Currency.Balance.zero then
183
                    Currency.Balance.max_int
×
184
                  else Currency.Balance.zero
×
185
                in
186
                let balance =
187
                  Or_ignore.Check
188
                    { Zkapp_precondition.Closed_interval.lower = new_balance
189
                    ; upper = new_balance
190
                    }
191
                in
192
                return { predicate_account with balance }
193
            | Nonce ->
×
194
                let new_nonce =
195
                  if Account.Nonce.equal nonce Account.Nonce.zero then
196
                    Account.Nonce.max_value
×
197
                  else Account.Nonce.zero
×
198
                in
199
                let%bind nonce =
200
                  Zkapp_precondition.Numeric.gen (return new_nonce)
×
201
                    Account.Nonce.compare
202
                in
203
                return { predicate_account with nonce }
×
204
            | Receipt_chain_hash ->
×
205
                let%bind new_receipt_chain_hash = Receipt.Chain_hash.gen in
206
                let%bind receipt_chain_hash =
207
                  Or_ignore.gen (return new_receipt_chain_hash)
×
208
                in
209
                return { predicate_account with receipt_chain_hash }
×
210
            | Delegate ->
×
211
                let%bind delegate =
212
                  Or_ignore.gen Signature_lib.Public_key.Compressed.gen
×
213
                in
214
                return { predicate_account with delegate }
×
215
            | State ->
×
216
                let fields =
217
                  Zkapp_state.V.to_list predicate_account.state |> Array.of_list
×
218
                in
219
                let%bind ndx = Int.gen_incl 0 (Array.length fields - 1) in
×
220
                let%bind field = Snark_params.Tick.Field.gen in
221
                fields.(ndx) <- Or_ignore.Check field ;
×
222
                let state = Zkapp_state.V.of_list_exn (Array.to_list fields) in
×
223
                return { predicate_account with state }
×
224
            | Sequence_state ->
×
225
                let%bind field = Snark_params.Tick.Field.gen in
226
                let action_state = Or_ignore.Check field in
×
227
                return { predicate_account with action_state }
228
            | Proved_state ->
×
229
                let%bind proved_state =
230
                  match predicate_account.proved_state with
231
                  | Check b ->
×
232
                      return (Or_ignore.Check (not b))
×
233
                  | Ignore ->
×
234
                      return (Or_ignore.Check true)
×
235
                in
236
                return { predicate_account with proved_state }
×
237
          in
238
          return faulty_predicate_account
×
239
      | _ ->
120✔
240
          return predicate_account
241
    else
242
      (* Nonce *)
243
      let { Account.nonce; _ } = account in
130✔
244
      match failure with
UNCOV
245
      | Some Invalid_account_precondition ->
×
UNCOV
246
          return @@ Zkapp_precondition.Account.nonce (Account.Nonce.succ nonce)
×
247
      | _ ->
130✔
248
          return @@ Zkapp_precondition.Account.nonce nonce
130✔
249

250
let gen_fee ?fee_range ~num_updates ~(genesis_constants : Genesis_constants.t)
251
    (account : Account.t) =
252
  let balance = account.balance in
40✔
253
  let lo_fee =
254
    Option.value_exn
255
      Currency.Fee.(
256
        scale genesis_constants.minimum_user_command_fee (num_updates * 2))
40✔
257
  in
258
  let hi_fee = Option.value_exn Currency.Fee.(scale lo_fee 2) in
40✔
259
  assert (
40✔
260
    Currency.(
261
      Fee.(hi_fee <= (Balance.to_amount balance |> Currency.Amount.to_fee))) ) ;
40✔
262
  Option.value_map fee_range ~default:(Currency.Fee.gen_incl lo_fee hi_fee)
40✔
UNCOV
263
    ~f:(fun (lo, hi) -> Currency.Fee.(gen_incl lo hi))
×
264

265
(*Fee payer balance change is Neg*)
266
let fee_to_amt fee =
267
  Currency.Amount.(Signed.of_unsigned (of_fee fee) |> Signed.negate)
40✔
268

269
let gen_balance_change ?permissions_auth (account : Account.t) ?failure
270
    ?balance_change_range ~new_account =
271
  let open Quickcheck.Let_syntax in
169✔
272
  let%bind sgn =
273
    if new_account then return Sgn.Pos
70✔
274
    else
275
      match (failure, permissions_auth) with
99✔
276
      | Some (Update_not_permitted `Send), _ ->
×
277
          return Sgn.Neg
×
278
      | Some (Update_not_permitted `Receive), _ ->
×
279
          return Sgn.Pos
×
280
      | _, Some auth -> (
99✔
281
          match auth with
282
          | Control.Tag.None_given ->
17✔
283
              return Sgn.Pos
17✔
284
          | _ ->
82✔
285
              Quickcheck.Generator.of_list [ Sgn.Pos; Neg ] )
82✔
286
      | _, None ->
×
287
          Quickcheck.Generator.of_list [ Sgn.Pos; Neg ]
×
288
  in
289
  (* if negative, magnitude constrained to balance in account
290
     the effective balance is what's in the account state table,
291
  *)
292
  let effective_balance = account.balance in
169✔
293
  let small_balance_change =
294
    (*make small transfers to allow generating large number of zkapp_command without an overflow*)
295
    let open Currency in
296
    if Balance.(effective_balance < of_mina_string_exn "1.0") && not new_account
70✔
297
    then failwith "account has low balance"
×
298
    else Balance.of_mina_string_exn "0.000001"
169✔
299
  in
300
  let%map (magnitude : Currency.Amount.t) =
301
    Option.value_map balance_change_range
169✔
302
      ~default:
303
        ( if new_account then
304
          Currency.Amount.gen_incl
70✔
305
            (Currency.Amount.of_mina_string_exn "50.0")
70✔
306
            (Currency.Amount.of_mina_string_exn "100.0")
70✔
307
        else
308
          Currency.Amount.gen_incl Currency.Amount.zero
99✔
309
            (Currency.Balance.to_amount small_balance_change) )
99✔
310
      ~f:(fun { min_balance_change
311
              ; max_balance_change
312
              ; min_new_zkapp_balance
313
              ; max_new_zkapp_balance
314
              } ->
UNCOV
315
        if new_account then
×
316
          Currency.Amount.(gen_incl min_new_zkapp_balance max_new_zkapp_balance)
×
UNCOV
317
        else Currency.Amount.(gen_incl min_balance_change max_balance_change) )
×
318
  in
319
  match sgn with
169✔
320
  | Pos ->
127✔
321
      ({ magnitude; sgn = Sgn.Pos } : Currency.Amount.Signed.t)
322
  | Neg ->
42✔
323
      ({ magnitude; sgn = Sgn.Neg } : Currency.Amount.Signed.t)
324

325
let gen_use_full_commitment ~increment_nonce ~account_precondition
326
    ~authorization () : bool Base_quickcheck.Generator.t =
327
  (* check conditions to avoid replays*)
328
  let incr_nonce_and_constrains_nonce =
250✔
329
    increment_nonce
330
    && Zkapp_precondition.Numeric.is_constant
213✔
331
         Zkapp_precondition.Numeric.Tc.nonce
332
         account_precondition.Zkapp_precondition.Account.nonce
333
  in
334
  let does_not_use_a_signature =
335
    Control.(not (Tag.equal (tag authorization) Tag.Signature))
250✔
336
  in
337
  if incr_nonce_and_constrains_nonce || does_not_use_a_signature then
23✔
338
    Bool.quickcheck_generator
135✔
339
  else Quickcheck.Generator.return true
115✔
340

341
let closed_interval_exact value =
342
  Zkapp_precondition.Closed_interval.{ lower = value; upper = value }
232✔
343

344
let gen_epoch_data_predicate
345
    (epoch_data :
346
      ( ( Frozen_ledger_hash.Stable.V1.t
347
        , Currency.Amount.Stable.V1.t )
348
        Epoch_ledger.Poly.Stable.V1.t
349
      , Epoch_seed.Stable.V1.t
350
      , State_hash.Stable.V1.t
351
      , State_hash.Stable.V1.t
352
      , Mina_numbers.Length.Stable.V1.t )
353
      Zkapp_precondition.Protocol_state.Epoch_data.Poly.t ) :
354
    Zkapp_precondition.Protocol_state.Epoch_data.t Base_quickcheck.Generator.t =
355
  let open Quickcheck.Let_syntax in
232✔
356
  let%bind ledger =
357
    let%bind hash =
358
      Zkapp_basic.Or_ignore.gen @@ return epoch_data.ledger.hash
232✔
359
    in
360
    let%map total_currency =
361
      closed_interval_exact epoch_data.ledger.total_currency
362
      |> return |> Zkapp_basic.Or_ignore.gen
232✔
363
    in
364
    { Epoch_ledger.Poly.hash; total_currency }
232✔
365
  in
366
  let%bind seed = Zkapp_basic.Or_ignore.gen @@ return epoch_data.seed in
232✔
367
  let%bind start_checkpoint =
368
    Zkapp_basic.Or_ignore.gen @@ return epoch_data.start_checkpoint
232✔
369
  in
370
  let%bind lock_checkpoint =
371
    Zkapp_basic.Or_ignore.gen @@ return epoch_data.lock_checkpoint
232✔
372
  in
373
  let%map epoch_length =
374
    let open Mina_numbers in
375
    let%bind epsilon1 = Length.gen_incl (Length.of_int 0) (Length.of_int 10) in
232✔
376
    let%bind epsilon2 = Length.gen_incl (Length.of_int 0) (Length.of_int 10) in
232✔
377
    Zkapp_precondition.Closed_interval.
232✔
378
      { lower =
379
          Length.sub epoch_data.epoch_length epsilon1
380
          |> Option.value ~default:Length.zero
232✔
381
      ; upper = Length.add epoch_data.epoch_length epsilon2
232✔
382
      }
383
    |> return |> Zkapp_basic.Or_ignore.gen
232✔
384
  in
385
  { Epoch_data.Poly.ledger
232✔
386
  ; seed
387
  ; start_checkpoint
388
  ; lock_checkpoint
389
  ; epoch_length
390
  }
391

392
let gen_protocol_state_precondition
393
    (psv : Zkapp_precondition.Protocol_state.View.t) :
394
    Zkapp_precondition.Protocol_state.t Base_quickcheck.Generator.t =
395
  let open Quickcheck.Let_syntax in
116✔
396
  let open Zkapp_precondition.Closed_interval in
397
  let%bind snarked_ledger_hash =
398
    Zkapp_basic.Or_ignore.gen @@ return psv.snarked_ledger_hash
116✔
399
  in
400
  let%bind blockchain_length =
401
    let open Mina_numbers in
402
    let%bind epsilon1 = Length.gen_incl (Length.of_int 0) (Length.of_int 10) in
116✔
403
    let%bind epsilon2 = Length.gen_incl (Length.of_int 0) (Length.of_int 10) in
116✔
404
    { lower =
116✔
405
        Length.sub psv.blockchain_length epsilon1
406
        |> Option.value ~default:Length.zero
116✔
407
    ; upper = Length.add psv.blockchain_length epsilon2
116✔
408
    }
409
    |> return |> Zkapp_basic.Or_ignore.gen
116✔
410
  in
411
  let%bind min_window_density =
412
    let open Mina_numbers in
413
    let%bind epsilon1 = Length.gen_incl (Length.of_int 0) (Length.of_int 10) in
116✔
414
    let%bind epsilon2 = Length.gen_incl (Length.of_int 0) (Length.of_int 10) in
116✔
415
    { lower =
116✔
416
        Length.sub psv.min_window_density epsilon1
417
        |> Option.value ~default:Length.zero
116✔
418
    ; upper = Length.add psv.min_window_density epsilon2
116✔
419
    }
420
    |> return |> Zkapp_basic.Or_ignore.gen
116✔
421
  in
422
  let%bind total_currency =
423
    let open Currency in
424
    let%bind epsilon1 =
425
      Amount.gen_incl Amount.zero (Amount.of_mina_int_exn 1)
116✔
426
    in
427
    let%bind epsilon2 =
428
      Amount.gen_incl Amount.zero (Amount.of_mina_int_exn 1)
116✔
429
    in
430
    { lower =
116✔
431
        Amount.sub psv.total_currency epsilon1
432
        |> Option.value ~default:Amount.zero
116✔
433
    ; upper =
434
        Amount.add psv.total_currency epsilon2
435
        |> Option.value ~default:psv.total_currency
116✔
436
    }
437
    |> return |> Zkapp_basic.Or_ignore.gen
116✔
438
  in
439
  let%bind global_slot_since_genesis =
440
    let open Mina_numbers in
441
    let%bind epsilon1 =
442
      Global_slot_span.gen_incl
116✔
443
        (Global_slot_span.of_int 0)
116✔
444
        (Global_slot_span.of_int 10)
116✔
445
    in
446
    let%bind epsilon2 =
447
      Global_slot_span.gen_incl
116✔
448
        (Global_slot_span.of_int 0)
116✔
449
        (Global_slot_span.of_int 10)
116✔
450
    in
451
    { lower =
116✔
452
        Global_slot_since_genesis.sub psv.global_slot_since_genesis epsilon1
453
        |> Option.value ~default:Global_slot_since_genesis.zero
116✔
454
    ; upper =
455
        Global_slot_since_genesis.add psv.global_slot_since_genesis epsilon2
116✔
456
    }
457
    |> return |> Zkapp_basic.Or_ignore.gen
116✔
458
  in
459
  let%bind staking_epoch_data =
460
    gen_epoch_data_predicate psv.staking_epoch_data
116✔
461
  in
462
  let%map next_epoch_data = gen_epoch_data_predicate psv.next_epoch_data in
116✔
463
  { Zkapp_precondition.Protocol_state.Poly.snarked_ledger_hash
116✔
464
  ; blockchain_length
465
  ; min_window_density
466
  ; total_currency
467
  ; global_slot_since_genesis
468
  ; staking_epoch_data
469
  ; next_epoch_data
470
  }
471

472
let gen_invalid_protocol_state_precondition
473
    (psv : Zkapp_precondition.Protocol_state.View.t) :
474
    Zkapp_precondition.Protocol_state.t Base_quickcheck.Generator.t =
475
  let module Tamperable = struct
×
476
    type t =
477
      | Blockchain_length
478
      | Min_window_density
479
      | Total_currency
480
      | Global_slot_since_genesis
481
  end in
482
  let open Quickcheck.Let_syntax in
483
  let open Zkapp_precondition.Closed_interval in
484
  let protocol_state_precondition = Zkapp_precondition.Protocol_state.accept in
485
  let%bind lower = Bool.quickcheck_generator in
486
  match%bind
487
    Quickcheck.Generator.of_list
×
488
      ( [ Blockchain_length
489
        ; Min_window_density
490
        ; Total_currency
491
        ; Global_slot_since_genesis
492
        ]
493
        : Tamperable.t list )
494
  with
495
  | Blockchain_length ->
×
496
      let open Mina_numbers in
497
      let%map blockchain_length =
498
        let%map epsilon = Length.(gen_incl (of_int 1) (of_int 10)) in
×
499
        if lower || Length.(psv.blockchain_length > epsilon) then
×
500
          { lower = Length.zero
×
501
          ; upper =
502
              Length.sub psv.blockchain_length epsilon
503
              |> Option.value ~default:Length.zero
×
504
          }
505
        else
506
          { lower = Length.add psv.blockchain_length epsilon
×
507
          ; upper = Length.max_value
508
          }
509
      in
510
      { protocol_state_precondition with
×
511
        blockchain_length = Zkapp_basic.Or_ignore.Check blockchain_length
512
      }
513
  | Min_window_density ->
×
514
      let open Mina_numbers in
515
      let%map min_window_density =
516
        let%map epsilon = Length.(gen_incl (of_int 1) (of_int 10)) in
×
517
        if lower || Length.(psv.min_window_density > epsilon) then
×
518
          { lower = Length.zero
×
519
          ; upper =
520
              Length.sub psv.min_window_density epsilon
521
              |> Option.value ~default:Length.zero
×
522
          }
523
        else
524
          { lower = Length.add psv.blockchain_length epsilon
×
525
          ; upper = Length.max_value
526
          }
527
      in
528
      { protocol_state_precondition with
×
529
        min_window_density = Zkapp_basic.Or_ignore.Check min_window_density
530
      }
531
  | Total_currency ->
×
532
      let open Currency in
533
      let%map total_currency =
534
        let%map epsilon =
535
          Amount.(gen_incl (of_nanomina_int_exn 1_000) (of_mina_int_exn 1))
×
536
        in
537
        if lower || Amount.(psv.total_currency > epsilon) then
×
538
          { lower = Amount.zero
×
539
          ; upper =
540
              Amount.sub psv.total_currency epsilon
541
              |> Option.value ~default:Amount.zero
×
542
          }
543
        else
544
          { lower =
×
545
              Amount.add psv.total_currency epsilon
546
              |> Option.value ~default:Amount.max_int
×
547
          ; upper = Amount.max_int
548
          }
549
      in
550
      { protocol_state_precondition with
×
551
        total_currency = Zkapp_basic.Or_ignore.Check total_currency
552
      }
553
  | Global_slot_since_genesis ->
×
554
      let open Mina_numbers in
555
      let%map global_slot_since_genesis =
556
        let%map epsilon = Global_slot_span.(gen_incl (of_int 1) (of_int 10)) in
×
557
        let increment =
×
558
          Global_slot_span.to_uint32 epsilon
559
          |> Global_slot_since_genesis.of_uint32
×
560
        in
561
        if
×
562
          lower
×
563
          || Global_slot_since_genesis.(
564
               psv.global_slot_since_genesis > increment)
×
565
        then
566
          { lower = Global_slot_since_genesis.zero
×
567
          ; upper =
568
              Global_slot_since_genesis.sub psv.global_slot_since_genesis
569
                epsilon
570
              |> Option.value ~default:Global_slot_since_genesis.zero
×
571
          }
572
        else
573
          { lower =
×
574
              Global_slot_since_genesis.add psv.global_slot_since_genesis
×
575
                epsilon
576
          ; upper = Global_slot_since_genesis.max_value
577
          }
578
      in
579
      { protocol_state_precondition with
×
580
        global_slot_since_genesis =
581
          Zkapp_basic.Or_ignore.Check global_slot_since_genesis
582
      }
583

584
module Account_update_body_components = struct
585
  type ( 'pk
586
       , 'update
587
       , 'token_id
588
       , 'amount
589
       , 'events
590
       , 'actions
591
       , 'call_data
592
       , 'int
593
       , 'bool
594
       , 'protocol_state_precondition
595
       , 'account_precondition
596
       , 'valid_while_precondition
597
       , 'may_use_token
598
       , 'authorization_kind )
599
       t =
600
    { public_key : 'pk
601
    ; update : 'update
602
    ; token_id : 'token_id
603
    ; balance_change : 'amount
604
    ; increment_nonce : 'bool
605
    ; events : 'events
606
    ; actions : 'actions
607
    ; call_data : 'call_data
608
    ; call_depth : 'int
609
    ; protocol_state_precondition : 'protocol_state_precondition
610
    ; account_precondition : 'account_precondition
611
    ; valid_while_precondition : 'valid_while_precondition
612
    ; use_full_commitment : 'bool
613
    ; may_use_token : 'may_use_token
614
    ; authorization_kind : 'authorization_kind
615
    }
616

617
  let to_fee_payer t : Account_update.Body.Fee_payer.t =
618
    { public_key = t.public_key
40✔
619
    ; fee = t.balance_change
620
    ; valid_until =
621
        ( match
622
            t.protocol_state_precondition
623
              .Zkapp_precondition.Protocol_state.Poly.global_slot_since_genesis
624
          with
625
        | Zkapp_basic.Or_ignore.Ignore ->
40✔
626
            None
627
        | Zkapp_basic.Or_ignore.Check
×
628
            { Zkapp_precondition.Closed_interval.upper; _ } ->
629
            Some upper )
630
    ; nonce = t.account_precondition
631
    }
632

633
  let to_typical_account_update t : Account_update.Body.Simple.t =
634
    { public_key = t.public_key
250✔
635
    ; update = t.update
636
    ; token_id = t.token_id
637
    ; balance_change = t.balance_change
638
    ; increment_nonce = t.increment_nonce
639
    ; events = t.events
640
    ; actions = t.actions
641
    ; call_data = t.call_data
642
    ; call_depth = t.call_depth
643
    ; preconditions =
644
        { Account_update.Preconditions.network = t.protocol_state_precondition
645
        ; account = t.account_precondition
646
        ; valid_while = t.valid_while_precondition
647
        }
648
    ; use_full_commitment = t.use_full_commitment
649
    ; implicit_account_creation_fee = false
650
    ; may_use_token = t.may_use_token
651
    ; authorization_kind = t.authorization_kind
652
    }
653
end
654

655
(* The type `a` is associated with the `delta` field, which is an unsigned fee
656
   for the fee payer, and a signed amount for other zkapp_command.
657
   The type `b` is associated with the `use_full_commitment` field, which is
658
   `unit` for the fee payer, and `bool` for other zkapp_command.
659
   The type `c` is associated with the `token_id` field, which is `unit` for the
660
   fee payer, and `Token_id.t` for other zkapp_command.
661
   The type `d` is associated with the `account_precondition` field, which is
662
   a nonce for the fee payer, and `Account_precondition.t` for other zkapp_command
663
*)
664
let gen_account_update_body_components (type a b c d) ?global_slot
665
    ?(update = None) ?account_id ?token_id ?may_use_token ?account_ids_seen
40✔
666
    ~account_state_tbl ?vk ?failure ?(new_account = false)
40✔
667
    ?(zkapp_account = false) ?(is_fee_payer = false) ?available_public_keys
40✔
668
    ?permissions_auth ?(required_balance_change : a option) ?protocol_state_view
669
    ~zkapp_account_ids
670
    ~(gen_balance_change : Account.t -> a Quickcheck.Generator.t)
671
    ~(gen_use_full_commitment :
672
          account_precondition:Account_update.Account_precondition.t
673
       -> b Quickcheck.Generator.t )
674
    ~(f_balance_change : a -> Currency.Amount.Signed.t)
675
    ~(increment_nonce : b * bool) ~(f_token_id : Token_id.t -> c)
676
    ~(f_account_precondition :
677
       first_use_of_account:bool -> Account.t -> d Quickcheck.Generator.t )
678
    ~(f_account_update_account_precondition :
679
       d -> Account_update.Account_precondition.t ) ~authorization_tag () :
680
    (_, _, _, a, _, _, _, _, b, _, d, _, _, _) Account_update_body_components.t
681
    Quickcheck.Generator.t =
682
  let open Quickcheck.Let_syntax in
290✔
683
  (* fee payers have to be in the ledger *)
684
  assert (not (is_fee_payer && new_account)) ;
40✔
685
  let token_account = match token_id with None -> false | Some _ -> true in
41✔
686
  let%bind update =
687
    match update with
688
    | None ->
290✔
689
        Account_update.Update.gen ?permissions_auth ?vk ~zkapp_account
290✔
690
          ~token_account ()
691
    | Some update ->
×
692
        return update
×
693
  in
694
  (* account_update_increment_nonce for fee payer is unit and increment_nonce is true *)
695
  let account_update_increment_nonce, increment_nonce = increment_nonce in
290✔
696
  let verification_key =
697
    Option.value vk
698
      ~default:
699
        With_hash.
700
          { data = Pickles.Side_loaded.Verification_key.dummy
701
          ; hash = Zkapp_account.dummy_vk_hash ()
290✔
702
          }
703
  in
704
  let%bind account =
705
    if new_account then (
70✔
706
      if Option.is_some account_id then
707
        failwith
×
708
          "gen_account_update_body: new account_update is true, but an account \
709
           id, presumably from an existing account, was supplied" ;
710
      match available_public_keys with
70✔
711
      | None ->
×
712
          failwith
713
            "gen_account_update_body: new_account is true, but \
714
             available_public_keys not provided"
715
      | Some available_pks ->
70✔
716
          let available_pk =
717
            match
718
              Hash_set.fold_until ~init:None
719
                ~f:(fun _ x -> Stop (Some x))
70✔
720
                ~finish:ident available_pks
721
            with
722
            | None ->
×
723
                failwith "gen_account_update_body: no available public keys"
724
            | Some pk ->
70✔
725
                pk
726
          in
727
          (* available public key no longer available *)
728
          Hash_set.remove available_pks available_pk ;
729
          let account_id =
70✔
730
            match token_id with
731
            | Some custom_token_id ->
41✔
732
                Account_id.create available_pk custom_token_id
41✔
733
            | None ->
29✔
734
                Account_id.create available_pk Token_id.default
29✔
735
          in
736
          let account_with_pk =
737
            Account.create account_id Currency.Balance.zero
738
          in
739
          let account =
70✔
740
            if zkapp_account then
741
              { account_with_pk with
×
742
                zkapp =
743
                  Some
744
                    { Zkapp_account.default with
745
                      verification_key = Some verification_key
746
                    }
747
              }
748
            else account_with_pk
70✔
749
          in
750
          return account )
70✔
751
    else
752
      match account_id with
220✔
753
      | None ->
116✔
754
          if zkapp_account then
755
            let%map zkapp_account_id =
756
              Quickcheck.Generator.of_list zkapp_account_ids
6✔
757
            in
758
            match Account_id.Table.find account_state_tbl zkapp_account_id with
6✔
759
            | None ->
×
760
                failwith "gen_account_update_body: fail to find zkapp account"
761
            | Some (_, `Fee_payer)
×
762
            | Some (_, `New_account)
×
763
            | Some (_, `New_token_account) ->
×
764
                failwith
765
                  "gen_account_update_body: all zkapp accounts were new \
766
                   accounts or used as fee_payer accounts"
767
            | Some (acct, `Ordinary_participant) ->
6✔
768
                acct
769
          else
770
            let accts =
110✔
771
              Account_id.Table.filteri account_state_tbl
772
                ~f:(fun ~key:_ ~data:(_, role) ->
773
                  match (authorization_tag, role) with
949✔
774
                  | _, `Fee_payer ->
110✔
775
                      false
776
                  | Control.Tag.Proof, `New_account ->
×
777
                      false
778
                  | _, `New_token_account ->
13✔
779
                      false
780
                  | _, `New_account ->
60✔
781
                      (* `required_balance_change` is only for balancing account_update. Newly created account
782
                         should not be used in balancing account_update *)
783
                      Option.is_none required_balance_change
784
                  | _, `Ordinary_participant ->
766✔
785
                      true )
786
              |> Account_id.Table.data
110✔
787
            in
788
            Quickcheck.Generator.of_list accts >>| fst
110✔
789
      | Some account_id ->
104✔
790
          (*get the latest state of the account*)
791
          let acct =
792
            Account_id.Table.find_exn account_state_tbl account_id |> fst
104✔
793
          in
794
          if zkapp_account && Option.is_none acct.zkapp then
6✔
795
            failwith
×
796
              "gen_account_update_body: provided account has no zkapp field" ;
797
          return acct
104✔
798
  in
799
  let public_key = account.public_key in
290✔
800
  let token_id = account.token_id in
801
  let%bind balance_change =
802
    match required_balance_change with
803
    | Some bal_change ->
81✔
804
        return bal_change
81✔
805
    | None ->
209✔
806
        gen_balance_change account
209✔
807
  in
808
  let field_array_list_gen ~max_array_len ~max_list_len =
290✔
809
    let array_gen =
580✔
810
      let%bind array_len = Int.gen_uniform_incl 0 max_array_len in
580✔
811
      let%map fields =
812
        Quickcheck.Generator.list_with_length array_len
314✔
813
          Snark_params.Tick.Field.gen
814
      in
815
      Array.of_list fields
314✔
816
    in
817
    let%bind list_len = Int.gen_uniform_incl 0 max_list_len in
580✔
818
    Quickcheck.Generator.list_with_length list_len array_gen
580✔
819
  in
820
  let%bind events = field_array_list_gen ~max_array_len:2 ~max_list_len:1 in
821
  let%bind actions = field_array_list_gen ~max_array_len:2 ~max_list_len:1 in
822
  let%bind call_data = Snark_params.Tick.Field.gen in
823
  let first_use_of_account =
290✔
824
    let account_id = Account_id.create public_key token_id in
825
    match account_ids_seen with
290✔
826
    | None ->
40✔
827
        (* fee payer *)
828
        true
829
    | Some hash_set ->
250✔
830
        (* other account_updates *)
831
        not @@ Hash_set.mem hash_set account_id
250✔
832
  in
833
  let%bind account_precondition =
834
    f_account_precondition ~first_use_of_account account
290✔
835
  in
836
  (* update the depth when generating `account_updates` in Zkapp_command.t *)
837
  let call_depth = 0 in
290✔
838
  let%bind use_full_commitment =
839
    let full_account_precondition =
840
      f_account_update_account_precondition account_precondition
841
    in
842
    gen_use_full_commitment ~account_precondition:full_account_precondition
290✔
843
  in
844
  let%map protocol_state_precondition =
845
    Option.value_map protocol_state_view
290✔
846
      ~f:
847
        ( match failure with
848
        | Some Invalid_protocol_state_precondition ->
×
849
            gen_invalid_protocol_state_precondition
850
        | _ ->
290✔
851
            gen_protocol_state_precondition )
852
      ~default:(return Zkapp_precondition.Protocol_state.accept)
290✔
853
  and valid_while_precondition =
854
    match global_slot with
855
    | None ->
290✔
856
        return Zkapp_basic.Or_ignore.Ignore
290✔
857
    | Some global_slot ->
×
858
        let open Mina_numbers in
859
        let%bind epsilon1 =
860
          Global_slot_span.gen_incl
×
861
            (Global_slot_span.of_int 0)
×
862
            (Global_slot_span.of_int 10)
×
863
        in
864
        let%bind epsilon2 =
865
          Global_slot_span.gen_incl
×
866
            (Global_slot_span.of_int 0)
×
867
            (Global_slot_span.of_int 10)
×
868
        in
869
        Zkapp_precondition.Closed_interval.
×
870
          { lower =
871
              Global_slot_since_genesis.sub global_slot epsilon1
872
              |> Option.value ~default:Global_slot_since_genesis.zero
×
873
          ; upper = Global_slot_since_genesis.add global_slot epsilon2
×
874
          }
875
        |> return |> Zkapp_basic.Or_ignore.gen
×
876
  and may_use_token =
877
    match may_use_token with
878
    | None ->
40✔
879
        Account_update.May_use_token.quickcheck_generator
880
    | Some may_use_token ->
250✔
881
        return may_use_token
250✔
882
  in
883
  let token_id = f_token_id token_id in
290✔
884
  let authorization_kind =
290✔
885
    match authorization_tag with
886
    | Control.Tag.None_given ->
31✔
887
        Account_update.Authorization_kind.None_given
888
    | Signature ->
253✔
889
        Signature
890
    | Proof ->
6✔
891
        Proof (With_hash.hash verification_key)
6✔
892
  in
893
  (* update account state table with all the changes*)
894
  (let add_balance_and_balance_change balance
895
       (balance_change : (Currency.Amount.t, Sgn.t) Currency.Signed_poly.t) =
896
     match balance_change.sgn with
290✔
897
     | Pos -> (
133✔
898
         match Currency.Balance.add_amount balance balance_change.magnitude with
899
         | Some bal ->
133✔
900
             bal
901
         | None ->
×
902
             failwith "add_balance_and_balance_change: overflow for sum" )
903
     | Neg -> (
157✔
904
         match Currency.Balance.sub_amount balance balance_change.magnitude with
905
         | Some bal ->
157✔
906
             bal
907
         | None ->
×
908
             failwith "add_balance_and_balance_change: underflow for difference"
909
         )
910
   in
911
   let balance_change = f_balance_change balance_change in
912
   let nonce_incr n = if increment_nonce then Account.Nonce.succ n else n in
37✔
913
   let value_to_be_updated (type a) (c : a Zkapp_basic.Set_or_keep.t)
914
       ~(default : a) : a =
915
     match c with Zkapp_basic.Set_or_keep.Set x -> x | Keep -> default
462✔
916
   in
917
   let delegate (account : Account.t) =
918
     if is_fee_payer then account.delegate
40✔
919
     else
920
       Option.map
250✔
921
         ~f:(fun delegate ->
922
           value_to_be_updated update.delegate ~default:delegate )
209✔
923
         account.delegate
924
   in
925
   let zkapp (account : Account.t) =
926
     if is_fee_payer then account.zkapp
40✔
927
     else
928
       match account.zkapp with
250✔
929
       | None ->
158✔
930
           None
931
       | Some zk ->
92✔
932
           let app_state =
933
             let account_app_state = zk.app_state in
934
             List.zip_exn
935
               (Zkapp_state.V.to_list update.app_state)
92✔
936
               (Zkapp_state.V.to_list account_app_state)
92✔
937
             |> List.map ~f:(fun (to_be_updated, current) ->
92✔
938
                    value_to_be_updated to_be_updated ~default:current )
736✔
939
             |> Zkapp_state.V.of_list_exn
92✔
940
           in
941
           let action_state =
942
             let last_action_slot = zk.last_action_slot in
943
             let txn_global_slot =
944
               Option.value_map protocol_state_view ~default:last_action_slot
945
                 ~f:(fun ps ->
946
                   ps
40✔
947
                     .Zkapp_precondition.Protocol_state.Poly
948
                      .global_slot_since_genesis )
949
             in
950
             let action_state, _last_action_slot =
92✔
951
               Mina_ledger.Ledger.update_action_state zk.action_state
952
                 (Zkapp_account.Actions.of_event_list actions)
92✔
953
                 ~txn_global_slot ~last_action_slot
954
             in
955
             action_state
92✔
956
           in
957
           let proved_state =
958
             let keeping_app_state =
959
               List.for_all ~f:Fn.id
960
                 (List.map ~f:Zkapp_basic.Set_or_keep.is_keep
92✔
961
                    (Pickles_types.Vector.to_list update.app_state) )
92✔
962
             in
963
             let changing_entire_app_state =
92✔
964
               List.for_all ~f:Fn.id
965
                 (List.map ~f:Zkapp_basic.Set_or_keep.is_set
92✔
966
                    (Pickles_types.Vector.to_list update.app_state) )
92✔
967
             in
968
             let proof_verifies = Control.Tag.(equal Proof authorization_tag) in
92✔
UNCOV
969
             if keeping_app_state then zk.proved_state
×
970
             else if proof_verifies then
92✔
UNCOV
971
               if changing_entire_app_state then true else zk.proved_state
×
972
             else false
86✔
973
           in
974
           Some { zk with app_state; action_state; proved_state }
975
   in
976
   Account_id.Table.update account_state_tbl (Account.identifier account)
290✔
977
     ~f:(function
978
     | None ->
70✔
979
         (* new entry in table *)
980
         ( { account with
981
             balance =
982
               add_balance_and_balance_change account.balance balance_change
70✔
983
           ; nonce = nonce_incr account.nonce
70✔
984
           ; delegate = delegate account
70✔
985
           ; zkapp = zkapp account
70✔
986
           }
987
         , if token_account then `New_token_account else `New_account )
29✔
988
     | Some (updated_account, role) ->
220✔
989
         (* update entry in table *)
990
         ( { updated_account with
991
             balance =
992
               add_balance_and_balance_change updated_account.balance
220✔
993
                 balance_change
994
           ; nonce = nonce_incr updated_account.nonce
220✔
995
           ; delegate = delegate updated_account
220✔
996
           ; zkapp = zkapp updated_account
220✔
997
           }
998
         , role ) ) ) ;
999
  { Account_update_body_components.public_key
1000
  ; update =
1001
      ( if new_account then
1002
        { update with
70✔
1003
          verification_key = Zkapp_basic.Set_or_keep.Set verification_key
1004
        }
1005
      else update )
220✔
1006
  ; token_id
1007
  ; balance_change
1008
  ; increment_nonce = account_update_increment_nonce
1009
  ; events
1010
  ; actions
1011
  ; call_data
1012
  ; call_depth
1013
  ; protocol_state_precondition
1014
  ; account_precondition
1015
  ; valid_while_precondition
1016
  ; use_full_commitment
1017
  ; may_use_token
1018
  ; authorization_kind
1019
  }
1020

1021
let gen_account_update_from ?(no_account_precondition = false)
×
1022
    ?balance_change_range ?global_slot ?(update = None) ?failure
122✔
1023
    ?(new_account = false) ?(zkapp_account = false) ?account_id ?token_id
105✔
1024
    ?may_use_token ?permissions_auth ?required_balance_change ~zkapp_account_ids
1025
    ~authorization ~account_ids_seen ~available_public_keys ~account_state_tbl
1026
    ?protocol_state_view ?vk ~ignore_sequence_events_precond () =
1027
  let open Quickcheck.Let_syntax in
250✔
1028
  let increment_nonce =
1029
    (* permissions_auth is used to generate updated permissions consistent with a contemplated authorization;
1030
       allow incrementing the nonce only if we know the authorization will be Signature
1031
    *)
1032
    match permissions_auth with
1033
    | Some tag -> (
250✔
1034
        match tag with
1035
        | Control.Tag.Signature ->
213✔
1036
            true
1037
        | Proof | None_given ->
6✔
1038
            false )
1039
    | None ->
×
1040
        false
1041
  in
1042
  let%bind body_components =
1043
    gen_account_update_body_components ?global_slot ~update ?failure
250✔
1044
      ~new_account ~zkapp_account
1045
      ~increment_nonce:(increment_nonce, increment_nonce)
1046
      ?permissions_auth ?account_id ?token_id ?may_use_token
1047
      ?protocol_state_view ?vk ~zkapp_account_ids ~account_ids_seen
1048
      ~available_public_keys ?required_balance_change ~account_state_tbl
1049
      ~gen_balance_change:
1050
        (gen_balance_change ?permissions_auth ~new_account ?failure
1051
           ?balance_change_range )
1052
      ~f_balance_change:Fn.id () ~f_token_id:Fn.id
1053
      ~f_account_precondition:(fun ~first_use_of_account acct ->
1054
        gen_account_precondition_from_account ~ignore_sequence_events_precond
250✔
1055
          ~no_account_precondition ~first_use_of_account acct )
1056
      ~f_account_update_account_precondition:Fn.id
1057
      ~gen_use_full_commitment:(fun ~account_precondition ->
1058
        gen_use_full_commitment ~increment_nonce ~account_precondition
250✔
1059
          ~authorization () )
1060
      ~authorization_tag:(Control.tag authorization)
250✔
1061
  in
1062
  let body =
250✔
1063
    Account_update_body_components.to_typical_account_update body_components
1064
  in
1065
  let account_id = Account_id.create body.public_key body.token_id in
250✔
1066
  Hash_set.add account_ids_seen account_id ;
250✔
1067
  return @@ Account_update.with_no_aux ~body ~authorization
250✔
1068

1069
(* takes an account id, if we want to sign this data *)
1070
let gen_account_update_body_fee_payer ?global_slot ?fee_range ?failure
1071
    ?permissions_auth ~account_id ?vk ?protocol_state_view ~account_state_tbl
1072
    ~num_account_updates ~genesis_constants () :
1073
    Account_update.Body.Fee_payer.t Quickcheck.Generator.t =
1074
  let open Quickcheck.Let_syntax in
40✔
1075
  let account_precondition_gen (account : Account.t) =
1076
    Quickcheck.Generator.return account.nonce
40✔
1077
  in
1078
  let%map body_components =
1079
    gen_account_update_body_components ?global_slot ?failure ?permissions_auth
40✔
1080
      ~account_id ~account_state_tbl ?vk ~zkapp_account_ids:[]
1081
      ~is_fee_payer:true ~increment_nonce:((), true)
1082
      ~gen_balance_change:
1083
        (gen_fee ?fee_range ~num_updates:num_account_updates ~genesis_constants)
1084
      ~f_balance_change:fee_to_amt
1085
      ~f_token_id:(fun token_id ->
1086
        (* make sure the fee payer's token id is the default,
1087
           which is represented by the unit value in the body
1088
        *)
1089
        assert (Token_id.equal token_id Token_id.default) ;
40✔
1090
        () )
1091
      ~f_account_precondition:(fun ~first_use_of_account:_ acct ->
1092
        account_precondition_gen acct )
40✔
1093
      ~f_account_update_account_precondition:(fun nonce ->
1094
        Zkapp_precondition.Account.nonce nonce )
40✔
1095
      ~gen_use_full_commitment:(fun ~account_precondition:_ -> return ())
40✔
1096
      ?protocol_state_view ~authorization_tag:Control.Tag.Signature ()
1097
  in
1098
  Account_update_body_components.to_fee_payer body_components
40✔
1099

1100
let gen_fee_payer ?global_slot ?fee_range ?failure ?permissions_auth ~account_id
1101
    ?protocol_state_view ?vk ~account_state_tbl ~num_account_updates
1102
    ~genesis_constants () : Account_update.Fee_payer.t Quickcheck.Generator.t =
1103
  let open Quickcheck.Let_syntax in
40✔
1104
  let%map body =
1105
    gen_account_update_body_fee_payer ?global_slot ?fee_range ?failure
40✔
1106
      ?permissions_auth ~account_id ?vk ?protocol_state_view ~account_state_tbl
1107
      ~num_account_updates ~genesis_constants ()
1108
  in
1109
  (* real signature to be added when this data inserted into a Zkapp_command.t *)
1110
  let authorization = Signature.dummy in
40✔
1111
  Account_update.Fee_payer.make ~body ~authorization
1112

1113
(* keep max_account_updates small, so zkApp integration tests don't need lots
1114
   of block producers
1115

1116
   because the other zkapp_command are split into a permissions-setter
1117
   and another account_update, the actual number of other zkapp_command is
1118
   twice this value, plus one, for the "balancing" account_update
1119

1120
   when we have separate transaction accounts in integration tests
1121
   this number can be increased
1122
*)
1123
let max_account_updates = 2
1124

1125
let max_token_updates = 2
1126

1127
let proof_cache_db = Proof_cache_tag.For_tests.create_db ()
77✔
1128

1129
let gen_zkapp_command_from ?global_slot ?memo ?(no_account_precondition = false)
40✔
1130
    ?fee_range ?balance_change_range ?(ignore_sequence_events_precond = false)
40✔
1131
    ?(no_token_accounts = false) ?(limited = false)
40✔
1132
    ?(generate_new_accounts = true) ?failure
40✔
1133
    ?(max_account_updates = max_account_updates)
20✔
1134
    ?(max_token_updates = max_token_updates) ?(map_account_update = ident)
20✔
1135
    ~(fee_payer_keypair : Signature_lib.Keypair.t)
1136
    ~(keymap :
1137
       Signature_lib.Private_key.t Signature_lib.Public_key.Compressed.Map.t )
1138
    ?account_state_tbl ~ledger ?protocol_state_view ?vk ?available_public_keys
1139
    ~genesis_constants
1140
    ~(constraint_constants : Genesis_constants.Constraint_constants.t) () :
1141
    Zkapp_command.t Quickcheck.Generator.t =
1142
  let signature_kind = Mina_signature_kind.t_DEPRECATED in
40✔
1143
  let open Quickcheck.Let_syntax in
1144
  let fee_payer_pk =
1145
    Signature_lib.Public_key.compress fee_payer_keypair.public_key
1146
  in
1147
  let fee_payer_acct_id = Account_id.create fee_payer_pk Token_id.default in
40✔
1148
  let ledger_accounts = lazy (Ledger.to_list_sequential ledger) in
40✔
1149
  (* table of public keys to accounts, updated when generating each account_update
1150

1151
     a Map would be more principled, but threading that map through the code
1152
     adds complexity
1153
  *)
1154
  let account_state_tbl =
1155
    Option.value account_state_tbl ~default:(Account_id.Table.create ())
40✔
1156
  in
1157
  if not limited then
40✔
1158
    (* make sure all ledger keys are in the keymap *)
1159
    List.iter (Lazy.force ledger_accounts) ~f:(fun acct ->
40✔
1160
        let acct_id = Account.identifier acct in
320✔
1161
        (*Initialize account states*)
1162
        Account_id.Table.update account_state_tbl acct_id ~f:(function
320✔
1163
          | None ->
320✔
1164
              if Account_id.equal acct_id fee_payer_acct_id then
1165
                (acct, `Fee_payer)
40✔
1166
              else (acct, `Ordinary_participant)
280✔
UNCOV
1167
          | Some a ->
×
1168
              a ) ) ;
1169
  List.iter (Account_id.Table.keys account_state_tbl) ~f:(fun id ->
40✔
1170
      let pk = Account_id.public_key id in
320✔
1171
      if Option.is_none (Signature_lib.Public_key.Compressed.Map.find keymap pk)
320✔
1172
      then
1173
        failwithf
×
1174
          "gen_zkapp_command_from: public key %s is in ledger, but not keymap"
1175
          (Signature_lib.Public_key.Compressed.to_base58_check pk)
×
1176
          () ) ;
1177
  (* table of public keys not in the ledger, to be used for new zkapp_command
1178
     we have the corresponding private keys, so we can create signatures for those new zkapp_command
1179
  *)
1180
  let available_public_keys =
40✔
1181
    match available_public_keys with
1182
    | Some pks ->
×
1183
        pks
1184
    | None ->
40✔
1185
        let ledger_account_ids =
1186
          List.map (Lazy.force ledger_accounts) ~f:Account.identifier
40✔
1187
          |> Account_id.Set.of_list
40✔
1188
        in
1189
        let ledger_account_list =
40✔
1190
          Account_id.Set.union_list
1191
            [ ledger_account_ids
1192
            ; Account_id.Set.of_hashtbl_keys account_state_tbl
40✔
1193
            ]
1194
          |> Account_id.Set.to_list
40✔
1195
        in
1196
        let ledger_pk_list =
40✔
1197
          List.map ledger_account_list ~f:(fun account_id ->
1198
              Account_id.public_key account_id )
320✔
1199
        in
1200
        let ledger_pk_set =
40✔
1201
          Signature_lib.Public_key.Compressed.Set.of_list ledger_pk_list
1202
        in
1203
        let tbl = Signature_lib.Public_key.Compressed.Hash_set.create () in
40✔
1204
        Signature_lib.Public_key.Compressed.Map.iter_keys keymap ~f:(fun pk ->
40✔
1205
            if
640✔
1206
              not (Signature_lib.Public_key.Compressed.Set.mem ledger_pk_set pk)
640✔
1207
            then Hash_set.strict_add_exn tbl pk ) ;
320✔
1208
        tbl
40✔
1209
  in
1210
  (* account ids seen, to generate receipt chain hash precondition only if
1211
     a account_update with a given account id has not been encountered before
1212
  *)
1213
  let account_ids_seen = Account_id.Hash_set.create () in
1214
  let%bind num_zkapp_command = Int.gen_uniform_incl 1 max_account_updates in
40✔
1215
  let%bind fee_payer =
1216
    gen_fee_payer ?global_slot ?fee_range ?failure
40✔
1217
      ~permissions_auth:Control.Tag.Signature ~account_id:fee_payer_acct_id ?vk
1218
      ~account_state_tbl ~num_account_updates:num_zkapp_command
1219
      ~genesis_constants ()
1220
  in
1221
  let zkapp_account_ids =
40✔
1222
    Account_id.Table.filteri account_state_tbl ~f:(fun ~key:_ ~data:(a, role) ->
1223
        match role with
320✔
UNCOV
1224
        | `Fee_payer | `New_account | `New_token_account ->
×
1225
            false
1226
        | `Ordinary_participant ->
280✔
1227
            Option.is_some a.zkapp )
1228
    |> Account_id.Table.keys
40✔
1229
  in
1230
  Hash_set.add account_ids_seen fee_payer_acct_id ;
40✔
1231
  let mk_forest ps =
40✔
1232
    List.map ps ~f:(fun p -> { With_stack_hash.elt = p; stack_hash = () })
250✔
1233
  in
1234
  let mk_node p calls =
1235
    { Zkapp_command.Call_forest.Tree.account_update = p
250✔
1236
    ; account_update_digest = ()
1237
    ; calls = mk_forest calls
250✔
1238
    }
1239
  in
1240
  let gen_zkapp_command_with_dynamic_balance ~new_account num_zkapp_command =
1241
    let rec go acc n =
80✔
1242
      let open Zkapp_basic in
144✔
1243
      let open Permissions in
1244
      if n <= 0 then return (List.rev acc)
80✔
1245
      else
1246
        (* choose a random authorization
1247

1248
           first Account_update.t updates the permissions, using the Signature authorization,
1249
            according the random authorization
1250

1251
           second Account_update.t uses the random authorization
1252
        *)
1253
        let%bind permissions_auth, update =
1254
          match failure with
1255
          | Some (Update_not_permitted update_type) ->
×
1256
              let%bind is_proof = Bool.quickcheck_generator in
1257
              let auth_tag =
×
1258
                if is_proof then Control.Tag.Proof else Control.Tag.Signature
×
1259
              in
1260
              let%map perm = Permissions.gen ~auth_tag in
1261
              let update =
×
1262
                match update_type with
1263
                | `Delegate ->
×
1264
                    { Account_update.Update.dummy with
1265
                      permissions =
1266
                        Set_or_keep.Set
1267
                          { perm with
1268
                            set_delegate = Auth_required.from ~auth_tag
1269
                          }
1270
                    }
1271
                | `App_state ->
×
1272
                    { Account_update.Update.dummy with
1273
                      permissions =
1274
                        Set_or_keep.Set
1275
                          { perm with
1276
                            edit_state = Auth_required.from ~auth_tag
1277
                          }
1278
                    }
1279
                | `Verification_key ->
×
1280
                    { Account_update.Update.dummy with
1281
                      permissions =
1282
                        Set_or_keep.Set
1283
                          { perm with
1284
                            set_verification_key =
1285
                              ( Auth_required.from ~auth_tag
1286
                              , Mina_numbers.Txn_version.current )
1287
                          }
1288
                    }
1289
                | `Zkapp_uri ->
×
1290
                    { Account_update.Update.dummy with
1291
                      permissions =
1292
                        Set_or_keep.Set
1293
                          { perm with
1294
                            set_zkapp_uri = Auth_required.from ~auth_tag
1295
                          }
1296
                    }
1297
                | `Token_symbol ->
×
1298
                    { Account_update.Update.dummy with
1299
                      permissions =
1300
                        Set_or_keep.Set
1301
                          { perm with
1302
                            set_token_symbol = Auth_required.from ~auth_tag
1303
                          }
1304
                    }
1305
                | `Voting_for ->
×
1306
                    { Account_update.Update.dummy with
1307
                      permissions =
1308
                        Set_or_keep.Set
1309
                          { perm with
1310
                            set_voting_for = Auth_required.from ~auth_tag
1311
                          }
1312
                    }
1313
                | `Send ->
×
1314
                    { Account_update.Update.dummy with
1315
                      permissions =
1316
                        Set_or_keep.Set
1317
                          { perm with send = Auth_required.from ~auth_tag }
1318
                    }
1319
                | `Receive ->
×
1320
                    { Account_update.Update.dummy with
1321
                      permissions =
1322
                        Set_or_keep.Set
1323
                          { perm with receive = Auth_required.from ~auth_tag }
1324
                    }
1325
              in
1326
              (auth_tag, Some update)
1327
          | _ ->
64✔
1328
              let%map tag =
1329
                if new_account then
1330
                  Quickcheck.Generator.of_list
29✔
1331
                    [ Control.Tag.Signature; None_given ]
1332
                else Control.Tag.gen
35✔
1333
              in
1334
              (tag, None)
64✔
1335
        in
1336
        let zkapp_account =
64✔
1337
          match (failure, permissions_auth) with
1338
          | Some (Update_not_permitted _), _ | _, Proof ->
×
1339
              true
1340
          | _, Signature | _, None_given ->
26✔
1341
              false
1342
        in
1343
        let%bind account_update0 =
1344
          (* Signature authorization to start *)
1345
          let authorization = Control.Poly.Signature Signature.dummy in
1346
          gen_account_update_from ~no_account_precondition ?balance_change_range
64✔
1347
            ?global_slot ~zkapp_account_ids ~account_ids_seen ~update ?failure
1348
            ~authorization ~new_account ~permissions_auth ~zkapp_account
1349
            ~available_public_keys ~may_use_token:No ~account_state_tbl
1350
            ?protocol_state_view ?vk ~ignore_sequence_events_precond ()
1351
        in
1352
        let%bind account_update =
1353
          (* authorization according to chosen permissions auth *)
1354
          let%bind authorization, update =
1355
            match failure with
1356
            | Some (Update_not_permitted update_type) ->
×
1357
                let auth =
1358
                  match permissions_auth with
1359
                  | Proof ->
×
1360
                      Control.(dummy_of_tag Signature)
×
1361
                  | Signature ->
×
1362
                      Control.(dummy_of_tag Proof)
×
1363
                  | _ ->
×
1364
                      Control.(dummy_of_tag None_given)
×
1365
                in
1366
                let%bind update =
1367
                  match update_type with
1368
                  | `Delegate ->
×
1369
                      let%map delegate =
1370
                        Signature_lib.Public_key.Compressed.gen
1371
                      in
1372
                      { Account_update.Update.dummy with
×
1373
                        delegate = Set_or_keep.Set delegate
1374
                      }
1375
                  | `App_state ->
×
1376
                      let%map app_state =
1377
                        let%map fields =
1378
                          let field_gen =
1379
                            Snark_params.Tick.Field.gen
1380
                            >>| fun x -> Set_or_keep.Set x
×
1381
                          in
NEW
1382
                          Quickcheck.Generator.list_with_length
×
1383
                            Zkapp_state.max_size_int field_gen
1384
                        in
1385
                        Zkapp_state.V.of_list_exn fields
×
1386
                      in
1387
                      { Account_update.Update.dummy with app_state }
×
1388
                  | `Verification_key ->
×
1389
                      let data = Pickles.Side_loaded.Verification_key.dummy in
1390
                      let hash = Zkapp_account.digest_vk data in
1391
                      let verification_key =
×
1392
                        Set_or_keep.Set { With_hash.data; hash }
1393
                      in
1394
                      return
×
1395
                        { Account_update.Update.dummy with verification_key }
1396
                  | `Zkapp_uri ->
×
1397
                      let zkapp_uri = Set_or_keep.Set "https://o1labs.org" in
1398
                      return { Account_update.Update.dummy with zkapp_uri }
×
1399
                  | `Token_symbol ->
×
1400
                      let token_symbol = Set_or_keep.Set "CODA" in
1401
                      return { Account_update.Update.dummy with token_symbol }
×
1402
                  | `Voting_for ->
×
1403
                      let%map field = Snark_params.Tick.Field.gen in
1404
                      let voting_for = Set_or_keep.Set field in
×
1405
                      { Account_update.Update.dummy with voting_for }
1406
                  | `Send | `Receive ->
×
1407
                      return Account_update.Update.dummy
×
1408
                in
1409
                let%map new_perm =
1410
                  Permissions.gen ~auth_tag:Control.Tag.Signature
1411
                in
1412
                ( auth
×
1413
                , Some { update with permissions = Set_or_keep.Set new_perm } )
1414
            | _ ->
64✔
1415
                return (Control.dummy_of_tag permissions_auth, None)
64✔
1416
          in
1417
          let account_id =
64✔
1418
            Account_id.create account_update0.body.public_key
1419
              account_update0.body.token_id
1420
          in
1421
          let permissions_auth = Control.Tag.Signature in
64✔
1422
          gen_account_update_from ~no_account_precondition ?balance_change_range
1423
            ?global_slot ~update ?failure ~zkapp_account_ids ~account_ids_seen
1424
            ~account_id ~authorization ~permissions_auth ~zkapp_account
1425
            ~available_public_keys ~may_use_token:No ~account_state_tbl
1426
            ?protocol_state_view ?vk ~ignore_sequence_events_precond ()
1427
        in
1428
        (* this list will be reversed, so `account_update0` will execute before `account_update` *)
1429
        go
64✔
1430
          (mk_node account_update [] :: mk_node account_update0 [] :: acc)
64✔
1431
          (n - 1)
1432
    in
1433
    go [] num_zkapp_command
1434
  in
1435
  (* at least 1 account_update *)
1436
  let%bind num_new_accounts =
1437
    if generate_new_accounts then Int.gen_uniform_incl 0 num_zkapp_command
40✔
UNCOV
1438
    else return 0
×
1439
  in
1440
  let num_old_zkapp_command = num_zkapp_command - num_new_accounts in
40✔
1441
  let%bind old_zkapp_command =
1442
    gen_zkapp_command_with_dynamic_balance ~new_account:false
40✔
1443
      num_old_zkapp_command
1444
  in
1445
  let%bind new_zkapp_command =
1446
    gen_zkapp_command_with_dynamic_balance ~new_account:true num_new_accounts
40✔
1447
  in
1448
  let account_updates0 = old_zkapp_command @ new_zkapp_command in
40✔
1449
  let balance_change_sum =
1450
    List.fold account_updates0
1451
      ~init:
1452
        ( if num_new_accounts = 0 then Currency.Amount.Signed.zero
18✔
1453
        else
1454
          Currency.Amount.(
22✔
1455
            Signed.of_unsigned
22✔
1456
              ( scale
1457
                  (of_fee constraint_constants.account_creation_fee)
22✔
1458
                  num_new_accounts
1459
              |> Option.value_exn )) )
22✔
1460
      ~f:(fun acc node ->
1461
        match
128✔
1462
          Currency.Amount.Signed.add acc node.account_update.body.balance_change
1463
        with
1464
        | Some sum ->
128✔
1465
            sum
1466
        | None ->
×
1467
            failwith "Overflow adding other zkapp_command balances" )
1468
  in
1469

1470
  (* modify the balancing account_update with balance change to yield a zero sum
1471

1472
     balancing account_update is created immediately after the fee payer
1473
     account_update is created. This is because the preconditions generation
1474
     is sensitive to the order of account_update generation.
1475
  *)
1476
  let balance_change = Currency.Amount.Signed.negate balance_change_sum in
40✔
1477
  let%bind balancing_account_update =
1478
    let authorization = Control.Poly.Signature Signature.dummy in
1479
    gen_account_update_from ~no_account_precondition ?balance_change_range
40✔
1480
      ?global_slot ?failure ~permissions_auth:Control.Tag.Signature
1481
      ~zkapp_account_ids ~account_ids_seen ~authorization ~new_account:false
1482
      ~available_public_keys ~may_use_token:No ~account_state_tbl
1483
      ~required_balance_change:balance_change ?protocol_state_view ?vk
1484
      ~ignore_sequence_events_precond ()
1485
  in
1486
  let gen_zkapp_command_with_token_accounts ~num_zkapp_command =
40✔
1487
    let authorization = Control.Poly.Signature Signature.dummy in
40✔
1488
    let permissions_auth = Control.Tag.Signature in
1489
    let rec gen_tree acc n =
1490
      if n <= 0 then return (List.rev acc)
40✔
1491
      else
1492
        let%bind parent =
1493
          let required_balance_change =
1494
            Currency.Amount.(
1495
              Signed.negate
41✔
1496
                (Signed.of_unsigned
41✔
1497
                   (of_fee constraint_constants.account_creation_fee) ))
41✔
1498
          in
1499
          gen_account_update_from ~no_account_precondition ?balance_change_range
41✔
1500
            ?global_slot ~zkapp_account_ids ~account_ids_seen ~authorization
1501
            ~permissions_auth ~available_public_keys ~may_use_token:No
1502
            ~account_state_tbl ~required_balance_change ?protocol_state_view ?vk
1503
            ~ignore_sequence_events_precond ()
1504
        in
1505
        let token_id =
41✔
1506
          Account_id.derive_token_id
1507
            ~owner:
1508
              (Account_id.create parent.body.public_key parent.body.token_id)
41✔
1509
        in
1510
        let%bind child =
1511
          gen_account_update_from ~no_account_precondition ?global_slot
41✔
1512
            ~zkapp_account_ids ~account_ids_seen ~new_account:true ~token_id
1513
            ~may_use_token:Parents_own_token ~authorization ~permissions_auth
1514
            ~available_public_keys ~account_state_tbl ?protocol_state_view ?vk
1515
            ~ignore_sequence_events_precond ()
1516
        in
1517
        gen_tree (mk_node parent [ mk_node child [] ] :: acc) (n - 1)
41✔
1518
    in
1519
    gen_tree [] num_zkapp_command
1520
  in
1521
  let%bind num_new_token_zkapp_command =
UNCOV
1522
    if no_token_accounts then return 0
×
1523
    else Int.gen_uniform_incl 0 max_token_updates
40✔
1524
  in
1525
  let%bind new_token_zkapp_command =
1526
    gen_zkapp_command_with_token_accounts
1527
      ~num_zkapp_command:num_new_token_zkapp_command
1528
  in
1529
  let account_updates =
40✔
1530
    account_updates0
1531
    @ [ mk_node balancing_account_update [] ]
40✔
1532
    @ new_token_zkapp_command
1533
    |> mk_forest
1534
    |> Zkapp_command.Call_forest.map
40✔
1535
         ~f:(Fn.compose map_account_update Account_update.of_simple)
40✔
1536
  in
1537

1538
  let%map memo =
1539
    match memo with
1540
    | Some memo ->
×
1541
        return @@ Signed_command_memo.create_from_string_exn memo
×
1542
    | None ->
40✔
1543
        Signed_command_memo.gen
1544
  in
1545
  let zkapp_command =
40✔
1546
    Zkapp_command.write_all_proofs_to_disk ~signature_kind ~proof_cache_db
1547
      { Zkapp_command.Poly.fee_payer; account_updates; memo }
1548
  in
1549
  (* update receipt chain hashes in accounts table *)
1550
  let receipt_elt =
40✔
1551
    let _txn_commitment, full_txn_commitment =
1552
      (* also computed in replace_authorizations, but easier just to re-compute here *)
1553
      Zkapp_command.get_transaction_commitments ~signature_kind zkapp_command
1554
    in
1555
    Receipt.Zkapp_command_elt.Zkapp_command_commitment full_txn_commitment
40✔
1556
  in
1557
  Account_id.Table.update account_state_tbl fee_payer_acct_id ~f:(function
1558
    | None ->
×
1559
        failwith "Expected fee payer account id to be in table"
1560
    | Some (account, _) ->
40✔
1561
        let receipt_chain_hash =
1562
          Receipt.Chain_hash.cons_zkapp_command_commitment
1563
            Mina_numbers.Index.zero receipt_elt
1564
            account.Account.receipt_chain_hash
1565
        in
1566
        ({ account with receipt_chain_hash }, `Fee_payer) ) ;
40✔
1567
  Zkapp_command.Call_forest.iteri zkapp_command.account_updates
40✔
1568
    ~f:(fun ndx account_update ->
1569
      (* update receipt chain hash only for signature, proof authorizations *)
1570
      match account_update.Account_update.Poly.authorization with
250✔
1571
      | Control.Poly.Proof _ | Control.Poly.Signature _ ->
6✔
1572
          let acct_id = Account_update.account_id account_update in
1573
          Account_id.Table.update account_state_tbl acct_id ~f:(function
219✔
1574
            | None ->
×
1575
                failwith
1576
                  "Expected other account_update account id to be in table"
1577
            | Some (account, role) ->
219✔
1578
                let receipt_chain_hash =
1579
                  let account_update_index =
1580
                    Mina_numbers.Index.of_int (ndx + 1)
1581
                  in
1582
                  Receipt.Chain_hash.cons_zkapp_command_commitment
219✔
1583
                    account_update_index receipt_elt
1584
                    account.Account.receipt_chain_hash
1585
                in
1586
                ({ account with receipt_chain_hash }, role) )
1587
      | Control.Poly.None_given ->
31✔
1588
          () ) ;
1589
  zkapp_command
40✔
1590

1591
let gen_list_of_zkapp_command_from ?global_slot ?failure ?max_account_updates
1592
    ?max_token_updates ~(fee_payer_keypairs : Signature_lib.Keypair.t list)
1593
    ~keymap ?account_state_tbl ~ledger ?protocol_state_view ?vk ?length
1594
    ~genesis_constants ~constraint_constants () =
1595
  (* Since when generating multiple zkapp_command the fee payer's nonce should only
1596
     be incremented as the `Fee_payer` role, this is why we pre-computed the
1597
     `account_state_tbl` here.
1598
  *)
1599
  let account_state_tbl =
×
1600
    match account_state_tbl with
1601
    | None ->
×
1602
        let tbl = Account_id.Table.create () in
1603
        let accounts = Ledger.to_list_sequential ledger in
×
1604
        List.iter accounts ~f:(fun acct ->
×
1605
            let acct_id = Account.identifier acct in
×
1606
            Account_id.Table.update tbl acct_id ~f:(function
×
1607
              | None ->
×
1608
                  (acct, `Ordinary_participant)
1609
              | Some a ->
×
1610
                  a ) ) ;
1611
        List.iter fee_payer_keypairs ~f:(fun fee_payer_keypair ->
×
1612
            let acct_id =
×
1613
              Account_id.create
1614
                (Signature_lib.Public_key.compress fee_payer_keypair.public_key)
×
1615
                Token_id.default
1616
            in
1617
            Account_id.Table.update tbl acct_id ~f:(function
×
1618
              | None ->
×
1619
                  failwith "fee_payer not in ledger"
1620
              | Some (a, _) ->
×
1621
                  (a, `Fee_payer) ) ) ;
1622
        tbl
×
1623
    | Some tbl ->
×
1624
        tbl
1625
  in
1626
  let open Quickcheck.Generator.Let_syntax in
1627
  let%bind length =
1628
    match length with None -> Int.gen_uniform_incl 1 10 | Some n -> return n
×
1629
  in
1630
  let rec go n acc =
×
1631
    if n > 0 then
×
1632
      let%bind fee_payer_keypair =
1633
        Quickcheck.Generator.of_list fee_payer_keypairs
×
1634
      in
1635
      let%bind new_zkapp_command =
1636
        gen_zkapp_command_from ?global_slot ?failure ?max_account_updates
×
1637
          ?max_token_updates ~fee_payer_keypair ~keymap ~account_state_tbl
1638
          ~ledger ?protocol_state_view ?vk ~genesis_constants
1639
          ~constraint_constants ()
1640
      in
1641
      go (n - 1) (new_zkapp_command :: acc)
×
1642
    else return (List.rev acc)
×
1643
  in
1644
  go length []
1645

1646
let update_vk ~vk : Account_update.Update.t =
1647
  { Account_update.Update.dummy with
×
1648
    verification_key = Zkapp_basic.Set_or_keep.Set vk
1649
  }
1650

1651
let mk_account_update_body ~pk ~vk : Account_update.Body.Simple.t =
1652
  { public_key = pk
×
1653
  ; token_id = Token_id.default
1654
  ; update = update_vk ~vk
1655
  ; balance_change = { magnitude = Currency.Amount.zero; sgn = Sgn.Pos }
1656
  ; increment_nonce = false
1657
  ; events = []
1658
  ; actions = []
1659
  ; call_data = Pickles.Backend.Tick.Field.zero
1660
  ; call_depth = 0
1661
  ; preconditions = Account_update.Preconditions.accept
1662
  ; use_full_commitment = true
1663
  ; implicit_account_creation_fee = false
1664
  ; may_use_token = Account_update.May_use_token.No
1665
  ; authorization_kind = Proof (With_hash.hash vk)
×
1666
  }
1667

1668
let mk_account_update ~pk ~vk : Account_update.Simple.t =
1669
  Account_update.with_no_aux
×
1670
    ~body:(mk_account_update_body ~pk ~vk)
1671
    ~authorization:Control.(dummy_of_tag Proof)
×
1672

1673
let mk_fee_payer ~fee ~pk ~nonce : Account_update.Fee_payer.t =
1674
  Account_update.Fee_payer.make
×
1675
    ~body:{ public_key = pk; fee; valid_until = None; nonce }
1676
    ~authorization:Signature.dummy
1677

1678
let gen_max_cost_zkapp_command_from ?memo ?fee_range
1679
    ~(fee_payer_keypair : Signature_lib.Keypair.t)
1680
    ~(account_state_tbl : (Account.t * role) Account_id.Table.t) ~vk
1681
    ~(genesis_constants : Genesis_constants.t) () =
1682
  let signature_kind = Mina_signature_kind.t_DEPRECATED in
×
1683
  let open Quickcheck.Generator.Let_syntax in
1684
  let%bind memo =
1685
    match memo with
1686
    | Some memo ->
×
1687
        return @@ Signed_command_memo.create_from_string_exn memo
×
1688
    | None ->
×
1689
        Signed_command_memo.gen
1690
  in
1691
  let zkapp_accounts =
×
1692
    Account_id.Table.data account_state_tbl
1693
    |> List.filter_map ~f:(fun ((a, role) : Account.t * role) ->
×
1694
           match role with
×
1695
           | `Ordinary_participant ->
×
1696
               Option.map a.zkapp ~f:(fun _ -> a)
×
1697
           | _ ->
×
1698
               None )
1699
  in
1700
  let zkapp_pks = List.map zkapp_accounts ~f:(fun a -> a.public_key) in
×
1701
  let%bind pks =
1702
    Quickcheck.Generator.(of_list zkapp_pks |> list_with_length 5)
×
1703
  in
1704
  let[@warning "-8"] (head :: tail) =
×
1705
    List.map pks ~f:(fun pk -> mk_account_update ~pk ~vk)
×
1706
  in
1707
  let%bind events =
1708
    Snark_params.Tick.Field.gen
1709
    |> Quickcheck.Generator.map ~f:(fun x -> [| x |])
×
1710
    |> Quickcheck.Generator.list_with_length
×
1711
         genesis_constants.max_event_elements
1712
  in
1713
  let%bind actions =
1714
    Snark_params.Tick.Field.gen
1715
    |> Quickcheck.Generator.map ~f:(fun x -> [| x |])
×
1716
    |> Quickcheck.Generator.list_with_length
×
1717
         genesis_constants.max_action_elements
1718
  in
1719
  let account_updates =
×
1720
    { head with body = { head.body with events; actions } } :: tail
1721
  in
1722
  let fee_payer_pk =
1723
    Signature_lib.Public_key.compress fee_payer_keypair.public_key
1724
  in
1725
  let fee_payer_id = Account_id.create fee_payer_pk Token_id.default in
×
1726
  let fee_payer_account, _ =
×
1727
    Account_id.Table.find_exn account_state_tbl fee_payer_id
1728
  in
1729
  let%map fee =
1730
    Option.value_map fee_range
×
1731
      ~default:(return @@ Currency.Fee.of_mina_string_exn "1.0")
×
1732
      ~f:Currency.Fee.(fun (lo, hi) -> gen_incl lo hi)
×
1733
  in
1734
  let fee_payer =
×
1735
    mk_fee_payer ~fee ~pk:fee_payer_pk ~nonce:fee_payer_account.nonce
1736
  in
1737
  Account_id.Table.change account_state_tbl fee_payer_id ~f:(function
1738
    | None ->
×
1739
        None
1740
    | Some (a, role) ->
×
1741
        Some ({ a with nonce = Account.Nonce.succ a.nonce }, role) ) ;
×
1742
  Zkapp_command.of_simple ~signature_kind ~proof_cache_db
×
1743
    { fee_payer; account_updates; memo }
1744

1745
let%test_module _ =
1746
  ( module struct
1747
    open Signature_lib
1748

1749
    (* TODO: Should be For_unit_tests *)
1750
    let genesis_constants = Genesis_constants.For_unit_tests.t
1751

1752
    let constraint_constants =
1753
      Genesis_constants.For_unit_tests.Constraint_constants.t
1754

UNCOV
1755
    let `VK vk, `Prover _ = Transaction_snark.For_tests.create_trivial_snapp ()
×
1756

UNCOV
1757
    let vk = Async.Thread_safe.block_on_async_exn (fun () -> vk)
×
1758

1759
    let mk_ledger ~num_of_unused_keys () =
UNCOV
1760
      let keys = List.init 5 ~f:(fun _ -> Keypair.create ()) in
×
UNCOV
1761
      let zkapp_keys = List.init 5 ~f:(fun _ -> Keypair.create ()) in
×
UNCOV
1762
      let unused_keys =
×
UNCOV
1763
        List.init num_of_unused_keys ~f:(fun _ -> Keypair.create ())
×
1764
      in
UNCOV
1765
      let account_ids =
×
1766
        List.map keys ~f:(fun key ->
UNCOV
1767
            Account_id.create
×
UNCOV
1768
              (Signature_lib.Public_key.compress key.public_key)
×
1769
              Token_id.default )
1770
      in
UNCOV
1771
      let zkapp_account_ids =
×
1772
        List.map zkapp_keys ~f:(fun key ->
UNCOV
1773
            Account_id.create
×
UNCOV
1774
              (Signature_lib.Public_key.compress key.public_key)
×
1775
              Token_id.default )
1776
      in
UNCOV
1777
      let balance = Currency.Balance.of_mina_int_exn 1_000_000 in
×
UNCOV
1778
      let accounts =
×
UNCOV
1779
        List.map account_ids ~f:(fun id -> Account.create id balance)
×
1780
      in
UNCOV
1781
      let zkapp_accounts =
×
1782
        List.map zkapp_account_ids ~f:(fun id ->
UNCOV
1783
            let account = Account.create id balance in
×
UNCOV
1784
            let verification_key = Some vk in
×
1785
            let zkapp = Some { Zkapp_account.default with verification_key } in
1786
            { account with zkapp } )
1787
      in
UNCOV
1788
      let ledger = Mina_ledger.Ledger.create ~depth:10 () in
×
UNCOV
1789
      List.iter2_exn (account_ids @ zkapp_account_ids)
×
1790
        (accounts @ zkapp_accounts) ~f:(fun id account ->
UNCOV
1791
          Mina_ledger.Ledger.get_or_create_account ledger id account
×
UNCOV
1792
          |> Or_error.ok_exn
×
UNCOV
1793
          |> fun _ -> () ) ;
×
UNCOV
1794
      let keymap =
×
1795
        List.map
1796
          (keys @ zkapp_keys @ unused_keys)
1797
          ~f:(fun { public_key; private_key } ->
UNCOV
1798
            (Public_key.compress public_key, private_key) )
×
UNCOV
1799
        |> Public_key.Compressed.Map.of_alist_exn
×
1800
      in
UNCOV
1801
      (ledger, List.hd_exn keys, keymap)
×
1802

1803
    let%test_unit "generate 100 zkapps with only 3 unused keys" =
UNCOV
1804
      let ledger, fee_payer_keypair, keymap =
×
1805
        mk_ledger ~num_of_unused_keys:3 ()
1806
      in
UNCOV
1807
      let _ =
×
1808
        Quickcheck.Generator.(
UNCOV
1809
          generate
×
UNCOV
1810
            (list_with_length 100
×
UNCOV
1811
               (gen_zkapp_command_from ~genesis_constants ~constraint_constants
×
1812
                  ~fee_payer_keypair ~keymap ~no_token_accounts:true
UNCOV
1813
                  ~account_state_tbl:(Account_id.Table.create ())
×
1814
                  ~generate_new_accounts:false ~ledger () ) )
1815
            ~size:100
UNCOV
1816
            ~random:(Splittable_random.State.create Random.State.default))
×
1817
      in
1818
      ()
1819

1820
    let%test_unit "generate zkapps with balance and fee range" =
UNCOV
1821
      let ledger, fee_payer_keypair, keymap =
×
1822
        mk_ledger ~num_of_unused_keys:3 ()
1823
      in
UNCOV
1824
      let _ =
×
1825
        Quickcheck.Generator.(
UNCOV
1826
          generate
×
UNCOV
1827
            (list_with_length 100
×
UNCOV
1828
               (gen_zkapp_command_from ~genesis_constants ~constraint_constants
×
1829
                  ~no_account_precondition:true ~fee_payer_keypair ~keymap
1830
                  ~no_token_accounts:true
1831
                  ~fee_range:
UNCOV
1832
                    Currency.Fee.(of_mina_string_exn "2", of_mina_string_exn "4")
×
1833
                  ~balance_change_range:
1834
                    Currency.Amount.
UNCOV
1835
                      { min_balance_change = of_mina_string_exn "0"
×
UNCOV
1836
                      ; max_balance_change = of_mina_string_exn "0.00001"
×
UNCOV
1837
                      ; min_new_zkapp_balance = of_mina_string_exn "50"
×
UNCOV
1838
                      ; max_new_zkapp_balance = of_mina_string_exn "100"
×
1839
                      }
UNCOV
1840
                  ~account_state_tbl:(Account_id.Table.create ())
×
1841
                  ~generate_new_accounts:false ~ledger () ) )
1842
            ~size:100
UNCOV
1843
            ~random:(Splittable_random.State.create Random.State.default))
×
1844
      in
1845
      ()
1846
  end )
77✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc