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

MinaProtocol / mina / 242

04 Jun 2025 08:45PM UTC coverage: 36.779% (-19.8%) from 56.614%
242

push

buildkite

web-flow
Merge pull request #17086 from MinaProtocol/dkijania/port_terraform_removal

port terraform removal to develop

26262 of 71405 relevant lines covered (36.78%)

34615.67 hits per line

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

26.06
/src/lib/cli_lib/commands.ml
1
open Signature_lib
10✔
2

3
(* Alias ocamlp-streams before it's hidden by open
4
   Core_kernel *)
5
module Streams = Stream
6
open Core_kernel
7
open Async
8

9
let generate_keypair =
10
  Command.async ~summary:"Generate a new public, private keypair"
10✔
11
    (let%map_open.Command privkey_path = Flag.privkey_write_path in
12
     Exceptions.handle_nicely
×
13
     @@ fun () ->
14
     let env = Secrets.Keypair.env in
×
15
     if Option.is_some (Sys.getenv env) then
×
16
       eprintf "Using password from environment variable %s\n" env ;
×
17
     let kp = Keypair.create () in
×
18
     let%bind () = Secrets.Keypair.Terminal_stdin.write_exn kp ~privkey_path in
×
19
     printf "Keypair generated\nPublic key: %s\nRaw public key: %s\n"
×
20
       ( kp.public_key |> Public_key.compress
×
21
       |> Public_key.Compressed.to_base58_check )
×
22
       (Rosetta_coding.Coding.of_public_key kp.public_key) ;
×
23
     exit 0 )
×
24

25
let balance =
26
  Command.Arg_type.map Command.Param.string
10✔
27
    ~f:Currency.Balance.of_mina_string_exn
28

29
let generate_test_ledger =
30
  Command.async ~summary:"Generate a ledger for testing"
