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

MinaProtocol / mina / 2863

05 Nov 2024 06:20PM UTC coverage: 30.754% (-16.6%) from 47.311%
2863

push

buildkite

web-flow
Merge pull request #16296 from MinaProtocol/dkijania/more_multi_jobs

more multi jobs in CI

20276 of 65930 relevant lines covered (30.75%)

8631.7 hits per line

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

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

3✔
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
32
    ?(ignore_sequence_events_precond = false) ?(no_account_precondition = false)
×
33
    ?(is_nonce_precondition = false) ~first_use_of_account account =
256✔
34
  let open Quickcheck.Let_syntax in
256✔
35
  if no_account_precondition then return Zkapp_precondition.Account.accept
×
36
  else
37
    let { Account.balance; nonce; delegate; receipt_chain_hash; zkapp; _ } =
256✔
38
      account
39
    in
40
    (* choose constructor *)
41
    let%bind b =
42
      if is_nonce_precondition then return false else Quickcheck.Generator.bool
×
43
    in
44
    if b then
256✔
45
      (* Full *)
46
      let open Zkapp_basic in
121✔
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
121✔
50
          let balance_change =
121✔
51
            Currency.Amount.of_nanomina_int_exn balance_change_int
52
          in
53
          let lower =
121✔
54
            match Currency.Balance.sub_amount balance balance_change with
55
            | None ->
32✔
56
                Currency.Balance.zero
57
            | Some bal ->
89✔
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 ->
121✔
65
                bal
66
          in
67
          Or_ignore.gen
68
            (return { Zkapp_precondition.Closed_interval.lower; upper })
121✔
69
        in
70
        let%bind nonce =
71
          let%bind nonce_change_int = Int.gen_uniform_incl 1 100 in
121✔
72
          let nonce_change = Account.Nonce.of_int nonce_change_int in
121✔
73
          let lower =
121✔
74
            match Account.Nonce.sub nonce nonce_change with
75
            | None ->
120✔
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
121✔
83
            | None ->
×
84
                (* unreachable *)
85
                failwith
86
                  "gen_account_precondition_from: nonce subtraction failed \
87
                   unexpectedly"
88
            | Some n ->
121✔
89
                if Account.Nonce.( < ) n nonce then Account.Nonce.max_value
×
90
                else Account.Nonce.add nonce nonce_change
121✔
91
          in
92
          Or_ignore.gen
93
            (return { Zkapp_precondition.Closed_interval.lower; upper })
121✔
94
        in
95
        let receipt_chain_hash =
121✔
96
          if first_use_of_account then Or_ignore.Check receipt_chain_hash
82✔
97
          else Or_ignore.Ignore
39✔
98
        in
99
        let%bind delegate =
100
          match delegate with
101
          | None ->
18✔
102
              return Or_ignore.Ignore
18✔
103
          | Some pk ->
103✔
104
              Or_ignore.gen (return pk)
103✔
105
        in
106
        let%bind state, action_state, proved_state, is_new =
107
          match zkapp with
108
          | None ->
73✔
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 =
73✔
112
                Zkapp_state.V.of_list_exn
113
                  (List.init len ~f:(fun _ -> Or_ignore.Ignore))
73✔
114
              in
115
              let action_state = Or_ignore.Ignore in
73✔
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)
73✔
119
          | Some { Zkapp_account.app_state; action_state; proved_state; _ } ->
48✔
120
              let state =
121
                Zkapp_state.V.map app_state ~f:(fun field ->
122
                    Quickcheck.random_value (Or_ignore.gen (return field)) )
384✔
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 =
48✔
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)
48✔
133
                  in
134
                  return (Or_ignore.Check (List.nth_exn fields ndx))
48✔
135
              in
136
              let proved_state = Or_ignore.Check proved_state in
48✔
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
121✔
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
121✔
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
      | _ ->
121✔
240
          return predicate_account
241
    else
242
      (* Nonce *)
243
      let { Account.nonce; _ } = account in
135✔
244
      match failure with
245
      | Some Invalid_account_precondition ->
