@@ -39,6 +39,24 @@ module Ledger_inner = struct
3939 [@@ deriving sexp , compare , hash , bin_io_unversioned ]
4040 end
4141
42+ module type Intf = sig
43+ type t [@@deriving sexp, of_sexp, hash, equal, compare, yojson]
44+
45+ type account
46+
47+ include Binable. S with type t := t
48+
49+ include module type of Hashable. Make_binable (Arg )
50+
51+ val to_base58_check : t -> string
52+
53+ val merge : height :int -> t -> t -> t
54+
55+ val hash_account : account -> t
56+
57+ val empty_account : t
58+ end
59+
4260 [%% versioned
4361 module Stable = struct
4462 module V1 = struct
@@ -61,9 +79,40 @@ module Ledger_inner = struct
6179 Ledger_hash. of_digest (Lazy. force Account. empty_digest)
6280 end
6381 end ]
82+
83+ module Unstable = struct
84+ type t = Ledger_hash.Stable.V1 .t
85+ [@@ deriving sexp , compare , hash , equal , yojson , bin_io_unversioned ]
86+
87+ include Hashable. Make_binable (Arg )
88+
89+ let to_base58_check = Ledger_hash. to_base58_check
90+
91+ let merge = Ledger_hash. merge
92+
93+ let hash_account =
94+ Fn. compose Ledger_hash. of_digest Mina_base.Account.Unstable. digest
95+
96+ let empty_account =
97+ Ledger_hash. of_digest (Lazy. force Account.Unstable. empty_digest)
98+ end
6499 end
65100
66101 module Account = struct
102+ module type Intf = sig
103+ type t [@@deriving sexp, of_sexp, equal, compare]
104+
105+ include Binable. S with type t := t
106+
107+ val balance : t -> Currency.Balance .t
108+
109+ val empty : t
110+
111+ val identifier : t -> Account_id .t
112+
113+ val token : t -> Token_id .t
114+ end
115+
67116 [%% versioned
68117 module Stable = struct
69118 module V2 = struct
@@ -84,9 +133,18 @@ module Ledger_inner = struct
84133 let empty = Stable.Latest. empty
85134
86135 let initialize = Account. initialize
136+
137+ module Unstable = struct
138+ include Mina_base.Account. Unstable
139+
140+ let token = token_id
141+ end
87142 end
88143
89- module Inputs = struct
144+ module Make_inputs
145+ (Account : Account.Intf )
146+ (Hash : Hash.Intf with type account := Account.t ) =
147+ struct
90148 module Key = Public_key. Compressed
91149 module Token_id = Token_id
92150 module Account_id = Account_id
@@ -97,15 +155,15 @@ module Ledger_inner = struct
97155 let to_int = to_nanomina_int
98156 end
99157
100- module Account = Account.Stable. Latest
101- module Hash = Hash.Stable. Latest
158+ module Account = Account
159+ module Hash = Hash
102160 module Kvdb = Kvdb
103161 module Location = Location_at_depth
104162 module Location_binable = Location_binable
105163 module Storage_locations = Storage_locations
106164 end
107165
108- module Db :
166+ module type Account_Db =
109167 Merkle_ledger.Intf.Ledger. DATABASE
110168 with module Location = Location_at_depth
111169 with module Addr = Location_at_depth. Addr
@@ -114,10 +172,16 @@ module Ledger_inner = struct
114172 and type key := Public_key.Compressed. t
115173 and type token_id := Token_id. t
116174 and type token_id_set := Token_id.Set. t
117- and type account := Account. t
118175 and type account_id_set := Account_id.Set. t
119- and type account_id := Account_id. t =
120- Database. Make (Inputs )
176+ and type account_id := Account_id. t
177+
178+ module Inputs = Make_inputs (Account.Stable. Latest ) (Hash.Stable. Latest )
179+ module Unstable_inputs = Make_inputs (Account. Unstable ) (Hash. Unstable )
180+
181+ module Db : Account_Db with type account := Account. t = Database. Make (Inputs )
182+
183+ module Unstable_db : Account_Db with type account := Account.Unstable. t =
184+ Database. Make (Unstable_inputs )
121185
122186 module Null = Null_ledger. Make (Inputs )
123187
@@ -180,6 +244,18 @@ module Ledger_inner = struct
180244
181245 type maskable_ledger = t
182246
247+ module Converting_ledger =
248+ Converting_merkle_tree. Make
249+ (struct
250+ type converted_account = Account.Unstable .t
251+
252+ let convert = Account.Unstable. of_stable
253+
254+ include Inputs
255+ end )
256+ (Db )
257+ (Unstable_db )
258+
183259 let of_database db =
184260 let casted = Any_ledger. cast (module Db ) db in
185261 let mask = Mask. create ~depth: (Db. depth db) () in
@@ -201,6 +277,60 @@ module Ledger_inner = struct
201277 let _base, mask = create_ephemeral_with_base ~depth () in
202278 mask
203279
280+ type converting_config =
281+ { primary_directory_name : string option
282+ ; converting_directory_name : string option
283+ }
284+
285+ let default_converting_directory_name primary_directory_name =
286+ primary_directory_name ^ " _converting"
287+
288+ let converting_directory_name ~cfg ~primary_directory =
289+ Option. first_some cfg.converting_directory_name
290+ @@ Option. map primary_directory ~f: default_converting_directory_name
291+
292+ let empty_converting_config : converting_config =
293+ { primary_directory_name = None ; converting_directory_name = None }
294+
295+ let create_converting ?(cfg = empty_converting_config) ~logger ~depth () =
296+ let db1 = Db. create ?directory_name:cfg.primary_directory_name ~depth () in
297+ let db2_directory_name =
298+ converting_directory_name ~cfg ~primary_directory: (Db. get_directory db1)
299+ in
300+ let db2 = Unstable_db. create ?directory_name:db2_directory_name ~depth () in
301+ let converting_ledger =
302+ if Unstable_db. num_accounts db2 = 0 then
303+ Converting_ledger. create_with_migration db1 db2
304+ else
305+ let is_synced = ref true in
306+ Db. iteri db1 ~f: (fun idx stable_account ->
307+ let expected_unstable_account =
308+ Account.Unstable. of_stable stable_account
309+ in
310+ let actual_unstable_account =
311+ Unstable_db. get_at_index_exn db2 idx
312+ in
313+ if
314+ not
315+ (Account.Unstable. equal expected_unstable_account
316+ actual_unstable_account )
317+ then is_synced := false ) ;
318+ if ! is_synced then Converting_ledger. create db1 db2
319+ else (
320+ [% log warn]
321+ " Migrating DB desync, cleaning up unstable DB and remigrating..." ;
322+ Unstable_db. close db2 ;
323+ let db2 =
324+ Unstable_db. create ?directory_name:db2_directory_name ~fresh: true
325+ ~depth ()
326+ in
327+ Converting_ledger. create_with_migration db1 db2 )
328+ in
329+ let casted = Any_ledger. cast (module Converting_ledger ) converting_ledger in
330+ let mask = Mask. create ~depth () in
331+ ( Maskable. register_mask casted mask
332+ , Converting_ledger. converting_ledger converting_ledger )
333+
204334 (* * Create a new empty ledger.
205335
206336 Warning: This skips mask registration, for use in transaction logic,
@@ -272,6 +402,22 @@ module Ledger_inner = struct
272402
273403 let packed t = Any_ledger. cast (module Mask. Attached ) t
274404
405+ let with_converting_ledger ~logger ~depth ~f =
406+ let cfg : converting_config =
407+ { primary_directory_name = None ; converting_directory_name = None }
408+ in
409+ let ledger = create_converting ~logger ~cfg ~depth () in
410+ try
411+ let result = f ledger in
412+ close (fst ledger) ;
413+ Ok result
414+ with exn ->
415+ close (fst ledger) ;
416+ Error (Error. of_exn exn )
417+
418+ let with_converting_ledger_exn ~logger ~depth ~f =
419+ with_converting_ledger ~logger ~depth ~f |> Or_error. ok_exn
420+
275421 let register_mask t mask =
276422 let accumulated = Mask.Attached. to_accumulated t in
277423 Maskable. register_mask ~accumulated (packed t) mask
@@ -741,3 +887,48 @@ let%test_unit "zkapp_command application on masked ledger" =
741887 assert (not (Ledger_hash. equal init_merkle_root (L. merkle_root l))) ;
742888 (* Parent updates reflected in child masks*)
743889 assert (Ledger_hash. equal (L. merkle_root l) (L. merkle_root m)) ) )
890+
891+ let % test_unit " user_command application on converting ledger" =
892+ let open Mina_transaction_logic.For_tests in
893+ let module L = Ledger_inner in
894+ let constraint_constants =
895+ { Genesis_constants.For_unit_tests.Constraint_constants. t with
896+ account_creation_fee = Currency.Fee. of_nanomina_int_exn 1
897+ }
898+ in
899+ let logger = Logger. create () in
900+ Quickcheck. test ~trials: 1 Test_spec. gen ~f: (fun { init_ledger; specs } ->
901+ let cmds = List. map specs ~f: command_send in
902+ L. with_converting_ledger_exn ~logger ~depth ~f: (fun (l , cl ) ->
903+ Init_ledger. init (module L ) init_ledger l ;
904+ let init_merkle_root = L. merkle_root l in
905+ let init_cl_merkle_root = Unstable_db. merkle_root cl in
906+ let () =
907+ iter_err cmds
908+ ~f:
909+ (apply_user_command_unchecked ~constraint_constants
910+ ~txn_global_slot l )
911+ |> Or_error. ok_exn
912+ in
913+ (* Assert that the ledger and the converting ledger are non-empty *)
914+ assert (not (Ledger_hash. equal init_merkle_root (L. merkle_root l))) ;
915+ L. commit l ;
916+ assert (
917+ not
918+ (Ledger_hash. equal init_cl_merkle_root
919+ (Unstable_db. merkle_root cl) ) ) ;
920+ (* Assert that the converted ledger has the same accounts as the first one, up to the new field*)
921+ L. iteri l ~f: (fun index account ->
922+ let account_converted = Unstable_db. get_at_index_exn cl index in
923+ assert (
924+ Mina_base.Account.Key. (
925+ equal account.public_key account_converted.public_key) ) ;
926+ assert (
927+ Mina_base.Account.Nonce. (
928+ equal account_converted.nonce account_converted.unstable_field) ) ) ;
929+ (* Assert that the converted ledger doesn't have anything "extra" compared to the primary ledger *)
930+ Unstable_db. iteri cl ~f: (fun index account_converted ->
931+ let account = L. get_at_index_exn l index in
932+ assert (
933+ Mina_base.Account.Key. (
934+ equal account.public_key account_converted.public_key) ) ) ) )
0 commit comments