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

MinaProtocol / mina / 3207

23 Jan 2025 10:05PM UTC coverage: 60.545% (+27.8%) from 32.759%
3207

push

buildkite

web-flow
Merge pull request #16511 from MinaProtocol/dkijania/build_performance_tooling_in_ci_dev

[Dev] Run benchmarks in CI

49261 of 81362 relevant lines covered (60.55%)

473114.6 hits per line

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

23.93
/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 []
3✔
51

52
let with_itn_logger ~itn_features ~(compile_config : Mina_compile_config.t)
53
    ~logger =
54
  if itn_features then
×
55
    let conf =
×
56
      Logger.make_itn_logger_config
57
        ~rpc_handshake_timeout:compile_config.rpc_handshake_timeout
58
        ~rpc_heartbeat_timeout:compile_config.rpc_heartbeat_timeout
59
        ~rpc_heartbeat_send_every:compile_config.rpc_heartbeat_send_every
60
    in
61
    Logger.with_itn conf logger
62
  else logger
×
63

64
let setup_daemon logger ~itn_features =
65
  let open Command.Let_syntax in
6✔
66
  let open Cli_lib.Arg_type in
67
  let receiver_key_warning = Cli_lib.Default.receiver_key_warning in
68
  let%map_open conf_dir = Cli_lib.Flag.conf_dir
69
  and block_production_key =
70
    flag "--block-producer-key" ~aliases:[ "block-producer-key" ]
6✔
71
      ~doc:
72
        (sprintf
6✔
73
           "DEPRECATED: Use environment variable `MINA_BP_PRIVKEY` instead. \
74
            Private key file for the block producer. Providing this flag or \
75
            the environment variable will enable block production. You cannot \
76
            provide both `block-producer-key` and `block-producer-pubkey`. \
77
            (default: use environment variable `MINA_BP_PRIVKEY`, if provided, \
78
            or else don't produce any blocks) %s"
79
           receiver_key_warning )
80
      (optional string)
6✔
81
  and block_production_pubkey =
82
    flag "--block-producer-pubkey"
6✔
83
      ~aliases:[ "block-producer-pubkey" ]
84
      ~doc:
85
        (sprintf
6✔
86
           "PUBLICKEY Public key for the associated private key that is being \
87
            tracked by this daemon. You cannot provide both \
88
            `block-producer-key` (or `MINA_BP_PRIVKEY`) and \
89
            `block-producer-pubkey`. (default: don't produce blocks) %s"
90
           receiver_key_warning )
91
      (optional public_key_compressed)
6✔
92
  and block_production_password =
93
    flag "--block-producer-password"
6✔
94
      ~aliases:[ "block-producer-password" ]
95
      ~doc:
96
        "PASSWORD Password associated with the block-producer key. Setting \
97
         this is equivalent to setting the MINA_PRIVKEY_PASS environment \
98
         variable. Be careful when setting it in the commandline as it will \
99
         likely get tracked in your history. Mainly to be used from the \
100
         daemon.json config file"
101
      (optional string)
6✔
102
  and itn_keys =
103
    if itn_features then
104
      flag "--itn-keys" ~aliases:[ "itn-keys" ] (optional string)
×
105
        ~doc:
106
          "PUBLICKEYS A comma-delimited list of Ed25519 public keys that are \
107
           permitted to send signed requests to the incentivized testnet \
108
           GraphQL server"
109
    else Command.Param.return None
6✔
110
  and itn_max_logs =
111
    if itn_features then
112
      flag "--itn-max-logs" ~aliases:[ "itn-max-logs" ] (optional int)
×
113
        ~doc:
114
          "NN Maximum number of logs to store to be made available via GraphQL \
115
           for incentivized testnet"
116
    else Command.Param.return None
6✔
117
  and demo_mode =
118
    flag "--demo-mode" ~aliases:[ "demo-mode" ] no_arg
6✔
119
      ~doc:
120
        "Run the daemon in demo-mode -- assume we're \"synced\" to the network \
121
         instantly"
122
  and coinbase_receiver_flag =
123
    flag "--coinbase-receiver" ~aliases:[ "coinbase-receiver" ]
6✔
124
      ~doc:
125
        (sprintf
6✔
126
           "PUBLICKEY Address to send coinbase rewards to (if this node is \
127
            producing blocks). If not provided, coinbase rewards will be sent \
128
            to the producer of a block. %s"
129
           receiver_key_warning )
130
      (optional public_key_compressed)
6✔
131
  and genesis_dir =
132
    flag "--genesis-ledger-dir" ~aliases:[ "genesis-ledger-dir" ]
6✔
133
      ~doc:
134
        "DIR Directory that contains the genesis ledger and the genesis \
135
         blockchain proof (default: <config-dir>)"
136
      (optional string)
6✔
137
  and run_snark_worker_flag =
138
    flag "--run-snark-worker" ~aliases:[ "run-snark-worker" ]
6✔
139
      ~doc:
140
        (sprintf "PUBLICKEY Run the SNARK worker with this public key. %s"
6✔
141
           receiver_key_warning )
142
      (optional public_key_compressed)
6✔
143
  and run_snark_coordinator_flag =
144
    flag "--run-snark-coordinator"
6✔
145
      ~aliases:[ "run-snark-coordinator" ]
146
      ~doc:
147
        (sprintf
6✔
148
           "PUBLICKEY Run a SNARK coordinator with this public key (ignored if \
149
            the run-snark-worker is set). %s"
150
           receiver_key_warning )
151
      (optional public_key_compressed)
6✔
152
  and snark_worker_parallelism_flag =
153
    flag "--snark-worker-parallelism"
6✔
154
      ~aliases:[ "snark-worker-parallelism" ]
155
      ~doc:
156
        "NUM Run the SNARK worker using this many threads. Equivalent to \
157
         setting OMP_NUM_THREADS, but doesn't affect block production."
158
      (optional int)
6✔
159
  and work_selection_method_flag =
160
    flag "--work-selection" ~aliases:[ "work-selection" ]
6✔
161
      ~doc:
162
        "seq|rand|roffset Choose work sequentially (seq), randomly (rand), or \
163
         sequentially with a random offset (roffset) (default: rand)"
164
      (optional work_selection_method)
6✔
165
  and libp2p_port = Flag.Port.Daemon.external_
166
  and client_port = Flag.Port.Daemon.client
167
  and rest_server_port = Flag.Port.Daemon.rest_server
168
  and limited_graphql_port = Flag.Port.Daemon.limited_graphql_server
169
  and itn_graphql_port =
170
    if itn_features then
171
      flag "--itn-graphql-port" ~aliases:[ "itn-graphql-port" ]
×
172
        ~doc:"PORT GraphQL-server for incentivized testnet interaction"
173
        (optional int)
×
174
    else Command.Param.return None
6✔
175
  and open_limited_graphql_port =
176
    flag "--open-limited-graphql-port"
6✔
177
      ~aliases:[ "open-limited-graphql-port" ]
178
      no_arg
179
      ~doc:
180
        "Have the limited GraphQL server listen on all addresses, not just \
181
         localhost (this is INSECURE, make sure your firewall is configured \
182
         correctly!)"
183
  and archive_process_location = Flag.Host_and_port.Daemon.archive
184
  and metrics_server_port =
185
    flag "--metrics-port" ~aliases:[ "metrics-port" ]
6✔
186
      ~doc:
187
        "PORT metrics server for scraping via Prometheus (default no \
188
         metrics-server)"
189
      (optional int16)
6✔
190
  and gc_stat_interval =
191
    flag "--gc-stat-interval" ~aliases:[ "gc-stat-interval" ] (optional float)
6✔
192
      ~doc:
193
        (sprintf
6✔
194
           "INTERVAL in mins for collecting GC stats for metrics (Default: %f)"
195
           !Mina_metrics.Runtime.gc_stat_interval_mins )
196
  and libp2p_metrics_port =
197
    flag "--libp2p-metrics-port" ~aliases:[ "libp2p-metrics-port" ]
6✔
198
      ~doc:
199
        "PORT libp2p metrics server for scraping via Prometheus (default no \
200
         libp2p-metrics-server)"
201
      (optional int16)
6✔
202
  and external_ip_opt =
203
    flag "--external-ip" ~aliases:[ "external-ip" ]
6✔
204
      ~doc:
205
        "IP External IP address for other nodes to connect to. You only need \
206
         to set this if auto-discovery fails for some reason."
207
      (optional string)
6✔
208
  and bind_ip_opt =
209
    flag "--bind-ip" ~aliases:[ "bind-ip" ]
6✔
210
      ~doc:"IP IP of network interface to use for peer connections"
211
      (optional string)
6✔
212
  and working_dir =
213
    flag "--working-dir" ~aliases:[ "working-dir" ]
6✔
214
      ~doc:
215
        "PATH path to chdir into before starting (useful for background mode, \
216
         defaults to cwd, or / if -background)"
217
      (optional string)
6✔
218
  and is_background =
219
    flag "--background" ~aliases:[ "background" ] no_arg
6✔
220
      ~doc:"Run process on the background"
221
  and is_archive_rocksdb =
222
    flag "--archive-rocksdb" ~aliases:[ "archive-rocksdb" ] no_arg
6✔
223
      ~doc:"Stores all the blocks heard in RocksDB"
224
  and log_json = Flag.Log.json
225
  and log_level = Flag.Log.level
226
  and file_log_level = Flag.Log.file_log_level
227
  and file_log_rotations = Flag.Log.file_log_rotations
228
  and snark_work_fee =
229
    flag "--snark-worker-fee" ~aliases:[ "snark-worker-fee" ]
6✔
230
      ~doc:
231
        (sprintf
6✔
232
           "FEE Amount a worker wants to get compensated for generating a \
233
            snark proof" )
234
      (optional txn_fee)
6✔
235
  and work_reassignment_wait =
236
    flag "--work-reassignment-wait"
6✔
237
      ~aliases:[ "work-reassignment-wait" ]
238
      (optional int)
6✔
239
      ~doc:
240
        (sprintf
6✔
241
           "WAIT-TIME in ms before a snark-work is reassigned (default: %dms)"
242
           Cli_lib.Default.work_reassignment_wait )
243
  and enable_tracing =
244
    flag "--tracing" ~aliases:[ "tracing" ] no_arg
6✔
245
      ~doc:"Trace into $config-directory/trace/$pid.trace"
246
  and enable_internal_tracing =
247
    flag "--internal-tracing" ~aliases:[ "internal-tracing" ] no_arg
6✔
248
      ~doc:
249
        "Enables internal tracing into \
250
         $config-directory/internal-tracing/internal-trace.jsonl"
251
  and insecure_rest_server =
252
    flag "--insecure-rest-server" ~aliases:[ "insecure-rest-server" ] no_arg
6✔
253
      ~doc:
254
        "Have REST server listen on all addresses, not just localhost (this is \
255
         INSECURE, make sure your firewall is configured correctly!)"
256
  (* FIXME #4095
257
     and limit_connections =
258
       flag "--limit-concurrent-connections"
259
         ~aliases:[ "limit-concurrent-connections"]
260
         ~doc:
261
           "true|false Limit the number of concurrent connections per IP \
262
            address (default: true)"
263
         (optional bool)*)