×
246
          return @@ Zkapp_precondition.Account.nonce (Account.Nonce.succ nonce)
×
247
      | _ ->
135✔
248
          return @@ Zkapp_precondition.Account.nonce nonce
135✔
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✔
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
173✔
272
  let%bind sgn =
273
    if new_account then return Sgn.Pos
75✔
274
    else
275
      match (failure, permissions_auth) with
98✔
276
      | Some (Update_not_permitted `Send), _ ->
×
277
          return Sgn.Neg
×
278
      | Some (Update_not_permitted `Receive), _ ->
×
279
          return Sgn.Pos
×
280
      | _, Some auth -> (
98✔
281
          match auth with
282
          | Control.Tag.None_given ->
14✔
283
              return Sgn.Pos
14✔
284
          | _ ->
84✔
285
              Quickcheck.Generator.of_list [ Sgn.Pos; Neg ] )
84✔
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
173✔
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
75✔
297
    then failwith "account has low balance"
×
298
    else Balance.of_mina_string_exn "0.000001"
173✔
299
  in
300
  let%map (magnitude : Currency.Amount.t) =
301
    Option.value_map balance_change_range
173✔
302
      ~default:
303
        ( if new_account then
304
          Currency.Amount.gen_incl
75✔
305
            (Currency.Amount.of_mina_string_exn "50.0")
75✔
306
            (Currency.Amount.of_mina_string_exn "100.0")
75✔
307
        else
308
          Currency.Amount.gen_incl Currency.Amount.zero
98✔
309
            (Currency.Balance.to_amount small_balance_change) )
98✔
310
      ~f:(fun { min_balance_change
311
              ; max_balance_change
312
              ; min_new_zkapp_balance
313
              ; max_new_zkapp_balance
314
              } ->
315
        if new_account then
×
316
          Currency.Amount.(gen_incl min_new_zkapp_balance max_new_zkapp_balance)
×
317
        else Currency.Amount.(gen_incl min_balance_change max_balance_change) )
×
318
  in
319
  match sgn with
173✔
320
  | Pos ->
129✔
321
      ({ magnitude; sgn = Sgn.Pos } : Currency.Amount.Signed.t)
322
  | Neg ->
44✔
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 =
256✔
329
    increment_nonce
330
    && Zkapp_precondition.Numeric.is_constant
215✔
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))
256✔
336
  in
337
  if incr_nonce_and_constrains_nonce || does_not_use_a_signature then
22✔
338
    Bool.quickcheck_generator
134✔
339
  else Quickcheck.Generator.return true
122✔
340

341
let closed_interval_exact value =
342
  Zkapp_precondition.Closed_interval.{ lower = value; upper = value }
244✔
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
244✔
356
  let%bind ledger =
357
    let%bind hash =
358
      Zkapp_basic.Or_ignore.gen @@ return epoch_data.ledger.hash
244✔
359
    in
360
    let%map total_currency =
361
      closed_interval_exact epoch_data.ledger.total_currency
362
      |> return |> Zkapp_basic.Or_ignore.gen
244✔
363
    in
364
    { Epoch_ledger.Poly.hash; total_currency }
244✔
365
  in
366
  let%bind seed = Zkapp_basic.Or_ignore.gen @@ return epoch_data.seed in
244✔
367
  let%bind start_checkpoint =
368
    Zkapp_basic.Or_ignore.gen @@ return epoch_data.start_checkpoint
244✔
369
  in
370
  let%bind lock_checkpoint =
371
    Zkapp_basic.Or_ignore.gen @@ return epoch_data.lock_checkpoint
244✔
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
244✔
376
    let%bind epsilon2 = Length.gen_incl (Length.of_int 0) (Length.of_int 10) in
244✔
377
    Zkapp_precondition.Closed_interval.
244✔
378
      { lower =
379
          Length.sub epoch_data.epoch_length epsilon1
380
          |> Option.value ~default:Length.zero
244✔
381
      ; upper = Length.add epoch_data.epoch_length epsilon2
244✔
382
      }
383
    |> return |> Zkapp_basic.Or_ignore.gen
244✔
384
  in