10✔
31
    (let open Command.Let_syntax in
32
    let%map_open n =
33
      Command.Param.flag "-n"
10✔
34
        ~doc:(Printf.sprintf "NN number of accounts to generate")
10✔
35
        (required int)
10✔
36
    and min_balance =
37
      flag "--min-balance" ~doc:"MINA Minimum balance of a key"
10✔
38
        (optional balance)
10✔
39
    and max_balance =
40
      flag "--max-balance" ~doc:"MINA Maximum balance of a key"
10✔
41
        (optional balance)
10✔
42
    in
43
    Exceptions.handle_nicely
2✔
44
    @@ fun () ->
45
    let min_balance = Option.value ~default:Currency.Balance.zero min_balance in
2✔
46
    let max_balance =
2✔
47
      Option.value ~default:(Currency.Balance.of_mina_int_exn 100) max_balance
2✔
48
    in
49
    let balance_seq =
2✔
50
      Quickcheck.random_sequence ~seed:`Nondeterministic
51
      @@ Currency.Balance.gen_incl min_balance max_balance
2✔
52
    in
53
    let ledger =
2✔
54
      Sequence.take balance_seq n
2✔
55
      |> Sequence.map ~f:(fun balance ->
2✔
56
             let kp = Keypair.create () in
40,000✔
57
             { Runtime_config.Json_layout.Accounts.Single.default with
40,000✔
58
               pk =
59
                 Public_key.compress kp.public_key
40,000✔
60
                 |> Public_key.Compressed.to_base58_check
40,000✔
61
             ; sk = Some (Private_key.to_base58_check kp.private_key)
40,000✔
62
             ; balance
63
             } )
64
      |> Sequence.to_list
65
    in
66
    Yojson.Safe.pretty_print Format.std_formatter
2✔
67
    @@ Runtime_config.Json_layout.Accounts.to_yojson ledger ;
2✔
68
    exit 0)
2✔
69

70
let validate_keypair =
71
  Command.async ~summary:"Validate a public, private keypair"
10✔
72
    (let open Command.Let_syntax in
73
    let open Core_kernel in
74
    let%map_open privkey_path = Flag.privkey_write_path in
75
    Exceptions.handle_nicely
×
76
    @@ fun () ->
77
    let read_pk () =
×
78
      let pubkey_path = privkey_path ^ ".pub" in
×
79
      try
80
        In_channel.with_file pubkey_path ~f:(fun in_channel ->
×
81
            match In_channel.input_line in_channel with
×
82
            | Some line -> (
×
83
                try Public_key.Compressed.of_base58_check_exn line
×
84
                with _exn ->
×
85
                  eprintf
86
                    "Could not create public key in file %s from text: %s\n"
87
                    pubkey_path line ;
88
                  exit 1 )
×
89
            | None ->
×
90
                eprintf "No public key found in file %s\n" pubkey_path ;
91
                exit 1 )
×
92
      with exn ->
×
93
        eprintf "Could not read public key file %s, error: %s\n" pubkey_path
94
          (Exn.to_string exn) ;
×
95
        exit 1
×
96
    in
97
    let compare_public_keys ~pk_from_disk ~pk_from_keypair =
98
      if Public_key.Compressed.equal pk_from_disk pk_from_keypair then
×
99
        printf "Public key on-disk is derivable from private key\n"
×
100
      else (
×
101
        eprintf
102
          "Public key read from disk %s different than public key %s derived \
103
           from private key\n"
104
          (Public_key.Compressed.to_base58_check pk_from_disk)
×
105
          (Public_key.Compressed.to_base58_check pk_from_keypair) ;
×
106
        exit 1 )
×
107
    in
108
    let validate_transaction keypair =
109
      let signature_kind = Mina_signature_kind.t_DEPRECATED in
×
110
      let dummy_payload = Mina_base.Signed_command_payload.dummy in
111
      let signature =
112
        Mina_base.Signed_command.sign_payload ~signature_kind
113
          keypair.Keypair.private_key dummy_payload
114
      in
115
      let message = Mina_base.Signed_command.to_input_legacy dummy_payload in
×
116
      let verified =
×
117
        Schnorr.Legacy.verify ~signature_kind signature
118
          (Snark_params.Tick.Inner_curve.of_affine keypair.public_key)
×
119
          message
120
      in
121
      if verified then printf "Verified a transaction using specified keypair\n"
×
122
      else (
×
123
        eprintf "Failed to verify a transaction using the specific keypair\n" ;
124
        exit 1 )
×
125
    in
126
    let open Deferred.Let_syntax in
127
    let%bind () =
128
      let password =
129
        lazy (Secrets.Keypair.Terminal_stdin.prompt_password "Enter password: ")
×
130
      in
131
      match%map Secrets.Keypair.read ~privkey_path ~password with
132
      | Ok keypair ->
×
133
          let pk_from_disk = read_pk () in
134
          compare_public_keys ~pk_from_disk
×
135
            ~pk_from_keypair:(keypair.public_key |> Public_key.compress) ;
×
136
          validate_transaction keypair
137
      | Error err ->
×
138
          eprintf "Could not read the specified keypair: %s\n"
139
            (Secrets.Privkey_error.to_string err) ;
×
140
          exit 1
×
141
    in
142
    exit 0)
×
143

144
let validate_transaction =
145
  Command.async
10✔
146
    ~summary:
147
      "Validate the signature on one or more transactions, provided to stdin \
148
       in rosetta format"
149
    ( Command.Param.return
10✔
150
    @@ fun () ->
151
    let num_fails = ref 0 in
×
152
    (* TODO upgrade to yojson 2.0.0 when possible to use seq_from_channel
153
     * instead of the deprecated stream interface *)
154
    let jsons = Yojson.Safe.stream_from_channel In_channel.stdin in
155
    let signature_kind = Mina_signature_kind.t_DEPRECATED in
×
156
    ( match
157
        Or_error.try_with (fun () ->
158
            Streams.iter
×
159
              (fun transaction_json ->
160
                match
×
161
                  Rosetta_lib.Transaction.to_mina_signed transaction_json
162
                with
163
                | Ok cmd ->
×
164
                    if
165
                      Mina_base.Signed_command.check_signature ~signature_kind
166
                        cmd
167
                    then Format.eprintf "Transaction was valid@."
×
168
                    else (
×
169
                      incr num_fails ;
170
                      Format.eprintf "Transaction was invalid@." )
×
171
                | Error err ->
×
172
                    incr num_fails ;
173
                    Format.eprintf
×
174
                      "@[<v>Failed to validate transaction:@,\
175
                       %s@,\
176
                       Failed with error:%s@]@."
177
                      (Yojson.Safe.pretty_to_string transaction_json)
×
178
                      (Yojson.Safe.pretty_to_string
×
179
                         (Error_json.error_to_yojson err) ) )
×
180
              jsons )
181
      with
182
    | Ok () ->
×
183
        ()
184
    | Error err ->
×
185
        Format.eprintf "@[<v>Error:@,%s@,@]@."
186
          (Yojson.Safe.pretty_to_string (Error_json.error_to_yojson err)) ;
×
187
        Format.printf "Invalid transaction.@." ;
×
188
        Core_kernel.exit 1 ) ;
×
189
    if !num_fails > 0 then (
×
190
      Format.printf "Some transactions failed to verify@." ;
191
      exit 1 )
×
192
    else
193
      let first = Streams.peek jsons in
×
194
      match first with
×
195
      | None ->
×
196
          Format.printf "Could not parse any transactions@." ;
197
          exit 1
×
198
      | _ ->
×
199
          Format.printf "All transactions were valid@." ;
200
          exit 0 )
×
201

202
module Vrf = struct
203
  let generate_witness =
204
    Command.async
10✔
205
      ~summary:
206
        "Generate a vrf evaluation witness. This may be used to calculate \
207
         whether a given private key will win a given slot (by checking \
208
         threshold_met = true in the JSON output), or to generate a witness \
209
         that a 3rd account_update can use to verify a vrf evaluation."
210
      (let open Command.Let_syntax in
211
      let%map_open privkey_path = Flag.privkey_read_path
212
      and global_slot =
213
        flag "--global-slot" ~doc:"NUM Global slot to evaluate the VRF for"
10✔
214
          (required int)
10✔
215
      and epoch_seed =
216
        flag "--epoch-seed" ~doc:"SEED Epoch seed to evaluate the VRF with"
10✔
217
          (required string)
10✔
218
      and delegator_index =
219
        flag "--delegator-index"
10✔
220
          ~doc:"NUM The index of the delegating account in the epoch ledger"
221
          (required int)
10✔
222
      and generate_outputs =
223
        flag "--generate-outputs"
10✔
224
          ~doc:
225
            "true|false Whether to generate the vrf in addition to the witness \
226
             (default: false)"
227
          (optional_with_default false bool)
10✔
228
      and delegated_stake =
229
        flag "--delegated-stake"
10✔
230
          ~doc:
231
            "AMOUNT The balance of the delegating account in the epoch ledger"
232
          (optional int)
10✔
233
      and total_stake =
234
        flag "--total-stake"
10✔
235
          ~doc:"AMOUNT The total balance of all accounts in the epoch ledger"
236
          (optional int)
10✔
237
      in
238
      Exceptions.handle_nicely
×
239
      @@ fun () ->
240
      let env = Secrets.Keypair.env in
×
241
      let constraint_constants =
242
        Genesis_constants.Compiled.constraint_constants
243
      in
244
      if Option.is_some (Sys.getenv env) then
×
245
        eprintf "Using password from environment variable %s\n" env ;
×
246
      let open Deferred.Let_syntax in
×
247
      (* TODO-someday: constraint constants from config file. *)
248
      let%bind () =
249
        let password =
250
          lazy
251
            (Secrets.Keypair.Terminal_stdin.prompt_password "Enter password: ")
×
252
        in
253
        match%bind Secrets.Keypair.read ~privkey_path ~password with
254
        | Ok keypair ->
×
255
            let open Consensus_vrf.Layout in
256
            let evaluation =
257
              Evaluation.of_message_and_sk ~constraint_constants
258
                { global_slot =
259
                    Mina_numbers.Global_slot_since_hard_fork.of_int global_slot
×
260
                ; epoch_seed =
261
                    Mina_base.Epoch_seed.of_base58_check_exn epoch_seed
×
262
                ; delegator_index
263
                }
264
                keypair.private_key
265
            in
266
            let evaluation =
×
267
              match (delegated_stake, total_stake) with
268
              | Some delegated_stake, Some total_stake ->
×
269
                  { evaluation with
270
                    vrf_threshold =
271
                      Some
272
                        { delegated_stake =
273
                            Currency.Balance.of_nanomina_int_exn delegated_stake
×
274
                        ; total_stake =
275
                            Currency.Amount.of_nanomina_int_exn total_stake
×
276
                        }
277
                  }
278
              | _ ->
×
279
                  evaluation
280
            in
281
            let evaluation =
282
              if generate_outputs then
283
                Evaluation.compute_vrf ~constraint_constants evaluation
×
284
              else evaluation
×
285
            in
286
            Format.printf "%a@."
287
              (Yojson.Safe.pretty_print ?std:None)
288
              (Evaluation.to_yojson evaluation) ;
×
289
            Deferred.return ()
×
290
        | Error err ->
×
291
            eprintf "Could not read the specified keypair: %s\n"
292
              (Secrets.Privkey_error.to_string err) ;
×
293
            exit 1
×
294
      in
295
      exit 0)
×
296

297
  let batch_generate_witness =
298
    Command.async
10✔
299
      ~summary:
300
        "Generate a batch of vrf evaluation witnesses from {\"globalSlot\": _, \
301
         \"epochSeed\": _, \"delegatorIndex\": _} JSON message objects read on \
302
         stdin"
303
      (let open Command.Let_syntax in
304
      let%map_open privkey_path = Flag.privkey_read_path in
305
      Exceptions.handle_nicely
×
306
      @@ fun () ->
307
      let constraint_constants =
×
308
        Genesis_constants.Compiled.constraint_constants
309
      in
310
      let env = Secrets.Keypair.env in
311
      if Option.is_some (Sys.getenv env) then
×
312
        eprintf "Using password from environment variable %s\n" env ;
×
313
      let open Deferred.Let_syntax in
×
314
      (* TODO-someday: constraint constants from config file. *)
315
      let%bind () =
316
        let password =
317
          lazy
318
            (Secrets.Keypair.Terminal_stdin.prompt_password "Enter password: ")
×
319
        in
320
        match%bind Secrets.Keypair.read ~privkey_path ~password with
321
        | Ok keypair ->
×
322
            let lexbuf = Lexing.from_channel In_channel.stdin in
323
            let lexer = Yojson.init_lexer () in
×
324
            Deferred.repeat_until_finished () (fun () ->
×
325
                Deferred.Or_error.try_with ~here:[%here] (fun () ->
×
326
                    try
×
327
                      let message_json =
328
                        Yojson.Safe.from_lexbuf ~stream:true lexer lexbuf
329
                      in
330
                      let open Consensus_vrf.Layout in
×
331
                      let message =
332
                        Result.ok_or_failwith (Message.of_yojson message_json)
×
333
                      in
334
                      let evaluation =
×
335
                        Evaluation.of_message_and_sk ~constraint_constants
336
                          message keypair.private_key
337
                      in
338
                      Format.printf "%a@."
×
339
                        (Yojson.Safe.pretty_print ?std:None)
340
                        (Evaluation.to_yojson evaluation) ;
×
341
                      Deferred.return (`Repeat ())
