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

MinaProtocol / mina / 1274

09 Apr 2026 08:59PM UTC coverage: 34.128% (-3.9%) from 38.0%
1274

push

buildkite

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

25750 of 75451 relevant lines covered (34.13%)

16032.54 hits per line

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

3.57
/src/lib/transition_frontier/persistent_root/persistent_root.ml
1
open Async
146✔
2
open Core
3
open Mina_base
4
module Ledger = Mina_ledger.Ledger
5
open Frontier_base
6
module Ledger_transfer_any =
7
  Mina_ledger.Ledger_transfer.Make (Ledger.Any_ledger.M) (Ledger.Any_ledger.M)
8
module Root_ledger = Mina_ledger.Root
9

10
let with_file ?size filename access_level ~f =
11
  let open Unix in
×
12
  let shared, mode =
13
    match access_level with
14
    | `Read ->
×
15
        (false, [ O_RDONLY ])
16
    | `Write ->
×
17
        (true, [ O_RDWR; O_TRUNC; O_CREAT ])
18
  in
19
  let fd = Unix.openfile filename ~mode in
20
  let buf_size =
×
21
    match size with
22
    | None ->
×
23
        Int64.to_int_exn Unix.(fstat fd).st_size
×
24
    | Some sz ->
×
25
        sz
26
  in
27
  (* Bigstring.map_file has been removed. We copy its old implementation. *)
28
  let buf =
29
    Bigarray.(
30
      array1_of_genarray
×
31
        (Core.Unix.map_file fd char c_layout ~shared [| buf_size |]))
×
32
  in
33
  let x = f buf in
34
  Bigstring.unsafe_destroy buf ;
×
35
  Unix.close fd ;
×
36
  x
×
37

38
(* TODO: create a reusable singleton factory abstraction *)
39
module rec Instance_type : sig
40
  type t =
41
    { snarked_ledger : Root_ledger.t
42
    ; potential_snarked_ledgers : Root_ledger.Config.t Queue.t
43
    ; factory : Factory_type.t
44
    }
45
end =
46
  Instance_type
47

48
and Factory_type : sig
49
  type t =
50
    { directory : string
51
    ; logger : Logger.t
52
    ; mutable instance : Instance_type.t option
53
    ; ledger_depth : int
54
    ; backing_type : Root_ledger.Config.backing_type
55
    }
56
end =
57
  Factory_type
58

59
open Instance_type
60
open Factory_type
61

62
module Config = struct
63
  (** Helper to create a filesystem location (for a file or directory) inside
64
        the [Factory_type.t] directory. *)
65
  let make_instance_location filename t = Filename.concat t.directory filename
×
66

67
  (** Helper to create a [Root_ledger.Config.t] for a snarked ledger based on a
68
        subdirectory of the [Factory_type.t] directory *)
69
  let make_instance_config subdirectory t =
70
    Root_ledger.Config.with_directory ~backing_type:t.backing_type
×
71
      ~directory_name:(make_instance_location subdirectory t)
×
72

73
  (** The config for the actual snarked ledger that is initialized and used by
74
        the daemon *)
75
  let snarked_ledger = make_instance_config "snarked_ledger"
146✔
76

77
  (** The config for the temporary snarked ledger, used while recovering a
78
        vaild potential snarked ledger during startup *)
79
  let tmp_snarked_ledger = make_instance_config "tmp_snarked_ledger"
146✔
80

81
  (** The name of a json file that lists the directory names of the potential
82
        snarked ledgers in the [potential_snarked_ledgers] queue *)
83
  let potential_snarked_ledgers =
84
    make_instance_location "potential_snarked_ledgers.json"
146✔
85

86
  (** A method that generates fresh potential snarked ledger configs, each
87
        using a distinct root subdirectory *)
88
  let make_potential_snarked_ledger t =
89
    let uuid = Uuid_unix.create () in
×
90
    make_instance_config ("snarked_ledger" ^ Uuid.to_string_hum uuid) t
×
91

