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

MinaProtocol / mina / 748

30 Oct 2025 04:49AM UTC coverage: 36.902% (-0.1%) from 37.022%
748

push

buildkite

web-flow
Merge pull request #17974 from MinaProtocol/lyh/cjjdespres/allow-stop-time-randomness-disable/into-develop

Merge #17964 into dev to fix their clean merge issue.

2 of 6 new or added lines in 2 files covered. (33.33%)

69 existing lines in 9 files now uncovered.

26832 of 72712 relevant lines covered (36.9%)

36482.32 hits per line

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

22.08
/src/app/cli/src/cli_entrypoint/mina_cli_entrypoint.ml
1
open Core
2
open Async
3
open Mina_base
4
open Cli_lib
5
open Signature_lib
6
open Init
7
module YJ = Yojson.Safe
8

9
type mina_initialization =
10
  { mina : Mina_lib.t
11
  ; client_trustlist : Unix.Cidr.t list option
12
  ; rest_server_port : int
13
  ; limited_graphql_port : int option
14
  ; itn_graphql_port : int option
15
  }
16

17
(* keep this code in sync with Client.chain_id_inputs, Mina_commands.chain_id_inputs, and
18
   Daemon_rpcs.Chain_id_inputs
19
*)
20
let chain_id ~constraint_system_digests ~genesis_state_hash ~genesis_constants
21
    ~protocol_transaction_version ~protocol_network_version =
22
  (* if this changes, also change Mina_commands.chain_id_inputs *)
23
  let genesis_state_hash = State_hash.to_base58_check genesis_state_hash in
×
24
  let genesis_constants_hash = Genesis_constants.hash genesis_constants in
×
25
  let all_snark_keys =
×
26
    List.map constraint_system_digests ~f:(fun (_, digest) -> Md5.to_hex digest)
×
27
    |> String.concat ~sep:""
×
28
  in
29
  let version_digest v = Int.to_string v |> Md5.digest_string |> Md5.to_hex in
×
30
  let protocol_transaction_version_digest =
31
    version_digest protocol_transaction_version
32
  in
33
  let protocol_network_version_digest =
×
34
    version_digest protocol_network_version
35
  in
36
  let b2 =
×
37
    Blake2.digest_string
38
      ( genesis_state_hash ^ all_snark_keys ^ genesis_constants_hash
39
      ^ protocol_transaction_version_digest ^ protocol_network_version_digest )
40
  in
41
  Blake2.to_hex b2
×
42

43
let plugin_flag =
44
  if Node_config.plugins then
45
    let open Command.Param in
×
46
    flag "--load-plugin" ~aliases:[ "load-plugin" ] (listed string)
×
47
      ~doc:
48
        "PATH The path to load a .cmxs plugin from. May be passed multiple \
49
         times"
50
  else Command.Param.return []
108✔
51

52
module Chain_state_locations = struct
53
  (** The locations of the chain state in a daemon. These will be computed by
54
      [chain_state_locations] based on the runtime daemon config. By default,
55
      the [chain_state] will be located in the mina config directory and the
56
      other directories will be located in the [chain_state]. *)
57
  type t =
58
    { chain_state : string  (** The top-level chain state directory *)
59
    ; mina_net : string  (** Mina networking information *)
60
    ; trust : string  (** P2P trust information *)
61
    ; root : string  (** The root snarked ledgers *)
62
    ; genesis : string  (** The genesis ledgers *)
63
    ; frontier : string  (** The transition frontier *)
64
    ; epoch_ledger : string  (** The epoch ledger snapshots *)
65
    ; proof_cache : string  (** The proof cache *)
66
    ; zkapp_vk_cache : string  (** The zkApp vk cache *)
67
    ; snark_pool : string  (** The snark pool *)
68
    }
69

70
  (** Determine the locations of the chain state components based on the daemon
71
      runtime config *)
72
  let of_config ~conf_dir (config : Runtime_config.t) : t =
73
    (* TODO: post hard fork, we should not be ignoring this *)
74
    let _config = config in
×
75
    let chain_state = conf_dir in
76
    { chain_state
77
    ; mina_net = chain_state ^/ "mina_net2"
×
78
    ; trust = chain_state ^/ "trust"
×
79
    ; root = chain_state ^/ "root"
×
80
    ; genesis = chain_state ^/ "genesis"
×
81
    ; frontier = chain_state ^/ "frontier"
×
82
    ; epoch_ledger = chain_state
83
    ; proof_cache = chain_state ^/ "proof_cache"
×
84
    ; zkapp_vk_cache = chain_state ^/ "zkapp_vk_cache"
×
85
    ; snark_pool = chain_state ^/ "snark_pool"
×
86
    }
87
end
88

89
let load_config_files ~logger ~genesis_constants ~constraint_constants ~conf_dir
90
    ~genesis_dir ~cli_proof_level ~proof_level ~genesis_backing_type
91
    config_files =
92
  let%bind config_jsons =
93
    let config_files_paths =
94
      List.map config_files ~f:(fun (config_file, _) -> `String config_file)
×
95
    in
96
    [%log info] "Reading configuration files $config_files"
×
97
      ~metadata:[ ("config_files", `List config_files_paths) ] ;
98
    Deferred.List.filter_map config_files
