• 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

23.76
/src/lib/mina_base/user_command.ml
1
open Core_kernel
9✔
2

3
module Poly = struct
4
  [%%versioned
5
  module Stable = struct
6
    module V2 = struct
7
      type ('u, 's) t =
29✔
8
            ('u, 's) Mina_wire_types.Mina_base.User_command.Poly.V2.t =
9
        | Signed_command of 'u
×
10
        | Zkapp_command of 's
×
11
      [@@deriving sexp, compare, equal, hash, yojson]
63✔
12

13
      let to_latest = Fn.id
14
    end
15

16
    module V1 = struct
17
      type ('u, 's) t = Signed_command of 'u | Snapp_command of 's
×
18
      [@@deriving sexp, compare, equal, hash, yojson]
9✔
19

20
      let to_latest : _ t -> _ V2.t = function
21
        | Signed_command x ->
×
22
            Signed_command x
23
        | Snapp_command _ ->
×
24
            failwith "Snapp_command"
25
    end
26
  end]
27
end
28

29
type ('u, 's) t_ = ('u, 's) Poly.Stable.Latest.t =
30
  | Signed_command of 'u
31
  | Zkapp_command of 's
32

33
module Gen_make (C : Signed_command_intf.Gen_intf) = struct
34
  let to_signed_command f =
35
    Quickcheck.Generator.map f ~f:(fun c -> Signed_command c)
2✔
36

37
  open C.Gen
38

39
  let payment ?sign_type ~key_gen ?nonce ~max_amount ~fee_range () =
40
    to_signed_command
×
41
      (payment ?sign_type ~key_gen ?nonce ~max_amount ~fee_range ())
×
42

43
  let payment_with_random_participants ?sign_type ~keys ?nonce ~max_amount
44
      ~fee_range () =
45
    to_signed_command
1✔
46
      (payment_with_random_participants ?sign_type ~keys ?nonce ~max_amount
1✔
47
         ~fee_range () )
48

49
  let stake_delegation ~key_gen ?nonce ~fee_range () =
50
    to_signed_command (stake_delegation ~key_gen ?nonce ~fee_range ())
×
51

52
  let stake_delegation_with_random_participants ~keys ?nonce ~fee_range () =
53
    to_signed_command
×
54
      (stake_delegation_with_random_participants ~keys ?nonce ~fee_range ())
×
55

56
  let sequence ?length ?sign_type a =
57
    Quickcheck.Generator.map
×
58
      (sequence ?length ?sign_type a)
×
59
      ~f:(List.map ~f:(fun c -> Signed_command c))
×
60
end
61

62
module Gen = Gen_make (Signed_command)
63

64
let gen_signed =
65
  let module G = Signed_command.Gen in
66
  let open Quickcheck.Let_syntax in
67
  let%bind keys =
68
    Quickcheck.Generator.list_with_length 2
9✔
69
      Mina_base_import.Signature_keypair.gen
70
  in
71
  G.payment_with_random_participants ~sign_type:`Real ~keys:(Array.of_list keys)
×
72
    ~max_amount:10000 ~fee_range:1000 ()
73

74
let gen = Gen.to_signed_command gen_signed
9✔
75

76
[%%versioned
77
module Stable = struct
78
  module V2 = struct
79
    type t =
9✔
80
      (Signed_command.Stable.V2.t, Zkapp_command.Stable.V1.t) Poly.Stable.V2.t
×
81
    [@@deriving sexp, compare, equal, hash, yojson]
45✔
82

83
    let to_latest = Fn.id
84
  end
85
end]
86

87
let to_base64 : t -> string = function
88
  | Signed_command sc ->
×
89
      Signed_command.to_base64 sc
90
  | Zkapp_command zc ->
×
91
      Zkapp_command.to_base64 zc
92

93
let of_base64 s : t Or_error.t =
94
  match Signed_command.of_base64 s with
×
95
  | Ok sc ->
×
96
      Ok (Signed_command sc)
97
  | Error err1 -> (
×
98
      match Zkapp_command.of_base64 s with
99
      | Ok zc ->
×
100
          Ok (Zkapp_command zc)
101
      | Error err2 ->
×
102
          Error
103
            (Error.of_string
×
104
               (sprintf
×
105
                  "Could decode Base64 neither to signed command (%s), nor to \
106
                   zkApp (%s)"
107
                  (Error.to_string_hum err1) (Error.to_string_hum err2) ) ) )
×
108

109
(*
110
include Allocation_functor.Make.Versioned_v1.Full_compare_eq_hash (struct
111
  let id = "user_command"
112

113
  [%%versioned
114
  module Stable = struct
115
    module V1 = struct
116
      type t =
117
        (Signed_command.Stable.V1.t, Snapp_command.Stable.V1.t) Poly.Stable.V1.t
118
      [@@deriving sexp, compare, equal, hash, yojson]
119

120
      let to_latest = Fn.id
121

122
      type 'a creator : Signed_command.t -> Snapp_command.t -> 'a
123

124
      let create cmd1 cmd2 = (cmd1, cmd2)
125
    end
126
  end]
127
end)
128
*)
129

130
module Zero_one_or_two = struct
131
  [%%versioned
132
  module Stable = struct
133
    module V1 = struct
134
      type 'a t = [ `Zero | `One of 'a | `Two of 'a * 'a ]
