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

MinaProtocol / mina / 678

09 Oct 2025 05:29PM UTC coverage: 32.327% (-4.8%) from 37.108%
678

push

buildkite

web-flow
Merge pull request #17927 from MinaProtocol/dkijana/port_publish_fix_master_dev

port publish fix master to dev

23380 of 72324 relevant lines covered (32.33%)

23979.59 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
112✔
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

22
  val signature_kind : Mina_signature_kind.t
23
end
24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

278
include Make (struct
279
  module Transition_frontier = Transition_frontier
280
  module Best_tip_prover = Best_tip_prover
281
end)
224✔
282

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

289
    let hb_logger = Logger.create ()
290

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

293
    let trust_system = Trust_system.null ()
294

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

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

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

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