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

MinaProtocol / mina / 1265

09 Apr 2026 08:59PM UTC coverage: 32.927% (-28.3%) from 61.266%
1265

push

buildkite

web-flow
Merge pull request #18726 from MinaProtocol/dkijania/fix-connect-to-mesa-dependency

24719 of 75073 relevant lines covered (32.93%)

16068.75 hits per line

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

15.58
/src/lib/testing/integration_test_lib/event_type.ml
1
open Core_kernel
2✔
2
open Mina_base
3

4
(* TODO: abstract stackdriver specific log details *)
5

6
(* TODO: Monad_ext *)
7
let or_error_list_fold ls ~init ~f =
8
  let open Or_error.Let_syntax in
×
9
  List.fold ls ~init:(return init) ~f:(fun acc_or_error el ->
×
10
      let%bind acc = acc_or_error in
11
      f acc el )
×
12

13
let get_metadata (message : Logger.Message.t) key =
14
  match String.Map.find message.metadata key with
×
15
  | Some x ->
×
16
      Or_error.return x
17
  | None ->
×
18
      Or_error.errorf "did not find key \"%s\" in message metadata" key
19

20
let parse id (m : Logger.Message.t) =
21
  Or_error.try_with (fun () ->
×
22
      Structured_log_events.parse_exn id (Map.to_alist m.metadata) )
×
23

24
let bad_parse = Or_error.error_string "bad parse"
2✔
25

