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

MinaProtocol / mina / 535

25 Aug 2025 05:35PM UTC coverage: 32.09% (-28.7%) from 60.772%
535

push

buildkite

web-flow
Merge pull request #17673 from MinaProtocol/amcie-merge-release320-to-master

amcie-merge-release320-to-master

1010 of 3745 new or added lines in 242 files covered. (26.97%)

17403 existing lines in 378 files now uncovered.

23062 of 71866 relevant lines covered (32.09%)

24742.7 hits per line

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

1.96
/src/lib/sync_handler/sync_handler.ml
1
open Core_kernel
53✔
2
open Async
3
open Mina_base
4
module Ledger = Mina_ledger.Ledger
5
module Sync_ledger = Mina_ledger.Sync_ledger
6
open Frontier_base
7
open Network_peer
8

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

12
  val precomputed_values : Precomputed_values.t
13

14
  val constraint_constants : Genesis_constants.Constraint_constants.t
15

16
  val consensus_constants : Consensus.Constants.t
17

18
  val ledger_sync_config : Syncable_ledger.daemon_config
19

20
  val proof_cache_db : Proof_cache_tag.cache_db
21
end
22

23
module type Inputs_intf = sig
24
  module Transition_frontier : module type of Transition_frontier
25

26
  module Best_tip_prover :
27
    Mina_intf.Best_tip_prover_intf
28
      with type transition_frontier := Transition_frontier.t
29
end
30

31
module Make (Inputs : Inputs_intf) :
32
  Mina_intf.Sync_handler_intf
33
    with type transition_frontier := Inputs.Transition_frontier.t = struct
34
  open Inputs
35

36
  let find_in_root_history frontier state_hash =
UNCOV
37
    let open Transition_frontier.Extensions in
×
38
    let root_history =
UNCOV
39
      get_extension (Transition_frontier.extensions frontier) Root_history
×
40
    in
UNCOV
41
    Root_history.lookup root_history state_hash
×
42

43
  let protocol_states_in_root_history frontier state_hash =
44
    let open Transition_frontier.Extensions in
×
45
    let root_history =
46
      get_extension (Transition_frontier.extensions frontier) Root_history
×
47
    in
48
    Root_history.protocol_states_for_scan_state root_history state_hash
×
49

50
  let get_ledger_by_hash ~frontier ledger_hash =
UNCOV
51
    let root_ledger =
×
52
      Ledger.Any_ledger.cast (module Ledger.Db)
UNCOV
53
      @@ Transition_frontier.root_snarked_ledger frontier
×
54
    in
UNCOV
55
    let staking_epoch_ledger =
×
UNCOV
56
      Transition_frontier.consensus_local_state frontier
×
57
      |> Consensus.Data.Local_state.staking_epoch_ledger
58
    in
UNCOV
59
    let next_epoch_ledger =
×
UNCOV
60
      Transition_frontier.consensus_local_state frontier
×
61
      |> Consensus.Data.Local_state.next_epoch_ledger
62
    in
UNCOV
63
    if
×
64
      Ledger_hash.equal ledger_hash
UNCOV
65
        (Ledger.Any_ledger.M.merkle_root root_ledger)
×
UNCOV
66
    then Some root_ledger
×
UNCOV
67
    else if
×
68
      Ledger_hash.equal ledger_hash
UNCOV
69
        (Consensus.Data.Local_state.Snapshot.Ledger_snapshot.merkle_root
×
70
           staking_epoch_ledger )
71
    then
UNCOV
72
      match staking_epoch_ledger with
×
UNCOV
73
      | Consensus.Data.Local_state.Snapshot.Ledger_snapshot.Genesis_epoch_ledger
×
74
          _ ->
75
          None
UNCOV
76
      | Ledger_db ledger ->
×
UNCOV
77
          Some (Ledger.Any_ledger.cast (module Ledger.Db) ledger)
×
UNCOV
78
    else if
×
79
      Ledger_hash.equal ledger_hash
UNCOV
80
        (Consensus.Data.Local_state.Snapshot.Ledger_snapshot.merkle_root
×
81
           next_epoch_ledger )
82
    then
UNCOV
83
      match next_epoch_ledger with
×
84
      | Consensus.Data.Local_state.Snapshot.Ledger_snapshot.Genesis_epoch_ledger
×
85
          _ ->
86
          None
UNCOV
87
      | Ledger_db ledger ->
×
UNCOV
88
          Some (Ledger.Any_ledger.cast (module Ledger.Db) ledger)
×
89
    else None
×
90

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

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

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

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

205
  module Root = struct
206
    let prove ~context:(module Context : CONTEXT) ~frontier seen_consensus_state
207
        =
UNCOV
208
      let module Context = struct
×
209
        include Context
210

211
        let logger =
UNCOV
212
          Logger.extend logger [ ("selection_context", `String "Root.prove") ]
×
213
      end in
214
      let open Option.Let_syntax in
215
      let%bind best_tip_with_witness =
UNCOV
216
        Best_tip_prover.prove ~context:(module Context) frontier
×
217
      in
UNCOV
218
      let is_tip_better =
×
219
        Consensus.Hooks.equal_select_status
220
          (Consensus.Hooks.select
221
             ~context:(module Context)
222
             ~existing:
UNCOV
223
               (With_hash.map ~f:Mina_block.consensus_state
×
224
                  best_tip_with_witness.data )
225
             ~candidate:seen_consensus_state )
226
          `Keep
227
      in
UNCOV
228
      let%map () = Option.some_if is_tip_better () in
×
UNCOV
229
      { best_tip_with_witness with
×
UNCOV
230
        data = With_hash.data best_tip_with_witness.data
×
231
      }
232

233
    let verify ~context:(module Context : CONTEXT) ~verifier observed_state
234
        peer_root =
UNCOV
235
      let module Context = struct
×
236
        include Context
237

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

276
include Make (struct
277
  module Transition_frontier = Transition_frontier
278
  module Best_tip_prover = Best_tip_prover
279
end)
106✔
280

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

287
    let hb_logger = Logger.create ()
288

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

291
    let trust_system = Trust_system.null ()
292

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

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

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

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