385
  { Epoch_data.Poly.ledger
244✔
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
122✔
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
122✔
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
122✔
403
    let%bind epsilon2 = Length.gen_incl (Length.of_int 0) (Length.of_int 10) in
122✔
404
    { lower =
122✔
405
        Length.sub psv.blockchain_length epsilon1
406
        |> Option.value ~default:Length.zero
122✔
407
    ; upper = Length.add psv.blockchain_length epsilon2
122✔
408
    }
409
    |> return |> Zkapp_basic.Or_ignore.gen
122✔
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
122✔
414
    let%bind epsilon2 = Length.gen_incl (Length.of_int 0) (Length.of_int 10) in
122✔
415
    { lower =
122✔
416
        Length.sub psv.min_window_density epsilon1
417
        |> Option.value ~default:Length.zero
122✔
418
    ; upper = Length.add psv.min_window_density epsilon2
122✔
419
    }
420
    |> return |> Zkapp_basic.Or_ignore.gen
122✔
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)
122✔
426
    in
427
    let%bind epsilon2 =
428
      Amount.gen_incl Amount.zero (Amount.of_mina_int_exn 1)
122✔
429
    in
430
    { lower =
122✔
431
        Amount.sub psv.total_currency epsilon1
432
        |> Option.value ~default:Amount.zero
122✔
433
    ; upper =
434
        Amount.add psv.total_currency epsilon2
435
        |> Option.value ~default:psv.total_currency
122✔
436
    }
437
    |> return |> Zkapp_basic.Or_ignore.gen
122✔
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
122✔
443
        (Global_slot_span.of_int 0)
122✔
444
        (Global_slot_span.of_int 10)
122✔
445
    in
446
    let%bind epsilon2 =
447
      Global_slot_span.gen_incl
122✔
448
        (Global_slot_span.of_int 0)
122✔
449
        (Global_slot_span.of_int 10)
122✔
450
    in
451
    { lower =
122✔
452
        Global_slot_since_genesis.sub psv.global_slot_since_genesis epsilon1
453
        |> Option.value ~default:Global_slot_since_genesis.zero
122✔
454
    ; upper =
455
        Global_slot_since_genesis.add psv.global_slot_since_genesis epsilon2
122✔
456
    }
457
    |> return |> Zkapp_basic.Or_ignore.gen
122✔
458
  in
459
  let%bind staking_epoch_data =
460
    gen_epoch_data_predicate psv.staking_epoch_data
122✔
461
  in
462
  let%map next_epoch_data = gen_epoch_data_predicate psv.next_epoch_data in