×
135
      [@@deriving sexp, compare, equal, hash, yojson]
9✔
136
    end
137
  end]
138
end
139

140
module Verifiable = struct
141
  [%%versioned
142
  module Stable = struct
143
    module V2 = struct
144
      type t =
9✔
145
        ( Signed_command.Stable.V2.t
×
146
        , Zkapp_command.Verifiable.Stable.V1.t )
×
147
        Poly.Stable.V2.t
×
148
      [@@deriving sexp, compare, equal, hash, yojson]
45✔
149

150
      let to_latest = Fn.id
151
    end
152
  end]
153

154
  let fee_payer (t : t) =
155
    match t with
×
156
    | Signed_command x ->
×
157
        Signed_command.fee_payer x
158
    | Zkapp_command p ->
×
159
        Account_update.Fee_payer.account_id p.fee_payer
160
end
161

162
let to_verifiable (t : t) ~failed ~find_vk : Verifiable.t Or_error.t =
163
  match t with
×
164
  | Signed_command c ->
×
165
      Ok (Signed_command c)
166
  | Zkapp_command cmd ->
×
167
      Zkapp_command.Verifiable.create ~failed ~find_vk cmd
×
168
      |> Or_error.map ~f:(fun cmd -> Zkapp_command cmd)
×
169

170
module Make_to_all_verifiable
171
    (Strategy : Zkapp_command.Verifiable.Create_all_intf) =
172
struct
173
  let to_all_verifiable (ts : t Strategy.Command_wrapper.t list) ~load_vk_cache
174
      : Verifiable.t Strategy.Command_wrapper.t list Or_error.t =
175
    let open Or_error.Let_syntax in
200✔
176
    (* First we tag everything with its index *)
177
    let its = List.mapi ts ~f:(fun i x -> (i, x)) in
480✔
178
    (* then we partition out the zkapp commands *)
179
    let izk_cmds, is_cmds =
200✔
180
      List.partition_map its ~f:(fun (i, cmd) ->
181
          match Strategy.Command_wrapper.unwrap cmd with
480✔
182
          | Zkapp_command c ->
×
183
              First (i, Strategy.Command_wrapper.map cmd ~f:(Fn.const c))
×
184
          | Signed_command c ->
480✔
185
              Second (i, Strategy.Command_wrapper.map cmd ~f:(Fn.const c)) )
480✔
186
    in
187
    (* then unzip the indices *)
188
    let ixs, zk_cmds = List.unzip izk_cmds in
200✔
189
    (* then we verify the zkapp commands *)
190
    (* TODO: we could optimize this by skipping the fee payer and non-proof authorizations *)
191
    let accounts_referenced =
200✔
192
      List.fold_left zk_cmds ~init:Account_id.Set.empty ~f:(fun set zk_cmd ->
193
          Strategy.Command_wrapper.unwrap zk_cmd
×
194
          |> Zkapp_command.accounts_referenced |> Account_id.Set.of_list
×
195
          |> Set.union set )
196
    in