×
99
      ~f:(fun (config_file, handle_missing) ->
100
        match%bind Genesis_ledger_helper.load_config_json config_file with
×
101
        | Ok config_json ->
×
102
            let%map config_json =
103
              Genesis_ledger_helper.upgrade_old_config ~logger config_file
×
104
                config_json
105
            in
106
            Some (config_file, config_json)
×
107
        | Error err -> (
×
108
            match handle_missing with
109
            | `Must_exist ->
×
110
                Mina_stdlib.Mina_user_error.raisef
111
                  ~where:"reading configuration file"
112
                  "The configuration file %s could not be read:\n%s" config_file
113
                  (Error.to_string_hum err)
×
114
            | `May_be_missing ->
×
115
                [%log warn] "Could not read configuration from $config_file"
×
116
                  ~metadata:
117
                    [ ("config_file", `String config_file)
118
                    ; ("error", Error_json.error_to_yojson err)
×
119
                    ] ;
120
                return None ) )
×
121
  in
122
  let config =
×
123
    List.fold ~init:Runtime_config.default config_jsons
124
      ~f:(fun config (config_file, config_json) ->
125
        match Runtime_config.of_yojson config_json with
×
126
        | Ok loaded_config ->
×
127
            Runtime_config.combine config loaded_config
128
        | Error err ->
×
129
            [%log fatal]
×
130
              "Could not parse configuration from $config_file: $error"
131
              ~metadata:
132
                [ ("config_file", `String config_file)
133
                ; ("config_json", config_json)
134
                ; ("error", `String err)
135
                ] ;
136
            failwithf "Could not parse configuration file: %s" err () )
×
137
  in
138
  let chain_state_locations =
×
139
    Chain_state_locations.of_config ~conf_dir config
140
  in
141
  let genesis_dir =
×
142
    Option.value ~default:chain_state_locations.genesis genesis_dir
143
  in
144
  let%bind precomputed_values =
145
    match%map
146
      Genesis_ledger_helper.init_from_config_file ~cli_proof_level ~genesis_dir
×
147
        ~logger ~genesis_constants ~constraint_constants ~proof_level
148
        ~genesis_backing_type config
149
    with
150
    | Ok (precomputed_values, _) ->
×
151
        precomputed_values
152
    | Error err ->
×
153
        let ( json_config
154
            , `Accounts_omitted
155
                ( `Genesis genesis_accounts_omitted
156
                , `Staking staking_accounts_omitted
157
                , `Next next_accounts_omitted ) ) =
158
          Runtime_config.to_yojson_without_accounts config
159
        in
160
        let append_accounts_omitted s =
×
161
          Option.value_map
×
162
            ~f:(fun i -> List.cons (s ^ "_accounts_omitted", `Int i))
×
163
            ~default:Fn.id
164
        in
165
        let metadata =
166
          append_accounts_omitted "genesis" genesis_accounts_omitted
167
          @@ append_accounts_omitted "staking" staking_accounts_omitted
×
168
          @@ append_accounts_omitted "next" next_accounts_omitted []
×
169
          @ [ ("config", json_config)
170
            ; ( "name"
171
              , `String
172
                  (Option.value ~default:"not provided"
×
173
                     (let%bind.Option ledger = config.ledger in
174
                      Option.first_some ledger.name ledger.hash ) ) )
×
175
            ; ("error", Error_json.error_to_yojson err)
×
176
            ]
177
        in
178
        [%log info]
×
179
          "Initializing with runtime configuration. Ledger source: $name"
180
          ~metadata ;
181
        Error.raise err
×
182
  in
183
  return (precomputed_values, config_jsons, config, chain_state_locations)
×
184

185
let setup_daemon logger ~itn_features ~default_snark_worker_fee =
186
  let open Command.Let_syntax in
216✔
187
  let open Cli_lib.Arg_type in
188
  let receiver_key_warning = Cli_lib.Default.receiver_key_warning in
189
  let%map_open conf_dir = Cli_lib.Flag.conf_dir
190
  and block_production_key =
191
    flag "--block-producer-key" ~aliases:[ "block-producer-key" ]
216✔
192
      ~doc:
193
        (sprintf
216✔
194
           "DEPRECATED: Use environment variable `MINA_BP_PRIVKEY` instead. \
195
            Private key file for the block producer. Providing this flag or \
196
            the environment variable will enable block production. You cannot \
197
            provide both `block-producer-key` and `block-producer-pubkey`. \
198
            (default: use environment variable `MINA_BP_PRIVKEY`, if provided, \
199
            or else don't produce any blocks) %s"
200
           receiver_key_warning )
201
      (optional string)
216✔
202
  and block_production_pubkey =
203
    flag "--block-producer-pubkey"
216✔
204
      ~aliases:[ "block-producer-pubkey" ]
205
      ~doc:
206
        (sprintf
216✔
207
           "PUBLICKEY Public key for the associated private key that is being \
208
            tracked by this daemon. You cannot provide both \
209
            `block-producer-key` (or `MINA_BP_PRIVKEY`) and \
210
            `block-producer-pubkey`. (default: don't produce blocks) %s"
211
           receiver_key_warning )
212
      (optional public_key_compressed)
216✔
213
  and block_production_password =
214
    flag "--block-producer-password"
216✔
215
      ~aliases:[ "block-producer-password" ]
216
      ~doc:
217
        "PASSWORD Password associated with the block-producer key. Setting \
218
         this is equivalent to setting the MINA_PRIVKEY_PASS environment \
219
         variable. Be careful when setting it in the commandline as it will \
220
         likely get tracked in your history. Mainly to be used from the \
221
         daemon.json config file"
222
      (optional string)
216✔
223
  and itn_keys =
224
    if itn_features then
225
      flag "--itn-keys" ~aliases:[ "itn-keys" ] (optional string)
×
226
        ~doc:
227
          "PUBLICKEYS A comma-delimited list of Ed25519 public keys that are \
228
           permitted to send signed requests to the incentivized testnet \
229
           GraphQL server"
230
    else Command.Param.return None
216✔
231
  and itn_max_logs =
232
    if itn_features then
233
      flag "--itn-max-logs" ~aliases:[ "itn-max-logs" ] (optional int)
×
234
        ~doc:
235
          "NN Maximum number of logs to store to be made available via GraphQL \
236
           for incentivized testnet"
237
    else Command.Param.return None
216✔
238
  and demo_mode =
239
    flag "--demo-mode" ~aliases:[ "demo-mode" ] no_arg
216✔
240
      ~doc:
241
        "Run the daemon in demo-mode -- assume we're \"synced\" to the network \
242
         instantly"
243
  and coinbase_receiver_flag =
244
    flag "--coinbase-receiver" ~aliases:[ "coinbase-receiver" ]
216✔
245
      ~doc:
246
        (sprintf
216✔
247
           "PUBLICKEY Address to send coinbase rewards to (if this node is \
248
            producing blocks). If not provided, coinbase rewards will be sent \
249
            to the producer of a block. %s"
250
           receiver_key_warning )
251
      (optional public_key_compressed)
216✔
252
  and genesis_dir =
253
    flag "--genesis-ledger-dir" ~aliases:[ "genesis-ledger-dir" ]
216✔
254
      ~doc:
255
        "DIR Directory that contains the genesis ledger and the genesis \
256
         blockchain proof (default: <config-dir>)"
257
      (optional string)
216✔
258
  and run_snark_worker_flag =
259
    flag "--run-snark-worker" ~aliases:[ "run-snark-worker" ]
216✔
260
      ~doc:
261
        (sprintf "PUBLICKEY Run the SNARK worker with this public key. %s"
216✔
262
           receiver_key_warning )
263
      (optional public_key_compressed)
216✔
264
  and run_snark_coordinator_flag =
265
    flag "--run-snark-coordinator"
216✔
266
      ~aliases:[ "run-snark-coordinator" ]
267
      ~doc:
268
        (sprintf
216✔
269
           "PUBLICKEY Run a SNARK coordinator with this public key (ignored if \
270
            the run-snark-worker is set). %s"
271
           receiver_key_warning )
272
      (optional public_key_compressed)
216✔
273
  and snark_worker_parallelism_flag =
274
    flag "--snark-worker-parallelism"
216✔
275
      ~aliases:[ "snark-worker-parallelism" ]
276
      ~doc:
277
        "NUM Run the SNARK worker using this many threads. Equivalent to \
278
         setting OMP_NUM_THREADS, but doesn't affect block production."
279
      (optional int)
216✔
280
  and work_selection_method_flag =
281
    flag "--work-selection" ~aliases:[ "work-selection" ]
216✔
282
      ~doc:
283
        "seq|rand|roffset Choose work sequentially (seq), randomly (rand), or \
284
         sequentially with a random offset (roffset) (default: rand)"
285
      (optional work_selection_method)
216✔
286
  and libp2p_port = Flag.Port.Daemon.external_
287
  and client_port = Flag.Port.Daemon.client
288
  and rest_server_port = Flag.Port.Daemon.rest_server
289
  and limited_graphql_port = Flag.Port.Daemon.limited_graphql_server
290
  and itn_graphql_port =
291
    if itn_features then
292
      flag "--itn-graphql-port" ~aliases:[ "itn-graphql-port" ]
×
293
        ~doc:"PORT GraphQL-server for incentivized testnet interaction"
294
        (optional int)
×
295
    else Command.Param.return None
216✔
296
  and open_limited_graphql_port =
297
    flag "--open-limited-graphql-port"
216✔
298
      ~aliases:[ "open-limited-graphql-port" ]
299
      no_arg
300
      ~doc:
301
        "Have the limited GraphQL server listen on all addresses, not just \
302
         localhost (this is INSECURE, make sure your firewall is configured \
303
         correctly!)"
304
  and archive_process_location = Flag.Host_and_port.Daemon.archive
305
  and metrics_server_port =
306
    flag "--metrics-port" ~aliases:[ "metrics-port" ]
216✔
307
      ~doc:
308
        "PORT metrics server for scraping via Prometheus (default no \
309
         metrics-server)"
310
      (optional int16)
216✔
311
  and gc_stat_interval =
312
    flag "--gc-stat-interval" ~aliases:[ "gc-stat-interval" ] (optional float)
216✔
313
      ~doc:
314
        (sprintf
216✔
315
           "INTERVAL in mins for collecting GC stats for metrics (Default: %f)"
316
           !Mina_metrics.Runtime.gc_stat_interval_mins )
317
  and libp2p_metrics_port =
318
    flag "--libp2p-metrics-port" ~aliases:[ "libp2p-metrics-port" ]
216✔
319
      ~doc:
320
        "PORT libp2p metrics server for scraping via Prometheus (default no \
321
         libp2p-metrics-server)"
322
      (optional int16)
216✔
323
  and external_ip_opt =
324
    flag "--external-ip" ~aliases:[ "external-ip" ]
216✔
325
      ~doc:
326
        "IP External IP address for other nodes to connect to. You only need \
327
         to set this if auto-discovery fails for some reason."
328
      (optional string)
216✔
329
  and bind_ip_opt =
330
    flag "--bind-ip" ~aliases:[ "bind-ip" ]
216✔
331
      ~doc:"IP IP of network interface to use for peer connections"
332
      (optional string)
216✔
333
  and working_dir =
334
    flag "--working-dir" ~aliases:[ "working-dir" ]
216✔
335
      ~doc:
336
        "PATH path to chdir into before starting (useful for background mode, \
337
         defaults to cwd, or / if -background)"
338
      (optional string)
216✔
339
  and is_background =
340
    flag "--background" ~aliases:[ "background" ] no_arg
216✔
341
      ~doc:"Run process on the background"
342
  and is_archive_rocksdb =
343
    flag "--archive-rocksdb" ~aliases:[ "archive-rocksdb" ] no_arg
216✔
344
      ~doc:"Stores all the blocks heard in RocksDB"
345
  and log_json = Flag.Log.json
346
  and log_level = Flag.Log.level
347
  and file_log_level = Flag.Log.file_log_level
348
  and file_log_rotations = Flag.Log.file_log_rotations
349
  and snark_work_fee =
350
    flag "--snark-worker-fee" ~aliases:[ "snark-worker-fee" ]
216✔
351
      ~doc:
352
        (sprintf
216✔
353
           "FEE Amount a worker wants to get compensated for generating a \
354
            snark proof (default: %d)"
355
           (Currency.Fee.to_nanomina_int default_snark_worker_fee) )
216✔
356
      (optional txn_fee)
216✔
357
  and work_reassignment_wait =
358
    flag "--work-reassignment-wait"
216✔
359
      ~aliases:[ "work-reassignment-wait" ]
360
      (optional int)
216✔
361
      ~doc:
362
        (sprintf
216✔
363
           "WAIT-TIME in ms before a snark-work is reassigned (default: %dms)"
364
           Cli_lib.Default.work_reassignment_wait )
365
  and enable_tracing =
366
    flag "--tracing" ~aliases:[ "tracing" ] no_arg
216✔
367
      ~doc:"Trace into $config-directory/trace/$pid.trace"
368
  and enable_internal_tracing =
369
    flag "--internal-tracing" ~aliases:[ "internal-tracing" ] no_arg
216✔
370
      ~doc:
371
        "Enables internal tracing into \
372
         $config-directory/internal-tracing/internal-trace.jsonl"
373
  and insecure_rest_server =
374
    flag "--insecure-rest-server" ~aliases:[ "insecure-rest-server" ] no_arg
216✔
375
      ~doc:
376
        "Have REST server listen on all addresses, not just localhost (this is \
377
         INSECURE, make sure your firewall is configured correctly!)"
378
  (* FIXME #4095
379
     and limit_connections =
380
       flag "--limit-concurrent-connections"
381
         ~aliases:[ "limit-concurrent-connections"]
382
         ~doc:
383
           "true|false Limit the number of concurrent connections per IP \
384
            address (default: true)"
385
         (optional bool)*)
386
  (*TODO: This is being added to log all the snark works received for the
387
     beta-testnet challenge. We might want to remove this later?*)
388
  and log_received_snark_pool_diff =
389
    flag "--log-snark-work-gossip"
216✔
390
      ~aliases:[ "log-snark-work-gossip" ]
391
      ~doc:"true|false Log snark-pool diff received from peers (default: false)"
392
      (optional bool)
216✔
393
  and log_transaction_pool_diff =
394
    flag "--log-txn-pool-gossip" ~aliases:[ "log-txn-pool-gossip" ]
216✔
395
      ~doc:
396
        "true|false Log transaction-pool diff received from peers (default: \
397
         false)"
398
      (optional bool)
216✔
399
  and log_block_creation =
400
    flag "--log-block-creation" ~aliases:[ "log-block-creation" ]
216✔
401
      ~doc:
402
        "true|false Log the steps involved in including transactions and snark \
403
         work in a block (default: true)"
404
      (optional bool)
216✔
405
  and libp2p_keypair =
406
    flag "--libp2p-keypair" ~aliases:[ "libp2p-keypair" ] (optional string)
216✔
407
      ~doc:
408
        "KEYFILE Keypair (generated from `mina libp2p generate-keypair`) to \
409
         use with libp2p discovery"
410
  and is_seed =
411
    flag "--seed" ~aliases:[ "seed" ] ~doc:"Start the node as a seed node"
216✔
412
      no_arg
413
  and enable_flooding =
414
    flag "--enable-flooding" ~aliases:[ "enable-flooding" ]
216✔
415
      ~doc:
416
        "true|false Publish our own blocks/transactions to every peer we can \
417
         find (default: false)"
418
      (optional bool)
216✔
419
  and peer_exchange =
420
    flag "--enable-peer-exchange" ~aliases:[ "enable-peer-exchange" ]
216✔
421
      ~doc:
422
        "true|false Help keep the mesh connected when closing connections \
423
         (default: false)"
424
      (optional bool)
216✔
425
  and peer_protection_ratio =
426
    flag "--peer-protection-rate" ~aliases:[ "peer-protection-rate" ]
216✔
427
      ~doc:"float Proportion of peers to be marked as protected (default: 0.2)"
428
      (optional_with_default 0.2 float)
216✔
429
  and min_connections =
430
    flag "--min-connections" ~aliases:[ "min-connections" ]
216✔
431
      ~doc:
432
        (Printf.sprintf
216✔
433
           "NN min number of connections that this peer will have to neighbors \
434
            in the gossip network (default: %d)"
435
           Cli_lib.Default.min_connections )
436
      (optional int)
216✔
437
  and max_connections =
438
    flag "--max-connections" ~aliases:[ "max-connections" ]
216✔
439
      ~doc:
440
        (Printf.sprintf
216✔
441
           "NN max number of connections that this peer will have to neighbors \
442
            in the gossip network. Tuning this higher will strengthen your \
443
            connection to the network in exchange for using more RAM (default: \
444
            %d)"
445
           Cli_lib.Default.max_connections )
446
      (optional int)
216✔
447
  and validation_queue_size =
448
    flag "--validation-queue-size"
216✔
449
      ~aliases:[ "validation-queue-size" ]
450
      ~doc:
451
        (Printf.sprintf
216✔
452
           "NN size of the validation queue in the p2p network used to buffer \
453
            messages (like blocks and transactions received on the gossip \
454
            network) while validation is pending. If a transaction, for \
455
            example, is invalid, we don't forward the message on the gossip \
456
            net. If this queue is too small, we will drop messages without \
457
            validating them. If it is too large, we are susceptible to DoS \
458
            attacks on memory. (default: %d)"
459
           Cli_lib.Default.validation_queue_size )
460
      (optional int)
216✔
461
  and direct_peers_raw =
462
    flag "--direct-peer" ~aliases:[ "direct-peer" ]
216✔
463
      ~doc:
464
        "/ip4/IPADDR/tcp/PORT/p2p/PEERID Peers to always send new messages \
465
         to/from. These peers should also have you configured as a direct \
466
         peer, the relationship is intended to be symmetric"
467
      (listed string)
216✔
468
  and isolate =
469
    flag "--isolate-network" ~aliases:[ "isolate-network" ]
216✔
470
      ~doc:
471
        "true|false Only allow connections to the peers passed on the command \
472
         line or configured through GraphQL. (default: false)"
473
      (optional bool)
216✔
474
  and libp2p_peers_raw =
475
    flag "--peer" ~aliases:[ "peer" ]
216✔
476
      ~doc:
477
        "/ip4/IPADDR/tcp/PORT/p2p/PEERID initial \"bootstrap\" peers for \
478
         discovery"
479
      (listed string)
216✔
480
  and libp2p_peer_list_file =
481
    flag "--peer-list-file" ~aliases:[ "peer-list-file" ]
216✔
482
      ~doc:
483
        "PATH path to a file containing \"bootstrap\" peers for discovery, one \
484
         multiaddress per line"
485
      (optional string)
216✔
486
  and seed_peer_list_url =
487
    flag "--peer-list-url" ~aliases:[ "peer-list-url" ]
216✔
488
      ~doc:"URL URL of seed peer list file. Will be polled periodically."
489
      (optional string)
216✔
490
  and proposed_protocol_version =
491
    flag "--proposed-protocol-version"
216✔
492
      ~aliases:[ "proposed-protocol-version" ]
493
      (optional string)
216✔
494
      ~doc:"NN.NN.NN Proposed protocol version to signal other nodes"
495
  and config_files =
496
    flag "--config-file" ~aliases:[ "config-file" ]
216✔
497
      ~doc:
498
        "PATH path to a configuration file (overrides MINA_CONFIG_FILE, \
499
         default: <config_dir>/daemon.json). Pass multiple times to override \
500
         fields from earlier config files"
501
      (listed string)
216✔
502
  and _may_generate =
503
    flag "--generate-genesis-proof"
216✔
504
      ~aliases:[ "generate-genesis-proof" ]
505
      ~doc:"true|false Deprecated. Passing this flag has no effect"
506
      (optional bool)
216✔
507
  and disable_node_status =
508
    flag "--disable-node-status" ~aliases:[ "disable-node-status" ] no_arg
216✔
509
      ~doc:"Disable reporting node status to other nodes (default: enabled)"
510
  and cli_proof_level =
511
    flag "--proof-level" ~aliases:[ "proof-level" ]
216✔
512
      (optional (Arg_type.create Genesis_constants.Proof_level.of_string))
216✔
513
      ~doc:
514
        "full|check|none Internal, for testing. Start or connect to a network \
515
         with full proving (full), snark-testing with dummy proofs (check), or \
516
         dummy proofs (none)"
517
  and plugins = plugin_flag
518
  and precomputed_blocks_path =
519
    flag "--precomputed-blocks-file"
216✔
520
      ~aliases:[ "precomputed-blocks-file" ]
521
      (optional string)
216✔
522
      ~doc:"PATH Path to write precomputed blocks to, for replay or archiving"
523
  and log_precomputed_blocks =
524
    flag "--log-precomputed-blocks"
216✔
525
      ~aliases:[ "log-precomputed-blocks" ]
526
      (optional_with_default false bool)
216✔
527
      ~doc:"true|false Include precomputed blocks in the log (default: false)"
528
  and start_filtered_logs =
529
    flag "--start-filtered-logs" (listed string)
216✔
530
      ~doc:
531
        "LOG-FILTER Include filtered logs for the given filter. May be passed \
532
         multiple times"
533
  and block_reward_threshold =
534
    flag "--minimum-block-reward" ~aliases:[ "minimum-block-reward" ]
216✔
535
      ~doc:
536
        "AMOUNT Minimum reward a block produced by the node should have. Empty \
537
         blocks are created if the rewards are lower than the specified \
538
         threshold (default: No threshold, transactions and coinbase will be \
539
         included as long as the required snark work is available and can be \
540
         paid for)"
541
      (optional txn_amount)
216✔
542
  and stop_time =
543
    flag "--stop-time" ~aliases:[ "stop-time" ] (optional int)
216✔
544
      ~doc:
545
        (sprintf
216✔
546
           "UPTIME in hours after which the daemon stops itself (only if there \
547
            were no slots won within an hour after the stop time) (Default: \
548
            %d)"
549
           Cli_lib.Default.stop_time )
550
  and stop_time_interval =
551
    flag "--stop-time-interval" ~aliases:[ "stop-time-interval" ] (optional int)
216✔
552
      ~doc:
553
        (sprintf
216✔
554
           "UPTIME An upper bound (inclusive) on the random number of hours \
555
            added to the stop-time. Setting it to zero disables this \
556
            randomness. (Default: %d)"
557
           Cli_lib.Default.stop_time_interval )
558
  and upload_blocks_to_gcloud =
559
    flag "--upload-blocks-to-gcloud"
216✔
560
      ~aliases:[ "upload-blocks-to-gcloud" ]
561
      (optional_with_default false bool)
216✔
562
      ~doc:
563
        "true|false upload blocks to gcloud storage. Requires the environment \
564
         variables GCLOUD_KEYFILE, NETWORK_NAME, and \
565
         GCLOUD_BLOCK_UPLOAD_BUCKET"
566
  and all_peers_seen_metric =
567
    flag "--all-peers-seen-metric"
216✔
568
      ~aliases:[ "all-peers-seen-metric" ]
569
      (optional_with_default false bool)
216✔
570
      ~doc:
571
        "true|false whether to track the set of all peers ever seen for the \
572
         all_peers metric (default: false)"
573
  and node_status_url =
574
    flag "--node-status-url" ~aliases:[ "node-status-url" ] (optional string)
216✔
575
      ~doc:"URL of the node status collection service"
576
  and node_error_url =
577
    flag "--node-error-url" ~aliases:[ "node-error-url" ] (optional string)
216✔
578
      ~doc:"URL of the node error collection service"
579
  and simplified_node_stats =
580
    flag "--simplified-node-stats"
216✔
581
      ~aliases:[ "simplified-node-stats" ]
582
      (optional_with_default true bool)
216✔
583
      ~doc:"whether to report simplified node stats (default: true)"
584
  and contact_info =
585
    flag "--contact-info" ~aliases:[ "contact-info" ] (optional string)
216✔
586
      ~doc:
587
        "contact info used in node error report service (it could be either \
588
         email address or discord username), it should be less than 200 \
589
         characters"
590
    |> Command.Param.map ~f:(fun opt ->
216✔
591
           Option.value_map opt ~default:None ~f:(fun s ->
×
592
               if String.length s < 200 then Some s
×
593
               else
594
                 Mina_stdlib.Mina_user_error.raisef
×
595
                   "The length of contact info exceeds 200 characters:\n %s" s ) )
596
  and uptime_url_string =
597
    flag "--uptime-url" ~aliases:[ "uptime-url" ] (optional string)
216✔
598
      ~doc:"URL URL of the uptime service of the Mina delegation program"
599
  and uptime_submitter_key =
600
    flag "--uptime-submitter-key" ~aliases:[ "uptime-submitter-key" ]
216✔
601
      ~doc:
602
        "KEYFILE Private key file for the uptime submitter. You cannot provide \
603
         both `uptime-submitter-key` and `uptime-submitter-pubkey`."
604
      (optional string)
216✔
605
  and uptime_submitter_pubkey =
606
    flag "--uptime-submitter-pubkey"
216✔
607
      ~aliases:[ "uptime-submitter-pubkey" ]
608
      (optional string)
216✔
609
      ~doc:
610
        "PUBLICKEY Public key of the submitter to the Mina delegation program, \
611
         for the associated private key that is being tracked by this daemon. \
612
         You cannot provide both `uptime-submitter-key` and \
613
         `uptime-submitter-pubkey`."
614
  and uptime_send_node_commit =
615
    flag "--uptime-send-node-commit-sha"
216✔
616
      ~aliases:[ "uptime-send-node-commit-sha" ]
617
      ~doc:
618
        "true|false Whether to send the commit SHA used to build the node to \
619
         the uptime service. (default: false)"
620
      no_arg
621
  and hardfork_mode =
622
    (*
623
      There's 2 hardfork mode, namely Legacy and Auto. Reference:
624
        - https://www.notion.so/o1labs/HF-Mina-node-changes-specification-216e79b1f910805d9865e44f2f4baf0e 
625
        - https://www.notion.so/o1labs/V2-MIP-draft-HF-automation-250e79b1f9108010b0c5f2b1f416640b
626
        *)
627
    flag "--hardfork-mode" ~aliases:[ "hardfork-mode" ]
216✔
628
      ~doc:
629
        "auto|legacy Mode of hardfork. Under auto mode, the daemon would back \
630
         all ledgers that are needed for post-hardfork node to bootstrap with \
631
         2 databases, one for before, and one for after the hardfork. Under \
632
         legacy mode, all databased backed ledgers are backed by one database. \
633
         THIS FLAG IS INTERNAL USE ONLY AND WOULD BE REMOVED WITHOUT NOTICE"
634
      (optional_with_default Hardfork_mode.Legacy Hardfork_mode.arg)
216✔
635
  in
636
  let to_pubsub_topic_mode_option =
×
637
    let open Gossip_net.Libp2p in
638
    function
639
    | `String "ro" ->
×
640
        Some RO
641
    | `String "rw" ->
×
642
        Some RW
643
    | `String "none" ->
×
644
        Some N
645
    | `Null ->
×
646
        None
647
    | _ ->
×
648
        raise (Error.to_exn (Error.of_string "Invalid pubsub topic mode"))
×
649
  in
650
  fun () ->
651
    O1trace.thread "mina" (fun () ->
×
652
        let open Deferred.Let_syntax in
×
653
        let conf_dir = Mina_lib.Conf_dir.compute_conf_dir conf_dir in
654
        let%bind () = Mina_stdlib_unix.File_system.create_dir conf_dir in
×
655
        let () =
×
656
          if is_background then (
×
657
            Core.printf "Starting background mina daemon. (Log Dir: %s)\n%!"
658
              conf_dir ;
659
            Daemon.daemonize ~allow_threads_to_have_been_created:true
×
660
              ~redirect_stdout:`Dev_null ?cd:working_dir
661
              ~redirect_stderr:`Dev_null () )
662
          else Option.iter working_dir ~f:Caml.Sys.chdir
×
663
        in
664
        Stdout_log.setup log_json log_level ;
665
        (* 512MB logrotate max size = 1GB max filesystem usage *)
666
        let logrotate_max_size = 1024 * 1024 * 10 in
×
667
        Logger.Consumer_registry.register ~commit_id:Mina_version.commit_id
668
          ~id:Logger.Logger_id.mina
669
          ~processor:(Logger.Processor.raw ~log_level:file_log_level ())
×
670
          ~transport:
671
            (Logger_file_system.dumb_logrotate ~directory:conf_dir
672
               ~log_filename:"mina.log" ~max_size:logrotate_max_size
673
               ~num_rotate:file_log_rotations )
674
          () ;
675
        let best_tip_diff_log_size = 1024 * 1024 * 5 in
×
676
        Logger.Consumer_registry.register ~commit_id:Mina_version.commit_id
677
          ~id:Logger.Logger_id.best_tip_diff
678
          ~processor:(Logger.Processor.raw ())
×
679
          ~transport:
680
            (Logger_file_system.dumb_logrotate ~directory:conf_dir
681
               ~log_filename:"mina-best-tip.log"
682
               ~max_size:best_tip_diff_log_size ~num_rotate:1 )
683
          () ;
684
        let rejected_blocks_log_size = 1024 * 1024 * 5 in
×
685
        Logger.Consumer_registry.register ~commit_id:Mina_version.commit_id
686
          ~id:Logger.Logger_id.rejected_blocks
687
          ~processor:(Logger.Processor.raw ())
×
688
          ~transport:
689
            (Logger_file_system.dumb_logrotate ~directory:conf_dir
690
               ~log_filename:"mina-rejected-blocks.log"
691
               ~max_size:rejected_blocks_log_size ~num_rotate:50 )
692
          () ;
693
        Logger.Consumer_registry.register ~commit_id:Mina_version.commit_id
×
694
          ~id:Logger.Logger_id.oversized_logs
695
          ~processor:(Logger.Processor.raw ())
×
696
          ~transport:
697
            (Logger_file_system.dumb_logrotate ~directory:conf_dir
698
               ~log_filename:"mina-oversized-logs.log"
699
               ~max_size:logrotate_max_size ~num_rotate:20 )
700
          () ;
701
        (* Consumer for `[%log internal]` logging used for internal tracing *)
702
        Itn_logger.set_message_postprocessor
×
703
          Internal_tracing.For_itn_logger.post_process_message ;
704
        Logger.Consumer_registry.register ~commit_id:Mina_version.commit_id
×
705
          ~id:Logger.Logger_id.mina
706
          ~processor:Internal_tracing.For_logger.processor
707
          ~transport:
708
            (Internal_tracing.For_logger.json_lines_rotate_transport
×
709
               ~directory:(conf_dir ^ "/internal-tracing")
710
               () )
711
          () ;
712
        let version_metadata = [ ("commit", `String Mina_version.commit_id) ] in
×
713
        [%log info] "Mina daemon is booting up; built with commit $commit"
×
714
          ~metadata:version_metadata ;
715
        let%bind () =
716
          Mina_lib.Conf_dir.check_and_set_lockfile ~logger conf_dir
×
717
        in
718
        [%log info] "Booting may take several seconds, please wait" ;
×
719
        let wallets_disk_location = conf_dir ^/ "wallets" in
×
720
        let%bind wallets =
721
          (* Load wallets early, to give user errors before expensive
722
             initialization starts.
723
          *)
724
          Secrets.Wallets.load ~logger ~disk_location:wallets_disk_location
725
        in
726
        let%bind libp2p_keypair =
727
          let libp2p_keypair_old_format =
728
            Option.bind libp2p_keypair ~f:(fun libp2p_keypair ->
729
                match Mina_net2.Keypair.of_string libp2p_keypair with
×
730
                | Ok kp ->
×
731
                    Some kp
732
                | Error _ ->
×
733
                    if String.contains libp2p_keypair ',' then
734
                      [%log warn]
×
735
                        "I think -libp2p-keypair is in the old format, but I \
736
                         failed to parse it! Using it as a path..." ;
737
                    None )
×
738
          in
739
          match libp2p_keypair_old_format with
×
740
          | Some kp ->
×
741
              return (Some kp)
×
742
          | None -> (
×
743
              match libp2p_keypair with
744
              | None ->
×
745
                  return None
×
746
              | Some s ->
×
747
                  Secrets.Libp2p_keypair.Terminal_stdin.read_exn
748
                    ~should_prompt_user:false ~which:"libp2p keypair" s
749
                  |> Deferred.map ~f:Option.some )
×
750
        in
751
        let%bind () =
752
          let version_filename = conf_dir ^/ "mina.version" in
753
          let make_version () =
×
754
            let%map () =
755
              (*Delete any trace files if version changes. TODO: Implement rotate logic similar to log files*)
756
              Mina_stdlib_unix.File_system.remove_dir (conf_dir ^/ "trace")
×
757
            in
758
            Yojson.Safe.to_file version_filename (`Assoc version_metadata)
×
759
          in
760
          match
761
            Or_error.try_with_join (fun () ->
762
                match Yojson.Safe.from_file version_filename with
×
763
                | `Assoc list -> (
×
764
                    match String.Map.(find (of_alist_exn list) "commit") with
×
765
                    | Some (`String commit) ->
×
766
                        Ok commit
767
                    | _ ->
×
768
                        Or_error.errorf "commit not found in version file %s"
769
                          version_filename )
770
                | _ ->
×
771
                    Or_error.errorf "Unexpected value in %s" version_filename )
772
          with
773
          | Ok c ->
×
774
              if String.equal c Mina_version.commit_id then return ()
×
775
              else (
×
776
                [%log warn]
×
777
                  "Different version of Mina detected in config directory \
778
                   $config_directory, removing existing configuration"
779
                  ~metadata:[ ("config_directory", `String conf_dir) ] ;
780
                make_version () )
×
781
          | Error e ->
×
782
              [%log debug]
×
783
                "Error reading $file: $error. Cleaning up the config directory \
784
                 $config_directory"
785
                ~metadata:
786
                  [ ("error", `String (Error.to_string_mach e))
×
787
                  ; ("config_directory", `String conf_dir)
788
                  ; ("file", `String version_filename)
789
                  ] ;
790
              make_version ()
×
791
        in
792
        Parallel.init_master () ;
×
793
        let monitor = Async.Monitor.create ~name:"coda" () in
×
794
        let time_controller = Block_time.Controller.basic ~logger in
×
795
        let pids = Child_processes.Termination.create_pid_table () in
796
        let mina_initialization_deferred () =
×
797
          let config_file_installed =
×
798
            (* Search for config files installed as part of a deb/brew package.
799
               These files are commit-dependent, to ensure that we don't clobber
800
               configuration for dev builds or use incompatible configs.
801
            *)
802
            let config_file_installed =
803
              let json = "config_" ^ Mina_version.commit_id_short ^ ".json" in
804
              List.fold_until ~init:None
×
805
                (Cache_dir.possible_paths json)
×
806
                ~f:(fun _acc f ->
807
                  match Core.Sys.file_exists f with
×
808
                  | `Yes ->
×
809
                      Stop (Some f)
810
                  | _ ->
×
811
                      Continue None )
812
                ~finish:Fn.id
813
            in
814
            match config_file_installed with
815
            | Some config_file ->
×
816
                Some (config_file, `Must_exist)
817
            | None ->
×
818
                None
819
          in
820
          let config_file_configdir =
821
            (conf_dir ^/ "daemon.json", `May_be_missing)
×
822
          in
823
          let config_file_envvar =
824
            match Sys.getenv "MINA_CONFIG_FILE" with
825
            | Some config_file ->
×
826
                Some (config_file, `Must_exist)
827
            | None ->
×
828
                None
829
          in
830
          let config_files =
831
            Option.to_list config_file_installed
×
832
            @ (config_file_configdir :: Option.to_list config_file_envvar)
×
833
            @ List.map config_files ~f:(fun config_file ->
×
834
                  (config_file, `Must_exist) )
×
835
          in
836
          let genesis_constants =
837
            Genesis_constants.Compiled.genesis_constants
838
          in
839
          let constraint_constants =
840
            Genesis_constants.Compiled.constraint_constants
841
          in
842
          let compile_config = Mina_compile_config.Compiled.t in
843
          let ledger_backing_type =
844
            Mina_lib.Config.ledger_backing ~hardfork_mode
845
          in
846
          let%bind ( precomputed_values
847
                   , config_jsons
848
                   , config
849
                   , chain_state_locations ) =
850
            load_config_files ~logger ~conf_dir ~genesis_dir
×
851
              ~proof_level:Genesis_constants.Compiled.proof_level config_files
852
              ~genesis_constants ~constraint_constants ~cli_proof_level
853
              ~genesis_backing_type:ledger_backing_type
854
          in
855
          constraint_constants.block_window_duration_ms |> Float.of_int
×
856
          |> Time.Span.of_ms |> Mina_metrics.initialize_all ;
×
857

858
          let rev_daemon_configs =
×
859
            List.rev_filter_map config_jsons
860
              ~f:(fun (config_file, config_json) ->
861
                Option.map
×
862
                  YJ.Util.(
863
                    to_option Fn.id (YJ.Util.member "daemon" config_json))
×
864
                  ~f:(fun daemon_config -> (config_file, daemon_config)) )
×
865
          in
866
          let maybe_from_config (type a) (f : YJ.t -> a option)
×
867
              (keyname : string) (actual_value : a option) : a option =
868
            let open Option.Let_syntax in
×
869
            let open YJ.Util in
870
            match actual_value with
871
            | Some v ->
×
872
                Some v
873
            | None ->
×
874
                (* Load value from the latest config file that both
875
                   * has the key we are looking for, and
876
                   * has the key in a format that [f] can parse.
877
                *)
878
                let%map config_file, data =
879
                  List.find_map rev_daemon_configs
×
880
                    ~f:(fun (config_file, daemon_config) ->
881
                      let%bind json_val =
882
                        to_option Fn.id (member keyname daemon_config)
×
883
                      in
884
                      let%map data = f json_val in
×
885
                      (config_file, data) )
×
886
                in
887
                [%log debug] "Key $key being used from config file $config_file"
×
888
                  ~metadata:
889
                    [ ("key", `String keyname)
890
                    ; ("config_file", `String config_file)
891
                    ] ;
892
                data
×
893
          in
894
          let or_from_config map keyname actual_value ~default =
895
            match maybe_from_config map keyname actual_value with
×
896
            | Some x ->
×
897
                x
898
            | None ->
×
899
                [%log trace]
×
900
                  "Key '$key' not found in the config file, using default"
901
                  ~metadata:[ ("key", `String keyname) ] ;
902
                default
×
903
          in
904
          let get_port { Flag.Types.value; default; name } =
905
            or_from_config YJ.Util.to_int_option name ~default value
×
906
          in
907
          let libp2p_port = get_port libp2p_port in
908
          let rest_server_port = get_port rest_server_port in
×
909
          let limited_graphql_port =
×
910
            let ({ value; name } : int option Flag.Types.with_name) =
911
              limited_graphql_port
912
            in
913
            maybe_from_config YJ.Util.to_int_option name value
×
914
          in
915
          let client_port = get_port client_port in
916
          let snark_work_fee_flag =
×
917
            let json_to_currency_fee_option json =
918
              YJ.Util.to_int_option json
×
919
              |> Option.map ~f:Currency.Fee.of_nanomina_int_exn
×
920
            in
921
            or_from_config json_to_currency_fee_option "snark-worker-fee"
×
922
              ~default:compile_config.default_snark_worker_fee snark_work_fee
923
          in
924
          let node_status_url =
925
            maybe_from_config YJ.Util.to_string_option "node-status-url"
926
              node_status_url
927
          in
928
          (* FIXME #4095: pass this through to Gossip_net.Libp2p *)
929
          let _max_concurrent_connections =
×
930
            (*if
931
                 or_from_config YJ.Util.to_bool_option "max-concurrent-connections"
932
                   ~default:true limit_connections
933
               then Some 40
934
               else *)
935
            None
936
          in
937
          let work_selection_method =
938
            or_from_config
939
              (Fn.compose Option.return
×
940
                 (Fn.compose work_selection_method_val YJ.Util.to_string) )
×
941
              "work-selection"
942
              ~default:Cli_lib.Arg_type.Work_selection_method.Random
943
              work_selection_method_flag
944
          in
945
          let work_reassignment_wait =
×
946
            or_from_config YJ.Util.to_int_option "work-reassignment-wait"
947
              ~default:Cli_lib.Default.work_reassignment_wait
948
              work_reassignment_wait
949
          in
950
          let log_received_snark_pool_diff =
×
951
            or_from_config YJ.Util.to_bool_option "log-snark-work-gossip"
952
              ~default:false log_received_snark_pool_diff
953
          in
954
          let log_transaction_pool_diff =
×
955
            or_from_config YJ.Util.to_bool_option "log-txn-pool-gossip"
956
              ~default:false log_transaction_pool_diff
957
          in
958
          let log_block_creation =
×
959
            or_from_config YJ.Util.to_bool_option "log-block-creation"
960
              ~default:true log_block_creation
961
          in
962
          let log_gossip_heard =
×
963
            { Mina_networking.Config.snark_pool_diff =
964
                log_received_snark_pool_diff
965
            ; transaction_pool_diff = log_transaction_pool_diff
966
            ; new_state = true
967
            }
968
          in
969
          let json_to_publickey_compressed_option which json =
970
            YJ.Util.to_string_option json
×
971
            |> Option.bind ~f:(fun pk_str ->
×
972
                   match Public_key.Compressed.of_base58_check pk_str with
×
973
                   | Ok key -> (
×
974
                       match Public_key.decompress key with
975
                       | None ->
×
976
                           Mina_stdlib.Mina_user_error.raisef
977
                             ~where:"decompressing a public key"
978
                             "The %s public key %s could not be decompressed."
979
                             which pk_str
980
                       | Some _ ->
×
981
                           Some key )
982
                   | Error _e ->
×
983
                       Mina_stdlib.Mina_user_error.raisef
984
                         ~where:"decoding a public key"
985
                         "The %s public key %s could not be decoded." which
986
                         pk_str )
987
          in
988
          let run_snark_worker_flag =
989
            maybe_from_config
990
              (json_to_publickey_compressed_option "snark worker")
×
991
              "run-snark-worker" run_snark_worker_flag
992
          in
993
          let run_snark_coordinator_flag =
×
994
            maybe_from_config
995
              (json_to_publickey_compressed_option "snark coordinator")
×
996
              "run-snark-coordinator" run_snark_coordinator_flag
997
          in
998
          let snark_worker_parallelism_flag =
×
999
            maybe_from_config YJ.Util.to_int_option "snark-worker-parallelism"
1000
              snark_worker_parallelism_flag
1001
          in
1002
          let coinbase_receiver_flag =
×
1003
            maybe_from_config
1004
              (json_to_publickey_compressed_option "coinbase receiver")
×
1005
              "coinbase-receiver" coinbase_receiver_flag
1006
          in
1007
          let%bind external_ip =
1008
            match external_ip_opt with
1009
            | None ->
×
1010
                Find_ip.find ~logger
1011
            | Some ip ->
×
1012
                return @@ Unix.Inet_addr.of_string ip
×
1013
          in
1014
          let bind_ip =
×
1015
            Option.value bind_ip_opt ~default:"0.0.0.0"
1016
            |> Unix.Inet_addr.of_string
×
1017
          in
1018
          let addrs_and_ports : Node_addrs_and_ports.t =
×
1019
            { external_ip; bind_ip; peer = None; client_port; libp2p_port }
1020
          in
1021
          let block_production_key =
1022
            maybe_from_config YJ.Util.to_string_option "block-producer-key"
1023
              block_production_key
1024
          in
1025
          let block_production_pubkey =
×
1026
            maybe_from_config
1027
              (json_to_publickey_compressed_option "block producer")
×
1028
              "block-producer-pubkey" block_production_pubkey
1029
          in
1030
          let block_production_password =
×
1031
            maybe_from_config YJ.Util.to_string_option "block-producer-password"
1032
              block_production_password
1033
          in
1034
          Option.iter
×
1035
            ~f:(fun password ->
1036
              match Sys.getenv Secrets.Keypair.env with
×
1037
              | Some env_pass when not (String.equal env_pass password) ->
×
1038
                  [%log warn]
×
1039
                    "$envkey environment variable doesn't match value provided \
1040
                     on command-line or daemon.json. Using value from $envkey"
1041
                    ~metadata:[ ("envkey", `String Secrets.Keypair.env) ]
1042
              | _ ->
×
1043
                  Unix.putenv ~key:Secrets.Keypair.env ~data:password )
1044
            block_production_password ;
1045
          let%bind block_production_keypair =
1046
            match
1047
              ( block_production_key
1048
              , block_production_pubkey
1049
              , Sys.getenv "MINA_BP_PRIVKEY" )
×
1050
            with
1051
            | Some _, Some _, _ ->
×
1052
                Mina_stdlib.Mina_user_error.raise
×
1053
                  "You cannot provide both `block-producer-key` and \
1054
                   `block_production_pubkey`"
1055
            | None, Some _, Some _ ->
×
1056
                Mina_stdlib.Mina_user_error.raise
×
1057
                  "You cannot provide both `MINA_BP_PRIVKEY` and \
1058
                   `block_production_pubkey`"
1059
            | None, None, None ->
×
1060
                Deferred.return None
×
1061
            | None, None, Some base58_privkey ->
×
1062
                let kp =
1063
                  Private_key.of_base58_check_exn base58_privkey
1064
                  |> Keypair.of_private_key_exn
×
1065
                in
1066
                Deferred.return (Some kp)
×
1067
            (* CLI argument takes precedence over env variable *)
1068
            | Some sk_file, None, (Some _ | None) ->
×
1069
                [%log warn]
×
1070
                  "`block-producer-key` is deprecated. Please set \
1071
                   `MINA_BP_PRIVKEY` environment variable instead." ;
1072
                let%map kp =
1073
                  Secrets.Keypair.Terminal_stdin.read_exn
×
1074
                    ~should_prompt_user:false ~which:"block producer keypair"
1075
                    sk_file
1076
                in
1077
                Some kp
×
1078
            | None, Some tracked_pubkey, None ->
×
1079
                let%map kp =
1080
                  Secrets.Wallets.get_tracked_keypair ~logger
×
1081
                    ~which:"block producer keypair"
1082
                    ~read_from_env_exn:
1083
                      (Secrets.Keypair.Terminal_stdin.read_exn
1084
                         ~should_prompt_user:false ~should_reask:false )
1085
                    ~conf_dir tracked_pubkey
1086
                in
1087
                Some kp
×
1088
          in
1089
          let%bind client_trustlist =
1090
            Reader.load_sexp
×
1091
              (conf_dir ^/ "client_trustlist")
×
1092
              [%of_sexp: Unix.Cidr.t list]
1093
            >>| Or_error.ok
×
1094
          in
1095
          let client_trustlist =
×
1096
            let mina_client_trustlist = "MINA_CLIENT_TRUSTLIST" in
1097
            let cidrs_of_env_str env_str env_var =
1098
              let cidrs =
×
1099
                String.split ~on:',' env_str
1100
                |> List.filter_map ~f:(fun str ->
×
1101
                       try Some (Unix.Cidr.of_string str)
×
1102
                       with _ ->
×
1103
                         [%log warn] "Could not parse address $address in %s"
×
1104
                           env_var
1105
                           ~metadata:[ ("address", `String str) ] ;
1106
                         None )
×
1107
              in
1108
              Some
×
1109
                (List.append cidrs (Option.value ~default:[] client_trustlist))
×
1110
            in
1111
            match Unix.getenv mina_client_trustlist with
1112
            | Some env_str ->
×
1113
                cidrs_of_env_str env_str mina_client_trustlist
×
1114
            | None ->
×
1115
                client_trustlist
1116
          in
1117
          let get_monitor_infos monitor =
1118
            let rec get_monitors accum monitor =
×
1119
              match Async_kernel.Monitor.parent monitor with
×
1120
              | None ->
×
1121
                  List.rev accum
1122
              | Some parent ->
×
1123
                  get_monitors (parent :: accum) parent
1124
            in
1125
            let monitors = get_monitors [ monitor ] monitor in
1126
            List.map monitors ~f:(fun monitor ->
×
1127
                match Async_kernel.Monitor.sexp_of_t monitor with
×
1128
                | Sexp.List sexps ->
×
1129
                    `List (List.map ~f:Error_json.sexp_record_to_yojson sexps)
×
1130
                | Sexp.Atom _ ->
×
1131
                    failwith "Expected a sexp list" )
1132
          in
1133
          let o1trace context =
1134
            Execution_context.find_local context O1trace.local_storage_id
×
1135
            |> Option.value ~default:[]
×
1136
            |> List.map ~f:(fun x -> `String x)
×
1137
          in
1138
          Stream.iter
1139
            (Async_kernel.Async_kernel_scheduler.long_cycles_with_context
1140
               ~at_least:(sec 0.5 |> Time_ns.Span.of_span_float_round_nearest) )
×
1141
            ~f:(fun (span, context) ->
1142
              let secs = Time_ns.Span.to_sec span in
×
1143
              let monitor_infos = get_monitor_infos context.monitor in
×
1144
              let o1trace = o1trace context in
×
1145
              [%log internal] "Long_async_cycle"
×
1146
                ~metadata:
1147
                  [ ("duration", `Float secs); ("trace", `List o1trace) ] ;
1148
              [%log debug]
×
1149
                ~metadata:
1150
                  [ ("long_async_cycle", `Float secs)
1151
                  ; ("monitors", `List monitor_infos)
1152
                  ; ("o1trace", `List o1trace)
1153
                  ]
1154
                "Long async cycle, $long_async_cycle seconds, $monitors, \
1155
                 $o1trace" ;
1156
              Mina_metrics.(
×
1157
                Runtime.Long_async_histogram.observe Runtime.long_async_cycle
1158
                  secs) ) ;
1159
          Stream.iter Async_kernel.Async_kernel_scheduler.long_jobs_with_context
×
1160
            ~f:(fun (context, span) ->
1161
              let secs = Time_ns.Span.to_sec span in
×
1162
              let monitor_infos = get_monitor_infos context.monitor in
×
1163
              let o1trace = o1trace context in
×
1164
              [%log internal] "Long_async_job"
×
1165
                ~metadata:
1166
                  [ ("duration", `Float secs); ("trace", `List o1trace) ] ;
1167
              [%log debug]
×
1168
                ~metadata:
1169
                  [ ("long_async_job", `Float secs)
1170
                  ; ("monitors", `List monitor_infos)
1171
                  ; ("o1trace", `List o1trace)
1172
                  ; ( "most_recent_2_backtrace"
1173
                    , `String
1174
                        (String.concat ~sep:"␤"
×
1175
                           (List.map ~f:Backtrace.to_string
×
1176
                              (List.take
×
1177
                                 (Execution_context.backtrace_history context)
×
1178
                                 2 ) ) ) )
1179
                  ]
1180
                "Long async job, $long_async_job seconds, $monitors, $o1trace" ;
1181
              Mina_metrics.(
×
1182
                Runtime.Long_job_histogram.observe Runtime.long_async_job secs) ) ;
1183
          let trace_database_initialization typ location =
×
1184
            (* can't use %log ppx here, because we're using the passed-in location *)
1185
            Logger.trace logger ~module_:__MODULE__ "Creating %s at %s"
×
1186
              ~location typ
1187
          in
1188
          let trust_dir = chain_state_locations.trust in
1189
          let%bind () = Async.Unix.mkdir ~p:() trust_dir in
×
1190
          let%bind trust_system = Trust_system.create trust_dir in
×
1191
          trace_database_initialization "trust_system" __LOC__ trust_dir ;
×
1192
          let genesis_state_hash =
×
1193
            (Precomputed_values.genesis_state_hashes precomputed_values)
×
1194
              .state_hash
1195
          in
1196
          let genesis_ledger_hash =
1197
            Precomputed_values.genesis_ledger precomputed_values
1198
            |> Lazy.force |> Mina_ledger.Ledger.merkle_root
×
1199
          in
1200
          let block_production_keypairs =
×
1201
            block_production_keypair
1202
            |> Option.map ~f:(fun kp ->
1203
                   (kp, Public_key.compress kp.Keypair.public_key) )
×
1204
            |> Option.to_list |> Keypair.And_compressed_pk.Set.of_list
×
1205
          in
1206
          let epoch_ledger_location =
×
1207
            chain_state_locations.epoch_ledger ^/ "epoch_ledger"
1208
          in
1209
          let module Context = struct
×
1210
            let logger = logger
1211

1212
            let constraint_constants = precomputed_values.constraint_constants
1213

1214
            let consensus_constants = precomputed_values.consensus_constants
1215
          end in
1216
          let consensus_local_state =
1217
            Consensus.Data.Local_state.create
1218
              ~context:(module Context)
1219
              ~genesis_ledger:precomputed_values.genesis_ledger
1220
              ~genesis_epoch_data:precomputed_values.genesis_epoch_data
1221
              ~epoch_ledger_location
1222
              ( Option.map block_production_keypair ~f:(fun keypair ->
1223
                    let open Keypair in
×
1224
                    Public_key.compress keypair.public_key )
1225
              |> Option.to_list |> Public_key.Compressed.Set.of_list )
×
1226
              ~genesis_state_hash:
1227
                precomputed_values.protocol_state_with_hashes.hash.state_hash
1228
              ~epoch_ledger_backing_type:ledger_backing_type
1229
          in
1230
          trace_database_initialization "epoch ledger" __LOC__
×
1231
            epoch_ledger_location ;
1232
          let%bind peer_list_file_contents_or_empty =
1233
            match libp2p_peer_list_file with
1234
            | None ->
×
1235
                return []
×
1236
            | Some file -> (
×
1237
                match%bind
1238
                  Monitor.try_with_or_error ~here:[%here] (fun () ->
×
1239
                      Reader.file_contents file )
×
1240
                with
1241
                | Ok contents ->
×
1242
                    return (Mina_net2.Multiaddr.of_file_contents contents)
×
1243
                | Error _ ->
×
1244
                    Mina_stdlib.Mina_user_error.raisef
1245
                      ~where:"reading libp2p peer address file"
1246
                      "The file %s could not be read.\n\n\
1247
                       It must be a newline-separated list of libp2p \
1248
                       multiaddrs (ex: /ip4/IPADDR/tcp/PORT/p2p/PEERID)"
1249
                      file )
1250
          in
1251
          List.iter libp2p_peers_raw ~f:(fun raw_peer ->
×
1252
              if not Mina_net2.Multiaddr.(valid_as_peer @@ of_string raw_peer)
×
1253
              then
1254
                Mina_stdlib.Mina_user_error.raisef
×
1255
                  ~where:"decoding peer as a multiaddress"
1256
                  "The given peer \"%s\" is not a valid multiaddress (ex: \
1257
                   /ip4/IPADDR/tcp/PORT/p2p/PEERID)"
1258
                  raw_peer ) ;
1259
          let initial_peers =
×
1260
            List.concat
1261
              [ List.map ~f:Mina_net2.Multiaddr.of_string libp2p_peers_raw
×
1262
              ; peer_list_file_contents_or_empty
1263
              ; List.map ~f:Mina_net2.Multiaddr.of_string
×
1264
                @@ or_from_config
×
1265
                     (Fn.compose Option.some
×
1266
                        (YJ.Util.convert_each YJ.Util.to_string) )
×
1267
                     "peers" None ~default:[]
1268
              ]
1269
          in
1270
          let direct_peers =
×
1271
            List.map ~f:Mina_net2.Multiaddr.of_string direct_peers_raw
1272
          in
1273
          let min_connections =
×
1274
            or_from_config YJ.Util.to_int_option "min-connections"
1275
              ~default:Cli_lib.Default.min_connections min_connections
1276
          in
1277
          let max_connections =
×
1278
            or_from_config YJ.Util.to_int_option "max-connections"
1279
              ~default:Cli_lib.Default.max_connections max_connections
1280
          in
1281
          let pubsub_v1 = Gossip_net.Libp2p.N in
×
1282
          (* TODO uncomment after introducing Bitswap-based block retrieval *)
1283
          (* let pubsub_v1 =
1284
               or_from_config to_pubsub_topic_mode_option "pubsub-v1"
1285
                 ~default:Cli_lib.Default.pubsub_v1 pubsub_v1
1286
             in *)
1287
          let pubsub_v0 =
1288
            or_from_config to_pubsub_topic_mode_option "pubsub-v0"
1289
              ~default:Cli_lib.Default.pubsub_v0 None
1290
          in
1291
          let validation_queue_size =
×
1292
            or_from_config YJ.Util.to_int_option "validation-queue-size"
1293
              ~default:Cli_lib.Default.validation_queue_size
1294
              validation_queue_size
1295
          in
1296
          let stop_time =
×
1297
            or_from_config YJ.Util.to_int_option "stop-time"
1298
              ~default:Cli_lib.Default.stop_time stop_time
1299
          in
NEW
1300
          let stop_time_interval =
×
1301
            or_from_config YJ.Util.to_int_option "stop-time-interval"
1302
              ~default:Cli_lib.Default.stop_time_interval stop_time_interval
1303
          in
UNCOV
1304
          if enable_tracing then Mina_tracing.start conf_dir |> don't_wait_for ;
×
1305
          let%bind () =
1306
            if enable_internal_tracing then
1307
              Internal_tracing.toggle ~commit_id:Mina_version.commit_id ~logger
×
1308
                `Enabled
1309
            else Deferred.unit
×
1310
          in
1311
          let seed_peer_list_url =
×
1312
            Option.value_map seed_peer_list_url ~f:Option.some
1313
              ~default:
1314
                (Option.bind config.daemon
×
1315
                   ~f:(fun { Runtime_config.Daemon.peer_list_url; _ } ->
1316
                     peer_list_url ) )
×
1317
          in
1318
          if is_seed then [%log info] "Starting node as a seed node"
×
1319
          else if demo_mode then [%log info] "Starting node in demo mode"
×
1320
          else if
×
1321
            List.is_empty initial_peers && Option.is_none seed_peer_list_url
×
1322
          then
1323
            Mina_stdlib.Mina_user_error.raise
×
1324
              {|No peers were given.
1325

1326
Pass one of -peer, -peer-list-file, -seed, -peer-list-url.|} ;
1327
          let chain_id =
1328
            let protocol_transaction_version =
1329
              Protocol_version.(transaction current)
×
1330
            in
1331
            let protocol_network_version =
1332
              Protocol_version.(transaction current)
×
1333
            in
1334
            chain_id ~genesis_state_hash
1335
              ~genesis_constants:precomputed_values.genesis_constants
1336
              ~constraint_system_digests:
1337
                (Lazy.force precomputed_values.constraint_system_digests)
×
1338
              ~protocol_transaction_version ~protocol_network_version
1339
          in
1340
          [%log info] "Daemon will use chain id %s" chain_id ;
×
1341
          [%log info] "Daemon running protocol version %s"
×
1342
            Protocol_version.(to_string current) ;
×
1343
          let gossip_net_params =
×
1344
            Gossip_net.Libp2p.Config.
1345
              { timeout = Time.Span.of_sec 3.
×
1346
              ; logger
1347
              ; mina_net_location = chain_state_locations.mina_net
1348
              ; chain_id
1349
              ; unsafe_no_trust_ip = false
1350
              ; seed_peer_list_url =
1351
                  Option.map seed_peer_list_url ~f:Uri.of_string
×
1352
              ; initial_peers
1353
              ; addrs_and_ports
1354
              ; metrics_port = libp2p_metrics_port
1355
              ; trust_system
1356
              ; flooding = Option.value ~default:false enable_flooding
×
1357
              ; direct_peers
1358
              ; peer_protection_ratio
1359
              ; peer_exchange = Option.value ~default:false peer_exchange
×
1360
              ; min_connections
1361
              ; max_connections
1362
              ; validation_queue_size
1363
              ; isolate = Option.value ~default:false isolate
×
1364
              ; keypair = libp2p_keypair
1365
              ; all_peers_seen_metric
1366
              ; known_private_ip_nets =
1367
                  Option.value ~default:[] client_trustlist
×
1368
              ; time_controller
1369
              ; pubsub_v1
1370
              ; pubsub_v0
1371
              }
1372
          in
1373
          let net_config =
1374
            { Mina_networking.Config.genesis_ledger_hash
1375
            ; log_gossip_heard
1376
            ; is_seed
1377
            ; creatable_gossip_net =
1378
                Mina_networking.Gossip_net.(
1379
                  Any.Creatable
1380
                    ((module Libp2p), Libp2p.create ~pids gossip_net_params))
×
1381
            }
1382
          in
1383
          let coinbase_receiver : Consensus.Coinbase_receiver.t =
1384
            Option.value_map coinbase_receiver_flag ~default:`Producer
×
1385
              ~f:(fun pk -> `Other pk)
×
1386
          in
1387
          let proposed_protocol_version_opt =
1388
            Mina_run.get_proposed_protocol_version_opt ~conf_dir ~logger
1389
              proposed_protocol_version
1390
          in
1391
          ( match
×
1392
              (uptime_url_string, uptime_submitter_key, uptime_submitter_pubkey)
1393
            with
1394
          | Some _, Some _, None | Some _, None, Some _ | None, None, None ->
×
1395
              ()
1396
          | _ ->
×
1397
              Mina_stdlib.Mina_user_error.raise
×
1398
                "Must provide both --uptime-url and exactly one of \
1399
                 --uptime-submitter-key or --uptime-submitter-pubkey" ) ;
1400
          let uptime_url =
1401
            Option.map uptime_url_string ~f:(fun s -> Uri.of_string s)
×
1402
          in
1403
          let uptime_submitter_opt =
×
1404
            Option.map uptime_submitter_pubkey ~f:(fun s ->
1405
                match Public_key.Compressed.of_base58_check s with
×
1406
                | Ok pk -> (
×
1407
                    match Public_key.decompress pk with
1408
                    | Some _ ->
×
1409
                        pk
1410
                    | None ->
×
1411
                        failwithf
1412
                          "Invalid public key %s for uptime submitter (could \
1413
                           not decompress)"
1414
                          s () )
1415
                | Error err ->
×
1416
                    Mina_stdlib.Mina_user_error.raisef
1417
                      "Invalid public key %s for uptime submitter, %s" s
1418
                      (Error.to_string_hum err) () )
×
1419
          in
1420
          let%bind uptime_submitter_keypair =
1421
            match (uptime_submitter_key, uptime_submitter_opt) with
1422
            | None, None ->
×
1423
                return None
×
1424
            | None, Some pk ->
×
1425
                let%map kp =
1426
                  Secrets.Wallets.get_tracked_keypair ~logger
×
1427
                    ~which:"uptime submitter keypair"
1428
                    ~read_from_env_exn:
1429
                      (Secrets.Uptime_keypair.Terminal_stdin.read_exn
1430
                         ~should_prompt_user:false ~should_reask:false )
1431
                    ~conf_dir pk
1432
                in
1433
                Some kp
×
1434
            | Some sk_file, None ->
×
1435
                let%map kp =
1436
                  Secrets.Uptime_keypair.Terminal_stdin.read_exn
×
1437
                    ~should_prompt_user:false ~should_reask:false
1438
                    ~which:"uptime submitter keypair" sk_file
1439
                in
1440
                Some kp
×
1441
            | _ ->
×
1442
                (* unreachable, because of earlier check *)
1443
                failwith
1444
                  "Cannot provide both uptime submitter public key and uptime \
1445
                   submitter keyfile"
1446
          in
1447
          if itn_features then
×
1448
            (* set queue bound directly in Itn_logger
1449
               adding bound to Mina_lib config introduces cycle
1450
            *)
1451
            Option.iter itn_max_logs ~f:Itn_logger.set_queue_bound ;
×
1452
          let start_time = Time.now () in
×
1453
          let%map mina =
1454
            Mina_lib.create ~commit_id:Mina_version.commit_id ~wallets
×
1455
              (Mina_lib.Config.make ~logger ~pids ~trust_system ~conf_dir
×
1456
                 ~file_log_level ~log_level ~log_json ~chain_id ~is_seed
1457
                 ~disable_node_status ~demo_mode ~coinbase_receiver ~net_config
1458
                 ~gossip_net_params ~proposed_protocol_version_opt
1459
                 ~work_selection_method:
1460
                   (Cli_lib.Arg_type.work_selection_method_to_module
×
1461
                      work_selection_method )
1462
                 ~snark_worker_config:
1463
                   { Mina_lib.Config.Snark_worker_config
1464
                     .initial_snark_worker_key = run_snark_worker_flag
1465
                   ; shutdown_on_disconnect = true
1466
                   ; num_threads = snark_worker_parallelism_flag
1467
                   }
1468
                 ~snark_coordinator_key:run_snark_coordinator_flag
1469
                 ~snark_pool_disk_location:chain_state_locations.snark_pool
1470
                 ~wallets_disk_location:(conf_dir ^/ "wallets")
×
1471
                 ~persistent_root_location:chain_state_locations.root
1472
                 ~persistent_frontier_location:chain_state_locations.frontier
1473
                 ~epoch_ledger_location
1474
                 ~proof_cache_location:chain_state_locations.proof_cache
1475
                 ~zkapp_vk_cache_location:chain_state_locations.zkapp_vk_cache
1476
                 ~snark_work_fee:snark_work_fee_flag ~time_controller
1477
                 ~block_production_keypairs ~monitor ~consensus_local_state
1478
                 ~is_archive_rocksdb ~work_reassignment_wait
1479
                 ~archive_process_location ~log_block_creation
1480
                 ~precomputed_values ~start_time ?precomputed_blocks_path
1481
                 ~log_precomputed_blocks ~start_filtered_logs
1482
                 ~upload_blocks_to_gcloud ~block_reward_threshold ~uptime_url
1483
                 ~uptime_submitter_keypair ~uptime_send_node_commit ~stop_time
1484
                 ~stop_time_interval ~node_status_url
1485
                 ~graphql_control_port:itn_graphql_port ~simplified_node_stats
1486
                 ~zkapp_cmd_limit:(ref compile_config.zkapp_cmd_limit)
1487
                 ~itn_features ~compile_config ~hardfork_mode () )
1488
          in
1489
          { mina
×
1490
          ; client_trustlist
1491
          ; rest_server_port
1492
          ; limited_graphql_port
1493
          ; itn_graphql_port
1494
          }
1495
        in
1496
        (* Breaks a dependency cycle with monitor initilization and coda *)
1497
        let mina_ref : Mina_lib.t option ref = ref None in
1498
        Option.iter node_error_url ~f:(fun url ->
1499
            let get_node_state () =
×
1500
              match !mina_ref with
×
1501
              | None ->
×
1502
                  Deferred.return None
1503
              | Some mina ->
×
1504
                  let%map node_state = Mina_lib.get_node_state mina in
×
1505
                  Some node_state
×
1506
            in
1507
            Node_error_service.set_config ~get_node_state
1508
              ~node_error_url:(Uri.of_string url) ~contact_info ) ;
×
1509
        Mina_run.handle_shutdown ~monitor ~time_controller ~conf_dir
×
1510
          ~child_pids:pids ~top_logger:logger mina_ref ;
1511
        Async.Scheduler.within' ~monitor
×
1512
        @@ fun () ->
1513
        let%bind { mina
1514
                 ; client_trustlist
1515
                 ; rest_server_port
1516
                 ; limited_graphql_port
1517
                 ; itn_graphql_port
1518
                 } =
1519
          mina_initialization_deferred ()
×
1520
        in
1521
        mina_ref := Some mina ;
×
1522
        (*This pipe is consumed only by integration tests*)
1523
        don't_wait_for
1524
          (Pipe_lib.Strict_pipe.Reader.iter_without_pushback
×
1525
             (Mina_lib.validated_transitions mina)
×
1526
             ~f:ignore ) ;
1527
        Mina_run.setup_local_server ?client_trustlist ~rest_server_port
×
1528
          ~insecure_rest_server ~open_limited_graphql_port ?limited_graphql_port
1529
          ?itn_graphql_port ?auth_keys:itn_keys mina ;
1530
        let%bind () =
1531
          Option.map metrics_server_port ~f:(fun port ->
1532
              let forward_uri =
×
1533
                Option.map libp2p_metrics_port ~f:(fun port ->
1534
                    Uri.with_uri ~scheme:(Some "http") ~host:(Some "127.0.0.1")
×
1535
                      ~port:(Some port) ~path:(Some "/metrics") Uri.empty )
1536
              in
1537
              Mina_metrics.Runtime.(
×
1538
                gc_stat_interval_mins :=
1539
                  Option.value ~default:!gc_stat_interval_mins gc_stat_interval) ;
×
1540
              Mina_metrics.server ?forward_uri ~port ~logger () >>| ignore )
×
1541
          |> Option.value ~default:Deferred.unit
×
1542
        in
1543
        let () = Mina_plugins.init_plugins ~logger mina plugins in
×
1544
        return mina )
×
1545

1546
let daemon logger ~itn_features =
1547
  let compile_config = Mina_compile_config.Compiled.t in
108✔
1548
  Command.async ~summary:"Mina daemon"
1549
    (Command.Param.map
108✔
1550
       (setup_daemon logger ~itn_features
108✔
1551
          ~default_snark_worker_fee:compile_config.default_snark_worker_fee )
1552
       ~f:(fun setup_daemon () ->
1553
         (* Immediately disable updating the time offset. *)
1554
         Block_time.Controller.disable_setting_offset () ;
×
1555
         let%bind mina = setup_daemon () in
×
1556
         let%bind () = Mina_lib.start mina in
×
1557
         [%log info] "Daemon ready. Clients can now connect" ;
×
1558
         Async.never () ) )
×
1559

1560
let replay_blocks logger ~itn_features =
1561
  let replay_flag =
108✔
1562
    let open Command.Param in
1563
    flag "--blocks-filename" ~aliases:[ "-blocks-filename" ] (required string)
108✔
1564
      ~doc:"PATH The file to read the precomputed blocks from"
1565
  in
1566
  let read_kind =
1567
    let open Command.Param in
1568
    flag "--format" ~aliases:[ "-format" ] (optional string)
108✔
1569
      ~doc:"json|sexp The format to read lines of the file in (default: json)"
1570
  in
1571
  let compile_config = Mina_compile_config.Compiled.t in
1572
  Command.async ~summary:"Start mina daemon with blocks replayed from a file"
1573
    (Command.Param.map3 replay_flag read_kind
108✔
1574
       (setup_daemon logger ~itn_features
108✔
1575
          ~default_snark_worker_fee:compile_config.default_snark_worker_fee )
1576
       ~f:(fun blocks_filename read_kind setup_daemon () ->
1577
         (* Enable updating the time offset. *)
1578
         Block_time.Controller.enable_setting_offset () ;
×
1579
         let read_block_line =
×
1580
           match Option.map ~f:String.lowercase read_kind with
1581
           | Some "json" | None -> (
×
1582
               fun line ->
1583
                 match
×
1584
                   Yojson.Safe.from_string line
1585
                   |> Mina_block.Precomputed.of_yojson
×
1586
                 with
1587
                 | Ok block ->
×
1588
                     block
1589
                 | Error err ->
×
1590
                     failwithf "Could not read block: %s" err () )
1591
           | Some "sexp" ->
×
1592
               fun line ->
1593
                 Sexp.of_string_conv_exn line Mina_block.Precomputed.t_of_sexp
×
1594
           | _ ->
×
1595
               failwith "Expected one of 'json', 'sexp' for -format flag"
1596
         in
1597
         let blocks =
1598
           Sequence.unfold ~init:(In_channel.create blocks_filename)
×
1599
             ~f:(fun blocks_file ->
1600
               match In_channel.input_line blocks_file with
×
1601
               | Some line ->
×
1602
                   Some (read_block_line line, blocks_file)
×
1603
               | None ->
×
1604
                   In_channel.close blocks_file ;
1605
                   None )
×
1606
         in
1607
         let%bind mina = setup_daemon () in
×
1608
         let%bind () = Mina_lib.start_with_precomputed_blocks mina blocks in
×
1609
         [%log info]
×
1610
           "Daemon is ready, replayed precomputed blocks. Clients can now \
1611
            connect" ;
1612
         Async.never () ) )
×
1613

1614
let dump_type_shapes =
1615
  let max_depth_flag =
1616
    let open Command.Param in
1617
    flag "--max-depth" ~aliases:[ "-max-depth" ] (optional int)
108✔
1618
      ~doc:"NN Maximum depth of shape S-expressions"
1619
  in
1620
  Command.basic ~summary:"Print serialization shapes of versioned types"
108✔
1621
    (Command.Param.map max_depth_flag ~f:(fun max_depth () ->
108✔
1622
         Ppx_version_runtime.Shapes.iteri
×
1623
           ~f:(fun ~key:path ~data:(shape, ty_decl) ->
1624
             let open Bin_prot.Shape in
×
1625
             let canonical = eval shape in
1626
             let digest = Canonical.to_digest canonical |> Digest.to_hex in
×
1627
             let shape_summary =
×
1628
               let shape_sexp =
1629
                 Canonical.to_string_hum canonical |> Sexp.of_string
×
1630
               in
1631
               (* elide the shape below specified depth, so that changes to
1632
                  contained types aren't considered a change to the containing
1633
                  type, even though the shape digests differ
1634
               *)
1635
               let summary_sexp =
×
1636
                 match max_depth with
1637
                 | None ->
×
1638
                     shape_sexp
1639
                 | Some n ->
×
1640
                     let rec go sexp depth =
1641
                       if depth > n then Sexp.Atom "."
×
1642
                       else
1643
                         match sexp with
×
1644
                         | Sexp.Atom _ ->
×
1645
                             sexp
1646
                         | Sexp.List items ->
×
1647
                             Sexp.List
1648
                               (List.map items ~f:(fun item ->
×
1649
                                    go item (depth + 1) ) )
×
1650
                     in
1651
                     go shape_sexp 0
×
1652
               in
1653
               Sexp.to_string summary_sexp
×
1654
             in
1655
             Core_kernel.printf "%s, %s, %s, %s\n" path digest shape_summary
1656
               ty_decl ) ) )
1657

1658
let primitive_ok = function
1659
  | "array" | "bytes" | "string" | "bigstring" ->
×
1660
      false
1661
  | "int" | "int32" | "int64" | "nativeint" | "char" | "bool" | "float" ->
×
1662
      true
1663
  | "unit" | "option" | "list" ->
×
1664
      true
1665
  | "kimchi_backend_bigint_32_V1" ->
×
1666
      true
1667
  | "Mina_stdlib.Bounded_types.String.t"
×
1668
  | "Mina_stdlib.Bounded_types.String.Tagged.t"
×
1669
  | "Mina_stdlib.Bounded_types.Array.t" ->
×
1670
      true
1671
  | "8fabab0a-4992-11e6-8cca-9ba2c4686d9e" ->
×
1672
      true (* hashtbl *)
1673
  | "ac8a9ff4-4994-11e6-9a1b-9fb4e933bd9d" ->
×
1674
      true (* Make_iterable_binable *)
1675
  | s ->
×
1676
      failwithf "unknown primitive %s" s ()
1677

1678
let audit_type_shapes : Command.t =
1679
  let rec shape_ok (shape : Sexp.t) : bool =
1680
    match shape with
×
1681
    | List [ Atom "Exp"; exp ] ->
×
1682
        exp_ok exp
1683
    | List [] ->
×
1684
        true
1685
    | _ ->
×
1686
        failwithf "bad shape: %s" (Sexp.to_string shape) ()
×
1687
  and exp_ok (exp : Sexp.t) : bool =
1688
    match exp with
×
1689
    | List [ Atom "Base"; Atom tyname; List exps ] ->
×
1690
        primitive_ok tyname && List.for_all exps ~f:shape_ok
×
1691
    | List [ Atom "Record"; List fields ] ->
×
1692
        List.for_all fields ~f:(fun field ->
1693
            match field with
×
1694
            | List [ Atom _; sh ] ->
×
1695
                shape_ok sh
1696
            | _ ->
×
1697
                failwithf "unhandled rec field: %s" (Sexp.to_string_hum field)
×
1698
                  () )
1699
    | List [ Atom "Tuple"; List exps ] ->
×
1700
        List.for_all exps ~f:shape_ok
1701
    | List [ Atom "Variant"; List ctors ] ->
×
1702
        List.for_all ctors ~f:(fun ctor ->
1703
            match ctor with
×
1704
            | List [ Atom _ctr; List exps ] ->
×
1705
                List.for_all exps ~f:shape_ok
1706
            | _ ->
×
1707
                failwithf "unhandled variant: %s" (Sexp.to_string_hum ctor) () )
×
1708
    | List [ Atom "Poly_variant"; List [ List [ Atom "sorted"; List ctors ] ] ]
×
1709
      ->
1710
        List.for_all ctors ~f:(fun ctor ->
1711
            match ctor with
×
1712
            | List [ Atom _ctr ] ->
×
1713
                true
1714
            | List [ Atom _ctr; List fields ] ->
×
1715
                List.for_all fields ~f:shape_ok
1716
            | _ ->
×
1717
                failwithf "unhandled poly variant: %s" (Sexp.to_string_hum ctor)
×
1718
                  () )
1719
    | List [ Atom "Application"; sh; List args ] ->
×
1720
        shape_ok sh && List.for_all args ~f:shape_ok
×
1721
    | List [ Atom "Rec_app"; Atom _; List args ] ->
×
1722
        List.for_all args ~f:shape_ok
1723
    | List [ Atom "Var"; Atom _ ] ->
×
1724
        true
1725
    | List (Atom ctr :: _) ->
×
1726
        failwithf "unhandled ctor (%s) in exp_ok: %s" ctr
1727
          (Sexp.to_string_hum exp) ()
×
1728
    | List [] | List _ | Atom _ ->
×
1729
        failwithf "bad format: %s" (Sexp.to_string_hum exp) ()
×
1730
  in
1731
  let handle_shape (path : string) (shape : Bin_prot.Shape.t) (ty_decl : string)
1732
      (good : int ref) (bad : int ref) =
1733
    let open Bin_prot.Shape in
×
1734
    let path, file = String.lsplit2_exn ~on:':' path in
1735
    let canonical = eval shape in
×
1736
    let shape_sexp = Canonical.to_string_hum canonical |> Sexp.of_string in
×
1737
    if not @@ shape_ok shape_sexp then (
×
1738
      incr bad ;
1739
      Core.eprintf "%s has a bad shape in %s (%s):\n%s\n" path file ty_decl
×
1740
        (Canonical.to_string_hum canonical) )
×
1741
    else incr good
×
1742
  in
1743
  Command.basic ~summary:"Audit shapes of versioned types"
108✔
1744
    (Command.Param.return (fun () ->
108✔
1745
         let bad, good = (ref 0, ref 0) in
×
1746
         Ppx_version_runtime.Shapes.iteri
1747
           ~f:(fun ~key:path ~data:(shape, ty_decl) ->
1748
             handle_shape path shape ty_decl good bad ) ;
×
1749
         Core.printf "good shapes:\n\t%d\nbad shapes:\n\t%d\n%!" !good !bad ;
1750
         if !bad > 0 then Core.exit 1 ) )
×
1751

1752
(*NOTE A previous version of this function included compile time ppx that didn't compile, and was never
1753
  evaluated under any build profile
1754
*)
1755
let ensure_testnet_id_still_good _ = Deferred.unit
108✔
1756

1757
let snark_hashes =
1758
  let module Hashes = struct
1759
    type t = string list [@@deriving to_yojson]
×
1760
  end in
1761
  let open Command.Let_syntax in
1762
  Command.basic ~summary:"List hashes of proving and verification keys"
108✔
1763
    [%map_open
1764
      let json = Cli_lib.Flag.json in
1765
      fun () -> if json then Core.printf "[]\n%!"]
×
1766

1767
let internal_commands logger ~itn_features =
1768
  [ ( Snark_worker.Intf.command_name
108✔
1769
    , Snark_worker.command ~proof_level:Genesis_constants.Compiled.proof_level
1770
        ~constraint_constants:Genesis_constants.Compiled.constraint_constants
1771
        ~commit_id:Mina_version.commit_id )
1772
  ; ("snark-hashes", snark_hashes)
1773
  ; ( "run-prover"
1774
    , Command.async
108✔
1775
        ~summary:"Run prover on a sexp provided on a single line of stdin"
1776
        (let%map_open.Command signature_kind = Cli_lib.Flag.signature_kind in
1777
         fun () ->
1778
           let logger = Logger.create () in
×
1779
           let constraint_constants =
×
1780
             Genesis_constants.Compiled.constraint_constants
1781
           in
1782
           let proof_level = Genesis_constants.Compiled.proof_level in
1783
           Parallel.init_master () ;
1784
           match%bind Reader.read_sexp (Lazy.force Reader.stdin) with
×
1785
           | `Ok sexp ->
×
1786
               let%bind conf_dir = Unix.mkdtemp "/tmp/mina-prover" in
×
1787
               [%log info] "Prover state being logged to %s" conf_dir ;
×
1788
               let%bind prover =
1789
                 Prover.create ~commit_id:Mina_version.commit_id ~logger
×
1790
                   ~proof_level ~constraint_constants
1791
                   ~pids:(Pid.Table.create ()) ~conf_dir ~signature_kind ()
×
1792
               in
1793
               Prover.prove_from_input_sexp prover sexp >>| ignore
×
1794
           | `Eof ->
×
1795
               failwith "early EOF while reading sexp" ) )
1796
  ; ( "run-snark-worker-single"
1797
    , Command.async
108✔
1798
        ~summary:"Run snark-worker on a sexp provided on a single line of stdin"
1799
        (let open Command.Let_syntax in
1800
        let%map_open filename =
1801
          flag "--file" (required string)
108✔
1802
            ~doc:"File containing the s-expression of the snark work to execute"
1803
        and signature_kind = Cli_lib.Flag.signature_kind in
1804
        fun () ->
1805
          let open Deferred.Let_syntax in
×
1806
          let logger = Logger.create () in
1807
          let constraint_constants =
×
1808
            Genesis_constants.Compiled.constraint_constants
1809
          in
1810
          let proof_level = Genesis_constants.Compiled.proof_level in
1811
          Parallel.init_master () ;
1812
          match%bind
1813
            Reader.with_file filename ~f:(fun reader ->
×
1814
                [%log info] "Created reader for %s" filename ;
×
1815
                Reader.read_sexp reader )
×
1816
          with
1817
          | `Ok sexp -> (
×
1818
              let%bind worker_state =
1819
                Snark_worker.Inputs.Worker_state.create ~proof_level
×
1820
                  ~constraint_constants ~signature_kind ()
1821
              in
1822
              let sok_message =
×
1823
                { Mina_base.Sok_message.fee = Currency.Fee.of_mina_int_exn 0
×
1824
                ; prover = Quickcheck.random_value Public_key.Compressed.gen
×
1825
                }
1826
              in
1827
              let spec =
1828
                [%of_sexp:
1829
                  ( Transaction_witness.Stable.Latest.t
1830
                  , Ledger_proof.t )
1831
                  Snark_work_lib.Work.Single.Spec.t] sexp
1832
              in
1833
              match%map
1834
                Snark_worker.Inputs.perform_single worker_state
×
1835
                  ~message:sok_message spec
1836
              with
1837
              | Ok _ ->
×
1838
                  [%log info] "Successfully worked"
×
1839
              | Error err ->
×
1840
                  [%log error] "Work didn't work: $err"
×
1841
                    ~metadata:[ ("err", Error_json.error_to_yojson err) ] )
×
1842
          | `Eof ->
×
1843
              failwith "early EOF while reading sexp") )
1844
  ; ( "run-verifier"
1845
    , Command.async
108✔
1846
        ~summary:"Run verifier on a proof provided on a single line of stdin"
1847
        (let open Command.Let_syntax in
1848
        let%map_open mode =
1849
          flag "--mode" ~aliases:[ "-mode" ] (required string)
108✔
1850
            ~doc:"transaction/blockchain the snark to verify. Defaults to json"
1851
        and format =
1852
          flag "--format" ~aliases:[ "-format" ] (optional string)
108✔
1853
            ~doc:"sexp/json the format to parse input in"
1854
        and limit =
1855
          flag "--limit" ~aliases:[ "-limit" ] (optional int)
108✔
1856
            ~doc:"limit the number of proofs taken from the file"
1857
        in
1858
        fun () ->
1859
          let open Async in
×
1860
          let logger = Logger.create () in
1861
          let constraint_constants =
×
1862
            Genesis_constants.Compiled.constraint_constants
1863
          in
1864
          let proof_level = Genesis_constants.Compiled.proof_level in
1865
          Parallel.init_master () ;
1866
          let%bind conf_dir = Unix.mkdtemp "/tmp/mina-verifier" in
×
1867
          let mode =
×
1868
            match mode with
1869
            | "transaction" ->
×
1870
                `Transaction
1871
            | "blockchain" ->
×
1872
                `Blockchain
1873
            | mode ->
×
1874
                failwithf
×
1875
                  "Expected mode flag to be one of transaction, blockchain, \
1876
                   got '%s'"
1877
                  mode ()
1878
          in
1879
          let format =
1880
            match format with
1881
            | Some "sexp" ->
×
1882
                `Sexp
1883
            | Some "json" | None ->
×
1884
                `Json
1885
            | Some format ->
×
1886
                failwithf
×
1887
                  "Expected format flag to be one of sexp, json, got '%s'"
1888
                  format ()
1889
          in
1890
          let%bind input =
1891
            match format with
1892
            | `Sexp -> (
×
1893
                let%map input_sexp =
1894
                  match%map Reader.read_sexp (Lazy.force Reader.stdin) with
×
1895
                  | `Ok input_sexp ->
×
1896
                      input_sexp
1897
                  | `Eof ->
×
1898
                      failwith "early EOF while reading sexp"
1899
                in
1900
                match mode with
×
1901
                | `Transaction ->
×
1902
                    `Transaction
1903
                      (List.t_of_sexp
×
1904
                         (Tuple2.t_of_sexp Ledger_proof.t_of_sexp
×
1905
                            Sok_message.t_of_sexp )
1906
                         input_sexp )
1907
                | `Blockchain ->
×
1908
                    `Blockchain
1909
                      (List.t_of_sexp Blockchain_snark.Blockchain.t_of_sexp
×
1910
                         input_sexp ) )
1911
            | `Json -> (
×
1912
                let%map input_line =
1913
                  match%map Reader.read_line (Lazy.force Reader.stdin) with
×
1914
                  | `Ok input_line ->
×
1915
                      input_line
1916
                  | `Eof ->
×
1917
                      failwith "early EOF while reading json"
1918
                in
1919
                match mode with
×
1920
                | `Transaction -> (
×
1921
                    match
1922
                      [%derive.of_yojson: (Ledger_proof.t * Sok_message.t) list]
×
1923
                        (Yojson.Safe.from_string input_line)
×
1924
                    with
1925
                    | Ok input ->
×
1926
                        `Transaction input
1927
                    | Error err ->
×
1928
                        failwithf "Could not parse JSON: %s" err () )
1929
                | `Blockchain -> (
×
1930
                    match
1931
                      [%derive.of_yojson: Blockchain_snark.Blockchain.t list]
×
1932
                        (Yojson.Safe.from_string input_line)
×
1933
                    with
1934
                    | Ok input ->
×
1935
                        `Blockchain input
1936
                    | Error err ->
×
1937
                        failwithf "Could not parse JSON: %s" err () ) )
1938
          in
1939

1940
          let%bind verifier =
1941
            Verifier.For_tests.default ~constraint_constants ~proof_level
×
1942
              ~commit_id:Mina_version.commit_id ~logger
1943
              ~pids:(Pid.Table.create ()) ~conf_dir:(Some conf_dir) ()
×
1944
          in
1945
          let%bind result =
1946
            let cap lst =
1947
              Option.value_map ~default:Fn.id ~f:(Fn.flip List.take) limit lst
×
1948
            in
1949
            match input with
1950
            | `Transaction input ->
×
1951
                input |> cap |> Verifier.verify_transaction_snarks verifier
×
1952
            | `Blockchain input ->
×
1953
                input |> cap |> Verifier.verify_blockchain_snarks verifier
×
1954
          in
1955
          match result with
×
1956
          | Ok (Ok ()) ->
×
1957
              printf "Proofs verified successfully" ;
1958
              exit 0
×
1959
          | Ok (Error err) ->
×
1960
              printf "Proofs failed to verify:\n%s\n"
1961
                (Yojson.Safe.pretty_to_string (Error_json.error_to_yojson err)) ;
×
1962
              exit 1
×
1963
          | Error err ->
×
1964
              printf "Failed while verifying proofs:\n%s"
1965
                (Error.to_string_hum err) ;
×
1966
              exit 2) )
×
1967
  ; ( "dump-structured-events"
1968
    , Command.async ~summary:"Dump the registered structured events"
108✔
1969
        (let open Command.Let_syntax in
1970
        let%map outfile =
1971
          Core_kernel.Command.Param.flag "--out-file" ~aliases:[ "-out-file" ]
108✔
1972
            (Core_kernel.Command.Flag.optional Core_kernel.Command.Param.string)
108✔
1973
            ~doc:"FILENAME File to output to. Defaults to stdout"
1974
        and pretty =
1975
          Core_kernel.Command.Param.flag "--pretty" ~aliases:[ "-pretty" ]
108✔
1976
            Core_kernel.Command.Param.no_arg
1977
            ~doc:"  Set to output 'pretty' JSON"
1978
        in
1979
        fun () ->
1980
          let out_channel =
×
1981
            match outfile with
1982
            | Some outfile ->
×
1983
                Core_kernel.Out_channel.create outfile
×
1984
            | None ->
×
1985
                Core_kernel.Out_channel.stdout
1986
          in
1987
          let json =
1988
            Structured_log_events.dump_registered_events ()
1989
            |> [%derive.to_yojson:
1990
                 (string * Structured_log_events.id * string list) list]
×
1991
          in
1992
          if pretty then Yojson.Safe.pretty_to_channel out_channel json
×
1993
          else Yojson.Safe.to_channel out_channel json ;
×
1994
          ( match outfile with
1995
          | Some _ ->
×
1996
              Core_kernel.Out_channel.close out_channel
×
1997
          | None ->
×
1998
              () ) ;
1999
          Deferred.return ()) )
2000
  ; ("dump-type-shapes", dump_type_shapes)
2001
  ; ("replay-blocks", replay_blocks logger ~itn_features)
108✔
2002
  ; ("audit-type-shapes", audit_type_shapes)
2003
  ; ( "test-genesis-block-generation"
2004
    , Command.async ~summary:"Generate a genesis proof"
108✔
2005
        (let open Command.Let_syntax in
2006
        let%map_open config_files =
2007
          flag "--config-file" ~aliases:[ "config-file" ]
108✔
2008
            ~doc:
2009
              "PATH path to a configuration file (overrides MINA_CONFIG_FILE, \
2010
               default: <config_dir>/daemon.json). Pass multiple times to \
2011
               override fields from earlier config files"
2012
            (listed string)
108✔
2013
        and conf_dir = Cli_lib.Flag.conf_dir
2014
        and genesis_dir =
2015
          flag "--genesis-ledger-dir" ~aliases:[ "genesis-ledger-dir" ]
108✔
2016
            ~doc:
2017
              "DIR Directory that contains the genesis ledger and the genesis \
2018
               blockchain proof (default: <config-dir>)"
2019
            (optional string)
108✔
2020
        and signature_kind = Cli_lib.Flag.signature_kind in
2021
        fun () ->
2022
          let open Deferred.Let_syntax in
×
2023
          Parallel.init_master () ;
2024
          let logger = Logger.create () in
×
2025
          let conf_dir = Mina_lib.Conf_dir.compute_conf_dir conf_dir in
×
2026
          let genesis_constants =
×
2027
            Genesis_constants.Compiled.genesis_constants
2028
          in
2029
          let constraint_constants =
2030
            Genesis_constants.Compiled.constraint_constants
2031
          in
2032
          let proof_level = Genesis_constants.Proof_level.Full in
2033
          let config_files =
2034
            List.map config_files ~f:(fun config_file ->
2035
                (config_file, `Must_exist) )
×
2036
          in
2037
          let%bind ( precomputed_values
2038
                   , _config_jsons
2039
                   , _config
2040
                   , _chain_state_locations ) =
2041
            load_config_files ~logger ~conf_dir ~genesis_dir ~genesis_constants
×
2042
              ~constraint_constants ~proof_level ~cli_proof_level:None
2043
              ~genesis_backing_type:Stable_db config_files
2044
          in
2045
          let pids = Child_processes.Termination.create_pid_table () in
×
2046
          let%bind prover =
2047
            (* We create a prover process (unnecessarily) here, to have a more
2048
               realistic test.
2049
            *)
2050
            Prover.create ~commit_id:Mina_version.commit_id ~logger ~pids
×
2051
              ~conf_dir ~proof_level
2052
              ~constraint_constants:precomputed_values.constraint_constants
2053
              ~signature_kind ()
2054
          in
2055
          match%bind
2056
            Prover.create_genesis_block prover
×
2057
              (Genesis_proof.to_inputs precomputed_values)
×
2058
          with
2059
          | Ok block ->
×
2060
              Format.eprintf "Generated block@.%s@."
2061
                ( Yojson.Safe.to_string
×
2062
                @@ Blockchain_snark.Blockchain.to_yojson block ) ;
×
2063
              exit 0
×
2064
          | Error err ->
×
2065
              Format.eprintf "Failed to generate block@.%s@."
2066
                (Yojson.Safe.to_string @@ Error_json.error_to_yojson err) ;
×
2067
              exit 1) )
