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

MinaProtocol / mina / 1302

17 Apr 2026 09:25PM UTC coverage: 62.398% (+0.006%) from 62.392%
1302

push

buildkite

web-flow
Merge pull request #18778 from MinaProtocol/dkijania/fix-debian-upgrade-test-version

53840 of 86285 relevant lines covered (62.4%)

481414.61 hits per line

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

79.21
/src/lib/sync_handler/sync_handler.ml
1
open Core_kernel
152✔
2
open Async
3
open Mina_base
4
module Ledger = Mina_ledger.Ledger
5
module Root_ledger = Mina_ledger.Root
6
module Sync_ledger = Mina_ledger.Sync_ledger
7
open Frontier_base
8
open Network_peer
9

10
module type CONTEXT = sig
11
  val logger : Logger.t
12

13
  val precomputed_values : Precomputed_values.t
14

15
  val constraint_constants : Genesis_constants.Constraint_constants.t
16

17
  val consensus_constants : Consensus.Constants.t
18

19
  val ledger_sync_config : Syncable_ledger.daemon_config
20

21
  val proof_cache_db : Proof_cache_tag.cache_db
22

23
  val signature_kind : Mina_signature_kind.t
24
end
25

26
module type Inputs_intf = sig
27
  module Transition_frontier : module type of Transition_frontier
28

29
  module Best_tip_prover :
30
    Mina_intf.Best_tip_prover_intf
31
      with type transition_frontier := Transition_frontier.t
32
end
33

34
module Make (Inputs : Inputs_intf) :
35
  Mina_intf.Sync_handler_intf
36
    with type transition_frontier := Inputs.Transition_frontier.t = struct
37
  open Inputs
38

39
  let find_in_root_history frontier state_hash =
40
    let open Transition_frontier.Extensions in
28✔
41
    let root_history =
42
      get_extension (Transition_frontier.extensions frontier) Root_history
28✔
43
    in
44
    Root_history.lookup root_history state_hash
28✔
45

46
  let protocol_states_in_root_history frontier state_hash =
47
    let open Transition_frontier.Extensions in
×
48
    let root_history =
49
      get_extension (Transition_frontier.extensions frontier) Root_history
×
50
    in
51
    Root_history.protocol_states_for_scan_state root_history state_hash
×
52

53
  let get_ledger_by_hash ~frontier ledger_hash =
54
    let root_ledger =
47✔
55
      Root_ledger.as_unmasked
56
      @@ Transition_frontier.root_snarked_ledger frontier
47✔
57
    in
58
    let staking_epoch_ledger =
47✔
59
      Transition_frontier.consensus_local_state frontier
47✔
60
      |> Consensus.Data.Local_state.staking_epoch_ledger
61
    in
62
    let next_epoch_ledger =
47✔
63
      Transition_frontier.consensus_local_state frontier
47✔
64
      |> Consensus.Data.Local_state.next_epoch_ledger
65
    in
66
    if
47✔
67
      Ledger_hash.equal ledger_hash
68
        (Ledger.Any_ledger.M.merkle_root root_ledger)
47✔
69
    then Some root_ledger
2✔
70
    else if
45✔
71
      Ledger_hash.equal ledger_hash
72
        (Consensus.Data.Local_state.Snapshot.Ledger_snapshot.merkle_root
45✔
73
           staking_epoch_ledger )
74
    then
75
      match staking_epoch_ledger with
20✔
76
      | Consensus.Data.Local_state.Snapshot.Ledger_snapshot.Genesis_epoch_ledger
×
77
          _ ->
78
          None
79
      | Ledger_root ledger ->
20✔
80
          Some (Root_ledger.as_unmasked ledger)
20✔
81
    else if
25✔
82
      Ledger_hash.equal ledger_hash
83
        (Consensus.Data.Local_state.Snapshot.Ledger_snapshot.merkle_root
25✔
84
           next_epoch_ledger )
85
    then
86
      match next_epoch_ledger with
24✔
87
      | Consensus.Data.Local_state.Snapshot.Ledger_snapshot.Genesis_epoch_ledger
×
88
          _ ->
89
          None
90
      | Ledger_root ledger ->
24✔
91
          Some (Root_ledger.as_unmasked ledger)
24✔
92
    else None
1✔
93

94
  let answer_query :
95
         frontier:Inputs.Transition_frontier.t