264
  (*TODO: This is being added to log all the snark works received for the
265
     beta-testnet challenge. We might want to remove this later?*)
266
  and log_received_snark_pool_diff =
267
    flag "--log-snark-work-gossip"
6✔
268
      ~aliases:[ "log-snark-work-gossip" ]
269
      ~doc:"true|false Log snark-pool diff received from peers (default: false)"
270
      (optional bool)
6✔
271
  and log_transaction_pool_diff =
272
    flag "--log-txn-pool-gossip" ~aliases:[ "log-txn-pool-gossip" ]
6✔
273
      ~doc:
274
        "true|false Log transaction-pool diff received from peers (default: \
275
         false)"
276
      (optional bool)
6✔
277
  and log_block_creation =
278
    flag "--log-block-creation" ~aliases:[ "log-block-creation" ]
6✔
279
      ~doc:
280
        "true|false Log the steps involved in including transactions and snark \
281
         work in a block (default: true)"
282
      (optional bool)
6✔
283
  and libp2p_keypair =
284
    flag "--libp2p-keypair" ~aliases:[ "libp2p-keypair" ] (optional string)
6✔
285
      ~doc:
286
        "KEYFILE Keypair (generated from `mina libp2p generate-keypair`) to \
287
         use with libp2p discovery"
288
  and is_seed =
289
    flag "--seed" ~aliases:[ "seed" ] ~doc:"Start the node as a seed node"
6✔
290
      no_arg
291
  and no_super_catchup =
292
    flag "--no-super-catchup" ~aliases:[ "no-super-catchup" ]
6✔
293
      ~doc:"Don't use super-catchup" no_arg
294
  and enable_flooding =
295
    flag "--enable-flooding" ~aliases:[ "enable-flooding" ]
6✔
296
      ~doc:
297
        "true|false Publish our own blocks/transactions to every peer we can \
298
         find (default: false)"
299
      (optional bool)
6✔
300
  and peer_exchange =
301
    flag "--enable-peer-exchange" ~aliases:[ "enable-peer-exchange" ]
6✔
302
      ~doc:
303
        "true|false Help keep the mesh connected when closing connections \
304
         (default: false)"
305
      (optional bool)
6✔
306
  and peer_protection_ratio =
307
    flag "--peer-protection-rate" ~aliases:[ "peer-protection-rate" ]
6✔
308
      ~doc:"float Proportion of peers to be marked as protected (default: 0.2)"
309
      (optional_with_default 0.2 float)
6✔
310
  and min_connections =
311
    flag "--min-connections" ~aliases:[ "min-connections" ]
6✔
312
      ~doc:
313
        (Printf.sprintf
6✔
314
           "NN min number of connections that this peer will have to neighbors \
315
            in the gossip network (default: %d)"
316
           Cli_lib.Default.min_connections )
317
      (optional int)
6✔
318
  and max_connections =
319
    flag "--max-connections" ~aliases:[ "max-connections" ]
6✔
320
      ~doc:
321
        (Printf.sprintf
6✔
322
           "NN max number of connections that this peer will have to neighbors \
323
            in the gossip network. Tuning this higher will strengthen your \
324
            connection to the network in exchange for using more RAM (default: \
325
            %d)"
326
           Cli_lib.Default.max_connections )
327
      (optional int)
6✔
328
  and validation_queue_size =
329
    flag "--validation-queue-size"
6✔
330
      ~aliases:[ "validation-queue-size" ]
331
      ~doc:
332
        (Printf.sprintf
6✔
333
           "NN size of the validation queue in the p2p network used to buffer \
334
            messages (like blocks and transactions received on the gossip \
335
            network) while validation is pending. If a transaction, for \
336
            example, is invalid, we don't forward the message on the gossip \
337
            net. If this queue is too small, we will drop messages without \
338
            validating them. If it is too large, we are susceptible to DoS \
339
            attacks on memory. (default: %d)"
340
           Cli_lib.Default.validation_queue_size )
341
      (optional int)
6✔
342
  and direct_peers_raw =
343
    flag "--direct-peer" ~aliases:[ "direct-peer" ]
6✔
344
      ~doc:
345
        "/ip4/IPADDR/tcp/PORT/p2p/PEERID Peers to always send new messages \
346
         to/from. These peers should also have you configured as a direct \
347
         peer, the relationship is intended to be symmetric"
348
      (listed string)
6✔
349
  and isolate =
350
    flag "--isolate-network" ~aliases:[ "isolate-network" ]
6✔
351
      ~doc:
352
        "true|false Only allow connections to the peers passed on the command \
353
         line or configured through GraphQL. (default: false)"
354
      (optional bool)
6✔
355
  and libp2p_peers_raw =
356
    flag "--peer" ~aliases:[ "peer" ]
6✔
357
      ~doc:
358
        "/ip4/IPADDR/tcp/PORT/p2p/PEERID initial \"bootstrap\" peers for \
359
         discovery"
360
      (listed string)
6✔
361
  and libp2p_peer_list_file =
362
    flag "--peer-list-file" ~aliases:[ "peer-list-file" ]
6✔
363
      ~doc:
364
        "PATH path to a file containing \"bootstrap\" peers for discovery, one \
365
         multiaddress per line"
366
      (optional string)
6✔
367
  and seed_peer_list_url =
368
    flag "--peer-list-url" ~aliases:[ "peer-list-url" ]
6✔
369
      ~doc:"URL URL of seed peer list file. Will be polled periodically."
370
      (optional string)
6✔
371
  and proposed_protocol_version =
372
    flag "--proposed-protocol-version"
6✔
373
      ~aliases:[ "proposed-protocol-version" ]
374
      (optional string)
6✔
375
      ~doc:"NN.NN.NN Proposed protocol version to signal other nodes"
376
  and config_files =
377
    flag "--config-file" ~aliases:[ "config-file" ]
6✔
378
      ~doc:
379
        "PATH path to a configuration file (overrides MINA_CONFIG_FILE, \
380
         default: <config_dir>/daemon.json). Pass multiple times to override \
381
         fields from earlier config files"
382
      (listed string)
6✔
383
  and _may_generate =
384
    flag "--generate-genesis-proof"
6✔
385
      ~aliases:[ "generate-genesis-proof" ]
386
      ~doc:"true|false Deprecated. Passing this flag has no effect"
387
      (optional bool)
6✔
388
  and disable_node_status =
389
    flag "--disable-node-status" ~aliases:[ "disable-node-status" ] no_arg
6✔
390
      ~doc:"Disable reporting node status to other nodes (default: enabled)"
391
  and cli_proof_level =
392
    flag "--proof-level" ~aliases:[ "proof-level" ]
6✔
393
      (optional (Arg_type.create Genesis_constants.Proof_level.of_string))
6✔
394
      ~doc:
395
        "full|check|none Internal, for testing. Start or connect to a network \
396
         with full proving (full), snark-testing with dummy proofs (check), or \
397
         dummy proofs (none)"
398
  and plugins = plugin_flag
399
  and precomputed_blocks_path =
400
    flag "--precomputed-blocks-file"
6✔
401
      ~aliases:[ "precomputed-blocks-file" ]
402
      (optional string)
6✔
403
      ~doc:"PATH Path to write precomputed blocks to, for replay or archiving"
404
  and log_precomputed_blocks =
405
    flag "--log-precomputed-blocks"
6✔
406
      ~aliases:[ "log-precomputed-blocks" ]
407
      (optional_with_default false bool)
6✔
408
      ~doc:"true|false Include precomputed blocks in the log (default: false)"
409
  and start_filtered_logs =
410
    flag "--start-filtered-logs" (listed string)
6✔
411
      ~doc:
412
        "LOG-FILTER Include filtered logs for the given filter. May be passed \
413
         multiple times"
414
  and block_reward_threshold =
415
    flag "--minimum-block-reward" ~aliases:[ "minimum-block-reward" ]
6✔
416
      ~doc:
417
        "AMOUNT Minimum reward a block produced by the node should have. Empty \
418
         blocks are created if the rewards are lower than the specified \
419
         threshold (default: No threshold, transactions and coinbase will be \
420
         included as long as the required snark work is available and can be \
421
         paid for)"
422
      (optional txn_amount)
6✔
423
  and stop_time =
424
    flag "--stop-time" ~aliases:[ "stop-time" ] (optional int)
6✔
425
      ~doc:
426
        (sprintf
6✔
427
           "UPTIME in hours after which the daemon stops itself (only if there \
428
            were no slots won within an hour after the stop time) (Default: \
429
            %d)"
430
           Cli_lib.Default.stop_time )
431
  and upload_blocks_to_gcloud =
432
    flag "--upload-blocks-to-gcloud"
6✔
433
      ~aliases:[ "upload-blocks-to-gcloud" ]
434
      (optional_with_default false bool)
6✔
435
      ~doc:
436
        "true|false upload blocks to gcloud storage. Requires the environment \
437
         variables GCLOUD_KEYFILE, NETWORK_NAME, and \
438
         GCLOUD_BLOCK_UPLOAD_BUCKET"
439
  and all_peers_seen_metric =
440
    flag "--all-peers-seen-metric"
6✔
441
      ~aliases:[ "all-peers-seen-metric" ]
442
      (optional_with_default false bool)
6✔
443
      ~doc:
444
        "true|false whether to track the set of all peers ever seen for the \
445
         all_peers metric (default: false)"
446
  and node_status_url =
447
    flag "--node-status-url" ~aliases:[ "node-status-url" ] (optional string)
6✔
448
      ~doc:"URL of the node status collection service"
449
  and node_error_url =
450
    flag "--node-error-url" ~aliases:[ "node-error-url" ] (optional string)
6✔
451
      ~doc:"URL of the node error collection service"
452
  and simplified_node_stats =
453
    flag "--simplified-node-stats"
6✔
454
      ~aliases:[ "simplified-node-stats" ]
455
      (optional_with_default true bool)
6✔
456
      ~doc:"whether to report simplified node stats (default: true)"
457
  and contact_info =
458
    flag "--contact-info" ~aliases:[ "contact-info" ] (optional string)
6✔
459
      ~doc:
460
        "contact info used in node error report service (it could be either \
461
         email address or discord username), it should be less than 200 \
462
         characters"
463
    |> Command.Param.map ~f:(fun opt ->
6✔
464
           Option.value_map opt ~default:None ~f:(fun s ->
×
465
               if String.length s < 200 then Some s
×
466
               else
467
                 Mina_user_error.raisef
×
468
                   "The length of contact info exceeds 200 characters:\n %s" s ) )
469
  and uptime_url_string =
470
    flag "--uptime-url" ~aliases:[ "uptime-url" ] (optional string)
6✔
471
      ~doc:"URL URL of the uptime service of the Mina delegation program"
472
  and uptime_submitter_key =
473
    flag "--uptime-submitter-key" ~aliases:[ "uptime-submitter-key" ]
6✔
474
      ~doc:
475
        "KEYFILE Private key file for the uptime submitter. You cannot provide \
476
         both `uptime-submitter-key` and `uptime-submitter-pubkey`."
477
      (optional string)
6✔
478
  and uptime_submitter_pubkey =
479
    flag "--uptime-submitter-pubkey"
