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

MinaProtocol / mina / 80

14 Apr 2025 10:52PM UTC coverage: 60.82% (+24.9%) from 35.871%
80

push

buildkite

web-flow
Merge pull request #16871 from MinaProtocol/georgeee/last-before-zkapp-stable-hash-removal

Some changes required for hash removal from Zkapp_command.Stable

2 of 3 new or added lines in 2 files covered. (66.67%)

1322 existing lines in 28 files now uncovered.

49978 of 82173 relevant lines covered (60.82%)

475582.51 hits per line

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

0.26
/src/app/cli/src/init/mina_run.ml
1
open Core
6✔
2
open Async
3
module Graphql_cohttp_async =
4
  Graphql_internal.Make (Graphql_async.Schema) (Cohttp_async.Io)
5
    (Cohttp_async.Body)
6

7
let snark_job_list_json t =
8
  let open Participating_state.Let_syntax in
×
9
  let%map sl = Mina_lib.best_staged_ledger t in
×
10
  Staged_ledger.Scan_state.snark_job_list_json (Staged_ledger.scan_state sl)
×
11

12
let snark_pool_list t =
13
  Mina_lib.snark_pool t |> Network_pool.Snark_pool.resource_pool
×
14
  |> Network_pool.Snark_pool.Resource_pool.snark_pool_json
×
15
  |> Yojson.Safe.to_string
16

17
(* create reader, writer for protocol versions, but really for any one-line item in conf_dir *)
18
let make_conf_dir_item_io ~conf_dir ~filename =
19
  let item_file = conf_dir ^/ filename in
×
20
  let read_item () =
×
21
    let open Stdlib in
×
22
    let inp = open_in item_file in
23
    let res = input_line inp in
×
24
    close_in inp ; res
×
25
  in
26
  let write_item item =
27
    let open Stdlib in
×
28
    let outp = open_out item_file in
29
    output_string outp (item ^ "\n") ;
×
30
    close_out outp
×
31
  in
32
  (read_item, write_item)
33

34
let get_proposed_protocol_version_opt ~conf_dir ~logger =
35
  let read_protocol_version, write_protocol_version =
×
36
    make_conf_dir_item_io ~conf_dir ~filename:"proposed_protocol_version"
37
  in
38
  function
39
  | None -> (
×
40
      try
41
        (* not provided on command line, try to read from config dir *)
42
        let protocol_version = read_protocol_version () in
43
        [%log info]
×
44
          "Setting proposed protocol version to $protocol_version from config"
45
          ~metadata:[ ("protocol_version", `String protocol_version) ] ;
46
        Some (Protocol_version.of_string_exn protocol_version)
×
47
      with Sys_error _ ->
×
48
        (* not on command-line, not in config dir, there's no proposed protocol version *)
49
        None )
50
  | Some protocol_version -> (
×
51
      let validate_cli_protocol_version protocol_version =
52
        if Option.is_none (Protocol_version.of_string_opt protocol_version) then (
×
53
          [%log fatal]
×
54
            "Proposed protocol version provided on command line is invalid"
55
            ~metadata:
56
              [ ("proposed_protocol_version", `String protocol_version) ] ;
57
          failwith "Proposed protocol version from command line is invalid" )
×
58
      in
59
      try
60
        (* overwrite if the command line value disagrees with the value in the config *)
61
        let config_protocol_version = read_protocol_version () in
62
        if String.equal config_protocol_version protocol_version then (
×
63
          [%log info]
×
64
            "Using proposed protocol version $protocol_version from command \
65
             line, which matches the one in the config"
66
            ~metadata:[ ("protocol_version", `String protocol_version) ] ;
67
          Some (Protocol_version.of_string_exn config_protocol_version) )
×
68
        else (
×
69
          validate_cli_protocol_version protocol_version ;
70
          write_protocol_version protocol_version ;
×
71
          [%log info]
×
72
            "Overwriting Coda config proposed protocol version \
73
             $config_proposed_protocol_version with proposed protocol version \
74
             $protocol_version from the command line"
75
            ~metadata:
76
              [ ( "config_proposed_protocol_version"
77
                , `String config_protocol_version )
78
              ; ("proposed_protocol_version", `String protocol_version)
79
              ] ;
80
          Some (Protocol_version.of_string_exn protocol_version) )
×
81
      with Sys_error _ ->
×
82
        (* use value provided on command line, write to config dir *)
83
        validate_cli_protocol_version protocol_version ;
84
        write_protocol_version protocol_version ;
×
85
        [%log info]
×
86
          "Using proposed protocol version from command line, writing to config"
87
          ~metadata:[ ("protocol_version", `String protocol_version) ] ;
88
        Some (Protocol_version.of_string_exn protocol_version) )
×
89

90
(*TODO check deferred now and copy theose files to the temp directory*)
91
let log_shutdown ~conf_dir ~top_logger coda_ref =
92
  let logger =
×
93
    Logger.extend top_logger