96
      -> Ledger_hash.t
97
      -> Sync_ledger.Query.t Envelope.Incoming.t
98
      -> context:(module CONTEXT)
99
      -> trust_system:Trust_system.t
100
      -> Sync_ledger.Answer.t Or_error.t Deferred.t =
101
   fun ~frontier hash query ~context:(module Context) ~trust_system ->
102
    match get_ledger_by_hash ~frontier hash with
47✔
103
    | None ->
1✔
104
        return
105
          (Or_error.error_string
1✔
106
             (sprintf
1✔
107
                !"Failed to find ledger for hash %{sexp:Ledger_hash.t}"
1✔
108
                hash ) )
109
    | Some ledger ->
46✔
110
        let responder =
111
          Sync_ledger.Any_ledger.Responder.create ledger ignore
112
            ~context:(module Context)
113
            ~trust_system
114
        in
115
        Sync_ledger.Any_ledger.Responder.answer_query responder query
46✔
116

117
  let get_staged_ledger_aux_and_pending_coinbases_at_hash ~logger ~frontier
118
      state_hash =
119
    let open Option.Let_syntax in
1✔
120
    let protocol_states scan_state =
121
      Staged_ledger.Scan_state.required_state_hashes scan_state
1✔
122
      |> State_hash.Set.to_list
1✔
123
      |> List.fold_until ~init:(Some [])