6✔
480
      ~aliases:[ "uptime-submitter-pubkey" ]
481
      (optional string)
6✔
482
      ~doc:
483
        "PUBLICKEY Public key of the submitter to the Mina delegation program, \
484
         for the associated private key that is being tracked by this daemon. \
485
         You cannot provide both `uptime-submitter-key` and \
486
         `uptime-submitter-pubkey`."
487
  and uptime_send_node_commit =
488
    flag "--uptime-send-node-commit-sha"
6✔
489
      ~aliases:[ "uptime-send-node-commit-sha" ]
490
      ~doc:
491
        "true|false Whether to send the commit SHA used to build the node to \
492
         the uptime service. (default: false)"
493
      no_arg
494
  in
495
  let to_pubsub_topic_mode_option =
×
496
    let open Gossip_net.Libp2p in
497
    function
498
    | "ro" ->
×
499
        Some RO
500
    | "rw" ->
×
501
        Some RW
502
    | "none" ->
×
503
        Some N
504
    | _ ->
×
505
        raise (Error.to_exn (Error.of_string "Invalid pubsub topic mode"))
×
506
  in
507
  fun () ->
508
    O1trace.thread "mina" (fun () ->
×
509
        let open Deferred.Let_syntax in
×
510
        let conf_dir = Mina_lib.Conf_dir.compute_conf_dir conf_dir in
511
        let%bind () = File_system.create_dir conf_dir in
×
512
        let () =
×
513
          if is_background then (
×
514
            Core.printf "Starting background mina daemon. (Log Dir: %s)\n%!"
515
              conf_dir ;
516
            Daemon.daemonize ~allow_threads_to_have_been_created:true
×
517
              ~redirect_stdout:`Dev_null ?cd:working_dir
518
              ~redirect_stderr:`Dev_null () )
519
          else Option.iter working_dir ~f:Caml.Sys.chdir
×
520
        in
521
        Stdout_log.setup log_json log_level ;
522
        (* 512MB logrotate max size = 1GB max filesystem usage *)
523
        let logrotate_max_size = 1024 * 1024 * 10 in
×
524
        Logger.Consumer_registry.register ~commit_id:Mina_version.commit_id
525
          ~id:Logger.Logger_id.mina
526
          ~processor:(Logger.Processor.raw ~log_level:file_log_level ())
×
527
          ~transport:
528
            (Logger_file_system.dumb_logrotate ~directory:conf_dir
529
               ~log_filename:"mina.log" ~max_size:logrotate_max_size
530
               ~num_rotate:file_log_rotations )
531
          () ;
532
        let best_tip_diff_log_size = 1024 * 1024 * 5 in
×
533
        Logger.Consumer_registry.register ~commit_id:Mina_version.commit_id
534
          ~id:Logger.Logger_id.best_tip_diff
535
          ~processor:(Logger.Processor.raw ())
×
536
          ~transport:
537
            (Logger_file_system.dumb_logrotate ~directory:conf_dir
538
               ~log_filename:"mina-best-tip.log"
539
               ~max_size:best_tip_diff_log_size ~num_rotate:1 )
540
          () ;
541
        let rejected_blocks_log_size = 1024 * 1024 * 5 in
×
542
        Logger.Consumer_registry.register ~commit_id:Mina_version.commit_id
543
          ~id:Logger.Logger_id.rejected_blocks
544
          ~processor:(Logger.Processor.raw ())
×
545
          ~transport:
546
            (Logger_file_system.dumb_logrotate ~directory:conf_dir
547
               ~log_filename:"mina-rejected-blocks.log"
548
               ~max_size:rejected_blocks_log_size ~num_rotate:50 )
549
          () ;
550
        Logger.Consumer_registry.register ~commit_id:Mina_version.commit_id
×
551
          ~id:Logger.Logger_id.oversized_logs
552
          ~processor:(Logger.Processor.raw ())
×
553
          ~transport:
554
            (Logger_file_system.dumb_logrotate ~directory:conf_dir
555
               ~log_filename:"mina-oversized-logs.log"
556
               ~max_size:logrotate_max_size ~num_rotate:20 )
557
          () ;
558
        (* Consumer for `[%log internal]` logging used for internal tracing *)
559
        Itn_logger.set_message_postprocessor
×
560
          Internal_tracing.For_itn_logger.post_process_message ;
561
        Logger.Consumer_registry.register ~commit_id:Mina_version.commit_id
×
562
          ~id:Logger.Logger_id.mina
563
          ~processor:Internal_tracing.For_logger.processor
564
          ~transport:
565
            (Internal_tracing.For_logger.json_lines_rotate_transport
×
566
               ~directory:(conf_dir ^ "/internal-tracing")
567
               () )
568
          () ;
569
        let version_metadata = [ ("commit", `String Mina_version.commit_id) ] in
×
570
        [%log info] "Mina daemon is booting up; built with commit $commit"
×
571
          ~metadata:version_metadata ;
572
        let%bind () =
573
          Mina_lib.Conf_dir.check_and_set_lockfile ~logger conf_dir
×
574
        in
575
        [%log info] "Booting may take several seconds, please wait" ;
×
576
        let wallets_disk_location = conf_dir ^/ "wallets" in
×
577
        let%bind wallets =
578
          (* Load wallets early, to give user errors before expensive
579
             initialization starts.
580
          *)
581
          Secrets.Wallets.load ~logger ~disk_location:wallets_disk_location
582
        in
583
        let%bind libp2p_keypair =
584
          let libp2p_keypair_old_format =
585
            Option.bind libp2p_keypair ~f:(fun libp2p_keypair ->
586
                match Mina_net2.Keypair.of_string libp2p_keypair with
×
587
                | Ok kp ->
×
588
                    Some kp
589
                | Error _ ->
×
590
                    if String.contains libp2p_keypair ',' then
591
                      [%log warn]
×
592
                        "I think -libp2p-keypair is in the old format, but I \
593
                         failed to parse it! Using it as a path..." ;
594
                    None )
×
595
          in
596
          match libp2p_keypair_old_format with
×
597
          | Some kp ->
×
598
              return (Some kp)
×
599
          | None -> (
×
600
              match libp2p_keypair with
601
              | None ->
×
602
                  return None
×
603
              | Some s ->
×
604
                  Secrets.Libp2p_keypair.Terminal_stdin.read_exn
605
                    ~should_prompt_user:false ~which:"libp2p keypair" s
606
                  |> Deferred.map ~f:Option.some )
×
607
        in
608
        let%bind () =
609
          let version_filename = conf_dir ^/ "mina.version" in
610
          let make_version () =
×
611
            let%map () =
612
              (*Delete any trace files if version changes. TODO: Implement rotate logic similar to log files*)
613
              File_system.remove_dir (conf_dir ^/ "trace")
×
614
            in
615
            Yojson.Safe.to_file version_filename (`Assoc version_metadata)
×
616
          in
617
          match
618
            Or_error.try_with_join (fun () ->
619
                match Yojson.Safe.from_file version_filename with
×
620
                | `Assoc list -> (
×
621
                    match String.Map.(find (of_alist_exn list) "commit") with
×
622
                    | Some (`String commit) ->
×
623
                        Ok commit
624
                    | _ ->
×
625
                        Or_error.errorf "commit not found in version file %s"
626
                          version_filename )
627
                | _ ->
×
628
                    Or_error.errorf "Unexpected value in %s" version_filename )
629
          with
630
          | Ok c ->
×
631
              if String.equal c Mina_version.commit_id then return ()