×
342
                    with Yojson.End_of_input -> return (`Finished ()) )
×
343
                >>| function
344
                | Ok x ->
×
345
                    x
346
                | Error err ->
×
347
                    Format.eprintf "@[<v>Error:@,%s@,@]@."
348
                      (Yojson.Safe.pretty_to_string
×
349
                         (Error_json.error_to_yojson err) ) ;
×
350
                    `Repeat () )
×
351
        | Error err ->
×
352
            eprintf "Could not read the specified keypair: %s\n"
353
              (Secrets.Privkey_error.to_string err) ;
×
354
            exit 1
×
355
      in
356
      exit 0)
×
357

358
  let batch_check_witness =
359
    Command.async
10✔
360
      ~summary:
361
        "Check a batch of vrf evaluation witnesses read on stdin. Outputs the \
362
         verified vrf evaluations (or no vrf output if the witness is \
363
         invalid), and whether the vrf output satisfies the threshold values \
364
         if given. The threshold should be included in the JSON for each vrf \
365
         as the 'vrfThreshold' field, of format {delegatedStake: 1000, \
366
         totalStake: 1000000000}. The threshold is not checked against a \
367
         ledger; this should be done manually to confirm whether threshold_met \
368
         in the output corresponds to an actual won block."
369
      ( Command.Param.return @@ Exceptions.handle_nicely
10✔
370
      @@ fun () ->
371
      let open Deferred.Let_syntax in
×
372
      let constraint_constants =
373
        Genesis_constants.Compiled.constraint_constants
374
      in
375
      (* TODO-someday: constraint constants from config file. *)
376
      let lexbuf = Lexing.from_channel In_channel.stdin in
377
      let lexer = Yojson.init_lexer () in
×
378
      let%bind () =
379
        Deferred.repeat_until_finished () (fun () ->
×
380
            Deferred.Or_error.try_with ~here:[%here] (fun () ->
×
381
                try
×
382
                  let evaluation_json =
383
                    Yojson.Safe.from_lexbuf ~stream:true lexer lexbuf
384
                  in
385
                  let open Consensus_vrf.Layout in
×
386
                  let evaluation =
387
                    Result.ok_or_failwith (Evaluation.of_yojson evaluation_json)
×
388
                  in
389
                  let evaluation =
×
390
                    Evaluation.compute_vrf ~constraint_constants evaluation
391
                  in
392
                  Format.printf "%a@."
×
393
                    (Yojson.Safe.pretty_print ?std:None)
394
                    (Evaluation.to_yojson evaluation) ;
×
395
                  Deferred.return (`Repeat ())
×
396
                with Yojson.End_of_input -> return (`Finished ()) )
×
397
            >>| function
398
            | Ok x ->
×
399
                x
400
            | Error err ->
×
401
                Format.eprintf "@[<v>Error:@,%s@,@]@."
402
                  (Yojson.Safe.pretty_to_string
×
403
                     (Error_json.error_to_yojson err) ) ;
×
404
                `Repeat () )
×
405
      in
406
      exit 0 )
×
407

408
  let command_group =
409
    Command.group ~summary:"Commands for vrf evaluations"
10✔
410
      [ ("generate-witness", generate_witness)
411
      ; ("batch-generate-witness", batch_generate_witness)
412
      ; ("batch-check-witness", batch_check_witness)
413
      ]
414
end
20✔
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