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

MinaProtocol / mina / 236

21 May 2025 10:28PM UTC coverage: 63.682% (+26.9%) from 36.813%
236

push

buildkite

web-flow
Merge pull request #17252 from MinaProtocol/georgeee/merge-compatible-to-develop-2025-05-21

Merge compatible to develop (21 May 2025)

173 of 380 new or added lines in 30 files covered. (45.53%)

1311 existing lines in 29 files now uncovered.

48603 of 76321 relevant lines covered (63.68%)

509672.48 hits per line

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

82.84
/src/lib/bootstrap_controller/bootstrap_controller.ml
1
(* Only show stdout for failed inline tests. *)
12✔
2
open Core
3
open Async
4
open Mina_base
5
module Ledger = Mina_ledger.Ledger
6
module Sync_ledger = Mina_ledger.Sync_ledger
7
open Mina_state
8
open Pipe_lib.Strict_pipe
9
open Network_peer
10
module Transition_cache = Transition_cache
11

12
module type CONTEXT = sig
13
  val logger : Logger.t
14

15
  val precomputed_values : Precomputed_values.t
16

17
  val constraint_constants : Genesis_constants.Constraint_constants.t
18

19
  val consensus_constants : Consensus.Constants.t
20

21
  val ledger_sync_config : Syncable_ledger.daemon_config
22

23
  val proof_cache_db : Proof_cache_tag.cache_db
24
end
25

26
type Structured_log_events.t += Bootstrap_complete
27
  [@@deriving register_event { msg = "Bootstrap state: complete." }]
15✔
28

29
type t =
30
  { context : (module CONTEXT)
31
  ; trust_system : Trust_system.t
32
  ; verifier : Verifier.t
33
  ; mutable best_seen_transition : Mina_block.initial_valid_block
34
  ; mutable current_root : Mina_block.initial_valid_block
35
  ; network : Mina_networking.t
36
  ; mutable num_of_root_snarked_ledger_retargeted : int
37
  }
38

39
type time = Time.Span.t
40

41
let time_to_yojson span =
42
  `String (Printf.sprintf "%f seconds" (Time.Span.to_sec span))
5✔
43

44
type opt_time = time option
45

46
let opt_time_to_yojson = function
47
  | Some time ->
2✔
48
      time_to_yojson time
49
  | None ->
×
50
      `Null
51

52
(** An auxiliary data structure for collecting various metrics for bootstrap controller. *)
53
type bootstrap_cycle_stats =
1✔
54
  { cycle_result : string
1✔
55
  ; sync_ledger_time : time
1✔
56
  ; staged_ledger_data_download_time : time
1✔
57
  ; staged_ledger_construction_time : opt_time
1✔
58
  ; local_state_sync_required : bool
1✔
59
  ; local_state_sync_time : opt_time
1✔
60
  }
61
[@@deriving to_yojson]
62

63
let time_deferred deferred =
64
  let start_time = Time.now () in
5✔
65
  let%map result = deferred in
66
  let end_time = Time.now () in
5✔
67
  (Time.diff end_time start_time, result)
5✔
68

69
let worth_getting_root ({ context = (module Context); _ } as t) candidate =
70
  let module Consensus_context = struct
102✔
71
    include Context
72

73
    let logger =
74
      Logger.extend logger
