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

MinaProtocol / mina / 518

26 Aug 2025 11:55PM UTC coverage: 32.083% (-1.3%) from 33.368%
518

push

buildkite

web-flow
Merge pull request #17672 from MinaProtocol/lyh/sig-kind-archive-processor

Push sig kind out of Archive.Lib.Processor

0 of 1 new or added line in 1 file covered. (0.0%)

1035 existing lines in 27 files now uncovered.

23182 of 72257 relevant lines covered (32.08%)

24747.06 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
(*
2
  Mina_run provides the runtime layer for the Mina daemon, building on 
3
  Mina_lib’s core protocol logic. It manages GraphQL APIs, node status 
4
  reporting, crash/shutdown handling, and configuration setup, acting as the 
5
  control panel connecting Mina_lib to CLI tools and external services. *)
6

49✔
7
open Core
8
open Async
9
module Graphql_cohttp_async =
10
  Graphql_internal.Make (Graphql_async.Schema) (Cohttp_async.Io)
11
    (Cohttp_async.Body)
12

13
let snark_job_list_json t =
14
  let open Participating_state.Let_syntax in
×
UNCOV
15
  let%map sl = Mina_lib.best_staged_ledger t in
×
UNCOV
16
  Staged_ledger.Scan_state.snark_job_list_json (Staged_ledger.scan_state sl)
×
17

18
let snark_pool_list t =
19
  Mina_lib.snark_pool t |> Network_pool.Snark_pool.resource_pool
×
20
  |> Network_pool.Snark_pool.Resource_pool.snark_pool_json
×
21
  |> Yojson.Safe.to_string
22

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

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

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

121
let remove_prev_crash_reports ~conf_dir =
UNCOV
122
  Core.Sys.command (sprintf "rm -rf %s/coda_crash_report*" conf_dir)
×
123

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

139
let coda_status coda_ref =
UNCOV
140
  Option.value_map coda_ref
×
141
    ~default:
UNCOV
142
      (Deferred.return (`String "Shutdown before Mina instance was created"))
×
143
    ~f:(fun t ->
144
      Mina_commands.get_status ~flag:`Performance t
×
145
      >>| Daemon_rpcs.Types.Status.to_yojson )
146

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

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

658
let coda_crash_message ~log_issue ~action ~error =
UNCOV
659
  let followup =
×
660
    if log_issue then
UNCOV
661
      sprintf
×
662
        !{err| The Mina Protocol developers would like to know why!
663

664
    Please:
665
      Open an issue:
666
        <https://github.com/MinaProtocol/mina/issues/new>
667

668
      Briefly describe what you were doing and %s
669

670
    %!|err}
671
        action
UNCOV
672
    else action
×
673
  in
674
  sprintf !{err|
675

676
  ☠  Mina Daemon %s.
677
  %s
678
%!|err} error followup
679

680
let no_report exn_json status =
UNCOV
681
  sprintf
×
682
    "include the last 20 lines from .mina-config/mina.log and then paste the \
683
     following:\n\
684
     Summary:\n\
685
     %s\n\
686
     Status:\n\
687
     %s\n"
688
    (Yojson.Safe.to_string status)
×
UNCOV
689
    (Yojson.Safe.to_string (summary exn_json))
×
690

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

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