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

MinaProtocol / mina / 584

11 Sep 2025 07:15PM UTC coverage: 14.436% (-19.8%) from 34.248%
584

push

buildkite

web-flow
Merge pull request #17778 from MinaProtocol/dkijania/publish_mina_logproc

[CI] Publish logproc in nightly

9561 of 66228 relevant lines covered (14.44%)

279.58 hits per line

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

4.62
/src/lib/transition_frontier/persistent_root/persistent_root.ml
1
open Core
36✔
2
open Mina_base
3
module Ledger = Mina_ledger.Ledger
4
open Frontier_base
5
module Ledger_transfer_any =
6
  Mina_ledger.Ledger_transfer.Make (Ledger.Any_ledger.M) (Ledger.Any_ledger.M)
7

8
let genesis_root_identifier ~genesis_state_hash =
9
  let open Root_identifier.Stable.Latest in
×
10
  { state_hash = genesis_state_hash }
11

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

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

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

61
open Instance_type
62
open Factory_type
63

64
module Instance = struct
65
  type t = Instance_type.t
66

67
  module Config = struct
68
    (** Helper to create a filesystem location (for a file or directory) inside
69
        the [Factory_type.t] directory. *)
70
    let make_instance_location filename t = Filename.concat t.directory filename
×
71

72
    (** Helper to create a [Root.Config.t] for a snarked ledger based on a
73
        subdirectory of the [Factory_type.t] directory *)
74
    let make_instance_config subdirectory t =
75
      Ledger.Root.Config.with_directory ~backing_type:t.backing_type
×
76
        ~directory_name:(make_instance_location subdirectory t)
×
77

78
    (** The config for the actual snarked ledger that is initialized and used by
79
        the daemon *)
80
    let snarked_ledger = make_instance_config "snarked_ledger"
36✔
81

82
    (** The config for the temporary snarked ledger, used while recovering a
83
        vaild potential snarked ledger during startup *)
84
    let tmp_snarked_ledger = make_instance_config "tmp_snarked_ledger"
36✔
85

86
    (** The name of a json file that lists the directory names of the potential
87
        snarked ledgers in the [potential_snarked_ledgers] queue *)
88
    let potential_snarked_ledgers =
89
      make_instance_location "potential_snarked_ledgers.json"
36✔
90

91
    (** A method that generates fresh potential snarked ledger configs, each
92
        using a distinct root subdirectory *)
93
    let make_potential_snarked_ledger t =
94
      let uuid = Uuid_unix.create () in
×
95
      make_instance_config ("snarked_ledger" ^ Uuid.to_string_hum uuid) t
×
96

97
    (** The name of the file recording the [Root_identifier.t] of the snarked
98
        root *)
99
    let root_identifier = make_instance_location "root"
36✔
100
  end
101

102
  let potential_snarked_ledgers_to_yojson queue =
103
    `List (List.map (Queue.to_list queue) ~f:Ledger.Root.Config.to_yojson)
×
104

105
  let potential_snarked_ledgers_of_yojson json =
106
    Yojson.Safe.Util.to_list json
×
107
    |> List.map ~f:(fun json ->
108
           Ledger.Root.Config.of_yojson json |> Result.ok_or_failwith )
×
109

110
  let load_potential_snarked_ledgers_from_disk factory =
111
    let location = Config.potential_snarked_ledgers factory in
×
112
    if phys_equal (Sys.file_exists location) `Yes then
×
113
      Yojson.Safe.from_file location |> potential_snarked_ledgers_of_yojson
×
114
    else []
×
115

116
  let write_potential_snarked_ledgers_to_disk t =
117
    Yojson.Safe.to_file
×
118
      (Config.potential_snarked_ledgers t.factory)
×
119
      (potential_snarked_ledgers_to_yojson t.potential_snarked_ledgers)
×
120

121
  let enqueue_snarked_ledger ~config t =
122
    Queue.enqueue t.potential_snarked_ledgers config ;
×
123
    write_potential_snarked_ledgers_to_disk t
×
124

125
  let dequeue_snarked_ledger t =
126
    let config = Queue.dequeue_exn t.potential_snarked_ledgers in
×
127
    Ledger.Root.Config.delete_any_backing config ;