102✔
75
        [ ( "selection_context"
76
          , `String "Bootstrap_controller.worth_getting_root" )
77
        ]
78
  end in
79
  Consensus.Hooks.equal_select_status `Take
80
  @@ Consensus.Hooks.select
81
       ~context:(module Consensus_context)
82
       ~existing:
83
         ( t.best_seen_transition |> Mina_block.Validation.block_with_hash
102✔
84
         |> With_hash.map ~f:Mina_block.consensus_state )
102✔
85
       ~candidate
86

87
let received_bad_proof ({ context = (module Context); _ } as t) host e =
88
  let open Context in
×
89
  Trust_system.(
90
    record t.trust_system logger host
91
      Actions.
92
        ( Violated_protocol
93
        , Some
94
            ( "Bad ancestor proof: $error"
95
            , [ ("error", Error_json.error_to_yojson e) ] ) ))
×
96

97
let done_syncing_root root_sync_ledger =
98
  Option.is_some (Sync_ledger.Db.peek_valid_tree root_sync_ledger)
52✔
99

100
let should_sync ~root_sync_ledger t candidate_state =
101
  (not @@ done_syncing_root root_sync_ledger)
51✔
102
  && worth_getting_root t candidate_state
51✔
103

104
(** Update [Synced_ledger]'s target and [best_seen_transition] and [current_root] accordingly. *)
105
let start_sync_job_with_peer ~sender ~root_sync_ledger
106
    ({ context = (module Context); _ } as t) peer_best_tip peer_root =
107
  let open Context in
1✔
108
  let%bind () =
109
    Trust_system.(
110
      record t.trust_system logger sender
1✔
111
        Actions.
112
          ( Fulfilled_request
113
          , Some ("Received verified peer root and best tip", []) ))
114
  in
115
  t.best_seen_transition <- peer_best_tip ;
1✔
116
  t.current_root <- peer_root ;
117
  let blockchain_state =
118
    t.current_root |> Mina_block.Validation.block |> Mina_block.header
1✔
119
    |> Mina_block.Header.protocol_state |> Protocol_state.blockchain_state
1✔
120
  in
121
  let expected_staged_ledger_hash =
1✔
122
    blockchain_state |> Blockchain_state.staged_ledger_hash
123
  in
124
  let snarked_ledger_hash =
1✔
125
    blockchain_state |> Blockchain_state.snarked_ledger_hash
126
  in
127
  return
1✔
128
  @@
129
  match
130
    Sync_ledger.Db.new_goal root_sync_ledger
131
      (Frozen_ledger_hash.to_ledger_hash snarked_ledger_hash)
1✔
132
      ~data:
133
        ( State_hash.With_state_hashes.state_hash
1✔
134
          @@ Mina_block.Validation.block_with_hash t.current_root
1✔
135
        , sender
136
        , expected_staged_ledger_hash )
137
      ~equal:(fun (hash1, _, _) (hash2, _, _) -> State_hash.equal hash1 hash2)
×
138
  with
139
  | `New ->
1✔
140
      t.num_of_root_snarked_ledger_retargeted <-
141
        t.num_of_root_snarked_ledger_retargeted + 1 ;
142
      `Syncing_new_snarked_ledger
143
  | `Update_data ->
×
144
      `Updating_root_transition
145
  | `Repeat ->
×
146
      `Ignored
147

148
let to_consensus_state h =
149
  Mina_block.Header.protocol_state h |> Protocol_state.consensus_state
102✔
150

151
(** For each transition, this function would compare it with the existing one.
152
    If the incoming transition is better, then download the merkle list from
153
    that transition to its root and verify it. If we get a better root than
154
    the existing one, then reset the Sync_ledger's target by calling
155
    [start_sync_job_with_peer] function. *)
156
let on_transition ({ context = (module Context); _ } as t) ~sender
157
    ~root_sync_ledger candidate_header =
158
  let open Context in
51✔
159
  let candidate_consensus_state =
160
    With_hash.map ~f:to_consensus_state candidate_header
161
  in
162
  if not @@ should_sync ~root_sync_ledger t candidate_consensus_state then
51✔
163
    Deferred.return `Ignored
×
164
  else
165
    match%bind
166
      Mina_networking.get_ancestry t.network sender.Peer.peer_id
51✔
167
        (With_hash.map_hash candidate_consensus_state
51✔
168
           ~f:State_hash.State_hashes.state_hash )
169
    with
170
    | Error e ->
50✔
171
        [%log error]
50✔
172
          ~metadata:[ ("error", Error_json.error_to_yojson e) ]
50✔
173
          !"Could not get the proof of the root transition from the network: \
174
            $error" ;
175
        Deferred.return `Ignored
50✔
176
    | Ok peer_root_with_proof -> (
1✔
177
        let pcd =
178
          peer_root_with_proof.data
179
          |> Proof_carrying_data.map
1✔
180
               ~f:(Mina_block.write_all_proofs_to_disk ~proof_cache_db)
181
          |> Proof_carrying_data.map_proof
182
               ~f:
183
                 (Tuple2.map_snd
184
                    ~f:(Mina_block.write_all_proofs_to_disk ~proof_cache_db) )
185
        in
186
        match%bind
187
          Mina_block.verify_on_header
1✔
188
            ~verify:
189
              (Sync_handler.Root.verify
1✔
190
                 ~context:(module Context)
191
                 ~verifier:t.verifier candidate_consensus_state )
192
            pcd
193
        with
194
        | Ok (`Root root, `Best_tip best_tip) ->
1✔
195
            if done_syncing_root root_sync_ledger then return `Ignored
×
196
            else
197
              start_sync_job_with_peer ~sender ~root_sync_ledger t best_tip root
1✔
198
        | Error e ->
×
199
            return (received_bad_proof t sender e |> Fn.const `Ignored) )
×
200

201
(** A helper function that wraps the calls to Sync_ledger and iterate through
202
    incoming transitions, add those to the transition_cache and calls
203
    [on_transition] function. *)
204
let sync_ledger ({ context = (module Context); _ } as t) ~preferred
205
    ~root_sync_ledger ~transition_graph ~sync_ledger_reader =
206
  let open Context in
2✔
207
  let query_reader = Sync_ledger.Db.query_reader root_sync_ledger in
208
  let response_writer = Sync_ledger.Db.answer_writer root_sync_ledger in
2✔
209
  Mina_networking.glue_sync_ledger ~preferred t.network query_reader
2✔
210
    response_writer ;
211
  Reader.iter sync_ledger_reader ~f:(fun (b_or_h, `Valid_cb vc) ->
2✔
212
      let header_with_hash, sender, transition_cache_element =
51✔
213
        match b_or_h with
214
        | `Block b_env ->
51✔
215
            ( Envelope.Incoming.data b_env
51✔
216
              |> Mina_block.Validation.block_with_hash
51✔
217
              |> With_hash.map ~f:Mina_block.header
51✔
218
            , Envelope.Incoming.remote_sender_exn b_env
51✔
219
            , Envelope.Incoming.map ~f:(fun x -> `Block x) b_env )
51✔
220
        | `Header h_env ->
×
221
            ( Envelope.Incoming.data h_env
×
222
              |> Mina_block.Validation.header_with_hash
×
223
            , Envelope.Incoming.remote_sender_exn h_env
×
224
            , Envelope.Incoming.map ~f:(fun x -> `Header x) h_env )
×
225
      in
226
      let previous_state_hash =
227
        With_hash.data header_with_hash
51✔
228
        |> Mina_block.Header.protocol_state
51✔
229
        |> Protocol_state.previous_state_hash
230
      in
231
      Transition_cache.add transition_graph ~parent:previous_state_hash
51✔
232
        (transition_cache_element, vc) ;
233
      (* TODO: Efficiently limiting the number of green threads in #1337 *)
234
      if
51✔
235
        worth_getting_root t
236
          (With_hash.map ~f:to_consensus_state header_with_hash)
51✔
237
      then (
51✔
238
        [%log trace] "Added the transition from sync_ledger_reader into cache"
51✔
239
          ~metadata:
240
            [ ( "state_hash"
241
              , State_hash.to_yojson
51✔
242
                  (State_hash.With_state_hashes.state_hash header_with_hash) )
51✔
243
            ; ( "header"
244
              , Mina_block.Header.to_yojson (With_hash.data header_with_hash) )
51✔
245
            ] ;
246

247
        Deferred.ignore_m
51✔
248
        @@ on_transition t ~sender ~root_sync_ledger header_with_hash )
51✔
249
      else Deferred.unit )
×
250

251
let external_transition_compare ~context:(module Context : CONTEXT) =
252
  let get_consensus_state =
2✔
253
    Fn.compose Protocol_state.consensus_state Mina_block.Header.protocol_state
254
  in
255
  Comparable.lift
2✔
256
    (fun existing candidate ->
257
      (* To prevent the logger to spam a lot of messages, the logger input is set to null *)
258
      if
561✔
259
        State_hash.equal
260
          (State_hash.With_state_hashes.state_hash existing)
561✔
261
          (State_hash.With_state_hashes.state_hash candidate)
561✔
262
      then 0
50✔
263
      else if
511✔
264
        Consensus.Hooks.equal_select_status `Keep
265
        @@ Consensus.Hooks.select ~context:(module Context) ~existing ~candidate
266
      then -1
381✔
267
      else 1 )
130✔
268
    ~f:(With_hash.map ~f:get_consensus_state)
269

270
(** The entry point function for bootstrap controller. When bootstrap finished
271
    it would return a transition frontier with the root breadcrumb and a list
272
    of transitions collected during bootstrap.
273

274
    Bootstrap controller would do the following steps to contrust the
275
    transition frontier:
276
    1. Download the root snarked_ledger.
277
    2. Download the scan state and pending coinbases.
278
    3. Construct the staged ledger from the snarked ledger, scan state and
279
       pending coinbases.
280
    4. Synchronize the consensus local state if necessary.
281
    5. Close the old frontier and reload a new one from disk.
282
 *)
283
let run ~context:(module Context : CONTEXT) ~trust_system ~verifier ~network
284
    ~consensus_local_state ~transition_reader ~preferred_peers ~persistent_root
285
    ~persistent_frontier ~initial_root_transition ~catchup_mode =
286
  let open Context in
1✔
287
  O1trace.thread "bootstrap" (fun () ->
288
      let rec loop previous_cycles =
1✔
289
        let sync_ledger_pipe = "sync ledger pipe" in
1✔
290
        let sync_ledger_reader, sync_ledger_writer =
291
          create ~name:sync_ledger_pipe
292
            (Buffered
293
               ( `Capacity 50
294
               , `Overflow
295
                   (Drop_head
296
                      (fun (b_or_h, `Valid_cb valid_cb) ->
297
                        let hash =
×
298
                          match b_or_h with
299
                          | `Block b_env ->
×
300
                              Envelope.Incoming.data b_env
×
301
                              |> Mina_block.Validation.block_with_hash
×
302
                              |> With_hash.hash
×
303
                          | `Header h_env ->
×
304
                              Envelope.Incoming.data h_env
×
305
                              |> Mina_block.Validation.header_with_hash
×
306
                              |> With_hash.hash
×
307
                        in
308
                        Mina_metrics.(
309
                          Counter.inc_one
×
310
                            Pipe.Drop_on_overflow.bootstrap_sync_ledger) ;
311
                        Mina_block.handle_dropped_transition ?valid_cb hash
312
                          ~pipe_name:sync_ledger_pipe ~logger ) ) ) )
313
        in
314
        don't_wait_for
1✔
315
          (transfer_while_writer_alive transition_reader sync_ledger_writer
1✔
316
             ~f:Fn.id ) ;
317
        let initial_root_transition =
1✔
318
          initial_root_transition |> Mina_block.Validated.remember
1✔
319
          |> Mina_block.Validation.reset_frontier_dependencies_validation
1✔
320
          |> Mina_block.Validation.reset_staged_ledger_diff_validation
321
        in
322
        let t =
1✔
323
          { network
324
          ; context = (module Context)
325
          ; trust_system
326
          ; verifier
327
          ; best_seen_transition = initial_root_transition
328
          ; current_root = initial_root_transition
329
          ; num_of_root_snarked_ledger_retargeted = 0
330
          }
331
        in
332
        let transition_graph = Transition_cache.create () in
333
        let temp_persistent_root_instance =
1✔
334
          Transition_frontier.Persistent_root.create_instance_exn
335
            persistent_root
336
        in
337
        let temp_snarked_ledger =
1✔
338
          Transition_frontier.Persistent_root.Instance.snarked_ledger
339
            temp_persistent_root_instance
340
        in
341
        (* step 1. download snarked_ledger *)
342
        let%bind sync_ledger_time, (hash, sender, expected_staged_ledger_hash) =
343
          time_deferred
1✔
344
            (let root_sync_ledger =
345
               Sync_ledger.Db.create temp_snarked_ledger
346
                 ~context:(module Context)
347
                 ~trust_system
348
             in
349
             don't_wait_for
1✔
350
               (sync_ledger t ~preferred:preferred_peers ~root_sync_ledger
1✔
351
                  ~transition_graph ~sync_ledger_reader ) ;
352
             (* We ignore the resulting ledger returned here since it will always
353
                * be the same as the ledger we started with because we are syncing
354
                * a db ledger. *)
355
             let%map _, data = Sync_ledger.Db.valid_tree root_sync_ledger in
1✔
356
             Sync_ledger.Db.destroy root_sync_ledger ;
1✔
357
             data )
1✔
358
        in
359
        Mina_metrics.(
1✔
360
          Counter.inc Bootstrap.root_snarked_ledger_sync_ms
1✔
361
            Time.Span.(to_ms sync_ledger_time)) ;
1✔
362
        Mina_metrics.(
363
          Gauge.set Bootstrap.num_of_root_snarked_ledger_retargeted
1✔
364
            (Float.of_int t.num_of_root_snarked_ledger_retargeted)) ;
1✔
365
        (* step 2. Download scan state and pending coinbases. *)
366
        let%bind ( staged_ledger_data_download_time
367
                 , staged_ledger_construction_time
368
                 , staged_ledger_aux_result ) =
369
          let%bind ( staged_ledger_data_download_time
370
                   , staged_ledger_data_download_result ) =
371
            time_deferred
1✔
372
              (Mina_networking
373
               .get_staged_ledger_aux_and_pending_coinbases_at_hash t.network
1✔
374
                 sender.peer_id hash )
375
          in
376
          match staged_ledger_data_download_result with
1✔
377
          | Error err ->
×
378
              Deferred.return (staged_ledger_data_download_time, None, Error err)
379
          | Ok
1✔
380
              ( scan_state_uncached
381
              , expected_merkle_root
382
              , pending_coinbases
383
              , protocol_states ) -> (
384
              let%map staged_ledger_construction_result =
385
                O1trace.thread "construct_root_staged_ledger" (fun () ->
1✔
386
                    let open Deferred.Or_error.Let_syntax in
1✔
387
                    let received_staged_ledger_hash =
388
                      Staged_ledger_hash.of_aux_ledger_and_coinbase_hash
389
                        (Staged_ledger.Scan_state.Stable.Latest.hash
1✔
390
                           scan_state_uncached )
391
                        expected_merkle_root pending_coinbases
392
                    in
393
                    [%log debug]
1✔
394
                      ~metadata:
395
                        [ ( "expected_staged_ledger_hash"
396
                          , Staged_ledger_hash.to_yojson
1✔
397
                              expected_staged_ledger_hash )
398
                        ; ( "received_staged_ledger_hash"
399
                          , Staged_ledger_hash.to_yojson
1✔
400
                              received_staged_ledger_hash )
401
                        ]
402
                      "Comparing $expected_staged_ledger_hash to \
403
                       $received_staged_ledger_hash" ;
404
                    let%bind new_root =
405
                      t.current_root
406
                      |> Mina_block.Validation
407
                         .skip_frontier_dependencies_validation
1✔
408
                           `This_block_belongs_to_a_detached_subtree
409
                      |> Mina_block.Validation.validate_staged_ledger_hash
1✔
410
                           (`Staged_ledger_already_materialized
411
                             received_staged_ledger_hash )
412
                      |> Result.map_error ~f:(fun _ ->
1✔
413
                             Error.of_string
×
414
                               "received faulty scan state from peer" )
415
                      |> Deferred.return
1✔
416
                    in
417
                    let protocol_states =
1✔
418
                      List.map protocol_states
419
                        ~f:(With_hash.of_data ~hash_data:Protocol_state.hashes)
420
                    in
421
                    let scan_state =
1✔
422
                      Staged_ledger.Scan_state.write_all_proofs_to_disk
423
                        ~proof_cache_db scan_state_uncached
424
                    in
425
                    let%bind protocol_states =
426
                      Staged_ledger.Scan_state.check_required_protocol_states
1✔
427
                        scan_state ~protocol_states
428
                      |> Deferred.return
1✔
429
                    in
430
                    let protocol_states_map =
1✔
431
                      protocol_states
432
                      |> List.map ~f:(fun ps ->
1✔
433
                             (State_hash.With_state_hashes.state_hash ps, ps) )
4✔
434
                      |> State_hash.Map.of_alist_exn
435
                    in
436
                    let get_state hash =
1✔
437
                      match Map.find protocol_states_map hash with
56✔
438
                      | None ->
×
439
                          let new_state_hash =
440
                            State_hash.With_state_hashes.state_hash
441
                              (fst new_root)
×
442
                          in
443
                          [%log error]
×
444
                            ~metadata:
445
                              [ ("new_root", State_hash.to_yojson new_state_hash)
×
446
                              ; ("state_hash", State_hash.to_yojson hash)
×
447
                              ]
448
                            "Protocol state (for scan state transactions) for \
449
                             $state_hash not found when bootstrapping to the \
450
                             new root $new_root" ;
451
                          Or_error.errorf
×
452
                            !"Protocol state (for scan state transactions) for \
×
453
                              %{sexp:State_hash.t} not found when \
454
                              bootstrapping to the new root \
455
                              %{sexp:State_hash.t}"
456
                            hash new_state_hash
457
                      | Some protocol_state ->
56✔
458
                          Ok (With_hash.data protocol_state)
56✔
459
                    in
460
                    (* step 3. Construct staged ledger from snarked ledger, scan state
461
                       and pending coinbases. *)
462
                    (* Construct the staged ledger before constructing the transition
463
                     * frontier in order to verify the scan state we received.
464
                     * TODO: reorganize the code to avoid doing this twice (#3480) *)
465
                    let open Deferred.Let_syntax in
466
                    let%map staged_ledger_construction_time, construction_result
467
                        =
468
                      time_deferred
1✔
469
                        (let open Deferred.Let_syntax in
470
                        let temp_mask =
471
                          Ledger.of_database temp_snarked_ledger
472
                        in
473
                        let%map result =
474
                          Staged_ledger
475
                          .of_scan_state_pending_coinbases_and_snarked_ledger
476
                            ~logger
477
                            ~snarked_local_state:
478
                              Mina_block.(
479
                                t.current_root |> Validation.block |> header
1✔
480
                                |> Header.protocol_state
1✔
481
                                |> Protocol_state.blockchain_state
1✔
482
                                |> Blockchain_state.snarked_local_state)
1✔
483
                            ~verifier ~constraint_constants ~scan_state
484
                            ~snarked_ledger:temp_mask ~expected_merkle_root
485
                            ~pending_coinbases ~get_state
486
                        in
487
                        ignore
1✔
488
                          ( Ledger.Maskable.unregister_mask_exn ~loc:__LOC__
1✔
489
                              temp_mask
490
                            : Ledger.unattached_mask ) ;
491
                        Result.map result
492
                          ~f:
493
                            (const
1✔
494
                               ( scan_state
495
                               , pending_coinbases
496
                               , new_root
497
                               , protocol_states ) ))
498
                    in
499
                    Ok (staged_ledger_construction_time, construction_result) )
1✔
500
              in
501
              match staged_ledger_construction_result with
1✔
502
              | Error err ->
×
503
                  (staged_ledger_data_download_time, None, Error err)
504
              | Ok (staged_ledger_construction_time, result) ->
1✔
505
                  ( staged_ledger_data_download_time
506
                  , Some staged_ledger_construction_time
507
                  , result ) )
508
        in
509
        Transition_frontier.Persistent_root.Instance.close
1✔
510
          temp_persistent_root_instance ;
511
        match staged_ledger_aux_result with
1✔
512
        | Error e ->
×
513
            let%bind () =
514
              Trust_system.(
515
                record t.trust_system logger sender
×
516
                  Actions.
517
                    ( Outgoing_connection_error
518
                    , Some
519
                        ( "Can't find scan state from the peer or received \
520
                           faulty scan state from the peer."
521
                        , [] ) ))
522
            in
523
            [%log error]
×
524
              ~metadata:
525
                [ ("error", Error_json.error_to_yojson e)
×
526
                ; ("state_hash", State_hash.to_yojson hash)
×
527
                ; ( "expected_staged_ledger_hash"
528
                  , Staged_ledger_hash.to_yojson expected_staged_ledger_hash )
×
529
                ]
530
              "Failed to find scan state for the transition with hash \
531
               $state_hash from the peer or received faulty scan state: \
532
               $error. Retry bootstrap" ;
533
            Writer.close sync_ledger_writer ;
×
534
            let this_cycle =
×
535
              { cycle_result = "failed to download and construct scan state"
536
              ; sync_ledger_time
537
              ; staged_ledger_data_download_time
538
              ; staged_ledger_construction_time
539
              ; local_state_sync_required = false
540
              ; local_state_sync_time = None
541
              }
542
            in
543
            loop (this_cycle :: previous_cycles)
544
        | Ok (scan_state, pending_coinbase, new_root, protocol_states) -> (
1✔
545
            let%bind () =
546
              Trust_system.(
547
                record t.trust_system logger sender
1✔
548
                  Actions.
549
                    ( Fulfilled_request
550
                    , Some ("Received valid scan state from peer", []) ))
551
            in
552
            let best_seen_block_with_hash, _ = t.best_seen_transition in
1✔
553
            let consensus_state =
554
              With_hash.data best_seen_block_with_hash
1✔
555
              |> Mina_block.header |> Mina_block.Header.protocol_state
1✔
556
              |> Protocol_state.consensus_state
557
            in
558
            (* step 4. Synchronize consensus local state if necessary *)
559
            let%bind ( local_state_sync_time
560
                     , (local_state_sync_required, local_state_sync_result) ) =
561
              time_deferred
1✔
562
                ( match
563
                    Consensus.Hooks.required_local_state_sync
564
                      ~constants:precomputed_values.consensus_constants
565
                      ~consensus_state ~local_state:consensus_local_state
566
                  with
567
                | None ->
1✔
568
                    [%log debug]
1✔
569
                      ~metadata:
570
                        [ ( "local_state"
571
                          , Consensus.Data.Local_state.to_yojson
1✔
572
                              consensus_local_state )
573
                        ; ( "consensus_state"
574
                          , Consensus.Data.Consensus_state.Value.to_yojson
1✔
575
                              consensus_state )
576
                        ]
577
                      "Not synchronizing consensus local state" ;
578
                    Deferred.return (false, Or_error.return ())
1✔
579
                | Some sync_jobs ->
×
580
                    [%log info] "Synchronizing consensus local state" ;
×
581
                    let%map result =
582
                      Consensus.Hooks.sync_local_state
×
583
                        ~context:(module Context)
584
                        ~local_state:consensus_local_state ~trust_system
585
                        ~glue_sync_ledger:
586
                          (Mina_networking.glue_sync_ledger t.network)
×
587
                        sync_jobs
588
                    in
589
                    (true, result) )
×
590
            in
591
            match local_state_sync_result with
1✔
592
            | Error e ->
×
593
                [%log error]
×
594
                  ~metadata:[ ("error", Error_json.error_to_yojson e) ]
×
595
                  "Local state sync failed: $error. Retry bootstrap" ;
596
                Writer.close sync_ledger_writer ;
×
597
                let this_cycle =
×
598
                  { cycle_result = "failed to synchronize local state"
599
                  ; sync_ledger_time
600
                  ; staged_ledger_data_download_time
601
                  ; staged_ledger_construction_time
602
                  ; local_state_sync_required
603
                  ; local_state_sync_time = Some local_state_sync_time
604
                  }
605
                in
606
                loop (this_cycle :: previous_cycles)
607
            | Ok () ->
1✔
608
                (* step 5. Close the old frontier and reload a new one from disk. *)
609
                let new_root_data : Transition_frontier.Root_data.Limited.t =
610
                  Transition_frontier.Root_data.Limited.create
611
                    ~transition:(Mina_block.Validated.lift new_root)
1✔
612
                    ~scan_state ~pending_coinbase ~protocol_states
613
                in
614
                let%bind () =
615
                  Transition_frontier.Persistent_frontier.reset_database_exn
1✔
616
                    persistent_frontier ~root_data:new_root_data
617
                    ~genesis_state_hash:
618
                      (State_hash.With_state_hashes.state_hash
1✔
619
                         precomputed_values.protocol_state_with_hashes )
620
                in
621
                (* TODO: lazy load db in persistent root to avoid unnecessary opens like this *)
622
                Transition_frontier.Persistent_root.(
1✔
623
                  with_instance_exn persistent_root ~f:(fun instance ->
1✔
624
                      Instance.set_root_state_hash instance
1✔
625
                      @@ Mina_block.Validated.state_hash
1✔
626
                      @@ Mina_block.Validated.lift new_root )) ;
1✔
627
                let%map new_frontier =
628
                  let fail msg =
629
                    failwith
×
630
                      ( "failed to initialize transition frontier after \
631
                         bootstrapping: " ^ msg )
632
                  in
633
                  Transition_frontier.load
1✔
634
                    ~context:(module Context)
635
                    ~retry_with_fresh_db:false ~verifier ~consensus_local_state
636
                    ~persistent_root ~persistent_frontier ~catchup_mode ()
637
                  >>| function
1✔
638
                  | Ok frontier ->
1✔
639
                      frontier
640
                  | Error (`Failure msg) ->
×
641
                      fail msg
642
                  | Error `Bootstrap_required ->
×
643
                      fail
644
                        "bootstrap still required (indicates logical error in \
645
                         code)"
646
                  | Error `Persistent_frontier_malformed ->
×
647
                      fail "persistent frontier was malformed"
648
                  | Error `Snarked_ledger_mismatch ->
×
649
                      fail
650
                        "this should not happen, because we just reset the \
651
                         snarked_ledger"
652
                in
653
                [%str_log info] Bootstrap_complete ;
1✔
654
                let collected_transitions =
1✔
655
                  Transition_cache.data transition_graph
656
                in
657
                let logger =
1✔
658
                  Logger.extend logger
659
                    [ ( "context"
660
                      , `String "Filter collected transitions in bootstrap" )
661
                    ]
662
                in
663
                let root_consensus_state =
1✔
664
                  Transition_frontier.(
665
                    Breadcrumb.consensus_state_with_hashes (root new_frontier))
1✔
666
                in
667
                let filtered_collected_transitions =
668
                  List.filter collected_transitions
669
                    ~f:(fun (incoming_transition, _) ->
670
                      let transition =
1✔
671
                        Envelope.Incoming.data incoming_transition
1✔
672
                        |> Transition_cache.header_with_hash
673
                      in
674
                      Consensus.Hooks.equal_select_status `Take
1✔
675
                      @@ Consensus.Hooks.select
676
                           ~context:(module Context)
677
                           ~existing:root_consensus_state
678
                           ~candidate:
679
                             (With_hash.map
1✔
680
                                ~f:
681
                                  (Fn.compose Protocol_state.consensus_state
1✔
682
                                     Mina_block.Header.protocol_state )
683
                                transition ) )
684
                in
685
                [%log debug] "Sorting filtered transitions by consensus state"
1✔
686
                  ~metadata:[] ;
687
                let sorted_filtered_collected_transitions =
1✔
688
                  O1trace.sync_thread "sorting_collected_transitions" (fun () ->
689
                      List.sort filtered_collected_transitions
1✔
690
                        ~compare:
691
                          (Comparable.lift
1✔
692
                             ~f:(fun (x, _) ->
693
                               Transition_cache.header_with_hash
×
694
                               @@ Envelope.Incoming.data x )
×
695
                             (external_transition_compare
696
                                ~context:(module Context) ) ) )
697
                in
698
                let this_cycle =
1✔
699
                  { cycle_result = "success"
700
                  ; sync_ledger_time
701
                  ; staged_ledger_data_download_time
702
                  ; staged_ledger_construction_time
703
                  ; local_state_sync_required
704
                  ; local_state_sync_time = Some local_state_sync_time
705
                  }
706
                in
707
                ( this_cycle :: previous_cycles
708
                , (new_frontier, sorted_filtered_collected_transitions) ) )
709
      in
710
      let%map time_elapsed, (cycles, result) = time_deferred (loop []) in
1✔
711
      [%log info] "Bootstrap completed in $time_elapsed: $bootstrap_stats"
1✔
712
        ~metadata:
713
          [ ("time_elapsed", time_to_yojson time_elapsed)
1✔
714
          ; ( "bootstrap_stats"
715
            , `List (List.map ~f:bootstrap_cycle_stats_to_yojson cycles) )
1✔
716
          ] ;
717
      Mina_metrics.(
1✔
718
        Gauge.set Bootstrap.bootstrap_time_ms
1✔
719
          Core.Time.(Span.to_ms @@ time_elapsed)) ;
1✔
720
      result )
721

722
let%test_module "Bootstrap_controller tests" =
723
  ( module struct
724
    open Pipe_lib
725

726
    let max_frontier_length =
727
      Transition_frontier.global_max_length Genesis_constants.For_unit_tests.t
1✔
728

729
    let logger = Logger.null ()
1✔
730

731
    let () =
732
      (* Disable log messages from best_tip_diff logger. *)
733
      Logger.Consumer_registry.register ~commit_id:""
1✔
734
        ~id:Logger.Logger_id.best_tip_diff ~processor:(Logger.Processor.raw ())
1✔
735
        ~transport:
736
          (Logger.Transport.create
1✔
737
             ( module struct
738
               type t = unit
739

740
               let transport () _ = ()
61✔
741
             end )
742
             () )
743
        ()
744

745
    let trust_system =
746
      let s = Trust_system.null () in
747
      don't_wait_for
1✔
748
        (Pipe_lib.Strict_pipe.Reader.iter
1✔
749
           (Trust_system.upcall_pipe s)
1✔
750
           ~f:(const Deferred.unit) ) ;
1✔
751
      s
1✔
752

753
    let precomputed_values = Lazy.force Precomputed_values.for_unit_tests
1✔
754

755
    let proof_level = precomputed_values.proof_level
756

757
    let constraint_constants = precomputed_values.constraint_constants
758

759
    let ledger_sync_config =
760
      Syncable_ledger.create_config
1✔
761
        ~compile_config:Mina_compile_config.For_unit_tests.t
762
        ~max_subtree_depth:None ~default_subtree_depth:None ()
763

764
    module Context = struct
765
      let logger = logger
766

767
      let precomputed_values = precomputed_values
768

769
      let constraint_constants =
770
        Genesis_constants.For_unit_tests.Constraint_constants.t
771

772
      let consensus_constants = precomputed_values.consensus_constants
773

774
      let ledger_sync_config =
775
        Syncable_ledger.create_config
1✔
776
          ~compile_config:Mina_compile_config.For_unit_tests.t
777
          ~max_subtree_depth:None ~default_subtree_depth:None ()
778

779
      let proof_cache_db = Proof_cache_tag.For_tests.create_db ()
1✔
780
    end
781

782
    let verifier =
783
      Async.Thread_safe.block_on_async_exn (fun () ->
1✔
784
          Verifier.For_tests.default ~constraint_constants ~logger ~proof_level
1✔
785
            () )
786

787
    module Genesis_ledger = (val precomputed_values.genesis_ledger)
788

789
    let downcast_transition ~sender transition =
790
      let transition =
50✔
791
        transition |> Mina_block.Validated.remember
50✔
792
        |> Mina_block.Validation.reset_frontier_dependencies_validation
50✔
793
        |> Mina_block.Validation.reset_staged_ledger_diff_validation
794
      in
795
      Envelope.Incoming.wrap ~data:transition
50✔
796
        ~sender:(Envelope.Sender.Remote sender)
797

798
    let downcast_breadcrumb ~sender breadcrumb =
799
      downcast_transition ~sender
50✔
800
        (Transition_frontier.Breadcrumb.validated_transition breadcrumb)
50✔
801

802
    let make_non_running_bootstrap ~genesis_root ~network =
803
      let transition =
1✔
804
        genesis_root
805
        |> Mina_block.Validation.reset_frontier_dependencies_validation
1✔
806
        |> Mina_block.Validation.reset_staged_ledger_diff_validation
807
      in
808
      { context = (module Context)
1✔
809
      ; trust_system
810
      ; verifier
811
      ; best_seen_transition = transition
812
      ; current_root = transition
813
      ; network
814
      ; num_of_root_snarked_ledger_retargeted = 0
815
      }
816

817
    let%test_unit "Bootstrap controller caches all transitions it is passed \
818
                   through the transition_reader" =
819
      let branch_size = (max_frontier_length * 2) + 2 in
1✔
820
      Quickcheck.test ~trials:1
821
        (let open Quickcheck.Generator.Let_syntax in
822
        (* we only need one node for this test, but we need more than one peer so that mina_networking does not throw an error *)
823
        let%bind fake_network =
824
          Fake_network.Generator.(
825
            gen ~precomputed_values ~verifier ~max_frontier_length
1✔
826
              ~ledger_sync_config [ fresh_peer; fresh_peer ])
827
        in
828
        let%map make_branch =
829
          Transition_frontier.Breadcrumb.For_tests.gen_seq ~precomputed_values
1✔
830
            ~verifier
831
            ~accounts_with_secret_keys:(Lazy.force Genesis_ledger.accounts)
1✔
832
            branch_size
833
        in
834
        let [ me; _ ] = fake_network.peer_networks in
1✔
835
        let branch =
836
          Async.Thread_safe.block_on_async_exn (fun () ->
837
              make_branch (Transition_frontier.root me.state.frontier) )
1✔
838
        in
839
        (fake_network, branch))
1✔
840
        ~f:(fun (fake_network, branch) ->
841
          let [ me; other ] = fake_network.peer_networks in
1✔
842
          let genesis_root =
843
            Transition_frontier.(
844
              Breadcrumb.validated_transition @@ root me.state.frontier)
1✔
845
            |> Mina_block.Validated.remember
846
          in
847
          let transition_graph = Transition_cache.create () in
1✔
848
          let sync_ledger_reader, sync_ledger_writer =
1✔
849
            Pipe_lib.Strict_pipe.create ~name:"sync_ledger_reader" Synchronous
850
          in
851
          let bootstrap =
1✔
852
            make_non_running_bootstrap ~genesis_root ~network:me.network
853
          in
854
          let root_sync_ledger =
855
            Sync_ledger.Db.create
856
              (Transition_frontier.root_snarked_ledger me.state.frontier)
1✔
857
              ~context:(module Context)
858
              ~trust_system
859
          in
860
          Async.Thread_safe.block_on_async_exn (fun () ->
1✔
861
              let sync_deferred =
1✔
862
                sync_ledger bootstrap ~root_sync_ledger ~transition_graph
863
                  ~preferred:[] ~sync_ledger_reader
864
              in
865
              let%bind () =
866
                Deferred.List.iter branch ~f:(fun breadcrumb ->
1✔
867
                    Strict_pipe.Writer.write sync_ledger_writer
50✔
868
                      ( `Block
869
                          (downcast_breadcrumb ~sender:other.peer breadcrumb)
50✔
870
                      , `Valid_cb None ) )
871
              in
872
              Strict_pipe.Writer.close sync_ledger_writer ;
1✔
873
              sync_deferred ) ;
1✔
874
          let expected_transitions =
1✔
875
            List.map branch
876
              ~f:
877
                (Fn.compose
1✔
878
                   (With_hash.map ~f:Mina_block.header)
879
                   (Fn.compose Mina_block.Validation.block_with_hash
1✔
880
                      (Fn.compose Mina_block.Validated.remember
1✔
881
                         Transition_frontier.Breadcrumb.validated_transition ) ) )
882
          in
883
          let saved_transitions =
1✔
884
            Transition_cache.data transition_graph
1✔
885
            |> List.map ~f:(fun (x, _) ->
886
                   Transition_cache.header_with_hash @@ Envelope.Incoming.data x )
50✔
887
          in
888
          let module E = struct
1✔
889
            module T = struct
UNCOV
890
              type t = Mina_block.Header.t State_hash.With_state_hashes.t
×
891
              [@@deriving sexp]
892

893
              let compare = external_transition_compare ~context:(module Context)
894
            end
895

896
            include Comparable.Make (T)
897
          end in
898
          [%test_result: E.Set.t]
1✔
899
            (E.Set.of_list saved_transitions)
1✔
900
            ~expect:(E.Set.of_list expected_transitions) )
1✔
901

902
    let run_bootstrap ~timeout_duration ~my_net ~transition_reader =
903
      let open Fake_network in
1✔
904
      let time_controller = Block_time.Controller.basic ~logger in
905
      let persistent_root =
906
        Transition_frontier.persistent_root my_net.state.frontier
907
      in
908
      let persistent_frontier =
1✔
909
        Transition_frontier.persistent_frontier my_net.state.frontier
910
      in
911
      let initial_root_transition =
1✔
912
        Transition_frontier.(
913
          Breadcrumb.validated_transition (root my_net.state.frontier))
1✔
914
      in
915
      let%bind () =
916
        Transition_frontier.close ~loc:__LOC__ my_net.state.frontier
1✔
917
      in
918
      [%log info] "bootstrap begin" ;
1✔
919
      Block_time.Timeout.await_exn time_controller ~timeout_duration
1✔
920
        (run
921
           ~context:(module Context)
922
           ~trust_system ~verifier ~network:my_net.network ~preferred_peers:[]
923
           ~consensus_local_state:my_net.state.consensus_local_state
924
           ~transition_reader ~persistent_root ~persistent_frontier
925
           ~catchup_mode:`Super ~initial_root_transition )
926

927
    let assert_transitions_increasingly_sorted ~root
928
        (incoming_transitions : Transition_cache.element list) =
929
      let root =
1✔
930
        Transition_frontier.Breadcrumb.block root |> Mina_block.header
1✔
931
      in
932
      ignore
1✔
933
        ( List.fold_result ~init:root incoming_transitions
1✔
934
            ~f:(fun max_acc incoming_transition ->
935
              let With_hash.{ data = header; _ } =
1✔
936
                Transition_cache.header_with_hash
937
                  (Envelope.Incoming.data @@ fst incoming_transition)
1✔
938
              in
939
              let header_len h =
1✔
940
                Mina_block.Header.protocol_state h
2✔
941
                |> Protocol_state.consensus_state
2✔
942
                |> Consensus.Data.Consensus_state.blockchain_length
943
              in
944
              let open Result.Let_syntax in
945
              let%map () =
946
                Result.ok_if_true
1✔
947
                  Mina_numbers.Length.(header_len max_acc <= header_len header)
1✔
948
                  ~error:
949
                    (Error.of_string
1✔
950
                       "The blocks are not sorted in increasing order" )
951
              in
952
              header )
1✔
953
          |> Or_error.ok_exn
1✔
954
          : Mina_block.Header.t )
955

956
    let%test_unit "sync with one node after receiving a transition" =
957
      Quickcheck.test ~trials:1
1✔
958
        Fake_network.Generator.(
959
          gen ~precomputed_values ~verifier ~max_frontier_length
1✔
960
            ~ledger_sync_config
961
            [ fresh_peer
962
            ; peer_with_branch
963
                ~frontier_branch_size:((max_frontier_length * 2) + 2)
964
            ])
965
        ~f:(fun fake_network ->
966
          let [ my_net; peer_net ] = fake_network.peer_networks in
1✔
967
          let transition_reader, transition_writer =
968
            Pipe_lib.Strict_pipe.create ~name:(__MODULE__ ^ __LOC__)
969
              (Buffered (`Capacity 10, `Overflow (Drop_head ignore)))
970
          in
971
          let block =
1✔
972
            Envelope.Incoming.wrap
973
              ~data:
974
                ( Transition_frontier.best_tip peer_net.state.frontier
1✔
975
                |> Transition_frontier.Breadcrumb.validated_transition
1✔
976
                |> Mina_block.Validated.remember
1✔
977
                |> Mina_block.Validation.reset_frontier_dependencies_validation
1✔
978
                |> Mina_block.Validation.reset_staged_ledger_diff_validation )
1✔
979
              ~sender:(Envelope.Sender.Remote peer_net.peer)
980
          in
981
          Pipe_lib.Strict_pipe.Writer.write transition_writer
982
            (`Block block, `Valid_cb None) ;
983
          let new_frontier, sorted_external_transitions =
1✔
984
            Async.Thread_safe.block_on_async_exn (fun () ->
985
                run_bootstrap
1✔
986
                  ~timeout_duration:(Block_time.Span.of_ms 30_000L)
1✔
987
                  ~my_net ~transition_reader )
988
          in
989
          assert_transitions_increasingly_sorted
1✔
990
            ~root:(Transition_frontier.root new_frontier)
1✔
991
            sorted_external_transitions ;
992
          [%test_result: Ledger_hash.t]
1✔
993
            ( Ledger.Db.merkle_root
1✔
994
            @@ Transition_frontier.root_snarked_ledger new_frontier )
1✔
995
            ~expect:
996
              ( Ledger.Db.merkle_root
1✔
997
              @@ Transition_frontier.root_snarked_ledger peer_net.state.frontier
1✔
998
              ) )
999

1000
    let%test_unit "reconstruct staged_ledgers using \
1001
                   of_scan_state_and_snarked_ledger" =
1002
      Quickcheck.test ~trials:1
1✔
1003
        (Transition_frontier.For_tests.gen ~precomputed_values ~verifier
1✔
1004
           ~max_length:max_frontier_length ~size:max_frontier_length () )
1005
        ~f:(fun frontier ->
1006
          Thread_safe.block_on_async_exn
1✔
1007
          @@ fun () ->
1008
          Deferred.List.iter (Transition_frontier.all_breadcrumbs frontier)
1✔
1009
            ~f:(fun breadcrumb ->
1010
              let staged_ledger =
25✔
1011
                Transition_frontier.Breadcrumb.staged_ledger breadcrumb
1012
              in
1013
              let expected_merkle_root =
25✔
1014
                Staged_ledger.ledger staged_ledger |> Ledger.merkle_root
25✔
1015
              in
1016
              let snarked_ledger =
25✔
1017
                Transition_frontier.root_snarked_ledger frontier
25✔
1018
                |> Ledger.of_database
1019
              in
1020
              let snarked_local_state =
25✔
1021
                Transition_frontier.root frontier
25✔
1022
                |> Transition_frontier.Breadcrumb.protocol_state
25✔
1023
                |> Protocol_state.blockchain_state
25✔
1024
                |> Blockchain_state.snarked_local_state
1025
              in
1026
              let scan_state = Staged_ledger.scan_state staged_ledger in
25✔
1027
              let get_state hash =
25✔
1028
                match Transition_frontier.find_protocol_state frontier hash with
720✔
1029
                | Some protocol_state ->
720✔
1030
                    Ok protocol_state
UNCOV
1031
                | None ->
×
1032
                    Or_error.errorf
UNCOV
1033
                      !"Protocol state (for scan state transactions) for \
×
1034
                        %{sexp:State_hash.t} not found"
1035
                      hash
1036
              in
1037
              let pending_coinbases =
1038
                Staged_ledger.pending_coinbase_collection staged_ledger
1039
              in
1040
              let%map actual_staged_ledger =
1041
                Staged_ledger.of_scan_state_pending_coinbases_and_snarked_ledger
1042
                  ~scan_state ~logger ~verifier ~constraint_constants
1043
                  ~snarked_ledger ~snarked_local_state ~expected_merkle_root
1044
                  ~pending_coinbases ~get_state
1045
                |> Deferred.Or_error.ok_exn
25✔
1046
              in
1047
              let height =
25✔
1048
                Transition_frontier.Breadcrumb.consensus_state breadcrumb
25✔
1049
                |> Consensus.Data.Consensus_state.blockchain_length
25✔
1050
                |> Mina_numbers.Length.to_int
1051
              in
1052
              [%test_eq: Staged_ledger_hash.t]
25✔
1053
                ~message:
1054
                  (sprintf "mismatch of staged ledger hash height %d" height)
25✔
1055
                (Transition_frontier.Breadcrumb.staged_ledger_hash breadcrumb)
25✔
1056
                (Staged_ledger.hash actual_staged_ledger) ) )
25✔
1057

1058
    (*
1059
    let%test_unit "if we see a new transition that is better than the \
1060
                   transition that we are syncing from, than we should \
1061
                   retarget our root" =
1062
      Quickcheck.test ~trials:1
1063
        Fake_network.Generator.(
1064
          gen ~max_frontier_length
1065
            [ fresh_peer
1066
            ; peer_with_branch ~frontier_branch_size:max_frontier_length
1067
            ; peer_with_branch
1068
                ~frontier_branch_size:((max_frontier_length * 2) + 2) ])
1069
        ~f:(fun fake_network ->
1070
          let [me; weaker_chain; stronger_chain] =
1071
            fake_network.peer_networks
1072
          in
1073
          let transition_reader, transition_writer =
1074
            Pipe_lib.Strict_pipe.create ~name:(__MODULE__ ^ __LOC__)
1075
              (Buffered (`Capacity 10, `Overflow Drop_head))
1076
          in
1077
          Envelope.Incoming.wrap
1078
            ~data:
1079
              ( Transition_frontier.best_tip weaker_chain.state.frontier
1080
              |> Transition_frontier.Breadcrumb.validated_transition
1081
              |> Mina_block.Validated.to_initial_validated )
1082
            ~sender:
1083
              (Envelope.Sender.Remote
1084
                 (weaker_chain.peer.host, weaker_chain.peer.peer_id))
1085
          |> Pipe_lib.Strict_pipe.Writer.write transition_writer ;
1086
          Envelope.Incoming.wrap
1087
            ~data:
1088
              ( Transition_frontier.best_tip stronger_chain.state.frontier
1089
              |> Transition_frontier.Breadcrumb.validated_transition
1090
              |> Mina_block.Validated.to_initial_validated )
1091
            ~sender:
1092
              (Envelope.Sender.Remote
1093
                 (stronger_chain.peer.host, stronger_chain.peer.peer_id))
1094
          |> Pipe_lib.Strict_pipe.Writer.write transition_writer ;
1095
          let new_frontier, sorted_external_transitions =
1096
            Async.Thread_safe.block_on_async_exn (fun () ->
1097
                run_bootstrap
1098
                  ~timeout_duration:(Block_time.Span.of_ms 60_000L)
1099
                  ~my_net:me ~transition_reader )
1100
          in
1101
          assert_transitions_increasingly_sorted
1102
            ~root:(Transition_frontier.root new_frontier)
1103
            sorted_external_transitions ;
1104
          [%test_result: Ledger_hash.t]
1105
            ( Ledger.Db.merkle_root
1106
            @@ Transition_frontier.root_snarked_ledger new_frontier )
1107
            ~expect:
1108
              ( Ledger.Db.merkle_root
1109
              @@ Transition_frontier.root_snarked_ledger
1110
                   stronger_chain.state.frontier ) )
1111
*)
1112
  end )
24✔
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