124
           ~f:(fun acc hash ->
125
             match
4✔
126
               Option.map2
127
                 (Transition_frontier.find_protocol_state frontier hash)
4✔
128
                 acc ~f:List.cons
129
             with
130
             | None ->
×
131
                 Stop None
132
             | Some acc' ->
4✔
133
                 Continue (Some acc') )
134
           ~finish:Fn.id
135
    in
136
    match
137
      let%bind breadcrumb = Transition_frontier.find frontier state_hash in
1✔
138
      let staged_ledger =
1✔
139
        Transition_frontier.Breadcrumb.staged_ledger breadcrumb
140
      in
141
      let scan_state = Staged_ledger.scan_state staged_ledger in
1✔
142
      let staged_ledger_hash = Breadcrumb.staged_ledger_hash breadcrumb in
1✔
143
      let merkle_root = Staged_ledger_hash.ledger_hash staged_ledger_hash in
1✔
144
      let%map scan_state_protocol_states = protocol_states scan_state in
1✔
145
      let pending_coinbase =
1✔
146
        Staged_ledger.pending_coinbase_collection staged_ledger
147
      in
148
      [%log debug]
1✔
149
        ~metadata:
150
          [ ( "staged_ledger_hash"
151
            , Staged_ledger_hash.to_yojson staged_ledger_hash )
1✔
152
          ]
153
        "sending scan state and pending coinbase" ;
154
      (scan_state, merkle_root, pending_coinbase, scan_state_protocol_states)
1✔
155
    with
156
    | Some res ->
1✔
157
        Some res
158
    | None ->
×
159
        let open Root_data.Historical in
160
        let%bind root = find_in_root_history frontier state_hash in
×
161
        let%map scan_state_protocol_states =
162
          protocol_states_in_root_history frontier state_hash
×
163
        in
164
        ( scan_state root
×
165
        , staged_ledger_target_ledger_hash root
×
166
        , pending_coinbase root
×
167
        , scan_state_protocol_states )
168

169
  let get_transition_chain ~frontier hashes =
170
    let open Option.Let_syntax in
7✔
171
    let%bind () =
172
      let requested = List.length hashes in
173
      if requested <= Transition_frontier.max_catchup_chunk_length then Some ()
7✔
174
      else (
×
175
        [%log' trace (Logger.create ())]
×
176
          ~metadata:[ ("n", `Int requested) ]
177
          "get_transition_chain requested $n > %d hashes"
178
          Transition_frontier.max_catchup_chunk_length ;
179
        None )
×
180
    in
181
    let get hash =
7✔
182
      let%map validated_transition =
183
        Option.merge
28✔
184
          Transition_frontier.(
185
            find frontier hash >>| Breadcrumb.validated_transition)
28✔
186
          ( find_in_root_history frontier hash
28✔
187
          >>| Root_data.Historical.transition )
28✔
188
          ~f:Fn.const
189
      in
190
      With_hash.data @@ Mina_block.Validated.forget validated_transition
28✔
191
    in
192
    match Transition_frontier.catchup_state frontier with
193
    | Full _ ->
7✔
194
        (* Super catchup *)
195
        Option.return @@ List.filter_map hashes ~f:get
7✔
196

197
  let best_tip_path ~frontier =
198
    let rec go acc b =
×
199
      let acc = Breadcrumb.state_hash b :: acc in
×
200
      match Transition_frontier.find frontier (Breadcrumb.parent_hash b) with
×
201
      | None ->
×
202
          acc
203
      | Some b' ->
×
204
          go acc b'
205
    in
206
    go [] (Transition_frontier.best_tip frontier)
×
207

208
  module Root = struct
209
    let prove ~context:(module Context : CONTEXT) ~frontier seen_consensus_state
210
        =
211
      let module Context = struct
101✔
212
        include Context
213

214
        let logger =
215
          Logger.extend logger [ ("selection_context", `String "Root.prove") ]
101✔
216
      end in
217
      let open Option.Let_syntax in
218
      let%bind best_tip_with_witness =
219
        Best_tip_prover.prove ~context:(module Context) frontier
101✔
220
      in
221
      let is_tip_better =
101✔
222
        Consensus.Hooks.equal_select_status
223
          (Consensus.Hooks.select
224
             ~context:(module Context)
225
             ~existing:
226
               (Breadcrumb.consensus_state_with_hashes
101✔
227
                  best_tip_with_witness.data )
228
             ~candidate:seen_consensus_state )
229
          `Keep
230
      in
231
      let%map () = Option.some_if is_tip_better () in
101✔
232
      best_tip_with_witness
1✔
233

234
    let verify ~context:(module Context : CONTEXT) ~verifier observed_state
235
        peer_root =
236
      let module Context = struct
1✔
237
        include Context
238

239
        let logger =
240
          Logger.extend logger [ ("selection_context", `String "Root.verify") ]
1✔
241
      end in
242
      let open Context in
243
      let open Deferred.Result.Let_syntax in
244
      (*TODO: use precomputed_values.genesis_constants that's already passed*)
245
      let%bind ( (`Root _, `Best_tip (best_tip_transition, _)) as
246
               verified_witness ) =
247
        Best_tip_prover.verify ~verifier
1✔
248
          ~genesis_constants:precomputed_values.genesis_constants
249
          ~precomputed_values peer_root
250
      in
251
      let is_before_best_tip candidate =
1✔
252
        Consensus.Hooks.equal_select_status
1✔
253
          (Consensus.Hooks.select
254
             ~context:(module Context)
255
             ~existing:
256
               (With_hash.map
1✔
257
                  ~f:
258
                    (Fn.compose Mina_state.Protocol_state.consensus_state
1✔
259
                       Mina_block.Header.protocol_state )
260
                  best_tip_transition )
261
             ~candidate )
262
          `Keep
263
      in
264
      let%map () =
265
        Deferred.return
1✔
266
          (Result.ok_if_true
1✔
267
             (is_before_best_tip observed_state)
1✔
268
             ~error:
269
               (Error.createf
1✔
270
                  !"Peer lied about it's best tip %{sexp:State_hash.t}"
1✔
271
                  (State_hash.With_state_hashes.state_hash best_tip_transition) ) )
1✔
272
      in
273
      verified_witness
1✔
274
  end
275
end
276

277
include Make (struct
278
  module Transition_frontier = Transition_frontier
279
  module Best_tip_prover = Best_tip_prover
280
end)
304✔
281

282
(* TODO: port these tests *)
283
(*
284
let%test_module "Sync_handler" =
285
  ( module struct
286
    let logger = Logger.null ()
287

288
    let hb_logger = Logger.create ()
289

290
    let pids = Child_processes.Termination.create_pid_table ()
291

292
    let trust_system = Trust_system.null ()
293

294
    let f_with_verifier ~f ~logger ~pids =
295
      let%map verifier = Verifier.create ~logger ~pids in
296
      f ~logger ~verifier
297

298
    let%test "sync with ledgers from another peer via glue_sync_ledger" =
299
      Backtrace.elide := false ;
300
      Printexc.record_backtrace true ;
301
      heartbeat_flag := true ;
302
      Ledger.with_ephemeral_ledger ~f:(fun dest_ledger ->
303
          Thread_safe.block_on_async_exn (fun () ->
304
              print_heartbeat hb_logger |> don't_wait_for ;
305
              let%bind frontier =
306
                create_root_frontier ~logger ~pids Test_genesis_ledger.accounts
307
              in
308
              let source_ledger =
309
                Transition_frontier.For_tests.root_snarked_ledger frontier
310
                |> Ledger.of_database
311
              in
312
              let desired_root = Ledger.merkle_root source_ledger in
313
              let sync_ledger =
314
                Sync_ledger.Mask.create dest_ledger ~logger ~trust_system
315
              in
316
              let query_reader = Sync_ledger.Mask.query_reader sync_ledger in
317
              let answer_writer = Sync_ledger.Mask.answer_writer sync_ledger in
318
              let peer =
319
                Network_peer.Peer.create Unix.Inet_addr.localhost
320
                  ~discovery_port:0 ~communication_port:1
321
              in
322
              let network =
323
                Network.create_stub ~logger
324
                  ~ip_table:
325
                    (Hashtbl.of_alist_exn
326
                       (module Unix.Inet_addr)
327
                       [(peer.host, frontier)])
328
                  ~peers:(Hash_set.of_list (module Network_peer.Peer) [peer])
329
              in
330
              Network.glue_sync_ledger network query_reader answer_writer ;
331
              match%map
332
                Sync_ledger.Mask.fetch sync_ledger desired_root ~data:()
333
                  ~equal:(fun () () -> true)
334
              with
335
              | `Ok synced_ledger ->
336
                  heartbeat_flag := false ;
337
                  Ledger_hash.equal
338
                    (Ledger.merkle_root dest_ledger)
339
                    (Ledger.merkle_root source_ledger)
340
                  && Ledger_hash.equal
341
                       (Ledger.merkle_root synced_ledger)
342
                       (Ledger.merkle_root source_ledger)
343
              | `Target_changed _ ->
344
                  heartbeat_flag := false ;
345
                  failwith "target of sync_ledger should not change" ) )
346

347
    let to_external_transition breadcrumb =
348
      Transition_frontier.Breadcrumb.validated_transition breadcrumb
349
      |> Mina_block.Validated.forget
350

351
    let%test "a node should be able to give a valid proof of their root" =
352
      heartbeat_flag := true ;
353
      let max_length = 4 in
354
      (* Generating this many breadcrumbs will ernsure the transition_frontier to be full  *)
355
      let num_breadcrumbs = max_length + 2 in
356
      Thread_safe.block_on_async_exn (fun () ->
357
          print_heartbeat hb_logger |> don't_wait_for ;
358
          let%bind frontier =
359
            create_root_frontier ~logger ~pids Test_genesis_ledger.accounts
360
          in
361
          let%bind () =
362
            build_frontier_randomly frontier
363
              ~gen_root_breadcrumb_builder:
364
                (gen_linear_breadcrumbs ~logger ~pids ~trust_system
365
                   ~size:num_breadcrumbs
366
                   ~accounts_with_secret_keys:Test_genesis_ledger.accounts)
367
          in
368
          let seen_transition =
369
            Transition_frontier.(
370
              all_breadcrumbs frontier |> List.permute |> List.hd_exn
371
              |> Breadcrumb.validated_transition)
372
          in
373
          let observed_state =
374
            Mina_block.Validated.protocol_state seen_transition
375
            |> Protocol_state.consensus_state
376
          in
377
          let root_with_proof =
378
            Option.value_exn ~message:"Could not produce an ancestor proof"
379
              (Sync_handler.Root.prove ~logger ~frontier observed_state)
380
          in
381
          let%bind verify =
382
            f_with_verifier ~f:Sync_handler.Root.verify ~logger ~pids
383
          in
384
          let%map `Root (root_transition, _), `Best_tip (best_tip_transition, _)
385
              =
386
            verify observed_state root_with_proof |> Deferred.Or_error.ok_exn
387
          in
388
          heartbeat_flag := false ;
389
          Mina_block.(
390
            equal
391
              (With_hash.data root_transition)
392
              (to_external_transition (Transition_frontier.root frontier))
393
            && equal
394
                 (With_hash.data best_tip_transition)
395
                 (to_external_transition
396
                    (Transition_frontier.best_tip frontier))) )
397
  end )
398
*)
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