×
2068
  ]
2069

2070
let mina_commands logger ~itn_features =
2071
  [ ("accounts", Client.accounts)
108✔
2072
  ; ("daemon", daemon logger ~itn_features)
108✔
2073
  ; ("client", Client.client)
2074
  ; ("advanced", Client.advanced ~itn_features)
2075
  ; ("ledger", Client.ledger)
2076
  ; ("libp2p", Client.libp2p)
2077
  ; ( "internal"
2078
    , Command.group ~summary:"Internal commands"
108✔
2079
        (internal_commands logger ~itn_features) )
108✔
2080
  ; (Parallel.worker_command_name, Parallel.worker_command)
2081
  ; ("transaction-snark-profiler", Transaction_snark_profiler.command)
2082
  ]
2083

2084
let print_version_help coda_exe version =
2085
  (* mimic Jane Street command help *)
2086
  let lines =
×
2087
    [ "print version information"
2088
    ; ""
2089
    ; sprintf "  %s %s" (Filename.basename coda_exe) version
×
2090
    ; ""
2091
    ; "=== flags ==="
2092
    ; ""
2093
    ; "  [-help]  print this help text and exit"
2094
    ; "           (alias: -?)"
2095
    ]
2096
  in
2097
  List.iter lines ~f:(Core.printf "%s\n%!")