26
type 'a parse_event =
27
  | From_error_log of (Logger.Message.t -> 'a Or_error.t)
28
  | From_daemon_log of
29
      Structured_log_events.id * (Logger.Message.t -> 'a Or_error.t)
30
  | From_puppeteer_log of string * (Puppeteer_message.t -> 'a Or_error.t)
31

32
module type Event_type_intf = sig
33
  type t [@@deriving to_yojson]
34

35
  val name : string
36

37
  val parse : t parse_event
38
end
39

40
module Log_error = struct
41
  let name = "Log_error"
42

43
  type t = Logger.Message.t [@@deriving to_yojson]
×
44

45
  let parse_func = Or_error.return
46

47
  let parse = From_error_log parse_func
48
end
49

50
module Node_initialization = struct
51
  let name = "Node_initialization"
52

53
  type t = unit [@@deriving to_yojson]
×
54

55
  let structured_event_id =
56
    Transition_router
57
    .starting_transition_frontier_controller_structured_events_id
58

59
  let parse_func = Fn.const (Or_error.return ())
2✔
60

61
  let parse = From_daemon_log (structured_event_id, parse_func)
62
end
63

64
module Node_offline = struct
65
  let name = "Node_offline"
66

67
  type t = unit [@@deriving to_yojson]
×
68

69
  let puppeteer_event_type = "node_offline"
70

71
  let parse_func = Fn.const (Or_error.return ())
2✔
72

73
  let parse = From_puppeteer_log (puppeteer_event_type, parse_func)
74
end
75

76
module Transition_frontier_diff_application = struct
77
  let name = "Transition_frontier_diff_application"
78

79
  type root_transitioned =
×
80
    { new_root : State_hash.t; garbage : State_hash.t list }
×
81
  [@@deriving to_yojson]
82

83
  type t =
×
84
    { new_node : State_hash.t option
×
85
    ; best_tip_changed : State_hash.t option
×
86
    ; root_transitioned : root_transitioned option
×
87
    }
88
  [@@deriving lens, to_yojson]
89

90
  let empty =
91
    { new_node = None; best_tip_changed = None; root_transitioned = None }
92

93
  let register (lens : (t, 'a option) Lens.t) (result : t) (x : 'a) :
94
      t Or_error.t =
95
    match lens.get result with
×
96
    | Some _ ->
×
97
        Or_error.error_string
98
          "same transition frontier diff type unexpectedly encountered twice \
99
           in single application"
100
    | None ->
×
101
        Ok (lens.set (Some x) result)
×
102

103
  let structured_event_id =
104
    Transition_frontier.applying_diffs_structured_events_id
105

106
  let parse_func message =
107
    let open Json_parsing in
×
108
    let open Or_error.Let_syntax in
109
    let%bind diffs = get_metadata message "diffs" >>= parse (list json) in
×
110
    or_error_list_fold diffs ~init:empty ~f:(fun res diff ->
×
111
        match Yojson.Safe.Util.keys diff with
×
112
        | [ name ] -> (
×
113
            let%bind value = find json diff [ name ] in
×
114
            match name with
×
115
            | "New_node" ->
×
116
                let%bind state_hash = parse state_hash value in
×
117
                register new_node res state_hash
×
118
            | "Best_tip_changed" ->
×
119
                let%bind state_hash = parse state_hash value in
×
120
                register best_tip_changed res state_hash
×
121
            | "Root_transitioned" ->
×
122
                let%bind new_root = find state_hash value [ "new_root" ] in
×
123
                let%bind garbage = find (list state_hash) value [ "garbage" ] in
×
124
                let data = { new_root; garbage } in
×
125
                register root_transitioned res data
126
            | _ ->
×
127
                Or_error.error_string "unexpected transition frontier diff name"
128
            )
129
        | _ ->
×
130
            Or_error.error_string "unexpected transition frontier diff format" )
131

132
  let parse = From_daemon_log (structured_event_id, parse_func)
133
end
134

135
module Block_produced = struct
136
  let name = "Block_produced"
137

138
  type t =
×
139
    { block_height : int
×
140
    ; epoch : int
×
141
    ; global_slot : int
×
142
    ; snarked_ledger_generated : bool
×
143
    ; state_hash : State_hash.t
×
144
    }
145
  [@@deriving to_yojson, equal]
146

147
  (*
148
  let empty =
149
    {block_height= 0; epoch= 0; global_slot= 0; snarked_ledger_generated= false}
150

151
  (* Aggregated values for determining timeout conditions. Note: Slots passed and epochs passed are only determined if we produce a block. Add a log for these events to calculate these independently? *)
152
  type aggregated =
153
    {last_seen_result: t; blocks_generated: int; snarked_ledgers_generated: int}
154
  [@@deriving to_yojson]
155

156
  let empty_aggregated =
157
    {last_seen_result= empty; blocks_generated= 0; snarked_ledgers_generated= 0}
158

159
  let init_aggregated (result : t) =
160
    { last_seen_result= result
161
    ; blocks_generated= 1
162
    ; snarked_ledgers_generated=
163
        (if result.snarked_ledger_generated then 1 else 0) }
164

165
  (*Todo: Reorg will mess up the value of snarked_ledgers_generated*)
166
  let aggregate (aggregated : aggregated) (result : t) : aggregated =
167
    if result.block_height > aggregated.last_seen_result.block_height then
168
      { last_seen_result= result
169
      ; blocks_generated= aggregated.blocks_generated + 1
170
      ; snarked_ledgers_generated=
171
          ( if result.snarked_ledger_generated then
172
            aggregated.snarked_ledgers_generated + 1
173
          else aggregated.snarked_ledgers_generated ) }
174
    else aggregated
175
  *)
176

177
  let structured_event_id = Block_producer.block_produced_structured_events_id
178

179
  let parse_breadcrumb breadcrumb =
180
    let open Json_parsing in
×
181
    let open Or_error.Let_syntax in
182
    let%bind state_hash_str =
183
      find string breadcrumb [ "validated_transition"; "hash"; "state_hash" ]
×
184
    in
185
    let%bind state_hash =
186
      State_hash.of_yojson (`String state_hash_str)
×
187
      |> fun res ->
188
      match res with
×
189
      | Ok hash ->
×
190
          Or_error.return hash
191
      | Error str ->
×
192
          Or_error.error_string str
×
193
    in
194
    let%bind snarked_ledger_generated =
195
      find bool breadcrumb [ "just_emitted_a_proof" ]
×
196
    in
197
    let%bind breadcrumb_consensus_state =
198
      find json breadcrumb
×
199
        [ "validated_transition"
200
        ; "data"
201
        ; "protocol_state"
202
        ; "body"
203
        ; "consensus_state"
204
        ]
205
    in
206
    let%bind block_height =
207
      find int breadcrumb_consensus_state [ "blockchain_length" ]
×
208
    in
209
    let%bind global_slot =
210
      (* the associated field looks like "slot_number":1 *)
211
      let%map global_slot_since_hard_fork =
212
        find string breadcrumb_consensus_state
×
213
          [ "curr_global_slot_since_hard_fork"; "slot_number" ]
214
      in
215
      Int.of_string global_slot_since_hard_fork
×
216
    in
217
    let%map epoch = find int breadcrumb_consensus_state [ "epoch_count" ] in
×
218
    { block_height; global_slot; epoch; snarked_ledger_generated; state_hash }
×
219

220
  let parse_func message =
221
    let open Or_error.Let_syntax in
×
222
    let%bind breadcrumb = get_metadata message "breadcrumb" in
×
223
    parse_breadcrumb breadcrumb
×
224

225
  let parse = From_daemon_log (structured_event_id, parse_func)
226
end
227

228
module Breadcrumb_added = struct
229
  let name = "Breadcrumb_added"
230

231
  type t =
×
232
    { state_hash : State_hash.t
×
233
    ; transaction_hashes :
234
        Mina_transaction.Transaction_hash.t With_status.t list
×
235
    }
236
  [@@deriving to_yojson]
×
237

238
  let structured_event_id =
239
    Transition_frontier.added_breadcrumb_user_commands_structured_events_id
240

241
  let parse_func message =
242
    let open Json_parsing in
×
243
    let open Or_error.Let_syntax in
244
    let%bind state_hash_json = get_metadata message "state_hash" in
×
245
    let state_hash =
×
246
      parser_from_of_yojson State_hash.of_yojson state_hash_json
247
    in
248
    let%map transaction_hashes =
249
      get_metadata message "user_commands"
×
250
      >>= parse transaction_hashes_with_statuses
×
251
    in
252
    { state_hash; transaction_hashes }
×
253

254
  let parse = From_daemon_log (structured_event_id, parse_func)
255
end
256

257
module Transition_frontier_loaded_from_persistence = struct
258
  type t = unit [@@deriving to_yojson]
×
259

260
  let name = "Transition_frontier_loaded_from_persistence"
261

262
  let structured_event_id =
263
    Transition_frontier
264
    .transition_frontier_loaded_from_persistence_structured_events_id
265

266
  let parse_func = Fn.const (Or_error.return ())
2✔
267

268
  let parse = From_daemon_log (structured_event_id, parse_func)
269
end
270

271
module Persisted_frontier_loaded = struct
272
  type t = unit [@@deriving to_yojson]
×
273

274
  let name = "Persisted_frontier_loaded"
275

276
  let structured_event_id =
277
    Transition_frontier.persisted_frontier_loaded_structured_events_id
278

279
  let parse_func = Fn.const (Or_error.return ())
2✔
280

281
  let parse = From_daemon_log (structured_event_id, parse_func)
282
end
283

284
module Persisted_frontier_fresh_boot = struct
285
  type t = unit [@@deriving to_yojson]
×
286

287
  let name = "persistent frontier database does not exist"
288

289
  let structured_event_id =
290
    Transition_frontier.persisted_frontier_fresh_boot_structured_events_id
291

292
  let parse_func = Fn.const (Or_error.return ())
2✔
293

294
  let parse = From_daemon_log (structured_event_id, parse_func)
295
end
296

297
module Bootstrap_required = struct
298
  type t = unit [@@deriving to_yojson]
×
299

300
  let name = "Bootstrap required"
301

302
  let structured_event_id =
303
    Transition_frontier.bootstrap_required_structured_events_id
304

305
  let parse_func = Fn.const (Or_error.return ())
2✔
306

307
  let parse = From_daemon_log (structured_event_id, parse_func)
308
end
309

310
module Persisted_frontier_dropped = struct
311
  type t = unit [@@deriving to_yojson]
×
312

313
  let name = "Persistent frontier dropped"
314

315
  let structured_event_id =
316
    Transition_frontier.persisted_frontier_dropped_structured_events_id
317

318
  let parse_func = Fn.const (Or_error.return ())
2✔
319

320
  let parse = From_daemon_log (structured_event_id, parse_func)
321
end
322

323
module Gossip = struct
324
  open Or_error.Let_syntax
325

326
  module Direction = struct
327
    type t = Sent | Received [@@deriving yojson]
×
328
  end
329

330
  module With_direction = struct
331
    type 'a t = 'a * Direction.t [@@deriving yojson]
×
332
  end
333

334
  module Block = struct
335
    type r = { state_hash : State_hash.t } [@@deriving yojson, hash]
×
336

337
    type t = r With_direction.t [@@deriving yojson]
×
338

339
    let name = "Block_gossip"
340

341
    let id = Transition_handler.Block_sink.block_received_structured_events_id
342

343
    let parse_func message =
344
      match%bind parse id message with
×
345
      | Transition_handler.Block_sink.Block_received { state_hash; sender = _ }
×
346
        ->
347
          Ok ({ state_hash }, Direction.Received)
348
      | Mina_networking.Gossip_new_state { state_hash } ->
×
349
          Ok ({ state_hash }, Sent)
350
      | _ ->
×
351
          bad_parse
352

353
    let parse = From_daemon_log (id, parse_func)
354
  end
355

356
  module Snark_work = struct
357
    type r = { work : Network_pool.Snark_pool.Resource_pool.Diff.compact }
×
358
    [@@deriving yojson, hash]
359

360
    type t = r With_direction.t [@@deriving yojson]
×
361

362
    let name = "Snark_work_gossip"
363

364
    let id =
365
      Network_pool.Snark_pool.Resource_pool.Diff
366
      .snark_work_received_structured_events_id
367

368
    let parse_func message =
369
      match%bind parse id message with
×
370
      | Network_pool.Snark_pool.Resource_pool.Diff.Snark_work_received
×
371
          { work; sender = _ } ->
372
          Ok ({ work }, Direction.Received)
373
      | Mina_networking.Gossip_snark_pool_diff { work } ->
×
374
          Ok ({ work }, Direction.Sent)
375
      | _ ->
×
376
          bad_parse
377

378
    let parse = From_daemon_log (id, parse_func)
379
  end
380

381
  module Transactions = struct
382
    type r = { fee_payer_summaries : User_command.fee_payer_summary_t list }
×
383
    [@@deriving yojson, hash]
×
384

385
    type t = r With_direction.t [@@deriving yojson]
×
386

387
    let name = "Transactions_gossip"
388

389
    let id =
390
      Network_pool.Transaction_pool.Resource_pool.Diff
391
      .transactions_received_structured_events_id
392

393
    let parse_func message =
394
      match%bind parse id message with
×
395
      | Network_pool.Transaction_pool.Resource_pool.Diff.Transactions_received
×
396
          { fee_payer_summaries; sender = _ } ->
397
          Ok ({ fee_payer_summaries }, Direction.Received)
398
      | Mina_networking.Gossip_transaction_pool_diff { fee_payer_summaries } ->
×
399
          Ok ({ fee_payer_summaries }, Sent)
400
      | _ ->
×
401
          bad_parse
402

403
    let parse = From_daemon_log (id, parse_func)
404
  end
405
end
406

407
module Snark_work_failed = struct
408
  type t = { error : Yojson.Safe.t } [@@deriving yojson]
×
409

410
  let name = "Snark_work_failed"
411

412
  let id = Snark_worker.Events.generating_snark_work_failed_structured_events_id
413

414
  let parse_func message =
415
    let open Or_error.Let_syntax in
×
416
    match%bind parse id message with
×
417
    | Snark_worker.Events.Generating_snark_work_failed { error } ->
×
418
        Ok { error }
419
    | _ ->
×
420
        bad_parse
421

422
  let parse = From_daemon_log (id, parse_func)
423
end
424

425
type 'a t =
426
  | Log_error : Log_error.t t
427
  | Node_initialization : Node_initialization.t t
428
  | Node_offline : Node_offline.t t
429
  | Transition_frontier_diff_application
430
      : Transition_frontier_diff_application.t t
431
  | Block_produced : Block_produced.t t
432
  | Breadcrumb_added : Breadcrumb_added.t t
433
  | Block_gossip : Gossip.Block.t t
434
  | Snark_work_gossip : Gossip.Snark_work.t t
435
  | Transactions_gossip : Gossip.Transactions.t t
436
  | Snark_work_failed : Snark_work_failed.t t
437
  | Transition_frontier_loaded_from_persistence
438
      : Transition_frontier_loaded_from_persistence.t t
439
  | Persisted_frontier_loaded : Persisted_frontier_loaded.t t
440
  | Persisted_frontier_fresh_boot : Persisted_frontier_fresh_boot.t t
441
  | Persisted_frontier_dropped : Persisted_frontier_dropped.t t
442
  | Bootstrap_required : Bootstrap_required.t t
443

444
type existential = Event_type : 'a t -> existential
445

446
let existential_to_string = function
447
  | Event_type Log_error ->
×
448
      "Log_error"
449
  | Event_type Node_initialization ->
×
450
      "Node_initialization"
451
  | Event_type Node_offline ->
×
452
      "Node_offline"
453
  | Event_type Transition_frontier_diff_application ->
×
454
      "Transition_frontier_diff_application"
455
  | Event_type Block_produced ->
×
456
      "Block_produced"
457
  | Event_type Breadcrumb_added ->
×
458
      "Breadcrumb_added"
459
  | Event_type Block_gossip ->
×
460
      "Block_gossip"
461
  | Event_type Snark_work_gossip ->
×
462
      "Snark_work_gossip"
463
  | Event_type Transactions_gossip ->
×
464
      "Transactions_gossip"
465
  | Event_type Snark_work_failed ->
×
466
      "Snark_work_failed"
467
  | Event_type Transition_frontier_loaded_from_persistence ->
×
468
      "Transition_frontier_loaded_from_persistence"
469
  | Event_type Persisted_frontier_loaded ->
×
470
      "Persisted_frontier_loaded"
471
  | Event_type Persisted_frontier_fresh_boot ->
×
472
      "Persisted_frontier_fresh_boot"
473
  | Event_type Persisted_frontier_dropped ->
×
474
      "Persisted_frontier_dropped"
475
  | Event_type Bootstrap_required ->
×
476
      "Bootstrap_requied"
477

478
let to_string e = existential_to_string (Event_type e)
×
479

480
let existential_of_string_exn = function
481
  | "Log_error" ->
×
482
      Event_type Log_error
483
  | "Node_initialization" ->
×
484
      Event_type Node_initialization
485
  | "Node_offline" ->
×
486
      Event_type Node_offline
487
  | "Transition_frontier_diff_application" ->
×
488
      Event_type Transition_frontier_diff_application
489
  | "Block_produced" ->
×
490
      Event_type Block_produced
491
  | "Breadcrumb_added" ->
×
492
      Event_type Breadcrumb_added
493
  | "Block_gossip" ->
×
494
      Event_type Block_gossip
495
  | "Snark_work_gossip" ->
×
496
      Event_type Snark_work_gossip
497
  | "Transactions_gossip" ->
×
498
      Event_type Transactions_gossip
499
  | "Snark_work_failed" ->
×
500
      Event_type Snark_work_failed
501
  | "Persisted_frontier_loaded" ->
×
502
      Event_type Persisted_frontier_loaded
503
  | "Transition_frontier_loaded_from_persistence" ->
×
504
      Event_type Transition_frontier_loaded_from_persistence
505
  | "Persisted_frontier_fresh_boot" ->
×
506
      Event_type Persisted_frontier_fresh_boot
507
  | "Persisted_frontier_dropped" ->
×
508
      Event_type Persisted_frontier_dropped
509
  | "Bootstrap_requied" ->
×
510
      Event_type Bootstrap_required
511
  | _ ->
×
512
      failwith "invalid event type string"
513

514
let existential_to_yojson t = `String (existential_to_string t)
×
515

516
let existential_of_sexp = function
517
  | Sexp.Atom string ->
×
518
      existential_of_string_exn string
519
  | _ ->
×
520
      failwith "invalid sexp"
521

522
let sexp_of_existential t = Sexp.Atom (existential_to_string t)
×
523

524
module Existentially_comparable = Comparable.Make (struct
525
  type t = existential [@@deriving sexp]
×
526

527
  (* We can't derive a comparison for the GADTs in ['a t], so fall back to
528
     polymorphic comparison. This should be safe to use here as the variants in
529
     ['a t] are shallow.
530
  *)
531
  let compare = Poly.compare
532
end)
533

534
module Map = Existentially_comparable.Map
535

536
type event = Event : 'a t * 'a -> event
537

538
let type_of_event (Event (t, _)) = Event_type t
×
539

540
(* needs to contain each type in event_types *)
541
let all_event_types =
542
  [ Event_type Log_error
543
  ; Event_type Node_initialization
544
  ; Event_type Node_offline
545
  ; Event_type Transition_frontier_diff_application
546
  ; Event_type Block_produced
547
  ; Event_type Breadcrumb_added
548
  ; Event_type Block_gossip
549
  ; Event_type Snark_work_gossip
550
  ; Event_type Transactions_gossip
551
  ; Event_type Snark_work_failed
552
  ; Event_type Transition_frontier_loaded_from_persistence
553
  ; Event_type Persisted_frontier_loaded
554
  ; Event_type Persisted_frontier_fresh_boot
555
  ; Event_type Persisted_frontier_dropped
556
  ; Event_type Bootstrap_required
557
  ]
558

559
let event_type_module : type a. a t -> (module Event_type_intf with type t = a)
560
    = function
561
  | Log_error ->
22✔
562
      (module Log_error)
563
  | Node_initialization ->
22✔
564
      (module Node_initialization)
565
  | Node_offline ->
22✔
566
      (module Node_offline)
567
  | Transition_frontier_diff_application ->
22✔
568
      (module Transition_frontier_diff_application)
569
  | Block_produced ->
22✔
570
      (module Block_produced)
571
  | Breadcrumb_added ->
22✔
572
      (module Breadcrumb_added)
573
  | Block_gossip ->
22✔
574
      (module Gossip.Block)
575
  | Snark_work_gossip ->
22✔
576
      (module Gossip.Snark_work)
577
  | Transactions_gossip ->
22✔
578
      (module Gossip.Transactions)
579
  | Snark_work_failed ->
22✔
580
      (module Snark_work_failed)
581
  | Transition_frontier_loaded_from_persistence ->
22✔
582
      (module Transition_frontier_loaded_from_persistence)
583
  | Persisted_frontier_loaded ->
22✔
584
      (module Persisted_frontier_loaded)
585
  | Persisted_frontier_fresh_boot ->
22✔
586
      (module Persisted_frontier_fresh_boot)
587
  | Persisted_frontier_dropped ->
22✔
588
      (module Persisted_frontier_dropped)
589
  | Bootstrap_required ->
22✔
590
      (module Bootstrap_required)
591

592
let event_to_yojson event =
593
  let (Event (t, d)) = event in
×
594
  let (module Type) = event_type_module t in
595
  `Assoc [ (to_string t, Type.to_yojson d) ]
×
596

597
let to_structured_event_id event_type =
598
  let (Event_type t) = event_type in
30✔
599
  let (module Ty) = event_type_module t in
600
  match Ty.parse with From_daemon_log (id, _) -> Some id | _ -> None
4✔
601

602
let structured_events_table =
603
  let open Option.Let_syntax in
604
  all_event_types
605
  |> List.filter_map ~f:(fun t ->
2✔
606
         let%map event_id = to_structured_event_id t in
30✔
607
         (Structured_log_events.string_of_id event_id, t) )
26✔
608
  |> String.Table.of_alist_exn
2✔
609

610
let of_structured_event_id id =
611
  Structured_log_events.string_of_id id
×
612
  |> String.Table.find structured_events_table
613

614
let to_puppeteer_event_string event_type =
615
  let (Event_type t) = event_type in
30✔
616
  let (module Ty) = event_type_module t in
617
  match Ty.parse with From_puppeteer_log (id, _) -> Some id | _ -> None
2✔
618

619
let puppeteer_events_table =
620
  let open Option.Let_syntax in
621
  all_event_types
622
  |> List.filter_map ~f:(fun t ->
2✔
623
         let%map event_id = to_puppeteer_event_string t in
30✔
624
         (event_id, t) )
2✔
625
  |> String.Table.of_alist_exn
2✔
626

627
let of_puppeteer_event_string id = String.Table.find puppeteer_events_table id
×
628

629
let parse_error_log (message : Logger.Message.t) =
630
  let open Or_error.Let_syntax in
×
631
  match Log_error.parse with
632
  | From_error_log fnc ->
×
633
      let%map data = fnc message in
×
634
      Event (Log_error, data)
×
635
  | _ ->
×
636
      failwith
637
        "Log_error.parse should always be a `From_error_log variant, but was \
638
         mis-programmed as something else. this is a programmer error"
639

640
let parse_daemon_event (message : Logger.Message.t) =
641
  let open Or_error.Let_syntax in
×
642
  match message.event_id with
643
  | Some event_id -> (
×
644
      let (Event_type ev_type) =
645
        of_structured_event_id event_id
×
646
        |> Option.value ~default:(Event_type Log_error)
647
      in
648
      let (module Ty) = event_type_module ev_type in
×
649
      match Ty.parse with
×
650
      | From_error_log fnc | From_daemon_log (_, fnc) ->
×
651
          let%map data = fnc message in
×
652
          Event (ev_type, data)
×
653
      | _ ->
×
654
          failwith
655
            "the 'parse' field of a daemon event should always be a \
656
             `From_daemon_log variant, but was mis-programmed as something \
657
             else. this is a programmer error" )
658
  | None ->
×
659
      (* TODO: check log level to ensure it matches error log level *)
660
      parse_error_log message
661

662
let parse_puppeteer_event (message : Puppeteer_message.t) =
663
  let open Or_error.Let_syntax in
×
664
  match
665
    Option.bind message.puppeteer_event_type ~f:of_puppeteer_event_string
666
  with
667
  | Some exis -> (
×
668
      let (Event_type ev_type) = exis in
669
      let (module Ty) = event_type_module ev_type in
670
      match Ty.parse with
×
671
      | From_puppeteer_log (_, fnc) ->
×
672
          let%map data = fnc message in
×
673
          Event (ev_type, data)
×
674
      | _ ->
×
675
          failwith
676
            "the 'parse' field of a puppeteer event should always be a \
677
             `From_puppeteer_log variant, but was mis-programmed as something \
678
             else. this is a programmer error" )
679
  | None ->
×
680
      failwith
681
        (Printf.sprintf
×
682
           "the events emitting from the puppeteer script are either not \
683
            formatted correctly, or are trying to emit an event_type which is \
684
            not actually recognized by the integration test framework.  this \
685
            should not happen and is a programmer error" )
686

687
let dispatch_exn : type a b c. a t -> a -> b t -> (b -> c) -> c =
688
 fun t1 e t2 h ->
689
  match (t1, t2) with
×
690
  | Log_error, Log_error ->
×
691
      h e
692
  | Node_initialization, Node_initialization ->
×
693
      h e
694
  | Node_offline, Node_offline ->
×
695
      h e
696
  | Transition_frontier_diff_application, Transition_frontier_diff_application
×
697
    ->
698
      h e
699
  | Block_produced, Block_produced ->
×
700
      h e
701
  | Breadcrumb_added, Breadcrumb_added ->
×
702
      h e
703
  | Block_gossip, Block_gossip ->
×
704
      h e
705
  | Snark_work_gossip, Snark_work_gossip ->
×
706
      h e
707
  | Transactions_gossip, Transactions_gossip ->
×
708
      h e
709
  | Snark_work_failed, Snark_work_failed ->
×
710
      h e
711
  | ( Transition_frontier_loaded_from_persistence
×
712
    , Transition_frontier_loaded_from_persistence ) ->
713
      h e
714
  | Persisted_frontier_loaded, Persisted_frontier_loaded ->
×
715
      h e
716
  | Persisted_frontier_fresh_boot, Persisted_frontier_fresh_boot ->
×
717
      h e
718
  | Persisted_frontier_dropped, Persisted_frontier_dropped ->
×
719
      h e
720
  | Bootstrap_required, Bootstrap_required ->
×
721
      h e
722
  | _ ->
×
723
      failwithf "Mismatched event types: %s, %s" (to_string t1) (to_string t2)
×
724
        ()
725

726
(* TODO: tests on sexp and dispatch (etc) against all_event_types *)
727

728
let%test_unit "parse breadcrumb functions properly" =
729
  let breadcrumb_json =
×
730
    {json|
731
        {
732
          "transition_receipt_time": "2023-05-10T17:27:21.051560Z",
733
          "just_emitted_a_proof": false,
734
          "validated_transition": {
735
            "hash": {
736
              "state_body_hash": "3WuibKRQv4TmqEj48a39QehVueRp8fCZ1Ta4CHfCLdVGG1y2HvDy",
737
              "state_hash": "3NLBdEiVExFLZsTHJXqNwUFtG1nwTWN7Kd4mNCNjGKcFy2QeWjFL"
738
            },
739
            "data": {
740
              "delta_transition_chain_proof": "<opaque>",
741
              "protocol_state_proof": "<opaque>",
742
              "proposed_protocol_version": "<None>",
743
              "protocol_state": {
744
                "previous_state_hash": "3NKnRqx6Mp1PibTWL59P1hNCwyc4dhmam4TABP65bZyEc56h7PBF",
745
                "body": {
746
                  "genesis_state_hash": "3NKK9p9ydnSEHrUWz6RSkt9MDytEkie6yaXSVPC55wari7okTwRH",
747
                  "blockchain_state": {
748
                    "genesis_ledger_hash": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S",
749
                    "body_reference": "b94b2580ca80f27c9655289579a0d71df0b7604dfa7c404e6c309cccf7730d2f",
750
                    "ledger_proof_statement": {
751
                      "connecting_ledger_right": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S",
752
                      "sok_digest": null,
753
                      "target": {
754
                        "local_state": {
755
                          "full_transaction_commitment": "0x0000000000000000000000000000000000000000000000000000000000000000",
756
                          "call_stack": "0x0000000000000000000000000000000000000000000000000000000000000000",
757
                          "token_id": "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf",
758
                          "excess": {
759
                            "sgn": [
760
                              "Pos"
761
                            ],
762
                            "magnitude": "0"
763
                          },
764
                          "success": true,
765
                          "stack_frame": "0x0641662E94D68EC970D0AFC059D02729BBF4A2CD88C548CCD9FB1E26E570C66C",
766
                          "will_succeed": true,
767
                          "account_update_index": "0",
768
                          "supply_increase": {
769
                            "sgn": [
770
                              "Pos"
771
                            ],
772
                            "magnitude": "0"
773
                          },
774
                          "ledger": "jw6bz2wud1N6itRUHZ5ypo3267stk4UgzkiuWtAMPRZo9g4Udyd",
775
                          "failure_status_tbl": [],
776
                          "transaction_commitment": "0x0000000000000000000000000000000000000000000000000000000000000000"
777
                        },
778
                        "pending_coinbase_stack": {
779
                          "state": {
780
                            "init": "4Yx5U3t3EYQycZ91yj4478bHkLwGkhDHnPbCY9TxgUk69SQityej",
781
                            "curr": "4Yx5U3t3EYQycZ91yj4478bHkLwGkhDHnPbCY9TxgUk69SQityej"
782
                          },
783
                          "data": "4QNrZFBTDQCPfEZqBZsaPYx8qdaNFv1nebUyCUsQW9QUJqyuD3un"
784
                        },
785
                        "first_pass_ledger": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S",
786
                        "second_pass_ledger": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S"
787
                      },
788
                      "fee_excess": [
789
                        {
790
                          "token": "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf",
791
                          "amount": {
792
                            "sgn": [
793
                              "Pos"
794
                            ],
795
                            "magnitude": "0"
796
                          }
797
                        },
798
                        {
799
                          "amount": {
800
                            "sgn": [
801
                              "Pos"
802
                            ],
803
                            "magnitude": "0"
804
                          },
805
                          "token": "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"
806
                        }
807
                      ],
808
                      "source": {
809
                        "second_pass_ledger": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S",
810
                        "local_state": {
811
                          "account_update_index": "0",
812
                          "supply_increase": {
813
                            "sgn": [
814
                              "Pos"
815
                            ],
816
                            "magnitude": "0"
817
                          },
818
                          "stack_frame": "0x0641662E94D68EC970D0AFC059D02729BBF4A2CD88C548CCD9FB1E26E570C66C",
819
                          "ledger": "jw6bz2wud1N6itRUHZ5ypo3267stk4UgzkiuWtAMPRZo9g4Udyd",
820
                          "transaction_commitment": "0x0000000000000000000000000000000000000000000000000000000000000000",
821
                          "token_id": "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf",
822
                          "excess": {
823
                            "sgn": [
824
                              "Pos"
825
                            ],
826
                            "magnitude": "0"
827
                          },
828
                          "call_stack": "0x0000000000000000000000000000000000000000000000000000000000000000",
829
                          "will_succeed": true,
830
                          "full_transaction_commitment": "0x0000000000000000000000000000000000000000000000000000000000000000",
831
                          "failure_status_tbl": [],
832
                          "success": true
833
                        },
834
                        "first_pass_ledger": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S",
835
                        "pending_coinbase_stack": {
836
                          "data": "4QNrZFBTDQCPfEZqBZsaPYx8qdaNFv1nebUyCUsQW9QUJqyuD3un",
837
                          "state": {
838
                            "curr": "4Yx5U3t3EYQycZ91yj4478bHkLwGkhDHnPbCY9TxgUk69SQityej",
839
                            "init": "4Yx5U3t3EYQycZ91yj4478bHkLwGkhDHnPbCY9TxgUk69SQityej"
840
                          }
841
                        }
842
                      },
843
                      "supply_increase": {
844
                        "sgn": [
845
                          "Pos"
846
                        ],
847
                        "magnitude": "0"
848
                      },
849
                      "connecting_ledger_left": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S"
850
                    },
851
                    "staged_ledger_hash": {
852
                      "pending_coinbase_hash": "2n2RMBVXRRZ6t1vo2nvExPE4TREotVgq4ayBnDtNc56QoxajRDoS",
853
                      "non_snark": {
854
                        "pending_coinbase_aux": "WAQjrtxd2upVJCra8vm3pzeBD9zKq9FXnUfQcJumD9Ff9E81yR",
855
                        "ledger_hash": "jx6bKLsrYbnw6bQMYVBMGoi155GCuncBbvfLk8sp7U8aY5WTYup",
856
                        "aux_hash": "W8tzkbcpPqVSRy1PBPvkZ6dRyWcGBK9UADhs2wUp7GFjqrxRDS"
857
                      }
858
                    },
859
                    "timestamp": "1683739622600"
860
                  },
861
                  "consensus_state": {
862
                    "last_vrf_output": "0uRC1o3WvALejoQPlC3Axpz2jgIJopZr20cr3f0FPgE=",
863
                    "coinbase_receiver": "B62qpkCEM5N5ddVsYNbFtwWV4bsT9AwuUJXoehFhHUbYYvZ6j3fXt93",
864
                    "block_creator": "B62qpkCEM5N5ddVsYNbFtwWV4bsT9AwuUJXoehFhHUbYYvZ6j3fXt93",
865
                    "curr_global_slot_since_hard_fork": {
866
                      "slots_per_epoch": "480",
867
                      "slot_number": "14"
868
                    },
869
                    "staking_epoch_data": {
870
                      "lock_checkpoint": "3NK2tkzqqK5spR2sZ7tujjqPksL45M3UUrcA4WhCkeiPtnugyE2x",
871
                      "epoch_length": "1",
872
                      "start_checkpoint": "3NK2tkzqqK5spR2sZ7tujjqPksL45M3UUrcA4WhCkeiPtnugyE2x",
873
                      "ledger": {
874
                        "total_currency": "730300000001000",
875
                        "hash": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S"
876
                      },
877
                      "seed": "2va9BGv9JrLTtrzZttiEMDYw1Zj6a6EHzXjmP9evHDTG3oEquURA"
878
                    },
879
                    "next_epoch_data": {
880
                      "epoch_length": "9",
881
                      "start_checkpoint": "3NK2tkzqqK5spR2sZ7tujjqPksL45M3UUrcA4WhCkeiPtnugyE2x",
882
                      "ledger": {
883
                        "total_currency": "730300000001000",
884
                        "hash": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S"
885
                      },
886
                      "seed": "2vaXWiu9UcJ2wfF324copSsztwG1byEEGDCTVuNNmHcGzdDoSR5f",
887
                      "lock_checkpoint": "3NKnRqx6Mp1PibTWL59P1hNCwyc4dhmam4TABP65bZyEc56h7PBF"
888
                    },
889
                    "supercharge_coinbase": true,
890
                    "block_stake_winner": "B62qpkCEM5N5ddVsYNbFtwWV4bsT9AwuUJXoehFhHUbYYvZ6j3fXt93",
891
                    "sub_window_densities": [
892
                      "1",
893
                      "0",
894
                      "0",
895
                      "2",
896
                      "2",
897
                      "2",
898
                      "0",
899
                      "1",
900
                      "2",
901
                      "2",
902
                      "2"
903
                    ],
904
                    "total_currency": "730300000001000",
905
                    "global_slot_since_genesis": "14",
906
                    "epoch_count": "0",
907
                    "min_window_density": "22",
908
                    "has_ancestor_in_same_checkpoint_window": true,
909
                    "blockchain_length": "8"
910
                  },
911
                  "constants": {
912
                    "slots_per_sub_window": "2",
913
                    "genesis_state_timestamp": "1683737942600",
914
                    "delta": "0",
915
                    "slots_per_epoch": "480",
916
                    "k": "20"
917
                  }
918
                },
919
                "staged_ledger_diff": "<opaque>",
920
                "current_protocol_version": "3.0.0"
921
              }
922
            },
923
            "staged_ledger": "<opaque>"
924
          }
925

926
        }
927
  |json}
928
  in
929
  match
930
    let breadcrumb = Yojson.Safe.from_string breadcrumb_json in
931
    Block_produced.parse_breadcrumb breadcrumb
×
932
  with
933
  | Ok result ->
×
934
      let expected =
935
        Block_produced.
936
          { block_height = 8
937
          ; epoch = 0
938
          ; global_slot = 14
939
          ; snarked_ledger_generated = false
940
          ; state_hash =
941
              State_hash.of_base58_check_exn
×
942
                "3NLBdEiVExFLZsTHJXqNwUFtG1nwTWN7Kd4mNCNjGKcFy2QeWjFL"
943
          }
944
      in
945
      assert (Block_produced.equal result expected)
×
946
  | Error e ->
×
947
      failwith (Error.to_string_hum e)
×
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