×
128
    write_potential_snarked_ledgers_to_disk t
×
129

130
  let destroy t =
131
    List.iter
×
132
      (Queue.to_list t.potential_snarked_ledgers)
×
133
      ~f:Ledger.Root.Config.delete_any_backing ;
134
    Mina_stdlib_unix.File_system.rmrf
×
135
      (Config.potential_snarked_ledgers t.factory) ;
×
136
    Ledger.Root.close t.snarked_ledger ;
×
137
    t.factory.instance <- None
×
138

139
  let close t =
140
    Ledger.Root.close t.snarked_ledger ;
×
141
    t.factory.instance <- None
×
142

143
  let create ~logger factory =
144
    let snarked_ledger =
×
145
      Ledger.Root.create ~logger ~depth:factory.ledger_depth
146
        ~config:(Config.snarked_ledger factory)
×
147
        ()
148
    in
149
    { snarked_ledger; potential_snarked_ledgers = Queue.create (); factory }
×
150

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

254
  let snarked_ledger { snarked_ledger; _ } = snarked_ledger
×
255

256
  let set_root_identifier t new_root_identifier =
257
    [%log' trace t.factory.logger]
×
258
      ~metadata:
259
        [ ("root_identifier", Root_identifier.to_yojson new_root_identifier) ]
×
260
      "Setting persistent root identifier" ;
261
    let size = Root_identifier.Stable.Latest.bin_size_t new_root_identifier in
×
262
    with_file (Config.root_identifier t.factory) `Write ~size ~f:(fun buf ->
×
263
        ignore
×
264
          ( Root_identifier.Stable.Latest.bin_write_t buf ~pos:0
×
265
              new_root_identifier
266
            : int ) )
267

268
  (* defaults to genesis *)
269
  let load_root_identifier t =
270
    let file = Config.root_identifier t.factory in
×
271
    match Unix.access file [ `Exists; `Read ] with
×
272
    | Error _ ->
×
273
        None
274
    | Ok () ->
×
275
        with_file file `Read ~f:(fun buf ->
276
            let root_identifier =
×
277
              Root_identifier.Stable.Latest.bin_read_t buf ~pos_ref:(ref 0)
278
            in
279
            [%log' trace t.factory.logger]
×
280
              ~metadata:
281
                [ ("root_identifier", Root_identifier.to_yojson root_identifier)
×
282
                ]
283
              "Loaded persistent root identifier" ;
284
            Some root_identifier )
×
285

286
  let set_root_state_hash t state_hash = set_root_identifier t { state_hash }
×
287
end
288

289
type t = Factory_type.t
290

291
let create ~logger ~directory ~backing_type ~ledger_depth =
292
  { directory; logger; instance = None; ledger_depth; backing_type }
×
293

294
let create_instance_exn t =
295
  assert (Option.is_none t.instance) ;
×
296
  let instance = Instance.create ~logger:t.logger t in
297
  t.instance <- Some instance ;
×
298
  instance
299

300
let load_from_disk_exn t ~snarked_ledger_hash ~logger =
301
  let open Result.Let_syntax in
×
302
  assert (Option.is_none t.instance) ;
×
303
  let%map instance = Instance.load_from_disk t ~snarked_ledger_hash ~logger in
×
304
  t.instance <- Some instance ;
×
305
  instance
306

307
let with_instance_exn t ~f =
308
  let instance = create_instance_exn t in
×
309
  let x = f instance in
×
310
  Instance.close instance ; x
×
311

312
let reset_to_genesis_exn t ~precomputed_values =
313
  assert (Option.is_none t.instance) ;
×
314
  Mina_stdlib_unix.File_system.rmrf t.directory ;
315
  with_instance_exn t ~f:(fun instance ->
×
316
      ignore
×
317
        ( Precomputed_values.populate_root precomputed_values
×
318
            (Instance.snarked_ledger instance)
×
319
          |> Or_error.map ~f:Ledger.Root.as_unmasked
×
320
          : Ledger.Any_ledger.witness Or_error.t ) ;
321
      Instance.set_root_identifier instance
322
        (genesis_root_identifier
323
           ~genesis_state_hash:
324
             (Precomputed_values.genesis_state_hashes precomputed_values)
×
325
               .state_hash ) )
72✔
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