197
    let vk_cache = load_vk_cache accounts_referenced in
200✔
198
    let%map vzk_cmds = Strategy.create_all zk_cmds vk_cache in
200✔
199
    (* rezip indices *)
200
    let ivzk_cmds = List.zip_exn ixs vzk_cmds in
200✔
201
    (* Put them back in with a sort by index (un-partition) *)
202
    let ivs =
200✔
203
      List.map is_cmds ~f:(fun (i, cmd) ->
200✔
204
          (i, Strategy.Command_wrapper.map cmd ~f:(fun c -> Signed_command c)) )
480✔
205
      @ List.map ivzk_cmds ~f:(fun (i, cmd) ->
200✔
206
            (i, Strategy.Command_wrapper.map cmd ~f:(fun c -> Zkapp_command c)) )
×
207
      |> List.sort ~compare:(fun (i, _) (j, _) -> i - j)
700✔
208
    in
209
    (* Drop the indices *)
210
    List.unzip ivs |> snd
200✔
211
end
212

213
module Unapplied_sequence =
214
  Make_to_all_verifiable (Zkapp_command.Verifiable.From_unapplied_sequence)
215
module Applied_sequence =
216
  Make_to_all_verifiable (Zkapp_command.Verifiable.From_applied_sequence)
217

218
let of_verifiable (t : Verifiable.t) : t =
219
  match t with
×
220
  | Signed_command x ->
×
221
      Signed_command x
222
  | Zkapp_command p ->
×
223
      Zkapp_command (Zkapp_command.of_verifiable p)
×
224

225
let fee : t -> Currency.Fee.t = function
226
  | Signed_command x ->
20,000✔
227
      Signed_command.fee x
228
  | Zkapp_command p ->
×
229
      Zkapp_command.fee p
230

231
let has_insufficient_fee ~minimum_fee t = Currency.Fee.(fee t < minimum_fee)
×
232

233
let is_disabled = function
234
  | Zkapp_command _ ->
×
235
      Node_config_unconfigurable_constants.zkapps_disabled
236
  | _ ->
×
237
      false
238

239
(* always `Accessed` for fee payer *)
240
let accounts_accessed (t : t) (status : Transaction_status.t) :
241
    (Account_id.t * [ `Accessed | `Not_accessed ]) list =
242
  match t with
×
243
  | Signed_command x ->
×
244
      Signed_command.account_access_statuses x status
245
  | Zkapp_command ps ->
×
246
      Zkapp_command.account_access_statuses ps status
247

248
let accounts_referenced (t : t) =
249
  List.map (accounts_accessed t Applied) ~f:(fun (acct_id, _status) -> acct_id)
×
250

251
let fee_payer (t : t) =
252
  match t with
480✔
253
  | Signed_command x ->
480✔
254
      Signed_command.fee_payer x
255
  | Zkapp_command p ->
×
256
      Zkapp_command.fee_payer p
257

258
(** The application nonce is the nonce of the fee payer at which a user command can be applied. *)
259
let applicable_at_nonce (t : t) =
260
  match t with
×
261
  | Signed_command x ->
×
262
      Signed_command.nonce x
263
  | Zkapp_command p ->
×
264
      Zkapp_command.applicable_at_nonce p
265

266
let expected_target_nonce t = Account.Nonce.succ (applicable_at_nonce t)
×
267

268
let extract_vks : t -> (Account_id.t * Verification_key_wire.t) List.t =
269
  function
270
  | Signed_command _ ->
×
271
      []
272
  | Zkapp_command cmd ->
×
273
      Zkapp_command.extract_vks cmd
274

275
(** The target nonce is what the nonce of the fee payer will be after a user command is successfully applied. *)
276
let target_nonce_on_success (t : t) =
277
  match t with
×
278
  | Signed_command x ->
×
279
      Account.Nonce.succ (Signed_command.nonce x)
×
280
  | Zkapp_command p ->
×
281
      Zkapp_command.target_nonce_on_success p
282

283
let fee_token (t : t) =
284
  match t with
×
285
  | Signed_command x ->
×
286
      Signed_command.fee_token x
287
  | Zkapp_command x ->
×
288
      Zkapp_command.fee_token x