×
632
              else (
×
633
                [%log warn]
×
634
                  "Different version of Mina detected in config directory \
635
                   $config_directory, removing existing configuration"
636
                  ~metadata:[ ("config_directory", `String conf_dir) ] ;
637
                make_version () )
×
638
          | Error e ->
×
639
              [%log debug]
×
640
                "Error reading $file: $error. Cleaning up the config directory \
641
                 $config_directory"
642
                ~metadata:
643
                  [ ("error", `String (Error.to_string_mach e))
×
644
                  ; ("config_directory", `String conf_dir)
645
                  ; ("file", `String version_filename)
646
                  ] ;
647
              make_version ()
×
648
        in
649
        Parallel.init_master () ;
×
650
        let monitor = Async.Monitor.create ~name:"coda" () in
×
651
        let time_controller =
×
652
          Block_time.Controller.create @@ Block_time.Controller.basic ~logger
653
        in
654
        let pids = Child_processes.Termination.create_pid_table () in
×
655
        let mina_initialization_deferred () =
×
656
          let%bind precomputed_values, config =
657
            Genesis_ledger_helper.Config_loader.load_config_files ~logger
658
              ~conf_dir ?genesis_dir ?cli_proof_level ~itn_features config_files
659
            |> Deferred.Or_error.ok_exn
×
660
          in
661
          let constraint_constants = precomputed_values.consensus_constants in
×
662
          let compile_config = precomputed_values.compile_config in
663
          let logger = with_itn_logger ~itn_features ~compile_config ~logger in
664
          constraint_constants.block_window_duration_ms |> Block_time.Span.to_ms
665
          |> Float.of_int64 |> Time.Span.of_ms |> Mina_metrics.initialize_all ;
×
666

667
          let module DC = Runtime_config.Daemon in
×
668
          (* The explicit typing here is necessary to prevent type inference from specializing according
669
             to the first usage.
670
          *)
671
          let maybe_from_config (type a) :
672
              getter:(DC.t -> a option) -> preferred_value:a option -> a option
673
              =
674
           fun ~getter ~preferred_value ->
675
            Option.first_some preferred_value Option.(config.daemon >>= getter)
×
676
          in
677
          let or_from_config (type a) :
678
                 getter:(DC.t -> a option)
679
              -> preferred_value:a option
680
              -> default:a
681
              -> a =
682
           fun ~getter ~preferred_value ~default ->
683
            Option.first_some preferred_value Option.(config.daemon >>= getter)
×
684
            |> Option.value ~default
×
685
          in
686

687
          let libp2p_port =
688
            or_from_config ~getter:DC.libp2p_port
689
              ~preferred_value:libp2p_port.value ~default:libp2p_port.default
690
          in
691
          let rest_server_port =
692
            or_from_config ~getter:DC.rest_port
693
              ~preferred_value:rest_server_port.value
694
              ~default:rest_server_port.default
695
          in
696
          let limited_graphql_port =
697
            maybe_from_config ~getter:DC.graphql_port
698
              ~preferred_value:limited_graphql_port.value
699
          in
700
          let client_port =
701
            or_from_config ~getter:DC.client_port
702
              ~preferred_value:client_port.value ~default:client_port.default
703
          in
704
          let snark_work_fee =
705
            or_from_config
706
              ~getter:(fun x ->
707
                DC.snark_worker_fee x
×
708
                |> Option.map ~f:Currency.Fee.of_nanomina_int_exn )
×
709
              ~preferred_value:snark_work_fee
710
              ~default:compile_config.default_snark_worker_fee
711
          in
712
          let node_status_url =
713
            maybe_from_config ~getter:DC.node_status_url
714
              ~preferred_value:node_status_url
715
          in
716
          (* FIXME #4095: pass this through to Gossip_net.Libp2p *)
717
          let _max_concurrent_connections =
718
            (*if
719
                 or_from_config YJ.Util.to_bool_option "max-concurrent-connections"
720
                   ~default:true limit_connections
721
               then Some 40
722
               else *)
723
            None
724
          in
725
          let work_selection_method =
726
            or_from_config
727
              ~getter:(fun x ->
728
                DC.work_selection x
×
729
                |> Option.map ~f:Cli_lib.Arg_type.work_selection_method_val )
×
730
              ~preferred_value:work_selection_method_flag
731
              ~default:Cli_lib.Arg_type.Work_selection_method.Random
732
          in
733
          let work_reassignment_wait =
734
            or_from_config ~getter:DC.work_reassignment_wait
735
              ~preferred_value:work_reassignment_wait
736
              ~default:Cli_lib.Default.work_reassignment_wait
737
          in
738
          let log_received_snark_pool_diff =
739
            or_from_config ~getter:DC.log_snark_work_gossip
740
              ~preferred_value:log_received_snark_pool_diff ~default:false
741
          in
742
          let log_transaction_pool_diff =
743
            or_from_config ~getter:DC.log_txn_pool_gossip
744
              ~preferred_value:log_transaction_pool_diff ~default:false
745
          in
746
          let log_block_creation =
747
            or_from_config ~getter:DC.log_block_creation
748
              ~preferred_value:log_block_creation ~default:true
749
          in
750
          let log_gossip_heard =
751
            { Mina_networking.Config.snark_pool_diff =
752
                log_received_snark_pool_diff
753
            ; transaction_pool_diff = log_transaction_pool_diff
754
            ; new_state = true
755
            }
756
          in
757
          let to_publickey_compressed_option which pk_str =
758
            match Public_key.Compressed.of_base58_check pk_str with
×
759
            | Ok key -> (
×
760
                match Public_key.decompress key with
761
                | None ->
×
762
                    Mina_user_error.raisef ~where:"decompressing a public key"
763
                      "The %s public key %s could not be decompressed." which
764
                      pk_str
765
                | Some _ ->
×
766
                    Some key )
767
            | Error _e ->
×
768
                Mina_user_error.raisef ~where:"decoding a public key"
769
                  "The %s public key %s could not be decoded." which pk_str
770
          in
771
          let run_snark_worker_flag =
772
            maybe_from_config
773
              ~getter:
774
                Option.(
775
                  fun x ->
776
                    DC.run_snark_worker x
×
777
                    >>= to_publickey_compressed_option "snark_worker")
×
778
              ~preferred_value:run_snark_worker_flag
779
          in
780
          let run_snark_coordinator_flag =
781
            maybe_from_config
782
              ~getter:
783
                Option.(
784
                  fun x ->
785
                    DC.run_snark_coordinator x
×
786
                    >>= to_publickey_compressed_option "snark_coordinator")
×
787
              ~preferred_value:run_snark_coordinator_flag
788
          in
789
          let snark_worker_parallelism_flag =
790
            maybe_from_config ~getter:DC.snark_worker_parallelism
791
              ~preferred_value:snark_worker_parallelism_flag
792
          in
793
          let coinbase_receiver_flag =
794
            maybe_from_config
795
              ~getter:
796
                Option.(
797
                  fun x ->
798
                    DC.coinbase_receiver x
×
799
                    >>= to_publickey_compressed_option "coinbase_receiver")
×
800
              ~preferred_value:coinbase_receiver_flag
801
          in
802
          let%bind external_ip =
803
            match external_ip_opt with
804
            | None ->
×
805
                Find_ip.find ~logger
806
            | Some ip ->
×
807
                return @@ Unix.Inet_addr.of_string ip
×
808
          in
809
          let bind_ip =
×
810
            Option.value bind_ip_opt ~default:"0.0.0.0"
811
            |> Unix.Inet_addr.of_string
×
812
          in
813
          let addrs_and_ports : Node_addrs_and_ports.t =
×
814
            { external_ip; bind_ip; peer = None; client_port; libp2p_port }
815
          in
816
          let block_production_key =
817
            maybe_from_config ~getter:DC.block_producer_key
818
              ~preferred_value:block_production_key
819
          in
820
          let block_production_pubkey =
821
            maybe_from_config
822
              ~getter:
823
                Option.(
824
                  fun x ->
825
                    DC.block_producer_pubkey x
×
826
                    >>= to_publickey_compressed_option "block_producer")
×
827
              ~preferred_value:block_production_pubkey
828
          in
829
          let block_production_password =
830
            maybe_from_config ~getter:DC.block_producer_password
831
              ~preferred_value:block_production_password
832
          in
833
          Option.iter
834
            ~f:(fun password ->
835
              match Sys.getenv Secrets.Keypair.env with
×
836
              | Some env_pass when not (String.equal env_pass password) ->
×
837
                  [%log warn]
×
838
                    "$envkey environment variable doesn't match value provided \
839
                     on command-line or daemon.json. Using value from $envkey"
840
                    ~metadata:[ ("envkey", `String Secrets.Keypair.env) ]
841
              | _ ->
×
842
                  Unix.putenv ~key:Secrets.Keypair.env ~data:password )
843
            block_production_password ;
844
          let%bind block_production_keypair =
845
            match
846
              ( block_production_key
847
              , block_production_pubkey
848
              , Sys.getenv "MINA_BP_PRIVKEY" )
×
849
            with
850
            | Some _, Some _, _ ->
×
851
                Mina_user_error.raise
×
852
                  "You cannot provide both `block-producer-key` and \
853
                   `block_production_pubkey`"
854
            | None, Some _, Some _ ->
×
855
                Mina_user_error.raise
×
856
                  "You cannot provide both `MINA_BP_PRIVKEY` and \
857
                   `block_production_pubkey`"
858
            | None, None, None ->
×
859
                Deferred.return None
×
860
            | None, None, Some base58_privkey ->
×
861
                let kp =
862
                  Private_key.of_base58_check_exn base58_privkey
863
                  |> Keypair.of_private_key_exn
×
864
                in
865
                Deferred.return (Some kp)
×
866
            (* CLI argument takes precedence over env variable *)
867
            | Some sk_file, None, (Some _ | None) ->
×
868
                [%log warn]
×
869
                  "`block-producer-key` is deprecated. Please set \
870
                   `MINA_BP_PRIVKEY` environment variable instead." ;
871
                let%map kp =
872
                  Secrets.Keypair.Terminal_stdin.read_exn
×
873
                    ~should_prompt_user:false ~which:"block producer keypair"
874
                    sk_file
875
                in
876
                Some kp
×
877
            | None, Some tracked_pubkey, None ->
×
878
                let%map kp =
879
                  Secrets.Wallets.get_tracked_keypair ~logger
×
880
                    ~which:"block producer keypair"
881
                    ~read_from_env_exn:
882
                      (Secrets.Keypair.Terminal_stdin.read_exn
883
                         ~should_prompt_user:false ~should_reask:false )
884
                    ~conf_dir tracked_pubkey
885
                in
886
                Some kp
×
887
          in
888
          let%bind client_trustlist =
889
            Reader.load_sexp
×
890
              (conf_dir ^/ "client_trustlist")
×
891
              [%of_sexp: Unix.Cidr.t list]
892
            >>| Or_error.ok
×
893
          in
894
          let client_trustlist =
×
895
            let mina_client_trustlist = "MINA_CLIENT_TRUSTLIST" in
896
            let cidrs_of_env_str env_str env_var =
897
              let cidrs =
×
898
                String.split ~on:',' env_str
899
                |> List.filter_map ~f:(fun str ->
×
900
                       try Some (Unix.Cidr.of_string str)
×
901
                       with _ ->
×
902
                         [%log warn] "Could not parse address $address in %s"
×
903
                           env_var
904
                           ~metadata:[ ("address", `String str) ] ;
905
                         None )
×
906
              in
907
              Some
×
908
                (List.append cidrs (Option.value ~default:[] client_trustlist))
×
909
            in
910
            match Unix.getenv mina_client_trustlist with
911
            | Some env_str ->
×
912
                cidrs_of_env_str env_str mina_client_trustlist
×
913
            | None ->
×
914
                client_trustlist
915
          in
916
          let get_monitor_infos monitor =
917
            let rec get_monitors accum monitor =
×
918
              match Async_kernel.Monitor.parent monitor with
×
919
              | None ->
×
920
                  List.rev accum
921
              | Some parent ->
×
922
                  get_monitors (parent :: accum) parent
923
            in
924
            let monitors = get_monitors [ monitor ] monitor in
925
            List.map monitors ~f:(fun monitor ->
×
926
                match Async_kernel.Monitor.sexp_of_t monitor with
×
927
                | Sexp.List sexps ->
×
928
                    `List (List.map ~f:Error_json.sexp_record_to_yojson sexps)
×
929
                | Sexp.Atom _ ->
×
930
                    failwith "Expected a sexp list" )
931
          in
932
          let o1trace context =
933
            Execution_context.find_local context O1trace.local_storage_id
×
934
            |> Option.value ~default:[]
×
935
            |> List.map ~f:(fun x -> `String x)
×
936
          in
937
          Stream.iter
938
            (Async_kernel.Async_kernel_scheduler.long_cycles_with_context
939
               ~at_least:(sec 0.5 |> Time_ns.Span.of_span_float_round_nearest) )
×
940
            ~f:(fun (span, context) ->
941
              let secs = Time_ns.Span.to_sec span in
×
942
              let monitor_infos = get_monitor_infos context.monitor in
×
943
              let o1trace = o1trace context in
×
944
              [%log internal] "Long_async_cycle"
×
945
                ~metadata:
946
                  [ ("duration", `Float secs); ("trace", `List o1trace) ] ;
947
              [%log debug]
×
948
                ~metadata:
949
                  [ ("long_async_cycle", `Float secs)
950
                  ; ("monitors", `List monitor_infos)
951
                  ; ("o1trace", `List o1trace)
952
                  ]
953
                "Long async cycle, $long_async_cycle seconds, $monitors, \
954
                 $o1trace" ;
955
              Mina_metrics.(
×
956
                Runtime.Long_async_histogram.observe Runtime.long_async_cycle
957
                  secs) ) ;
958
          Stream.iter Async_kernel.Async_kernel_scheduler.long_jobs_with_context
×
959
            ~f:(fun (context, span) ->
960
              let secs = Time_ns.Span.to_sec span in
×
961
              let monitor_infos = get_monitor_infos context.monitor in
×
962
              let o1trace = o1trace context in
×
963
              [%log internal] "Long_async_job"
×
964
                ~metadata:
965
                  [ ("duration", `Float secs); ("trace", `List o1trace) ] ;
966
              [%log debug]
×
967
                ~metadata:
968
                  [ ("long_async_job", `Float secs)
969
                  ; ("monitors", `List monitor_infos)
970
                  ; ("o1trace", `List o1trace)
971
                  ; ( "most_recent_2_backtrace"
972
                    , `String
973
                        (String.concat ~sep:"␤"
×
974
                           (List.map ~f:Backtrace.to_string
×
975
                              (List.take
×
976
                                 (Execution_context.backtrace_history context)
×
977
                                 2 ) ) ) )
978
                  ]
979
                "Long async job, $long_async_job seconds, $monitors, $o1trace" ;
980
              Mina_metrics.(
×
981
                Runtime.Long_job_histogram.observe Runtime.long_async_job secs) ) ;
982
          let trace_database_initialization typ location =
×
983
            (* can't use %log ppx here, because we're using the passed-in location *)
984
            Logger.trace logger ~module_:__MODULE__ "Creating %s at %s"
×
985
              ~location typ
986
          in
987
          let trust_dir = conf_dir ^/ "trust" in
988
          let%bind () = Async.Unix.mkdir ~p:() trust_dir in
×
989
          let%bind trust_system = Trust_system.create trust_dir in
×
990
          trace_database_initialization "trust_system" __LOC__ trust_dir ;
×
991
          let genesis_state_hash =
×
992
            (Precomputed_values.genesis_state_hashes precomputed_values)
×
993
              .state_hash
994
          in
995
          let genesis_ledger_hash =
996
            Precomputed_values.genesis_ledger precomputed_values
997
            |> Lazy.force |> Mina_ledger.Ledger.merkle_root
×
998
          in
999
          let block_production_keypairs =
×
1000
            block_production_keypair
1001
            |> Option.map ~f:(fun kp ->
1002
                   (kp, Public_key.compress kp.Keypair.public_key) )
×
1003
            |> Option.to_list |> Keypair.And_compressed_pk.Set.of_list
×
1004
          in
1005
          let epoch_ledger_location = conf_dir ^/ "epoch_ledger" in
×
1006
          let module Context = struct
×
1007
            let logger = logger
1008

1009
            let compile_config = precomputed_values.compile_config
1010

1011
            let constraint_constants = precomputed_values.constraint_constants
1012

1013
            let consensus_constants = precomputed_values.consensus_constants
1014
          end in
1015
          let consensus_local_state =
1016
            Consensus.Data.Local_state.create
1017
              ~context:(module Context)
1018
              ~genesis_ledger:
1019
                (Precomputed_values.genesis_ledger precomputed_values)
×
1020
              ~genesis_epoch_data:precomputed_values.genesis_epoch_data
1021
              ~epoch_ledger_location
1022
              ( Option.map block_production_keypair ~f:(fun keypair ->
1023
                    let open Keypair in
×
1024
                    Public_key.compress keypair.public_key )
1025
              |> Option.to_list |> Public_key.Compressed.Set.of_list )
×
1026
              ~genesis_state_hash:
1027
                precomputed_values.protocol_state_with_hashes.hash.state_hash
1028
          in
1029
          trace_database_initialization "epoch ledger" __LOC__
×
1030
            epoch_ledger_location ;
1031
          let%bind peer_list_file_contents_or_empty =
1032
            match libp2p_peer_list_file with
1033
            | None ->
×
1034
                return []
×
1035
            | Some file -> (
×
1036
                match%bind
1037
                  Monitor.try_with_or_error ~here:[%here] (fun () ->
×
1038
                      Reader.file_contents file )
×
1039
                with
1040
                | Ok contents ->
×
1041
                    return (Mina_net2.Multiaddr.of_file_contents contents)
×
1042
                | Error _ ->
×
1043
                    Mina_user_error.raisef
1044
                      ~where:"reading libp2p peer address file"
1045
                      "The file %s could not be read.\n\n\
1046
                       It must be a newline-separated list of libp2p \
1047
                       multiaddrs (ex: /ip4/IPADDR/tcp/PORT/p2p/PEERID)"
1048
                      file )
1049
          in
1050
          List.iter libp2p_peers_raw ~f:(fun raw_peer ->
×
1051
              if not Mina_net2.Multiaddr.(valid_as_peer @@ of_string raw_peer)
×
1052
              then
1053
                Mina_user_error.raisef ~where:"decoding peer as a multiaddress"
×
1054
                  "The given peer \"%s\" is not a valid multiaddress (ex: \
1055
                   /ip4/IPADDR/tcp/PORT/p2p/PEERID)"
1056
                  raw_peer ) ;
1057
          let initial_peers =
×
1058
            let peers =
1059
              or_from_config ~getter:DC.peers ~preferred_value:None ~default:[]
1060
            in
1061
            List.concat
×
1062
              [ List.map ~f:Mina_net2.Multiaddr.of_string libp2p_peers_raw
×
1063
              ; peer_list_file_contents_or_empty
1064
              ; List.map ~f:Mina_net2.Multiaddr.of_string @@ peers
×
1065
              ]
1066
          in
1067
          let direct_peers =
1068
            List.map ~f:Mina_net2.Multiaddr.of_string direct_peers_raw
1069
          in
1070
          let min_connections =
×
1071
            or_from_config ~getter:DC.min_connections
1072
              ~preferred_value:min_connections
1073
              ~default:Cli_lib.Default.min_connections
1074
          in
1075
          let max_connections =
1076
            or_from_config ~getter:DC.max_connections
1077
              ~preferred_value:max_connections
1078
              ~default:Cli_lib.Default.max_connections
1079
          in
1080
          let pubsub_v1 = Gossip_net.Libp2p.N in
1081
          (* TODO uncomment after introducing Bitswap-based block retrieval *)
1082
          (* let pubsub_v1 =
1083
               or_from_config to_pubsub_topic_mode_option "pubsub-v1"
1084
                 ~default:Cli_lib.Default.pubsub_v1 pubsub_v1
1085
             in *)
1086
          let pubsub_v0 =
1087
            or_from_config
1088
              ~getter:
1089
                Option.(fun x -> DC.pubsub_v0 x >>= to_pubsub_topic_mode_option)
×
1090
              ~preferred_value:None ~default:Cli_lib.Default.pubsub_v0
1091
          in
1092

1093
          let validation_queue_size =
1094
            or_from_config ~getter:DC.validation_queue_size
1095
              ~preferred_value:validation_queue_size
1096
              ~default:Cli_lib.Default.validation_queue_size
1097
          in
1098
          let stop_time =
1099
            or_from_config ~getter:DC.stop_time ~preferred_value:stop_time
1100
              ~default:Cli_lib.Default.stop_time
1101
          in
1102
          if enable_tracing then Mina_tracing.start conf_dir |> don't_wait_for ;
×
1103
          let%bind () =
1104
            if enable_internal_tracing then
1105
              Internal_tracing.toggle ~commit_id:Mina_version.commit_id ~logger
×
1106
                `Enabled
1107
            else Deferred.unit
×
1108
          in
1109
          let seed_peer_list_url =
×
1110
            Option.value_map seed_peer_list_url ~f:Option.some
1111
              ~default:
1112
                (Option.bind config.daemon
×
1113
                   ~f:(fun { Runtime_config.Daemon.peer_list_url; _ } ->
1114
                     peer_list_url ) )
×
1115
          in
1116
          if is_seed then [%log info] "Starting node as a seed node"
×
1117
          else if demo_mode then [%log info] "Starting node in demo mode"
×
1118
          else if
×
1119
            List.is_empty initial_peers && Option.is_none seed_peer_list_url
×
1120
          then
1121
            Mina_user_error.raise
×
1122
              {|No peers were given.
1123

1124
Pass one of -peer, -peer-list-file, -seed, -peer-list-url.|} ;
1125
          let chain_id =
1126
            let protocol_transaction_version =
1127
              Protocol_version.(transaction current)
×
1128
            in
1129
            let protocol_network_version =
1130
              Protocol_version.(transaction current)
×
1131
            in
1132
            chain_id ~genesis_state_hash
1133
              ~genesis_constants:precomputed_values.genesis_constants
1134
              ~constraint_system_digests:
1135
                (Lazy.force precomputed_values.constraint_system_digests)
×
1136
              ~protocol_transaction_version ~protocol_network_version
1137
          in
1138
          [%log info] "Daemon will use chain id %s" chain_id ;
×
1139
          [%log info] "Daemon running protocol version %s"
×
1140
            Protocol_version.(to_string current) ;
×
1141
          let gossip_net_params =
×
1142
            Gossip_net.Libp2p.Config.
1143
              { timeout = Time.Span.of_sec 3.
×
1144
              ; logger
1145
              ; conf_dir
1146
              ; chain_id
1147
              ; unsafe_no_trust_ip = false
1148
              ; seed_peer_list_url =
1149
                  Option.map seed_peer_list_url ~f:Uri.of_string
×
1150
              ; initial_peers
1151
              ; addrs_and_ports
1152
              ; metrics_port = libp2p_metrics_port
1153
              ; trust_system
1154
              ; flooding = Option.value ~default:false enable_flooding
×
1155
              ; direct_peers
1156
              ; peer_protection_ratio
1157
              ; peer_exchange = Option.value ~default:false peer_exchange
×
1158
              ; min_connections
1159
              ; max_connections
1160
              ; validation_queue_size
1161
              ; isolate = Option.value ~default:false isolate
×
1162
              ; keypair = libp2p_keypair
1163
              ; all_peers_seen_metric
1164
              ; known_private_ip_nets =
1165
                  Option.value ~default:[] client_trustlist
×
1166
              ; time_controller
1167
              ; pubsub_v1
1168
              ; pubsub_v0
1169
              }
1170
          in
1171
          let net_config =
1172
            { Mina_networking.Config.genesis_ledger_hash
1173
            ; log_gossip_heard
1174
            ; is_seed
1175
            ; creatable_gossip_net =
1176
                Mina_networking.Gossip_net.(
1177
                  Any.Creatable
1178
                    ((module Libp2p), Libp2p.create ~pids gossip_net_params))
×
1179
            }
1180
          in
1181
          let coinbase_receiver : Consensus.Coinbase_receiver.t =
1182
            Option.value_map coinbase_receiver_flag ~default:`Producer
×
1183
              ~f:(fun pk -> `Other pk)
×
1184
          in
1185
          let proposed_protocol_version_opt =
1186
            Mina_run.get_proposed_protocol_version_opt ~conf_dir ~logger
1187
              proposed_protocol_version
1188
          in
1189
          ( match
×
1190
              (uptime_url_string, uptime_submitter_key, uptime_submitter_pubkey)
1191
            with
1192
          | Some _, Some _, None | Some _, None, Some _ | None, None, None ->
×
1193
              ()
1194
          | _ ->
×
1195
              Mina_user_error.raise
×
1196
                "Must provide both --uptime-url and exactly one of \
1197
                 --uptime-submitter-key or --uptime-submitter-pubkey" ) ;
1198
          let uptime_url =
1199
            Option.map uptime_url_string ~f:(fun s -> Uri.of_string s)
×
1200
          in
1201
          let uptime_submitter_opt =
×
1202
            Option.map uptime_submitter_pubkey ~f:(fun s ->
1203
                match Public_key.Compressed.of_base58_check s with
×
1204
                | Ok pk -> (
×
1205
                    match Public_key.decompress pk with
1206
                    | Some _ ->
×
1207
                        pk
1208
                    | None ->
×
1209
                        failwithf
1210
                          "Invalid public key %s for uptime submitter (could \
1211
                           not decompress)"
1212
                          s () )
1213
                | Error err ->
×
1214
                    Mina_user_error.raisef
1215
                      "Invalid public key %s for uptime submitter, %s" s
1216
                      (Error.to_string_hum err) () )
×
1217
          in
1218
          let%bind uptime_submitter_keypair =
1219
            match (uptime_submitter_key, uptime_submitter_opt) with
1220
            | None, None ->
×
1221
                return None
×
1222
            | None, Some pk ->
×
1223
                let%map kp =
1224
                  Secrets.Wallets.get_tracked_keypair ~logger
×
1225
                    ~which:"uptime submitter keypair"
1226
                    ~read_from_env_exn:
1227
                      (Secrets.Uptime_keypair.Terminal_stdin.read_exn
1228
                         ~should_prompt_user:false ~should_reask:false )
1229
                    ~conf_dir pk
1230
                in
1231
                Some kp
×
1232
            | Some sk_file, None ->
×
1233
                let%map kp =
1234
                  Secrets.Uptime_keypair.Terminal_stdin.read_exn
×
1235
                    ~should_prompt_user:false ~should_reask:false
1236
                    ~which:"uptime submitter keypair" sk_file
1237
                in
1238
                Some kp
×
1239
            | _ ->
×
1240
                (* unreachable, because of earlier check *)
1241
                failwith
1242
                  "Cannot provide both uptime submitter public key and uptime \
1243
                   submitter keyfile"
1244
          in
1245
          if compile_config.itn_features then
×
1246
            (* set queue bound directly in Itn_logger
1247
               adding bound to Mina_lib config introduces cycle
1248
            *)
1249
            Option.iter itn_max_logs ~f:Itn_logger.set_queue_bound ;
×
1250
          let start_time = Time.now () in
×
1251
          let%map mina =
1252
            Mina_lib.create ~commit_id:Mina_version.commit_id ~wallets
×
1253
              (Mina_lib.Config.make ~logger ~pids ~trust_system ~conf_dir
×
1254
                 ~chain_id ~is_seed ~super_catchup:(not no_super_catchup)
1255
                 ~disable_node_status ~demo_mode ~coinbase_receiver ~net_config
1256
                 ~gossip_net_params ~proposed_protocol_version_opt
1257
                 ~work_selection_method:
1258
                   (Cli_lib.Arg_type.work_selection_method_to_module
×
1259
                      work_selection_method )
1260
                 ~snark_worker_config:
1261
                   { Mina_lib.Config.Snark_worker_config
1262
                     .initial_snark_worker_key = run_snark_worker_flag
1263
                   ; shutdown_on_disconnect = true
1264
                   ; num_threads = snark_worker_parallelism_flag
1265
                   }
1266
                 ~snark_coordinator_key:run_snark_coordinator_flag
1267
                 ~snark_pool_disk_location:(conf_dir ^/ "snark_pool")
×
1268
                 ~wallets_disk_location:(conf_dir ^/ "wallets")
×
1269
                 ~persistent_root_location:(conf_dir ^/ "root")
×
1270
                 ~persistent_frontier_location:(conf_dir ^/ "frontier")
×
1271
                 ~epoch_ledger_location ~snark_work_fee ~time_controller
1272
                 ~block_production_keypairs ~monitor ~consensus_local_state
1273
                 ~is_archive_rocksdb ~work_reassignment_wait
1274
                 ~archive_process_location ~log_block_creation
1275
                 ~precomputed_values ~start_time ?precomputed_blocks_path
1276
                 ~log_precomputed_blocks ~start_filtered_logs
1277
                 ~upload_blocks_to_gcloud ~block_reward_threshold ~uptime_url
1278
                 ~uptime_submitter_keypair ~uptime_send_node_commit ~stop_time
1279
                 ~node_status_url ~graphql_control_port:itn_graphql_port
1280
                 ~simplified_node_stats
1281
                 ~zkapp_cmd_limit:(ref compile_config.zkapp_cmd_limit)
1282
                 ~compile_config () )
1283
          in
1284
          { mina
×
1285
          ; client_trustlist
1286
          ; rest_server_port
1287
          ; limited_graphql_port
1288
          ; itn_graphql_port
1289
          }
1290
        in
1291
        (* Breaks a dependency cycle with monitor initilization and coda *)
1292
        let mina_ref : Mina_lib.t option ref = ref None in
1293
        Option.iter node_error_url ~f:(fun url ->
1294
            let get_node_state () =
×
1295
              match !mina_ref with
×
1296
              | None ->
×
1297
                  Deferred.return None
1298
              | Some mina ->
×
1299
                  let%map node_state = Mina_lib.get_node_state mina in
×
1300
                  Some node_state
×
1301
            in
1302
            Node_error_service.set_config ~get_node_state
1303
              ~node_error_url:(Uri.of_string url) ~contact_info ) ;
×
1304
        Mina_run.handle_shutdown ~monitor ~time_controller ~conf_dir
×
1305
          ~child_pids:pids ~top_logger:logger mina_ref ;
1306
        Async.Scheduler.within' ~monitor
×
1307
        @@ fun () ->
1308
        let%bind { mina
1309
                 ; client_trustlist
1310
                 ; rest_server_port
1311
                 ; limited_graphql_port
1312
                 ; itn_graphql_port
1313
                 } =
1314
          mina_initialization_deferred ()
×
1315
        in
1316
        mina_ref := Some mina ;
×
1317
        (*This pipe is consumed only by integration tests*)
1318
        don't_wait_for
1319
          (Pipe_lib.Strict_pipe.Reader.iter_without_pushback
×
1320
             (Mina_lib.validated_transitions mina)
×
1321
             ~f:ignore ) ;
1322
        Mina_run.setup_local_server ?client_trustlist ~rest_server_port
×
1323
          ~insecure_rest_server ~open_limited_graphql_port ?limited_graphql_port
1324
          ?itn_graphql_port ?auth_keys:itn_keys mina ;
1325
        let%bind () =
1326
          Option.map metrics_server_port ~f:(fun port ->
1327
              let forward_uri =
×
1328
                Option.map libp2p_metrics_port ~f:(fun port ->
1329
                    Uri.with_uri ~scheme:(Some "http") ~host:(Some "127.0.0.1")
×
1330
                      ~port:(Some port) ~path:(Some "/metrics") Uri.empty )
1331
              in
1332
              Mina_metrics.Runtime.(
×
1333
                gc_stat_interval_mins :=
1334
                  Option.value ~default:!gc_stat_interval_mins gc_stat_interval) ;
×
1335
              Mina_metrics.server ?forward_uri ~port ~logger () >>| ignore )
×
1336
          |> Option.value ~default:Deferred.unit
×
1337
        in
1338
        let () = Mina_plugins.init_plugins ~logger mina plugins in
×
1339
        return mina )
×
1340

1341
let daemon logger ~itn_features =
1342
  Command.async ~summary:"Mina daemon"
3✔
1343
    (Command.Param.map (setup_daemon logger ~itn_features)
3✔
1344
       ~f:(fun setup_daemon () ->
1345
         (* Immediately disable updating the time offset. *)
1346
         Block_time.Controller.disable_setting_offset () ;
×
1347
         let%bind mina = setup_daemon () in
×
1348
         let%bind () = Mina_lib.start mina in
×
1349
         [%log info] "Daemon ready. Clients can now connect" ;
×
1350
         Async.never () ) )
×
1351

1352
let replay_blocks ~itn_features logger =
1353
  let replay_flag =
3✔
1354
    let open Command.Param in
1355
    flag "--blocks-filename" ~aliases:[ "-blocks-filename" ] (required string)
3✔
1356
      ~doc:"PATH The file to read the precomputed blocks from"
1357
  in
1358
  let read_kind =
1359
    let open Command.Param in
1360
    flag "--format" ~aliases:[ "-format" ] (optional string)
3✔
1361
      ~doc:"json|sexp The format to read lines of the file in (default: json)"
1362
  in
1363
  Command.async ~summary:"Start mina daemon with blocks replayed from a file"
1364
    (Command.Param.map3 replay_flag read_kind
3✔
1365
       (setup_daemon logger ~itn_features)
3✔
1366
       ~f:(fun blocks_filename read_kind setup_daemon () ->
1367
         (* Enable updating the time offset. *)
1368
         Block_time.Controller.enable_setting_offset () ;
×
1369
         let read_block_line =
×
1370
           match Option.map ~f:String.lowercase read_kind with
1371
           | Some "json" | None -> (
×
1372
               fun line ->
1373
                 match
×
1374
                   Yojson.Safe.from_string line
1375
                   |> Mina_block.Precomputed.of_yojson
×
1376
                 with
1377
                 | Ok block ->
×
1378
                     block
1379
                 | Error err ->
×
1380
                     failwithf "Could not read block: %s" err () )
1381
           | Some "sexp" ->
×
1382
               fun line ->
1383
                 Sexp.of_string_conv_exn line Mina_block.Precomputed.t_of_sexp
×
1384
           | _ ->
×
1385
               failwith "Expected one of 'json', 'sexp' for -format flag"
1386
         in
1387
         let blocks =
1388
           Sequence.unfold ~init:(In_channel.create blocks_filename)
×
1389
             ~f:(fun blocks_file ->
1390
               match In_channel.input_line blocks_file with
×
1391
               | Some line ->
×
1392
                   Some (read_block_line line, blocks_file)
×
1393
               | None ->
×
1394
                   In_channel.close blocks_file ;
1395
                   None )
×
1396
         in
1397
         let%bind mina = setup_daemon () in
×
1398
         let%bind () = Mina_lib.start_with_precomputed_blocks mina blocks in
×
1399
         [%log info]
×
1400
           "Daemon is ready, replayed precomputed blocks. Clients can now \
1401
            connect" ;
1402
         Async.never () ) )
×
1403

1404
let dump_type_shapes =
1405
  let max_depth_flag =
1406
    let open Command.Param in
1407
    flag "--max-depth" ~aliases:[ "-max-depth" ] (optional int)
3✔
1408
      ~doc:"NN Maximum depth of shape S-expressions"
1409
  in
1410
  Command.basic ~summary:"Print serialization shapes of versioned types"
3✔
1411
    (Command.Param.map max_depth_flag ~f:(fun max_depth () ->
3✔
1412
         Ppx_version_runtime.Shapes.iteri
×
1413
           ~f:(fun ~key:path ~data:(shape, ty_decl) ->
1414
             let open Bin_prot.Shape in
×
1415
             let canonical = eval shape in
1416
             let digest = Canonical.to_digest canonical |> Digest.to_hex in
×
1417
             let shape_summary =
×
1418
               let shape_sexp =
1419
                 Canonical.to_string_hum canonical |> Sexp.of_string
×
1420
               in
1421
               (* elide the shape below specified depth, so that changes to
1422
                  contained types aren't considered a change to the containing
1423
                  type, even though the shape digests differ
1424
               *)
1425
               let summary_sexp =
×
1426
                 match max_depth with
1427
                 | None ->
×
1428
                     shape_sexp
1429
                 | Some n ->
×
1430
                     let rec go sexp depth =
1431
                       if depth > n then Sexp.Atom "."
×
1432
                       else
1433
                         match sexp with
×
1434
                         | Sexp.Atom _ ->
×
1435
                             sexp
1436
                         | Sexp.List items ->
×
1437
                             Sexp.List
1438
                               (List.map items ~f:(fun item ->
×
1439
                                    go item (depth + 1) ) )
×
1440
                     in
1441
                     go shape_sexp 0
×
1442
               in
1443
               Sexp.to_string summary_sexp
×
1444
             in
1445
             Core_kernel.printf "%s, %s, %s, %s\n" path digest shape_summary
1446
               ty_decl ) ) )
1447

1448
let primitive_ok = function
1449
  | "array" | "bytes" | "string" | "bigstring" ->
×
1450
      false
1451
  | "int" | "int32" | "int64" | "nativeint" | "char" | "bool" | "float" ->
×
1452
      true
1453
  | "unit" | "option" | "list" ->
×
1454
      true
1455
  | "kimchi_backend_bigint_32_V1" ->
×
1456
      true
1457
  | "Bounded_types.String.t"
×
1458
  | "Bounded_types.String.Tagged.t"
×
1459
  | "Bounded_types.Array.t" ->
×
1460
      true
1461
  | "8fabab0a-4992-11e6-8cca-9ba2c4686d9e" ->
×
1462
      true (* hashtbl *)
1463
  | "ac8a9ff4-4994-11e6-9a1b-9fb4e933bd9d" ->
×
1464
      true (* Make_iterable_binable *)
1465
  | s ->
×
1466
      failwithf "unknown primitive %s" s ()
1467

1468
let audit_type_shapes : Command.t =
1469
  let rec shape_ok (shape : Sexp.t) : bool =
1470
    match shape with
×
1471
    | List [ Atom "Exp"; exp ] ->
×
1472
        exp_ok exp
1473
    | List [] ->
×
1474
        true
1475
    | _ ->
×
1476
        failwithf "bad shape: %s" (Sexp.to_string shape) ()
×
1477
  and exp_ok (exp : Sexp.t) : bool =
1478
    match exp with
×
1479
    | List [ Atom "Base"; Atom tyname; List exps ] ->
×
1480
        primitive_ok tyname && List.for_all exps ~f:shape_ok
×
1481
    | List [ Atom "Record"; List fields ] ->
×
1482
        List.for_all fields ~f:(fun field ->
1483
            match field with
×
1484
            | List [ Atom _; sh ] ->
×
1485
                shape_ok sh
1486
            | _ ->
×
1487
                failwithf "unhandled rec field: %s" (Sexp.to_string_hum field)
×
1488
                  () )
1489
    | List [ Atom "Tuple"; List exps ] ->
×
1490
        List.for_all exps ~f:shape_ok
1491
    | List [ Atom "Variant"; List ctors ] ->
×
1492
        List.for_all ctors ~f:(fun ctor ->
1493
            match ctor with
×
1494
            | List [ Atom _ctr; List exps ] ->
×
1495
                List.for_all exps ~f:shape_ok
1496
            | _ ->
×
1497
                failwithf "unhandled variant: %s" (Sexp.to_string_hum ctor) () )
×
1498
    | List [ Atom "Poly_variant"; List [ List [ Atom "sorted"; List ctors ] ] ]
×
1499
      ->
1500
        List.for_all ctors ~f:(fun ctor ->
1501
            match ctor with
×
1502
            | List [ Atom _ctr ] ->
×
1503
                true
1504
            | List [ Atom _ctr; List fields ] ->
×
1505
                List.for_all fields ~f:shape_ok
1506
            | _ ->
×
1507
                failwithf "unhandled poly variant: %s" (Sexp.to_string_hum ctor)
×
1508
                  () )
1509
    | List [ Atom "Application"; sh; List args ] ->
×
1510
        shape_ok sh && List.for_all args ~f:shape_ok
×
1511
    | List [ Atom "Rec_app"; Atom _; List args ] ->
×
1512
        List.for_all args ~f:shape_ok
1513
    | List [ Atom "Var"; Atom _ ] ->
×
1514
        true
1515
    | List (Atom ctr :: _) ->
×
1516
        failwithf "unhandled ctor (%s) in exp_ok: %s" ctr
1517
          (Sexp.to_string_hum exp) ()
×
1518
    | List [] | List _ | Atom _ ->
×
1519
        failwithf "bad format: %s" (Sexp.to_string_hum exp) ()
×
1520
  in
1521
  let handle_shape (path : string) (shape : Bin_prot.Shape.t) (ty_decl : string)
1522
      (good : int ref) (bad : int ref) =
1523
    let open Bin_prot.Shape in
×
1524
    let path, file = String.lsplit2_exn ~on:':' path in
1525
    let canonical = eval shape in
×
1526
    let shape_sexp = Canonical.to_string_hum canonical |> Sexp.of_string in
×
1527
    if not @@ shape_ok shape_sexp then (
×
1528
      incr bad ;
1529
      Core.eprintf "%s has a bad shape in %s (%s):\n%s\n" path file ty_decl
×
1530
        (Canonical.to_string_hum canonical) )
×
1531
    else incr good
×
1532
  in
1533
  Command.basic ~summary:"Audit shapes of versioned types"
3✔
1534
    (Command.Param.return (fun () ->
3✔
1535
         let bad, good = (ref 0, ref 0) in
×
1536
         Ppx_version_runtime.Shapes.iteri
1537
           ~f:(fun ~key:path ~data:(shape, ty_decl) ->
1538
             handle_shape path shape ty_decl good bad ) ;
×
1539
         Core.printf "good shapes:\n\t%d\nbad shapes:\n\t%d\n%!" !good !bad ;
1540
         if !bad > 0 then Core.exit 1 ) )
×
1541

1542
(*NOTE A previous version of this function included compile time ppx that didn't compile, and was never
1543
  evaluated under any build profile
1544
*)
1545
let ensure_testnet_id_still_good _ = Deferred.unit
3✔
1546

1547
let snark_hashes =
1548
  let module Hashes = struct
1549
    type t = string list [@@deriving to_yojson]
×
1550
  end in
1551
  let open Command.Let_syntax in
1552
  Command.basic ~summary:"List hashes of proving and verification keys"
3✔
1553
    [%map_open
1554
      let json = Cli_lib.Flag.json in
1555
      fun () -> if json then Core.printf "[]\n%!"]
×
1556

1557
let internal_commands ~itn_features logger =
1558
  [ ( Snark_worker.Intf.command_name
3✔
1559
    , Snark_worker.command ~commit_id:Mina_version.commit_id )
1560
  ; ("snark-hashes", snark_hashes)
1561
  ; ( "run-prover"
1562
    , Command.async
3✔
1563
        ~summary:"Run prover on a sexp provided on a single line of stdin"
1564
        (let open Command.Let_syntax in
1565
        let%map_open config_file = Cli_lib.Flag.config_files in
1566
        fun () ->
1567
          let open Deferred.Let_syntax in
×
1568
          let%bind constraint_constants, proof_level, compile_config =
1569
            let%map conf =
1570
              Runtime_config.Constants.load_constants_with_logging ~logger
×
1571
                config_file
1572
            in
1573
            Runtime_config.Constants.
×
1574
              (constraint_constants conf, proof_level conf, compile_config conf)
×
1575
          in
1576
          let logger = with_itn_logger ~itn_features ~compile_config ~logger in
×
1577
          Parallel.init_master () ;
1578
          match%bind Reader.read_sexp (Lazy.force Reader.stdin) with
×
1579
          | `Ok sexp ->
×
1580
              let%bind conf_dir = Unix.mkdtemp "/tmp/mina-prover" in
×
1581
              [%log info] "Prover state being logged to %s" conf_dir ;
×
1582
              let%bind prover =
1583
                Prover.create ~commit_id:Mina_version.commit_id ~logger
×
1584
                  ~proof_level ~constraint_constants ~pids:(Pid.Table.create ())
×
1585
                  ~conf_dir ()
1586
              in
1587
              Prover.prove_from_input_sexp prover sexp >>| ignore
×
1588
          | `Eof ->
×
1589
              failwith "early EOF while reading sexp") )
1590
  ; ( "run-snark-worker-single"
1591
    , Command.async
3✔
1592
        ~summary:"Run snark-worker on a sexp provided on a single line of stdin"
1593
        (let open Command.Let_syntax in
1594
        let%map_open filename =
1595
          flag "--file" (required string)
3✔
1596
            ~doc:"File containing the s-expression of the snark work to execute"
1597
        and config_file = Cli_lib.Flag.config_files in
1598

1599
        fun () ->
1600
          let open Deferred.Let_syntax in
×
1601
          let%bind constraint_constants, proof_level, compile_config =
1602
            let%map conf =
1603
              Runtime_config.Constants.load_constants_with_logging ~logger
×
1604
                config_file
1605
            in
1606
            Runtime_config.Constants.
×
1607
              (constraint_constants conf, proof_level conf, compile_config conf)
×
1608
          in
1609
          let logger = with_itn_logger ~itn_features ~compile_config ~logger in
×
1610
          Parallel.init_master () ;
1611
          match%bind
1612
            Reader.with_file filename ~f:(fun reader ->
×
1613
                [%log info] "Created reader for %s" filename ;
×
1614
                Reader.read_sexp reader )
×
1615
          with
1616
          | `Ok sexp -> (
×
1617
              let%bind worker_state =
1618
                Snark_worker.Prod.Inputs.Worker_state.create ~proof_level
×
1619
                  ~constraint_constants ()
1620
              in
1621
              let sok_message =
×
1622
                { Mina_base.Sok_message.fee = Currency.Fee.of_mina_int_exn 0
×
1623
                ; prover = Quickcheck.random_value Public_key.Compressed.gen
×
1624
                }
1625
              in
1626
              let spec =
1627
                [%of_sexp:
1628
                  ( Transaction_witness.t
1629
                  , Ledger_proof.t )
1630
                  Snark_work_lib.Work.Single.Spec.t] sexp
1631
              in
1632
              match%map
1633
                Snark_worker.Prod.Inputs.perform_single worker_state
×
1634
                  ~message:sok_message spec
1635
              with
1636
              | Ok _ ->
×
1637
                  [%log info] "Successfully worked"
×
1638
              | Error err ->
×
1639
                  [%log error] "Work didn't work: $err"
×
1640
                    ~metadata:[ ("err", Error_json.error_to_yojson err) ] )
×
1641
          | `Eof ->
×
1642
              failwith "early EOF while reading sexp") )
1643
  ; ( "run-verifier"
1644
    , Command.async
3✔
1645
        ~summary:"Run verifier on a proof provided on a single line of stdin"
1646
        (let open Command.Let_syntax in
1647
        let%map_open mode =
1648
          flag "--mode" ~aliases:[ "-mode" ] (required string)
3✔
1649
            ~doc:"transaction/blockchain the snark to verify. Defaults to json"
1650
        and format =
1651
          flag "--format" ~aliases:[ "-format" ] (optional string)
3✔
1652
            ~doc:"sexp/json the format to parse input in"
1653
        and limit =
1654
          flag "--limit" ~aliases:[ "-limit" ] (optional int)
3✔
1655
            ~doc:"limit the number of proofs taken from the file"
1656
        and config_file = Cli_lib.Flag.config_files in
1657
        fun () ->
1658
          let open Async in
×
1659
          let%bind constraint_constants, proof_level, compile_config =
1660
            let%map conf =
1661
              Runtime_config.Constants.load_constants_with_logging ~logger
×
1662
                config_file
1663
            in
1664
            Runtime_config.Constants.
×
1665
              (constraint_constants conf, proof_level conf, compile_config conf)
×
1666
          in
1667
          let logger = with_itn_logger ~itn_features ~compile_config ~logger in
×
1668
          Parallel.init_master () ;
1669
          let%bind conf_dir = Unix.mkdtemp "/tmp/mina-verifier" in
×
1670
          let mode =
×
1671
            match mode with
1672
            | "transaction" ->
×
1673
                `Transaction
1674
            | "blockchain" ->
×
1675
                `Blockchain
1676
            | mode ->
×
1677
                failwithf
×
1678
                  "Expected mode flag to be one of transaction, blockchain, \
1679
                   got '%s'"
1680
                  mode ()
1681
          in
1682
          let format =
1683
            match format with
1684
            | Some "sexp" ->
×
1685
                `Sexp
1686
            | Some "json" | None ->
×
1687
                `Json
1688
            | Some format ->
×
1689
                failwithf
×
1690
                  "Expected format flag to be one of sexp, json, got '%s'"
1691
                  format ()
1692
          in
1693
          let%bind input =
1694
            match format with
1695
            | `Sexp -> (
×
1696
                let%map input_sexp =
1697
                  match%map Reader.read_sexp (Lazy.force Reader.stdin) with
×
1698
                  | `Ok input_sexp ->
×
1699
                      input_sexp
1700
                  | `Eof ->
×
1701
                      failwith "early EOF while reading sexp"
1702
                in
1703
                match mode with
×
1704
                | `Transaction ->
×
1705
                    `Transaction
1706
                      (List.t_of_sexp
×
1707
                         (Tuple2.t_of_sexp Ledger_proof.t_of_sexp
×
1708
                            Sok_message.t_of_sexp )
1709
                         input_sexp )
1710
                | `Blockchain ->
×
1711
                    `Blockchain
1712
                      (List.t_of_sexp Blockchain_snark.Blockchain.t_of_sexp
×
1713
                         input_sexp ) )
1714
            | `Json -> (
×
1715
                let%map input_line =
1716
                  match%map Reader.read_line (Lazy.force Reader.stdin) with
×
1717
                  | `Ok input_line ->
×
1718
                      input_line
1719
                  | `Eof ->
×
1720
                      failwith "early EOF while reading json"
1721
                in
1722
                match mode with
×
1723
                | `Transaction -> (
×
1724
                    match
1725
                      [%derive.of_yojson: (Ledger_proof.t * Sok_message.t) list]
×
1726
                        (Yojson.Safe.from_string input_line)
×
1727
                    with
1728
                    | Ok input ->
×
1729
                        `Transaction input
1730
                    | Error err ->
×
1731
                        failwithf "Could not parse JSON: %s" err () )
1732
                | `Blockchain -> (
×
1733
                    match
1734
                      [%derive.of_yojson: Blockchain_snark.Blockchain.t list]
×
1735
                        (Yojson.Safe.from_string input_line)
×
1736
                    with
1737
                    | Ok input ->
×
1738
                        `Blockchain input
1739
                    | Error err ->
×
1740
                        failwithf "Could not parse JSON: %s" err () ) )
1741
          in
1742

1743
          let%bind verifier =
1744
            Verifier.For_tests.default ~constraint_constants ~proof_level
×
1745
              ~commit_id:Mina_version.commit_id ~logger
1746
              ~pids:(Pid.Table.create ()) ~conf_dir:(Some conf_dir) ()
×
1747
          in
1748
          let%bind result =
1749
            let cap lst =
1750
              Option.value_map ~default:Fn.id ~f:(Fn.flip List.take) limit lst
×
1751
            in
1752
            match input with
1753
            | `Transaction input ->
×
1754
                input |> cap |> Verifier.verify_transaction_snarks verifier
×
1755
            | `Blockchain input ->
×
1756
                input |> cap |> Verifier.verify_blockchain_snarks verifier
×
1757
          in
1758
          match result with
×
1759
          | Ok (Ok ()) ->
×
1760
              printf "Proofs verified successfully" ;
1761
              exit 0
×
1762
          | Ok (Error err) ->
×
1763
              printf "Proofs failed to verify:\n%s\n"
1764
                (Yojson.Safe.pretty_to_string (Error_json.error_to_yojson err)) ;
×
1765
              exit 1
×
1766
          | Error err ->
×
1767
              printf "Failed while verifying proofs:\n%s"
1768
                (Error.to_string_hum err) ;
×
1769
              exit 2) )
×
1770
  ; ( "dump-structured-events"
1771
    , Command.async ~summary:"Dump the registered structured events"
3✔
1772
        (let open Command.Let_syntax in
1773
        let%map outfile =
1774
          Core_kernel.Command.Param.flag "--out-file" ~aliases:[ "-out-file" ]
3✔
1775
            (Core_kernel.Command.Flag.optional Core_kernel.Command.Param.string)
3✔
1776
            ~doc:"FILENAME File to output to. Defaults to stdout"
1777
        and pretty =
1778
          Core_kernel.Command.Param.flag "--pretty" ~aliases:[ "-pretty" ]
3✔
1779
            Core_kernel.Command.Param.no_arg
1780
            ~doc:"  Set to output 'pretty' JSON"
1781
        in
1782
        fun () ->
1783
          let out_channel =
×
1784
            match outfile with
1785
            | Some outfile ->
×
1786
                Core_kernel.Out_channel.create outfile
×
1787
            | None ->
×
1788
                Core_kernel.Out_channel.stdout
1789
          in
1790
          let json =
1791
            Structured_log_events.dump_registered_events ()
1792
            |> [%derive.to_yojson:
1793
                 (string * Structured_log_events.id * string list) list]
×
1794
          in
1795
          if pretty then Yojson.Safe.pretty_to_channel out_channel json
×
1796
          else Yojson.Safe.to_channel out_channel json ;
×
1797
          ( match outfile with
1798
          | Some _ ->
×
1799
              Core_kernel.Out_channel.close out_channel
×
1800
          | None ->
×
1801
              () ) ;
1802
          Deferred.return ()) )
1803
  ; ("dump-type-shapes", dump_type_shapes)
1804
  ; ("replay-blocks", replay_blocks ~itn_features logger)
3✔
1805
  ; ("audit-type-shapes", audit_type_shapes)
1806
  ; ( "test-genesis-block-generation"
1807
    , Command.async ~summary:"Generate a genesis proof"
3✔
1808
        (let open Command.Let_syntax in
1809
        let%map_open config_file = Cli_lib.Flag.config_files
1810
        and conf_dir = Cli_lib.Flag.conf_dir
1811
        and genesis_dir =
1812
          flag "--genesis-ledger-dir" ~aliases:[ "genesis-ledger-dir" ]
3✔
1813
            ~doc:
1814
              "DIR Directory that contains the genesis ledger and the genesis \
1815
               blockchain proof (default: <config-dir>)"
1816
            (optional string)
3✔
1817
        in
1818
        fun () ->
1819
          let open Deferred.Let_syntax in
×
1820
          Parallel.init_master () ;
1821
          let conf_dir = Mina_lib.Conf_dir.compute_conf_dir conf_dir in
×
1822
          let%bind precomputed_values, _ =
1823
            Genesis_ledger_helper.Config_loader.load_config_files ~logger
1824
              ~conf_dir ?genesis_dir ~cli_proof_level:Full ~itn_features
1825
              config_file
1826
            |> Deferred.Or_error.ok_exn
×
1827
          in
1828
          let logger =
×
1829
            with_itn_logger ~itn_features
1830
              ~compile_config:precomputed_values.compile_config ~logger
1831
          in
1832
          let pids = Child_processes.Termination.create_pid_table () in
1833
          let%bind prover =
1834
            (* We create a prover process (unnecessarily) here, to have a more
1835
               realistic test.
1836
            *)
1837
            Prover.create ~commit_id:Mina_version.commit_id ~logger ~pids
×
1838
              ~conf_dir ~proof_level:precomputed_values.proof_level
1839
              ~constraint_constants:precomputed_values.constraint_constants ()
1840
          in
1841
          match%bind
1842
            Prover.create_genesis_block prover
×
1843
              (Genesis_proof.to_inputs precomputed_values)
×
1844
          with
1845
          | Ok block ->
×
1846
              Format.eprintf "Generated block@.%s@."
1847
                ( Yojson.Safe.to_string
×
1848
                @@ Blockchain_snark.Blockchain.to_yojson block ) ;
×
1849
              exit 0
×
1850
          | Error err ->
×
1851
              Format.eprintf "Failed to generate block@.%s@."
1852
                (Yojson.Safe.to_string @@ Error_json.error_to_yojson err) ;
×
1853
              exit 1) )
×
1854
  ]
1855

1856
let mina_commands logger ~itn_features =
1857
  [ ("accounts", Client.accounts)
3✔
1858
  ; ("daemon", daemon ~itn_features logger)
3✔
1859
  ; ("client", Client.client)
1860
  ; ("advanced", Client.advanced ~itn_features)
1861
  ; ("ledger", Client.ledger)
1862
  ; ("libp2p", Client.libp2p)
1863
  ; ( "internal"
1864
    , Command.group ~summary:"Internal commands"
3✔
1865
        (internal_commands ~itn_features logger) )
3✔
1866
  ; (Parallel.worker_command_name, Parallel.worker_command)
1867
  ; ("transaction-snark-profiler", Transaction_snark_profiler.command)
1868
  ]
1869

1870
let print_version_help coda_exe version =
1871
  (* mimic Jane Street command help *)
1872
  let lines =
×
1873
    [ "print version information"
1874
    ; ""
1875
    ; sprintf "  %s %s" (Filename.basename coda_exe) version
×
1876
    ; ""
1877
    ; "=== flags ==="
1878
    ; ""
1879
    ; "  [-help]  print this help text and exit"
1880
    ; "           (alias: -?)"
1881
    ]
1882
  in
1883
  List.iter lines ~f:(Core.printf "%s\n%!")
×
1884

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

1887
let () =
1888
  Random.self_init () ;
1889
  let logger = Logger.create () in
3✔
1890
  don't_wait_for (ensure_testnet_id_still_good logger) ;
3✔
1891
  (* Turn on snark debugging in prod for now *)
1892
  Snarky_backendless.Snark.set_eval_constraints true ;
3✔
1893
  (* intercept command-line processing for "version", because we don't
1894
     use the Jane Street scripts that generate their version information
1895
  *)
1896
  (let is_version_cmd s =
3✔
1897
     List.mem [ "version"; "-version"; "--version" ] s ~equal:String.equal
×
1898
   in
1899
   match Sys.get_argv () with
1900
   | [| _mina_exe; version |] when is_version_cmd version ->
×
1901
       Mina_version.print_version ()
×
1902
   | _ ->
3✔
1903
       let itn_features = Mina_compile_config.Compiled.t.itn_features in
1904
       Command.run
×
1905
         (Command.group ~summary:"Mina" ~preserve_subcommand_order:()
3✔
1906
            (mina_commands logger ~itn_features) ) ) ;
3✔
1907
  Core.exit 0
×
1908

1909
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