122✔
463
  { Zkapp_precondition.Protocol_state.Poly.snarked_ledger_hash
122✔
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
       , 'call_data
591
       , 'int
592
       , 'bool
593
       , 'protocol_state_precondition
594
       , 'account_precondition
595
       , 'valid_while_precondition
596
       , 'may_use_token
597
       , 'authorization_kind )
598
       t =
599
    { public_key : 'pk
600
    ; update : 'update
601
    ; token_id : 'token_id
602
    ; balance_change : 'amount
603
    ; increment_nonce : 'bool
604
    ; events : 'events
605
    ; actions : 'events
606
    ; call_data : 'call_data
607
    ; call_depth : 'int
608
    ; protocol_state_precondition : 'protocol_state_precondition
609
    ; account_precondition : 'account_precondition
610
    ; valid_while_precondition : 'valid_while_precondition
611
    ; use_full_commitment : 'bool
612
    ; may_use_token : 'may_use_token
613
    ; authorization_kind : 'authorization_kind
614
    }
615

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

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

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

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

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

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

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

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

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

1123
let max_token_updates = 2
1124

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

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

1242
           first Account_update.t updates the permissions, using the Signature authorization,
1243
            according the random authorization
1244

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

1463
  (* modify the balancing account_update with balance change to yield a zero sum
1464

1465
     balancing account_update is created immediately after the fee payer
1466
     account_update is created. This is because the preconditions generation
1467
     is sensitive to the order of account_update generation.
1468
  *)
1469
  let balance_change = Currency.Amount.Signed.negate balance_change_sum in
40✔
1470
  let%bind balancing_account_update =
1471
    let authorization = Control.Signature Signature.dummy in
1472
    gen_account_update_from ~no_account_precondition ?balance_change_range
40✔
1473
      ?global_slot ?failure ~permissions_auth:Control.Tag.Signature
1474
      ~zkapp_account_ids ~account_ids_seen ~authorization ~new_account:false
1475
      ~available_public_keys ~may_use_token:No ~account_state_tbl
1476
      ~required_balance_change:balance_change ?protocol_state_view ?vk
1477
      ~ignore_sequence_events_precond ()
1478
  in
1479
  let gen_zkapp_command_with_token_accounts ~num_zkapp_command =
40✔
1480
    let authorization = Control.Signature Signature.dummy in
40✔
1481
    let permissions_auth = Control.Tag.Signature in
1482
    let rec gen_tree acc n =
1483
      if n <= 0 then return (List.rev acc)
40✔
1484
      else
1485
        let%bind parent =
1486
          let required_balance_change =
1487
            Currency.Amount.(
1488
              Signed.negate
43✔
1489
                (Signed.of_unsigned
43✔
1490
                   (of_fee constraint_constants.account_creation_fee) ))
43✔
1491
          in
1492
          gen_account_update_from ~no_account_precondition ?balance_change_range
43✔
1493
            ?global_slot ~zkapp_account_ids ~account_ids_seen ~authorization
1494
            ~permissions_auth ~available_public_keys ~may_use_token:No
1495
            ~account_state_tbl ~required_balance_change ?protocol_state_view ?vk
1496
            ~ignore_sequence_events_precond ()
1497
        in
1498
        let token_id =
43✔
1499
          Account_id.derive_token_id
1500
            ~owner:
1501
              (Account_id.create parent.body.public_key parent.body.token_id)
43✔
1502
        in
1503
        let%bind child =
1504
          gen_account_update_from ~no_account_precondition ?global_slot
43✔
1505
            ~zkapp_account_ids ~account_ids_seen ~new_account:true ~token_id
1506
            ~may_use_token:Parents_own_token ~authorization ~permissions_auth
1507
            ~available_public_keys ~account_state_tbl ?protocol_state_view ?vk
1508
            ~ignore_sequence_events_precond ()
1509
        in
1510
        gen_tree (mk_node parent [ mk_node child [] ] :: acc) (n - 1)
43✔
1511
    in
1512
    gen_tree [] num_zkapp_command
1513
  in
1514
  let%bind num_new_token_zkapp_command =
1515
    if no_token_accounts then return 0
×
1516
    else Int.gen_uniform_incl 0 max_token_updates
40✔
1517
  in
1518
  let%bind new_token_zkapp_command =
1519
    gen_zkapp_command_with_token_accounts
1520
      ~num_zkapp_command:num_new_token_zkapp_command
1521
  in
1522
  let account_updates =
40✔
1523
    account_updates0
1524
    @ [ mk_node balancing_account_update [] ]
40✔
1525
    @ new_token_zkapp_command
1526
    |> mk_forest
1527
  in
1528
  let%map memo =
1529
    match memo with
1530
    | Some memo ->
×
1531
        return @@ Signed_command_memo.create_from_string_exn memo
×
1532
    | None ->
40✔
1533
        Signed_command_memo.gen
1534
  in
1535
  let zkapp_command_dummy_authorizations : Zkapp_command.t =
40✔
1536
    { fee_payer
1537
    ; account_updates =
1538
        account_updates
1539
        |> Zkapp_command.Call_forest.map ~f:Account_update.of_simple
1540
        |> Zkapp_command.Call_forest.accumulate_hashes_predicated
40✔
1541
    ; memo
1542
    }
1543
  in
1544
  (* update receipt chain hashes in accounts table *)
1545
  let receipt_elt =
1546
    let _txn_commitment, full_txn_commitment =
1547
      (* also computed in replace_authorizations, but easier just to re-compute here *)
1548
      Zkapp_command.get_transaction_commitments
1549
        zkapp_command_dummy_authorizations
1550
    in
1551
    Receipt.Zkapp_command_elt.Zkapp_command_commitment full_txn_commitment
40✔
1552
  in
1553
  Account_id.Table.update account_state_tbl fee_payer_acct_id ~f:(function
1554
    | None ->
×
1555
        failwith "Expected fee payer account id to be in table"
1556
    | Some (account, _) ->
40✔
1557
        let receipt_chain_hash =
1558
          Receipt.Chain_hash.cons_zkapp_command_commitment
1559
            Mina_numbers.Index.zero receipt_elt
1560
            account.Account.receipt_chain_hash
1561
        in
1562
        ({ account with receipt_chain_hash }, `Fee_payer) ) ;
40✔
1563
  let account_updates =
40✔
1564
    Zkapp_command.Call_forest.to_account_updates
1565
      zkapp_command_dummy_authorizations.account_updates
1566
  in
1567
  List.iteri account_updates ~f:(fun ndx account_update ->
40✔
1568
      (* update receipt chain hash only for signature, proof authorizations *)
1569
      match Account_update.authorization account_update with
256✔
1570
      | Control.Proof _ | Control.Signature _ ->
9✔
1571
          let acct_id = Account_update.account_id account_update in
1572
          Account_id.Table.update account_state_tbl acct_id ~f:(function
224✔
1573
            | None ->
×
1574
                failwith
1575
                  "Expected other account_update account id to be in table"
1576
            | Some (account, role) ->
224✔
1577
                let receipt_chain_hash =
1578
                  let account_update_index =
1579
                    Mina_numbers.Index.of_int (ndx + 1)
1580
                  in
1581
                  Receipt.Chain_hash.cons_zkapp_command_commitment
224✔
1582
                    account_update_index receipt_elt
1583
                    account.Account.receipt_chain_hash
1584
                in
1585
                ({ account with receipt_chain_hash }, role) )
1586
      | Control.None_given ->
32✔
1587
          () ) ;
1588
  zkapp_command_dummy_authorizations
40✔
1589

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

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

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

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

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

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

1742
let%test_module _ =
1743
  ( module struct
1744
    open Signature_lib
1745

1746
    (* TODO: Should be For_unit_tests *)
1747
    let genesis_constants = Genesis_constants.For_unit_tests.t
1748

1749
    let constraint_constants =
1750
      Genesis_constants.For_unit_tests.Constraint_constants.t
1751

1752
    let `VK vk, `Prover _ =
1753
      Transaction_snark.For_tests.create_trivial_snapp ~constraint_constants ()
×
1754

1755
    let vk = Async.Thread_safe.block_on_async_exn (fun () -> vk)
×
1756

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

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

1818
    let%test_unit "generate zkapps with balance and fee range" =
1819
      let ledger, fee_payer_keypair, keymap =
×
1820
        mk_ledger ~num_of_unused_keys:3 ()
1821
      in
1822
      let _ =
×
1823
        Quickcheck.Generator.(
1824
          generate
×
1825
            (list_with_length 100
×
1826
               (gen_zkapp_command_from ~genesis_constants ~constraint_constants
×
1827
                  ~no_account_precondition:true ~fee_payer_keypair ~keymap
1828
                  ~no_token_accounts:true
1829
                  ~fee_range:
1830
                    Currency.Fee.(of_mina_string_exn "2", of_mina_string_exn "4")
×
1831
                  ~balance_change_range:
1832
                    Currency.Amount.
1833
                      { min_balance_change = of_mina_string_exn "0"
×
1834
                      ; max_balance_change = of_mina_string_exn "0.00001"
×
1835
                      ; min_new_zkapp_balance = of_mina_string_exn "50"
×
1836
                      ; max_new_zkapp_balance = of_mina_string_exn "100"
×
1837
                      }
1838
                  ~account_state_tbl:(Account_id.Table.create ())
×
1839
                  ~generate_new_accounts:false ~ledger () ) )
1840
            ~size:100
1841
            ~random:(Splittable_random.State.create Random.State.default))
×
1842
      in
1843
      ()
1844
  end )
3✔
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