289

290
let valid_until (t : t) =
291
  match t with
×
292
  | Signed_command x ->
×
293
      Signed_command.valid_until x
294
  | Zkapp_command { fee_payer; _ } -> (
×
295
      match fee_payer.Account_update.Fee_payer.body.valid_until with
296
      | Some valid_until ->
×
297
          valid_until
298
      | None ->
×
299
          Mina_numbers.Global_slot_since_genesis.max_value )
300

301
module Valid = struct
302
  type t_ = t
303

304
  [%%versioned
305
  module Stable = struct
306
    module V2 = struct
307
      type t =
9✔
308
        ( Signed_command.With_valid_signature.Stable.V2.t
×
309
        , Zkapp_command.Valid.Stable.V1.t )
×
310
        Poly.Stable.V2.t
×
311
      [@@deriving sexp, compare, equal, hash, yojson]
45✔
312

313
      let to_latest = Fn.id
314
    end
315
  end]
316

317
  module Gen = Gen_make (Signed_command.With_valid_signature)
318
end
319

320
let check_verifiable (t : Verifiable.t) : Valid.t Or_error.t =
321
  match t with
×
322
  | Signed_command x -> (
×
323
      match Signed_command.check x with
324
      | Some c ->
×
325
          Ok (Signed_command c)
326
      | None ->
×
327
          Or_error.error_string "Invalid signature" )
328
  | Zkapp_command p ->
×
329
      Ok (Zkapp_command (Zkapp_command.Valid.of_verifiable p))
×
330

331
let check ~failed ~find_vk (t : t) : Valid.t Or_error.t =
332
  to_verifiable ~failed ~find_vk t |> Or_error.bind ~f:check_verifiable
×
333

334
let forget_check (t : Valid.t) : t =
335
  match t with
22,560✔
336
  | Zkapp_command x ->
×
337
      Zkapp_command (Zkapp_command.Valid.forget x)
×
338
  | Signed_command c ->
22,560✔
339
      Signed_command (c :> Signed_command.t)
340

341
let to_valid_unsafe (t : t) =
342
  `If_this_is_used_it_should_have_a_comment_justifying_it
960✔
343
    ( match t with
344
    | Zkapp_command x ->
×
345
        let (`If_this_is_used_it_should_have_a_comment_justifying_it x) =
346
          Zkapp_command.Valid.to_valid_unsafe x
347
        in
348
        Zkapp_command x
×
349
    | Signed_command x ->
960✔
350
        (* This is safe due to being immediately wrapped again. *)