92
  (** The name of the file recording the [Root_identifier.t] of the snarked
93
        root *)
94
  let root_identifier = make_instance_location "root"
146✔
95
end
96

97
module Instance = struct
98
  type t = Instance_type.t
99

100
  (* There is a breaking change in the (Yojson) schema for the Mesa HF ledger
101
     config type -- previously a list of directories (ie strings), it became
102
     a list of objects. This loader is compatible with both for the awkward
103
     period when we don't everyone is running a post-Mesa build.
104
  *)
105
  let potential_snarked_ledgers_to_yojson queue =
106
    `List
×
107
      (List.map (Queue.to_list queue) ~f:(fun cfg ->
×
108
           `String (Root_ledger.Config.primary_directory cfg) ) )
×
109

110
  (* See comment for potential_snarked_ledgers_to_yojson above *)
111
  let potential_snarked_ledgers_of_yojson json =
112
    Yojson.Safe.Util.to_list json
×
113
    |> List.map ~f:(fun json ->
114
           match json with
×
115
           | `String directory_name ->
×
116
               Root_ledger.Config.with_directory
117
                 ~backing_type:Root_ledger.Config.Stable_db ~directory_name
118
           | _ ->
×
119
               Root_ledger.Config.of_yojson json |> Result.ok_or_failwith )
×
120

121
  let load_potential_snarked_ledgers_from_disk factory =
122
    let location = Config.potential_snarked_ledgers factory in
×
123
    if phys_equal (Sys.file_exists location) `Yes then
×
124
      Yojson.Safe.from_file location |> potential_snarked_ledgers_of_yojson
×
125
    else []
×
126

127
  let write_potential_snarked_ledgers_to_disk t =
128
    Yojson.Safe.to_file
×
129
      (Config.potential_snarked_ledgers t.factory)
×
130
      (potential_snarked_ledgers_to_yojson t.potential_snarked_ledgers)
×
131

132
  let enqueue_snarked_ledger ~config t =
133
    Queue.enqueue t.potential_snarked_ledgers config ;
×
134
    write_potential_snarked_ledgers_to_disk t
×
135

136
  let dequeue_snarked_ledger t =
137
    let config = Queue.dequeue_exn t.potential_snarked_ledgers in
×
138
    Root_ledger.Config.delete_any_backing config ;
×
139
    write_potential_snarked_ledgers_to_disk t
×
140

141
  let destroy t =
142
    List.iter
×
143
      (Queue.to_list t.potential_snarked_ledgers)
×
144
      ~f:Root_ledger.Config.delete_any_backing ;
145
    Mina_stdlib_unix.File_system.rmrf
×
146
      (Config.potential_snarked_ledgers t.factory) ;
×
147
    Root_ledger.close t.snarked_ledger ;
×
148
    t.factory.instance <- None
×
149

150
  let close t =
151
    Root_ledger.close t.snarked_ledger ;
×
152
    t.factory.instance <- None
×
153

154
  let create ~logger factory =
155
    let snarked_ledger =
×
156
      Root_ledger.create ~logger ~depth:factory.ledger_depth
157
        ~config:(Config.snarked_ledger factory)
×
158
        ()
159
    in
160
    { snarked_ledger; potential_snarked_ledgers = Queue.create (); factory }
×
161

162
  (** When we load from disk,
163
      1. Check the potential_snarked_ledgers to see if any one of these
164
         matches the snarked_ledger_hash in persistent_frontier;
165
      2. if none of those works, we load the old snarked_ledger and check if
166
         the old snarked_ledger matches with persistent_frontier;
167
      3. if not, we just reset all the persisted data and start from genesis
168
   *)
169
  let load_from_disk factory ~snarked_ledger_hash ~logger =
170
    let potential_snarked_ledgers =
×
171
      load_potential_snarked_ledgers_from_disk factory
172
    in
173
    let snarked_ledger =
×
174
      List.fold_until potential_snarked_ledgers ~init:None
175
        ~f:(fun _ config ->
176
          let potential_snarked_ledger =
×
177
            Root_ledger.create ~logger ~depth:factory.ledger_depth ~config ()
178
          in
179
          let potential_snarked_ledger_hash =
×
180
            Frozen_ledger_hash.of_ledger_hash
181
            @@ Root_ledger.merkle_root potential_snarked_ledger
×
182
          in
183
          [%log debug]
×
184
            ~metadata:
185
              [ ( "potential_snarked_ledger_hash"
186
                , Frozen_ledger_hash.to_yojson potential_snarked_ledger_hash )
×
187
              ]
188
            "loaded potential_snarked_ledger from disk" ;
189
          if
×
190
            Frozen_ledger_hash.equal potential_snarked_ledger_hash
191
              snarked_ledger_hash
192
          then (
×
193
            let snarked_ledger =
194
              Root_ledger.create ~logger ~depth:factory.ledger_depth
195
                ~config:(Config.tmp_snarked_ledger factory)
×
196
                ()
197
            in
198
            match
×
199
              Ledger_transfer_any.transfer_accounts
200
                ~src:(Root_ledger.as_unmasked potential_snarked_ledger)
×
201
                ~dest:(Root_ledger.as_unmasked snarked_ledger)
×
202
            with
203
            | Ok _ ->
×
204
                Root_ledger.close potential_snarked_ledger ;
205
                Root_ledger.Config.delete_any_backing
×
206
                @@ Config.snarked_ledger factory ;
×
207
                Root_ledger.Config.move_backing_exn
×
208
                  ~src:(Config.tmp_snarked_ledger factory)
×
209
                  ~dst:(Config.snarked_ledger factory) ;
×
210
                List.iter potential_snarked_ledgers
211
                  ~f:Root_ledger.Config.delete_any_backing ;
212
                Mina_stdlib_unix.File_system.rmrf
×
213
                  (Config.potential_snarked_ledgers factory) ;
×
214
                Stop (Some snarked_ledger)
×
215
            | Error e ->
×
216
                Root_ledger.close potential_snarked_ledger ;
217
                List.iter potential_snarked_ledgers
×
218
                  ~f:Root_ledger.Config.delete_any_backing ;
219
                Mina_stdlib_unix.File_system.rmrf
×
220
                  (Config.potential_snarked_ledgers factory) ;
×
221
                [%log' error factory.logger]
×
222
                  ~metadata:[ ("error", `String (Error.to_string_hum e)) ]
×
223
                  "Ledger_transfer failed" ;
224
                Stop None )
×
225
          else (
×
226
            Root_ledger.close potential_snarked_ledger ;
227
            Continue None ) )
×
228
        ~finish:(fun _ ->
229
          List.iter potential_snarked_ledgers
×
230
            ~f:Root_ledger.Config.delete_any_backing ;
231
          Mina_stdlib_unix.File_system.rmrf
×
232
            (Config.potential_snarked_ledgers factory) ;
×
233
          None )
×
234
    in
235
    match snarked_ledger with
×
236
    | None ->
×
237
        let snarked_ledger =
238
          Root_ledger.create ~logger ~depth:factory.ledger_depth
239
            ~config:(Config.snarked_ledger factory)
×
240
            ()
241
        in
242
        let potential_snarked_ledger_hash =
×
243
          Frozen_ledger_hash.of_ledger_hash
244
          @@ Root_ledger.merkle_root snarked_ledger
×
245
        in
246
        if
×
247
          Frozen_ledger_hash.equal potential_snarked_ledger_hash
248
            snarked_ledger_hash
249
        then
250
          Ok
×
251
            { snarked_ledger
252
            ; potential_snarked_ledgers = Queue.create ()
×
253
            ; factory
254
            }
255
        else (
×
256
          Root_ledger.close snarked_ledger ;
257
          Error `Snarked_ledger_mismatch )
×
258
    | Some snarked_ledger ->
×
259
        Ok
260
          { snarked_ledger
261
          ; potential_snarked_ledgers = Queue.create ()
×
262
          ; factory
263
          }
264

265
  let snarked_ledger { snarked_ledger; _ } = snarked_ledger
×
266
end
267

268
type t = Factory_type.t
269

270
let create ~logger ~directory ~backing_type ~ledger_depth =
271
  { directory; logger; instance = None; ledger_depth; backing_type }
×
272

273
let create_instance_exn t =
274
  assert (Option.is_none t.instance) ;
×
275
  let instance = Instance.create ~logger:t.logger t in
276
  t.instance <- Some instance ;
×
277
  instance
278

279
let load_from_disk_exn t ~snarked_ledger_hash ~logger =
280
  let open Result.Let_syntax in
×
281
  assert (Option.is_none t.instance) ;
×
282
  let%map instance = Instance.load_from_disk t ~snarked_ledger_hash ~logger in
×
283
  t.instance <- Some instance ;
×
284
  instance
285

286
let with_instance_exn t ~f =
287
  let instance = create_instance_exn t in
×
288
  let x = f instance in
×
289
  Instance.close instance ; x
×
290

291
let set_root_identifier t new_root_identifier =
292
  [%log' trace t.logger]
×
293
    ~metadata:
294
      [ ("root_identifier", Root_identifier.to_yojson new_root_identifier) ]
×
295
    "Setting persistent root identifier" ;
296
  let size = Root_identifier.Stable.Latest.bin_size_t new_root_identifier in
×
297
  with_file (Config.root_identifier t) `Write ~size ~f:(fun buf ->
×
298
      ignore
×
299
        ( Root_identifier.Stable.Latest.bin_write_t buf ~pos:0
×
300
            new_root_identifier
301
          : int ) )
302

303
(* defaults to genesis *)
304
let load_root_identifier t =
305
  let file = Config.root_identifier t in
×
306
  match Unix.access file [ `Exists; `Read ] with
×
307
  | Error _ ->
×
308
      None
309
  | Ok () ->
×
310
      with_file file `Read ~f:(fun buf ->
311
          let root_identifier =
×
312
            Root_identifier.Stable.Latest.bin_read_t buf ~pos_ref:(ref 0)
313
          in
314
          [%log' trace t.logger]
×
315
            ~metadata:
316
              [ ("root_identifier", Root_identifier.to_yojson root_identifier) ]
×
317
            "Loaded persistent root identifier" ;
318
          Some root_identifier )
×
319

320
let set_root_state_hash t state_hash = set_root_identifier t { state_hash }
×
321

322
(** Clear the factory directory and recreate the snarked ledger instance for
323
    this factory with [create_root] and [setup] *)
324
let reset_factory_root_exn t ~create_root ~root_state_hash =
325
  let open Async.Deferred.Let_syntax in
×
326
  assert (Option.is_none t.instance) ;
×
327
  (* Certain database initialization methods, e.g. creation from a checkpoint,
328
     depend on the parent directory existing and the target directory _not_
329
     existing. *)
330
  let%bind () = Mina_stdlib_unix.File_system.remove_dir t.directory in
×
331
  let%bind () = Mina_stdlib_unix.File_system.create_dir t.directory in
×
332
  let%map root =
333
    create_root ~config:(Config.snarked_ledger t) ~depth:t.ledger_depth ()
×
334
    |> Deferred.map ~f:Or_error.ok_exn
×
335
  in
336
  Root_ledger.close root ;
×
337
  set_root_state_hash t root_state_hash
×
338

339
let reset_to_genesis_exn t ~precomputed_values =
340
  let open Async.Deferred.Let_syntax in
×
341
  let logger = t.logger in
342
  [%log debug] "Resetting snarked_root in $directory to genesis"
×
343
    ~metadata:[ ("directory", `String t.directory) ] ;
344
  let%map () =
345
    reset_factory_root_exn t
×
346
      ~create_root:(Precomputed_values.create_root precomputed_values)
×
347
      ~root_state_hash:
348
        (Precomputed_values.genesis_state_hashes precomputed_values).state_hash
×
349
  in
350
  [%log debug] "Finished resetting snarked_root to genesis"
×
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