×
2098

2099
let print_version_info () = Core.printf "Commit %s\n" Mina_version.commit_id
×
2100

2101
let () =
2102
  Random.self_init () ;
2103
  let itn_features = Sys.getenv "ITN_FEATURES" |> Option.is_some in
108✔
2104
  let logger = Logger.create ~itn_features () in
108✔
2105
  don't_wait_for (ensure_testnet_id_still_good logger) ;
108✔
2106
  (* Turn on snark debugging in prod for now *)
2107
  Snarky_backendless.Snark.set_eval_constraints true ;
108✔
2108
  (* intercept command-line processing for "version", because we don't
2109
     use the Jane Street scripts that generate their version information
2110
  *)
2111
  (let is_version_cmd s =
108✔
2112
     List.mem [ "version"; "-version"; "--version" ] s ~equal:String.equal
×
2113
   in
2114
   match Sys.get_argv () with
2115
   | [| _mina_exe; version |] when is_version_cmd version ->
×
2116
       Mina_version.print_version ()
×
2117
   | _ ->
108✔
2118
       Command.run
×
2119
         (Command.group ~summary:"Mina" ~preserve_subcommand_order:()
108✔
2120
            (mina_commands logger ~itn_features) ) ) ;
108✔
2121
  Core.exit 0
×
2122

2123
let linkme = ()
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