351
        let (`If_this_is_used_it_should_have_a_comment_justifying_it x) =
352
          Signed_command.to_valid_unsafe x
353
        in
354
        Signed_command x )
960✔
355

356
let filter_by_participant (commands : t list) public_key =
357
  List.filter commands ~f:(fun user_command ->
×
358
      Core_kernel.List.exists
×
359
        (accounts_referenced user_command)
×
360
        ~f:
361
          (Fn.compose
×
362
             (Signature_lib.Public_key.Compressed.equal public_key)
×
363
             Account_id.public_key ) )
364

365
(* A metric on user commands that should correspond roughly to resource costs
366
   for validation/application *)
367
let weight : t -> int = function
368
  | Signed_command signed_command ->
×
369
      Signed_command.payload signed_command |> Signed_command_payload.weight
×
370
  | Zkapp_command zkapp_command ->
×
371
      Zkapp_command.weight zkapp_command
372

373
(* Fee per weight unit *)
374
let fee_per_wu (user_command : Stable.Latest.t) : Currency.Fee_rate.t =
375
  (*TODO: return Or_error*)
376
  Currency.Fee_rate.make_exn (fee user_command) (weight user_command)
×
377

378
let valid_size ~genesis_constants = function
379
  | Signed_command _ ->
×
380
      Ok ()
381
  | Zkapp_command zkapp_command ->
×
382
      Zkapp_command.valid_size ~genesis_constants zkapp_command
383

384
let has_zero_vesting_period = function
385
  | Signed_command _ ->
×
386
      false
387
  | Zkapp_command p ->
×
388
      Zkapp_command.has_zero_vesting_period p
389

390
let is_incompatible_version = function
391
  | Signed_command _ ->
×
392
      false
393
  | Zkapp_command p ->
×
394
      Zkapp_command.is_incompatible_version p
395

396
let has_invalid_call_forest : t -> bool = function
397
  | Signed_command _ ->
×
398
      false
399
  | Zkapp_command cmd ->
×
400
      List.exists cmd.account_updates ~f:(fun call_forest ->
401
          let root_may_use_token =
×
402
            call_forest.elt.account_update.body.may_use_token
403
          in
404
          not (Account_update.May_use_token.equal root_may_use_token No) )
×
405

406
module Well_formedness_error = struct
407
  (* syntactically-evident errors such that a user command can never succeed *)
408
  type t =
×
409
    | Insufficient_fee
×
410
    | Zero_vesting_period
×
411
    | Zkapp_too_big of (Error.t[@to_yojson Error_json.error_to_yojson])
×
412
    | Zkapp_invalid_call_forest
×
413
    | Transaction_type_disabled
×
414
    | Incompatible_version
×
415
  [@@deriving compare, to_yojson, sexp]
416

417
  let to_string = function
418
    | Insufficient_fee ->
×
419
        "Insufficient fee"
420
    | Zero_vesting_period ->
×
421
        "Zero vesting period"
422
    | Zkapp_too_big err ->
×
423
        sprintf "Zkapp too big (%s)" (Error.to_string_hum err)
×
424
    | Zkapp_invalid_call_forest ->
×
425
        "Zkapp has an invalid call forest (root account updates may not use \
426
         tokens)"
427
    | Incompatible_version ->
×
428
        "Set verification-key permission is updated to an incompatible version"
429
    | Transaction_type_disabled ->
×
430
        "Transaction type disabled"
431
end
432

433
let check_well_formedness ~(genesis_constants : Genesis_constants.t) t :
434
    (unit, Well_formedness_error.t list) result =
435
  let preds =
×
436
    let open Well_formedness_error in
437
    [ ( has_insufficient_fee
438
          ~minimum_fee:genesis_constants.minimum_user_command_fee
439
      , Insufficient_fee )
440
    ; (has_zero_vesting_period, Zero_vesting_period)
441
    ; (is_incompatible_version, Incompatible_version)
442
    ; (is_disabled, Transaction_type_disabled)
443
    ; (has_invalid_call_forest, Zkapp_invalid_call_forest)
444
    ]
445
  in
446
  let errs0 =
447
    List.fold preds ~init:[] ~f:(fun acc (f, err) ->
448
        if f t then err :: acc else acc )
×
449
  in
450
  let errs =
×
451
    match valid_size ~genesis_constants t with
452
    | Ok () ->
×
453
        errs0
454
    | Error err ->
×
455
        Zkapp_too_big err :: errs0
456
  in
457
  if List.is_empty errs then Ok () else Error errs
×
458

459
type fee_payer_summary_t = Signature.t * Account.key * int
×
460
[@@deriving yojson, hash]
×
461

462
let fee_payer_summary : t -> fee_payer_summary_t = function
463
  | Zkapp_command cmd ->
×
464
      let fp = Zkapp_command.fee_payer_account_update cmd in
465
      let open Account_update in
×
466
      let body = Fee_payer.body fp in
467
      ( Fee_payer.authorization fp
×
468
      , Body.Fee_payer.public_key body
×
469
      , Body.Fee_payer.nonce body |> Unsigned.UInt32.to_int )
×
470
  | Signed_command cmd ->
×
471
      Signed_command.
472
        (signature cmd, fee_payer_pk cmd, nonce cmd |> Unsigned.UInt32.to_int)
×
473

474
let fee_payer_summary_json =
475
  Fn.compose fee_payer_summary_t_to_yojson fee_payer_summary
9✔
476

477
let fee_payer_summary_string =
478
  let to_string (signature, pk, nonce) =
479
    sprintf "%s (%s %d)"
×
480
      (Signature.to_base58_check signature)
×
481
      (Signature_lib.Public_key.Compressed.to_base58_check pk)
×
482
      nonce
483
  in
484
  Fn.compose to_string fee_payer_summary
9✔
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