94
      [ ("coda_run", `String "Logging state before program ends") ]
95
  in
96
  let frontier_file = conf_dir ^/ "frontier.dot" in
×
97
  let mask_file = conf_dir ^/ "registered_masks.dot" in
×
98
  (* ledger visualization *)
99
  [%log debug] "%s" (Visualization_message.success "registered masks" mask_file) ;
×
100
  Mina_ledger.Ledger.Debug.visualize ~filename:mask_file ;
×
101
  match !coda_ref with
102
  | None ->
×
103
      [%log warn]
×
104
        "Shutdown before Coda instance was created, not saving a visualization"
105
  | Some t -> (
×
106
      (*Transition frontier visualization*)
107
      match Mina_lib.visualize_frontier ~filename:frontier_file t with
108
      | `Active () ->
×
109
          [%log debug] "%s"
×
110
            (Visualization_message.success "transition frontier" frontier_file)
×
111
      | `Bootstrapping ->
×
112
          [%log debug] "%s"
×
113
            (Visualization_message.bootstrap "transition frontier") )
×
114

115
let remove_prev_crash_reports ~conf_dir =
116
  Core.Sys.command (sprintf "rm -rf %s/coda_crash_report*" conf_dir)
×
117

118
let summary exn_json =
119
  let uname = Core.Unix.uname () in
×
120
  let daemon_command =
×
121
    sprintf !"Command: %{sexp: string array}" (Sys.get_argv ())
×
122
  in
123
  `Assoc
×
124
    [ ("OS_type", `String Sys.os_type)
125
    ; ("Release", `String (Core.Unix.Utsname.release uname))
×
126
    ; ("Machine", `String (Core.Unix.Utsname.machine uname))
×
127
    ; ("Sys_name", `String (Core.Unix.Utsname.sysname uname))
×
128
    ; ("Exception", exn_json)
129
    ; ("Command", `String daemon_command)
130
    ; ("Coda_commit", `String Mina_version.commit_id)
131
    ]
132

133
let coda_status coda_ref =
134
  Option.value_map coda_ref
×
135
    ~default:
136
      (Deferred.return (`String "Shutdown before Coda instance was created"))
×
137
    ~f:(fun t ->
138
      Mina_commands.get_status ~flag:`Performance t
×
139
      >>| Daemon_rpcs.Types.Status.to_yojson )
140

141
let make_report exn_json ~conf_dir ~top_logger coda_ref =
142
  (* TEMP MAKE REPORT TRACE *)
143
  [%log' trace top_logger] "make_report: enter" ;
×
144
  ignore (remove_prev_crash_reports ~conf_dir : int) ;
×
145
  let crash_time = Time.to_filename_string ~zone:Time.Zone.utc (Time.now ()) in
×
146
  let temp_config = conf_dir ^/ "coda_crash_report_" ^ crash_time in
×
147
  let () = Core.Unix.mkdir temp_config in
×
148
  (*Transition frontier and ledger visualization*)
149
  log_shutdown ~conf_dir:temp_config ~top_logger coda_ref ;
×
150
  let report_file = temp_config ^ ".tar.gz" in
×
151
  (*Coda status*)
152
  let status_file = temp_config ^/ "coda_status.json" in
153
  let%map status = coda_status !coda_ref in
×
154
  Yojson.Safe.to_file status_file status ;
×
155
  (* TEMP MAKE REPORT TRACE *)
156
  [%log' trace top_logger] "make_report: acquired and wrote status" ;
×
157
  (*coda logs*)
158
  let coda_log = conf_dir ^/ "mina.log" in
×
159
  let () =
×
160
    match Core.Sys.file_exists coda_log with
161
    | `Yes ->
×
162
        let coda_short_log = temp_config ^/ "coda_short.log" in
163
        (*get the last 4MB of the log*)
164
        let log_size = 4 * 1024 * 1024 |> Int64.of_int in
×
165
        let log =
×
166
          In_channel.with_file coda_log ~f:(fun in_chan ->
167
              let len = In_channel.length in_chan in
×
168
              In_channel.seek in_chan
×
169
                Int64.(max 0L (Int64.( + ) len (Int64.neg log_size))) ;
×
170
              In_channel.input_all in_chan )
×
171
        in
172
        Out_channel.write_all coda_short_log ~data:log
×
173
    | _ ->
×
174
        ()
175
  in
176
  (*System info/crash summary*)
177
  let summary = summary exn_json in
178
  Yojson.Safe.to_file (temp_config ^/ "crash_summary.json") summary ;
×
179
  (*copy daemon_json to the temp dir *)
180
  let daemon_config = conf_dir ^/ "daemon.json" in
×
181
  let eq = [%equal: [ `Yes | `Unknown | `No ]] in
×
182
  let () =
183
    if eq (Core.Sys.file_exists daemon_config) `Yes then
×
184
      ignore
×
185
        ( Core.Sys.command
×
186
            (sprintf "cp %s %s" daemon_config (temp_config ^/ "daemon.json"))
×
187
          : int )
188
  in
189
  (*Zip them all up*)
190
  let tmp_files =
191
    [ "coda_short.log"
192
    ; "registered_mask.dot"
193
    ; "frontier.dot"
194
    ; "coda_status.json"
195
    ; "crash_summary.json"
196
    ; "daemon.json"
197
    ]
198
    |> List.filter ~f:(fun f ->
199
           eq (Core.Sys.file_exists (temp_config ^/ f)) `Yes )
×
200
  in
201
  let files = tmp_files |> String.concat ~sep:" " in
×
202
  let tar_command =
×
203
    sprintf "tar  -C %s -czf %s %s" temp_config report_file files
204
  in
205
  let exit = Core.Sys.command tar_command in
×
206
  if exit = 2 then (
×
207
    [%log' fatal top_logger] "Error making the crash report. Exit code: %d" exit ;
×
208
    None )
×
209
  else Some (report_file, temp_config)
×
210

211
(* TODO: handle participation_status more appropriately than doing participate_exn *)
212
let setup_local_server ?(client_trustlist = []) ?rest_server_port
×
213
    ?limited_graphql_port ?itn_graphql_port ?auth_keys
214
    ?(open_limited_graphql_port = false) ?(insecure_rest_server = false) mina =
×
215
  let compile_config = (Mina_lib.config mina).compile_config in
×
216
  let itn_features = (Mina_lib.config mina).itn_features in
×
217
  let client_trustlist =
218
    ref
219
      (Unix.Cidr.Set.of_list
×
220
         ( Unix.Cidr.create ~base_address:Unix.Inet_addr.localhost ~bits:8
221
         :: client_trustlist ) )
222
  in
223
  (* Setup RPC server for client interactions *)
224
  let implement rpc f =
225
    Rpc.Rpc.implement rpc (fun () input ->
×
226
        O1trace.thread ("serve_" ^ Rpc.Rpc.name rpc) (fun () -> f () input) )
×
227
  in
228
  let implement_notrace = Rpc.Rpc.implement in
229
  let logger =
230
    Logger.extend
231
      (Mina_lib.top_level_logger mina)
×
232
      [ ("mina_run", `String "Setting up server logs") ]
233
  in
234
  let client_impls =
×
235
    [ implement Daemon_rpcs.Send_user_commands.rpc (fun () ts ->
×
236
          Deferred.map
×
237
            ( Mina_commands.setup_and_submit_user_commands mina ts
×
238
            |> Participating_state.to_deferred_or_error )
×
239
            ~f:Or_error.join )
240
    ; implement Daemon_rpcs.Send_zkapp_commands.rpc (fun () zkapps ->
×
241
          Deferred.map
×
242
            ( Mina_commands.setup_and_submit_zkapp_commands mina zkapps
×
243
            |> Participating_state.to_deferred_or_error )
×
244
            ~f:Or_error.join )
245
    ; implement Daemon_rpcs.Get_balance.rpc (fun () aid ->
×
246
          return
×
247
            ( Mina_commands.get_balance mina aid
×
248
            |> Participating_state.active_error ) )
×
249
    ; implement Daemon_rpcs.Get_trust_status.rpc (fun () ip_address ->
×
250
          return (Mina_commands.get_trust_status mina ip_address) )
×
251
    ; implement Daemon_rpcs.Get_trust_status_all.rpc (fun () () ->
×
252
          return (Mina_commands.get_trust_status_all mina) )
×
253
    ; implement Daemon_rpcs.Reset_trust_status.rpc (fun () ip_address ->
×
254
          return (Mina_commands.reset_trust_status mina ip_address) )
×
255
    ; implement Daemon_rpcs.Chain_id_inputs.rpc (fun () () ->
×
256
          return (Mina_commands.chain_id_inputs mina) )
×
257
    ; implement Daemon_rpcs.Verify_proof.rpc (fun () (aid, tx, proof) ->
×
258
          return
×
259
            ( Mina_commands.verify_payment mina aid tx proof
×
260
            |> Participating_state.active_error |> Or_error.join ) )
×
261
    ; implement Daemon_rpcs.Get_public_keys_with_details.rpc (fun () () ->
×
262
          let%map keys = Mina_commands.get_keys_with_details mina in
×
263
          Participating_state.active_error keys )
×
264
    ; implement Daemon_rpcs.Get_public_keys.rpc (fun () () ->
×
265
          let%map keys = Mina_commands.get_public_keys mina in
×
266
          Participating_state.active_error keys )
×
267
    ; implement Daemon_rpcs.Get_nonce.rpc (fun () aid ->
×
268
          return
×
269
            ( Mina_commands.get_nonce mina aid
×
270
            |> Participating_state.active_error ) )
×
271
    ; implement Daemon_rpcs.Get_inferred_nonce.rpc (fun () aid ->
×
272
          return
×
273
            ( Mina_lib.get_inferred_nonce_from_transaction_pool_and_ledger mina
×
274
                aid
275
            |> Participating_state.active_error ) )
×
276
    ; implement_notrace Daemon_rpcs.Get_status.rpc (fun () flag ->
×
277
          Mina_commands.get_status ~flag mina )
×
278
    ; implement Daemon_rpcs.Clear_hist_status.rpc (fun () flag ->
×
279
          Mina_commands.clear_hist_status ~flag mina )
×
280
    ; implement Daemon_rpcs.Get_ledger.rpc (fun () lh ->
×
281
          Mina_lib.get_ledger mina lh )
×
282
    ; implement Daemon_rpcs.Get_snarked_ledger.rpc (fun () lh ->
×
283
          Mina_lib.get_snarked_ledger mina lh )
×
284
    ; implement Daemon_rpcs.Get_staking_ledger.rpc (fun () which ->
×
285
          let ledger_or_error =
×
286
            match which with
287
            | Next ->
×
288
                Option.value_map (Mina_lib.next_epoch_ledger mina)
×
289
                  ~default:
290
                    (Or_error.error_string "next staking ledger not available")
×
291
                  ~f:(function
292
                  | `Finalized ledger ->
×
293
                      Ok ledger
294
                  | `Notfinalized ->
×
295
                      Or_error.error_string
296
                        "next staking ledger is not finalized yet" )
297
            | Current ->
×
298
                Option.value_map
×
299
                  (Mina_lib.staking_ledger mina)
×
300
                  ~default:
301
                    (Or_error.error_string
×
302
                       "current staking ledger not available" )
303
                  ~f:Or_error.return
304
          in
305
          match ledger_or_error with
306
          | Ok ledger -> (
×
307
              match ledger with
308
              | Genesis_epoch_ledger l ->
×
309
                  let%map accts = Mina_ledger.Ledger.to_list l in
×
310
                  Ok accts
×
311
              | Ledger_db db ->
×
312
                  let%map accts = Mina_ledger.Ledger.Db.to_list db in
×
313
                  Ok accts )
×
314
          | Error err ->
×
315
              return (Error err) )
316
    ; implement Daemon_rpcs.Stop_daemon.rpc (fun () () ->
×
317
          Scheduler.yield () >>= (fun () -> exit 0) |> don't_wait_for ;
×
318
          Deferred.unit )
×
319
    ; implement Daemon_rpcs.Snark_job_list.rpc (fun () () ->
×
320
          return (snark_job_list_json mina |> Participating_state.active_error) )
×
321
    ; implement Daemon_rpcs.Snark_pool_list.rpc (fun () () ->
×
322
          return (snark_pool_list mina) )
×
323
    ; implement Daemon_rpcs.Start_tracing.rpc (fun () () ->
×
324
          let open Mina_lib.Config in
×
325
          Mina_tracing.start (Mina_lib.config mina).conf_dir )
×
326
    ; implement Daemon_rpcs.Stop_tracing.rpc (fun () () ->
×
327
          Mina_tracing.stop () ; Deferred.unit )
×
328
    ; implement Daemon_rpcs.Start_internal_tracing.rpc (fun () () ->
×
329
          Internal_tracing.toggle ~commit_id:Mina_version.commit_id ~logger
×
330
            `Enabled )
331
    ; implement Daemon_rpcs.Stop_internal_tracing.rpc (fun () () ->
×
332
          Internal_tracing.toggle ~commit_id:Mina_version.commit_id ~logger
×
333
            `Disabled )
334
    ; implement Daemon_rpcs.Visualization.Frontier.rpc (fun () filename ->
×
335
          return (Mina_lib.visualize_frontier ~filename mina) )
×
336
    ; implement Daemon_rpcs.Visualization.Registered_masks.rpc
×
337
        (fun () filename ->
338
          return (Mina_ledger.Ledger.Debug.visualize ~filename) )
×
339
    ; implement Daemon_rpcs.Add_trustlist.rpc (fun () cidr ->
×
340
          return
×
341
            (let cidr_str = Unix.Cidr.to_string cidr in
342
             if Unix.Cidr.Set.mem !client_trustlist cidr then
×
343
               Or_error.errorf "%s already present in trustlist" cidr_str
×
344
             else (
×
345
               client_trustlist := Unix.Cidr.Set.add !client_trustlist cidr ;
×
346
               Ok () ) ) )
347
    ; implement Daemon_rpcs.Remove_trustlist.rpc (fun () cidr ->
×
348
          return
×
349
            (let cidr_str = Unix.Cidr.to_string cidr in
350
             if not @@ Unix.Cidr.Set.mem !client_trustlist cidr then
×
351
               Or_error.errorf "%s not present in trustlist" cidr_str
×
352
             else (
×
353
               client_trustlist := Unix.Cidr.Set.remove !client_trustlist cidr ;
×
354
               Ok () ) ) )
355
    ; implement Daemon_rpcs.Get_trustlist.rpc (fun () () ->
×
356
          return (Set.to_list !client_trustlist) )
×
357
    ; implement Daemon_rpcs.Get_node_status.rpc (fun () peers ->
×
358
          Mina_networking.get_node_status_from_peers (Mina_lib.net mina) peers )
×
359
    ; implement Daemon_rpcs.Get_object_lifetime_statistics.rpc (fun () () ->
×
360
          return
×
361
            (Yojson.Safe.pretty_to_string @@ Allocation_functor.Table.dump ()) )
×
362
    ; implement Daemon_rpcs.Submit_internal_log.rpc
×
363
        (fun () { timestamp; message; metadata; process } ->
364
          let metadata =
×
365
            List.map metadata ~f:(fun (s, value) ->
366
                (s, Yojson.Safe.from_string value) )
×
367
          in
368
          return @@ Itn_logger.log ~process ~timestamp ~message ~metadata () )
×
369
    ]
370
  in
371
  let log_snark_work_metrics (work : Snark_worker.Work.Result.t) =
372
    Mina_metrics.(Counter.inc_one Snark_work.completed_snark_work_received_rpc) ;
×
373
    One_or_two.iter
374
      (One_or_two.zip_exn work.metrics
×
375
         (Snark_worker.Work.Result.transactions work) )
×
376
      ~f:(fun ((total, tag), transaction_opt) ->
377
        ( match tag with
×
378
        | `Merge ->
×
379
            Perf_histograms.add_span ~name:"snark_worker_merge_time" total ;
380
            Mina_metrics.(
×
381
              Cryptography.Snark_work_histogram.observe
×
382
                Cryptography.snark_work_merge_time_sec (Time.Span.to_sec total))
×
383
        | `Transition -> (
×
384
            (*should be Some in the case of `Transition*)
385
            match Option.value_exn transaction_opt with
386
            | Mina_transaction.Transaction.Command
×
387
                (Mina_base.User_command.Zkapp_command parties) ->
388
                let init =
389
                  match
390
                    (Mina_base.Account_update.of_fee_payer parties.fee_payer)
×
391
                      .authorization
392
                  with
393
                  | Proof _ ->
×
394
                      (1, 1)
395
                  | _ ->
×
396
                      (1, 0)
397
                in
398
                let parties_count, proof_parties_count =
399
                  Mina_base.Zkapp_command.Call_forest.fold
400
                    parties.account_updates ~init
401
                    ~f:(fun (count, proof_parties_count) party ->
402
                      ( count + 1
×
403
                      , if
404
                          Mina_base.Control.(
405
                            Tag.equal Proof
×
406
                              (tag
×
407
                                 (Mina_base.Account_update.Poly.authorization
×
408
                                    party ) ))
409
                        then proof_parties_count + 1
×
UNCOV
410
                        else proof_parties_count ) )
×
411
                in
UNCOV
412
                Mina_metrics.(
×
413
                  Cryptography.(
414
                    Counter.inc snark_work_zkapp_base_time_sec
415
                      (Time.Span.to_sec total) ;
×
416
                    Counter.inc_one snark_work_zkapp_base_submissions ;
×
417
                    Counter.inc zkapp_transaction_length
×
418
                      (Float.of_int parties_count) ;
×
419
                    Counter.inc zkapp_proof_updates
×
420
                      (Float.of_int proof_parties_count)))
×
UNCOV
421
            | _ ->
×
422
                Mina_metrics.(
423
                  Cryptography.(
424
                    Counter.inc_one snark_work_base_submissions ;
425
                    Counter.inc snark_work_base_time_sec
×
UNCOV
426
                      (Time.Span.to_sec total))) ) ) ;
×
427
        Perf_histograms.add_span ~name:"snark_worker_transition_time" total )
428
  in
429
  let snark_worker_impls =
430
    [ implement Snark_worker.Rpcs_versioned.Get_work.Latest.rpc (fun () () ->
×
UNCOV
431
          Deferred.return
×
432
            (let open Option.Let_syntax in
433
            let%bind key =
434
              Option.merge
×
435
                (Mina_lib.snark_worker_key mina)
×
UNCOV
436
                (Mina_lib.snark_coordinator_key mina)
×
437
                ~f:Fn.const
438
            in
439
            let%map work = Mina_lib.request_work mina in
×
UNCOV
440
            let work_wire =
×
441
              { work with
442
                instances =
UNCOV
443
                  One_or_two.map work.instances
×
444
                    ~f:
445
                      (Snark_work_lib.Work.Single.Spec.map
446
                         ~f_witness:
447
                           Transaction_witness.read_all_proofs_from_disk
448
                         ~f_proof:ident )
449
              }
450
            in
UNCOV
451
            [%log trace]
×
452
              ~metadata:
UNCOV
453
                [ ("work_spec", Snark_worker.Work.Spec.to_yojson work_wire) ]
×
454
              "responding to a Get_work request with some new work" ;
UNCOV
455
            Mina_metrics.(Counter.inc_one Snark_work.snark_work_assigned_rpc) ;
×
456
            (work_wire, key)) )
UNCOV
457
    ; implement Snark_worker.Rpcs_versioned.Submit_work.Latest.rpc
×
458
        (fun () (work : Snark_worker.Work.Result.t) ->
UNCOV
459
          [%log trace] "received completed work from a snark worker"
×
460
            ~metadata:
461
              [ ("work_spec", Snark_worker.Work.Spec.to_yojson work.spec) ] ;
×
462
          log_snark_work_metrics work ;
×
463
          Deferred.return @@ Mina_lib.add_work mina work )
×
UNCOV
464
    ; implement Snark_worker.Rpcs_versioned.Failed_to_generate_snark.Latest.rpc
×
465
        (fun
466
          ()
467
          ((error, _work_spec, _prover_public_key) :
468
            Error.t
469
            * Snark_worker.Work.Spec.t
470
            * Signature_lib.Public_key.Compressed.t )
471
        ->
UNCOV
472
          [%str_log error]
×
473
            (Snark_worker.Generating_snark_work_failed
474
               { error = Error_json.error_to_yojson error } ) ;
×
UNCOV
475
          Mina_metrics.(Counter.inc_one Snark_work.snark_work_failed_rpc) ;
×
476
          Deferred.unit )
477
    ]
478
  in
479
  let create_graphql_server_with_auth ~mk_context ?auth_keys ~bind_to_address
480
      ~schema ~server_description ~require_auth port =
481
    if require_auth && Option.is_none auth_keys then
×
UNCOV
482
      failwith
×
483
        "Could not create GraphQL server, authentication is required, but no \
484
         authentication keys were provided" ;
UNCOV
485
    let auth_keys =
×
486
      Option.map auth_keys ~f:(fun s ->
487
          let pk_strs = String.split_on_chars ~on:[ ',' ] s in
×
488
          List.map pk_strs ~f:(fun pk_str ->
×
489
              match Itn_crypto.pubkey_of_base64 pk_str with
×
UNCOV
490
              | Ok pk ->
×
491
                  pk
UNCOV
492
              | Error _ ->
×
493
                  failwithf "Could not decode %s to an Ed25519 public key"
494
                    pk_str () ) )
495
    in
UNCOV
496
    let graphql_callback =
×
497
      Graphql_cohttp_async.make_callback ?auth_keys mk_context schema
498
    in
499
    Cohttp_async.(
×
UNCOV
500
      Server.create_expert
×
501
        ~on_handler_error:
502
          (`Call
503
            (fun _net exn ->
UNCOV
504
              [%log error]
×
505
                "Exception while handling REST server request: $error"
506
                ~metadata:
UNCOV
507
                  [ ("error", `String (Exn.to_string_mach exn))
×
508
                  ; ("context", `String "rest_server")
509
                  ] ) )
UNCOV
510
        (Tcp.Where_to_listen.bind_to bind_to_address (On_port port))
×
511
        (fun ~body _sock req ->
512
          let uri = Cohttp.Request.uri req in
×
513
          let status flag =
×
514
            let%bind status = Mina_commands.get_status ~flag mina in
×
515
            Server.respond_string
×
516
              ( status |> Daemon_rpcs.Types.Status.to_yojson
×
UNCOV
517
              |> Yojson.Safe.pretty_to_string )
×
518
          in
UNCOV
519
          let lift x = `Response x in
×
520
          match Uri.path uri with
UNCOV
521
          | "/" ->
×
522
              let body =
523
                "This page is intentionally left blank. The graphql endpoint \
524
                 can be found at `/graphql`."
525
              in
526
              Server.respond_string ~status:`OK body >>| lift
×
527
          | "/graphql" ->
×
UNCOV
528
              [%log debug] "Received graphql request. Uri: $uri"
×
529
                ~metadata:
UNCOV
530
                  [ ("uri", `String (Uri.to_string uri))
×
531
                  ; ("context", `String "rest_server")
532
                  ] ;
533
              graphql_callback () req body
×
534
          | "/status" ->
×
535
              status `None >>| lift
×
536
          | "/status/performance" ->
×
537
              status `Performance >>| lift
×
538
          | _ ->
×
UNCOV
539
              Server.respond_string ~status:`Not_found "Route not found"
×
540
              >>| lift ))
541
    |> Deferred.map ~f:(fun _ ->
UNCOV
542
           [%log info]
×
543
             !"Created %s at: http://localhost:%i/graphql"
544
             server_description port )
545
  in
546
  let create_graphql_server =
547
    create_graphql_server_with_auth
UNCOV
548
      ~mk_context:(fun ~with_seq_no:_ _req -> mina)
×
549
      ?auth_keys:None
550
  in
551
  Option.iter rest_server_port ~f:(fun rest_server_port ->
552
      O1trace.background_thread "serve_graphql" (fun () ->
×
UNCOV
553
          create_graphql_server
×
554
            ~bind_to_address:
555
              Tcp.Bind_to_address.(
UNCOV
556
                if insecure_rest_server then All_addresses else Localhost)
×
557
            ~schema:Mina_graphql.schema ~server_description:"GraphQL server"
558
            ~require_auth:false rest_server_port ) ) ;
559
  (* Second graphql server with limited queries exposed *)
560
  Option.iter limited_graphql_port ~f:(fun rest_server_port ->
×
561
      O1trace.background_thread "serve_limited_graphql" (fun () ->
×
UNCOV
562
          create_graphql_server
×
563
            ~bind_to_address:
564
              Tcp.Bind_to_address.(
UNCOV
565
                if open_limited_graphql_port then All_addresses else Localhost)
×
566
            ~schema:Mina_graphql.schema_limited
567
            ~server_description:"GraphQL server with limited queries"
568
            ~require_auth:false rest_server_port ) ) ;
UNCOV
569
  if itn_features then
×
570
    (* Third graphql server with ITN-particular queries exposed *)
571
    Option.iter itn_graphql_port ~f:(fun rest_server_port ->
×
572
        O1trace.background_thread "serve_itn_graphql" (fun () ->
×
573
            create_graphql_server_with_auth
×
UNCOV
574
              ~mk_context:(fun ~with_seq_no _req -> (with_seq_no, mina))
×
575
              ?auth_keys
576
              ~bind_to_address:
577
                Tcp.Bind_to_address.(
UNCOV
578
                  if insecure_rest_server then All_addresses else Localhost)
×
579
              ~schema:Mina_graphql.schema_itn
580
              ~server_description:"GraphQL server for ITN queries"
581
              ~require_auth:true rest_server_port ) ) ;
UNCOV
582
  let where_to_listen =
×
583
    Tcp.Where_to_listen.bind_to All_addresses
UNCOV
584
      (On_port (Mina_lib.client_port mina))
×
585
  in
586
  O1trace.background_thread "serve_client_rpcs" (fun () ->
×
587
      Deferred.ignore_m
×
UNCOV
588
        (Tcp.Server.create
×
589
           ~on_handler_error:
590
             (`Call
591
               (fun _net exn ->
UNCOV
592
                 [%log error]
×
593
                   "Exception while handling TCP server request: $error"
594
                   ~metadata:
UNCOV
595
                     [ ("error", `String (Exn.to_string_mach exn))
×
596
                     ; ("context", `String "rpc_tcp_server")
597
                     ] ) )
598
           where_to_listen
599
           (fun address reader writer ->
600
             let address = Socket.Address.Inet.addr address in
×
UNCOV
601
             if
×
602
               not
603
                 (Set.exists !client_trustlist ~f:(fun cidr ->
×
604
                      Unix.Cidr.does_match cidr address ) )
×
605
             then (
×
UNCOV
606
               [%log error]
×
607
                 !"Rejecting client connection from $address, it is not \
608
                   present in the trustlist."
609
                 ~metadata:
610
                   [ ("$address", `String (Unix.Inet_addr.to_string address)) ] ;
×
UNCOV
611
               Deferred.unit )
×
612
             else
UNCOV
613
               Rpc.Connection.server_with_close
×
614
                 ~handshake_timeout:compile_config.rpc_handshake_timeout
615
                 ~heartbeat_config:
UNCOV
616
                   (Rpc.Connection.Heartbeat_config.create
×
617
                      ~timeout:
618
                        (Time_ns.Span.of_sec
×
UNCOV
619
                           (Time.Span.to_sec
×
620
                              compile_config.rpc_heartbeat_timeout ) )
621
                      ~send_every:
622
                        (Time_ns.Span.of_sec
×
UNCOV
623
                           (Time.Span.to_sec
×
624
                              compile_config.rpc_heartbeat_send_every ) )
625
                      () )
626
                 reader writer
627
                 ~implementations:
628
                   (Rpc.Implementations.create_exn
629
                      ~implementations:(client_impls @ snark_worker_impls)
630
                      ~on_unknown_rpc:`Raise )
UNCOV
631
                 ~connection_state:(fun _ -> ())
×
632
                 ~on_handshake_error:
633
                   (`Call
634
                     (fun exn ->
UNCOV
635
                       [%log warn]
×
636
                         "Handshake error while handling RPC server request \
637
                          from $address"
638
                         ~metadata:
UNCOV
639
                           [ ("error", `String (Exn.to_string_mach exn))
×
640
                           ; ("context", `String "rpc_server")
641
                           ; ( "address"
UNCOV
642
                             , `String (Unix.Inet_addr.to_string address) )
×
643
                           ] ;
UNCOV
644
                       Deferred.unit ) ) ) ) )
×
645

646
let coda_crash_message ~log_issue ~action ~error =
UNCOV
647
  let followup =
×
648
    if log_issue then
UNCOV
649
      sprintf
×
650
        !{err| The Mina Protocol developers would like to know why!
651

652
    Please:
653
      Open an issue:
654
        <https://github.com/MinaProtocol/mina/issues/new>
655

656
      Briefly describe what you were doing and %s
657

658
    %!|err}
659
        action
UNCOV
660
    else action
×
661
  in
662
  sprintf !{err|
663

664
  ☠  Mina Daemon %s.
665
  %s
666
%!|err} error followup
667

668
let no_report exn_json status =
UNCOV
669
  sprintf
×
670
    "include the last 20 lines from .mina-config/mina.log and then paste the \
671
     following:\n\
672
     Summary:\n\
673
     %s\n\
674
     Status:\n\
675
     %s\n"
676
    (Yojson.Safe.to_string status)
×
UNCOV
677
    (Yojson.Safe.to_string (summary exn_json))
×
678

679
let handle_crash e ~time_controller ~conf_dir ~child_pids ~top_logger coda_ref =
680
  (* attempt to free up some memory before handling crash *)
681
  (* this circumvents using Child_processes.kill, and instead sends SIGKILL to all children *)
UNCOV
682
  Hashtbl.keys child_pids
×
683
  |> List.iter ~f:(fun pid ->
684
         ignore (Signal.send Signal.kill (`Pid pid) : [ `No_such_process | `Ok ]) ) ;
×
685
  let exn_json = Error_json.error_to_yojson (Error.of_exn ~backtrace:`Get e) in
×
UNCOV
686
  [%log' fatal top_logger]
×
687
    "Unhandled top-level exception: $exn\nGenerating crash report"
688
    ~metadata:[ ("exn", exn_json) ] ;
UNCOV
689
  let%bind status = coda_status !coda_ref in
×
690
  (* TEMP MAKE REPORT TRACE *)
UNCOV
691
  [%log' trace top_logger] "handle_crash: acquired coda status" ;
×
692
  let%map action_string =
693
    match%map
694
      Block_time.Timeout.await
×
UNCOV
695
        ~timeout_duration:(Block_time.Span.of_ms 30_000L)
×
696
        time_controller
697
        ( try
698
            make_report exn_json ~conf_dir coda_ref ~top_logger
×
699
            >>| fun k -> Ok k
×
UNCOV
700
          with exn -> return (Error (Error.of_exn exn)) )
×
701
    with
702
    | `Ok (Ok (Some (report_file, temp_config))) ->
×
703
        ( try ignore (Core.Sys.command (sprintf "rm -rf %s" temp_config) : int)
×
UNCOV
704
          with _ -> () ) ;
×
705
        sprintf "attach the crash report %s" report_file
UNCOV
706
    | `Ok (Ok None) ->
×
707
        (*TODO: tar failed, should we ask people to zip the temp directory themselves?*)
708
        no_report exn_json status
709
    | `Ok (Error e) ->
×
710
        [%log' fatal top_logger] "Exception when generating crash report: $exn"
×
711
          ~metadata:[ ("exn", Error_json.error_to_yojson e) ] ;
×
712
        no_report exn_json status
×
713
    | `Timeout ->
×
714
        [%log' fatal top_logger] "Timed out while generated crash report" ;
×
UNCOV
715
        no_report exn_json status
×
716
  in
UNCOV
717
  let message =
×
718
    coda_crash_message ~error:"crashed" ~action:action_string ~log_issue:true
719
  in
720
  Core.print_string message
721

722
let handle_shutdown ~monitor ~time_controller ~conf_dir ~child_pids ~top_logger
723
    coda_ref =
724
  Monitor.detach_and_iter_errors monitor ~f:(fun exn ->
×
UNCOV
725
      don't_wait_for
×
726
        (let%bind () =
727
           match Monitor.extract_exn exn with
UNCOV
728
           | Mina_networking.No_initial_peers ->
×
729
               let message =
730
                 coda_crash_message
731
                   ~error:"failed to connect to any initial peers"
732
                   ~action:
733
                     "You might be trying to connect to a different network \
734
                      version, or need to troubleshoot your configuration. See \
735
                      https://codaprotocol.com/docs/troubleshooting/ for \
736
                      details."
737
                   ~log_issue:false
738
               in
739
               Core.print_string message ; Deferred.unit
×
UNCOV
740
           | Genesis_ledger_helper.Genesis_state_initialization_error ->
×
741
               let message =
742
                 coda_crash_message
743
                   ~error:"failed to initialize the genesis state"
744
                   ~action:
745
                     "include the last 50 lines from .mina-config/mina.log"
746
                   ~log_issue:true
747
               in
748
               Core.print_string message ; Deferred.unit
×
UNCOV
749
           | Mina_user_error.Mina_user_error { message; where } ->
×
750
               Core.print_string "\nFATAL ERROR" ;
UNCOV
751
               let error =
×
752
                 match where with
UNCOV
753
                 | None ->
×
754
                     "encountered a configuration error"
755
                 | Some where ->
×
UNCOV
756
                     sprintf "encountered a configuration error %s" where
×
757
               in
758
               let message =
759
                 coda_crash_message ~error ~action:("\n" ^ message)
760
                   ~log_issue:false
761
               in
762
               Core.print_string message ; Deferred.unit
×
UNCOV
763
           | Mina_lib.Offline_shutdown ->
×
764
               Core.print_string
765
                 "\n\
766
                  [FATAL] *** Mina daemon has been offline for too long ***\n\
767
                  *** Shutting down ***\n" ;
UNCOV
768
               handle_crash Mina_lib.Offline_shutdown ~time_controller ~conf_dir
×
769
                 ~child_pids ~top_logger coda_ref
UNCOV
770
           | Mina_lib.Bootstrap_stuck_shutdown ->
×
771
               Core.print_string
772
                 "\n\
773
                  [FATAL] *** Mina daemon has been stuck in bootstrap for too \
774
                  long ***\n\
775
                  *** Shutting down ***\n" ;
UNCOV
776
               handle_crash Mina_lib.Bootstrap_stuck_shutdown ~time_controller
×
777
                 ~conf_dir ~child_pids ~top_logger coda_ref
UNCOV
778
           | _exn ->
×
779
               let error = Error.of_exn ~backtrace:`Get exn in
780
               let%bind () =
781
                 Node_error_service.send_report
782
                   ~commit_id:Mina_version.commit_id ~logger:top_logger ~error
783
               in
UNCOV
784
               handle_crash exn ~time_controller ~conf_dir ~child_pids
×
785
                 ~top_logger coda_ref
786
         in
787
         Stdlib.exit 1 ) ) ;
×
UNCOV
788
  Async_unix.Signal.(
×
789
    handle terminating ~f:(fun signal ->
790
        log_shutdown ~conf_dir ~top_logger coda_ref ;
×
UNCOV
791
        let logger =
×
792
          Logger.extend top_logger
793
            [ ("coda_run", `String "Program was killed by signal") ]
794
        in
UNCOV
795
        [%log info]
×
796
          !"Mina process was interrupted by $signal"
UNCOV
797
          ~metadata:[ ("signal", `String (to_string signal)) ] ;
×
798
        (* causes async shutdown and at_exit handlers to run *)
UNCOV
799
        Async.shutdown 130 ))
×
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