Skip to content

Commit 0faf843

Browse files
committed
Add client method to generate hard fork config
1 parent 5e3d2a4 commit 0faf843

File tree

7 files changed

+308
-1
lines changed

7 files changed

+308
-1
lines changed

src/app/cli/src/init/client.ml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2289,6 +2289,27 @@ let thread_graph =
22892289
(humanize_graphql_error ~graphql_endpoint e) ) ;
22902290
exit 1 ) )
22912291

2292+
let generate_hardfork_config =
2293+
let open Command.Param in
2294+
let hardfork_config_dir_flag =
2295+
flag "--hardfork-config-dir"
2296+
~doc:"DIR Directory to generate hardfork configuration" (required string)
2297+
in
2298+
Command.async ~summary:"Generate reference hardfork configuration"
2299+
(Cli_lib.Background_daemon.rpc_init hardfork_config_dir_flag
2300+
~f:(fun port directory_name ->
2301+
match%bind
2302+
Daemon_rpcs.Client.dispatch_join_errors
2303+
Daemon_rpcs.Generate_hardfork_config.rpc directory_name port
2304+
with
2305+
| Ok () ->
2306+
printf "Hardfork configuration successfully generated\n" ;
2307+
exit 0
2308+
| Error e ->
2309+
eprintf "Failed to generate hard fork config: %s\n"
2310+
(Error.to_string_hum e) ;
2311+
exit 1 ) )
2312+
22922313
let signature_kind =
22932314
Command.basic
22942315
~summary:"Print the signature kind that this binary is compiled with"
@@ -2533,6 +2554,7 @@ let advanced ~itn_features =
25332554
; ("vrf", Cli_lib.Commands.Vrf.command_group)
25342555
; ("thread-graph", thread_graph)
25352556
; ("print-signature-kind", signature_kind)
2557+
; ("generate-hardfork-config", generate_hardfork_config)
25362558
; ( "test"
25372559
, Command.group ~summary:"Testing-only commands"
25382560
[ ("create-genesis", test_genesis_creation) ] )

src/app/cli/src/init/mina_run.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,10 @@ let setup_local_server ?(client_trustlist = []) ?rest_server_port
369369
; implement Daemon_rpcs.Get_object_lifetime_statistics.rpc (fun () () ->
370370
return
371371
(Yojson.Safe.pretty_to_string @@ Allocation_functor.Table.dump ()) )
372+
; implement Daemon_rpcs.Generate_hardfork_config.rpc
373+
(fun () directory_name ->
374+
Mina_lib.Hardfork_config.dump_reference_config
375+
~breadcrumb_spec:`Stop_slot ~directory_name mina )
372376
; implement Daemon_rpcs.Submit_internal_log.rpc
373377
(fun () { timestamp; message; metadata; process } ->
374378
let metadata =

src/lib/daemon_rpcs/daemon_rpcs.ml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,13 @@ module Get_object_lifetime_statistics = struct
346346
Rpc.Rpc.create ~name:"Get_object_lifetime_statistics" ~version:0 ~bin_query
347347
~bin_response
348348
end
349+
350+
module Generate_hardfork_config = struct
351+
type query = string [@@deriving bin_io_unversioned]
352+
353+
type response = unit Or_error.t [@@deriving bin_io_unversioned]
354+
355+
let rpc : (query, response) Rpc.Rpc.t =
356+
Rpc.Rpc.create ~name:"Generate_hardfork_config" ~version:0 ~bin_query
357+
~bin_response
358+
end

src/lib/mina_graphql/mina_graphql.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2618,6 +2618,7 @@ module Queries = struct
26182618
; staking_epoch_seed
26192619
; next_epoch_seed
26202620
; blockchain_length
2621+
; block_timestamp = _
26212622
} =
26222623
Mina_lib.Hardfork_config.prepare_inputs ~breadcrumb_spec mina
26232624
in

src/lib/mina_ledger/root.mli

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ module Make
143143
backed by that checkpoint. *)
144144
val create_checkpoint_with_directory : t -> directory_name:string -> t
145145

146-
(** Convert a root backed by a [Config.Stable_db] to *)
146+
(** Convert a root backed by a [Config.Stable_db] to one backed by a
147+
[Config.Converting_db] by gradually migrating the stable database. Does
148+
nothing if the backing is already [Config.Converting_db]. *)
147149
val make_converting : t -> t Async.Deferred.t
148150

149151
(** View the root ledger as an unmasked [Any_ledger] so it can be used by code

src/lib/mina_lib/mina_lib.ml

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2833,6 +2833,7 @@ module Hardfork_config = struct
28332833
; staking_epoch_seed : Epoch_seed.t
28342834
; next_epoch_seed : Epoch_seed.t
28352835
; blockchain_length : Mina_numbers.Length.t
2836+
; block_timestamp : Block_time.t
28362837
}
28372838

28382839
let prepare_inputs ~breadcrumb_spec mina =
@@ -2845,6 +2846,7 @@ module Hardfork_config = struct
28452846
|> Consensus.Data.Consensus_state.global_slot_since_genesis
28462847
in
28472848
let state_hash = Transition_frontier.Breadcrumb.state_hash breadcrumb in
2849+
let block_timestamp = block |> Mina_block.timestamp in
28482850
let protocol_state =
28492851
Transition_frontier.Breadcrumb.protocol_state breadcrumb
28502852
in
@@ -2864,7 +2866,256 @@ module Hardfork_config = struct
28642866
; staking_epoch_seed
28652867
; next_epoch_seed
28662868
; blockchain_length
2869+
; block_timestamp
28672870
}
2871+
2872+
(** Copy the roots of the [source_ledgers] and gather the stable ledger
2873+
diffs from the [source_ledgers] to their roots *)
2874+
let copy_genesis_roots_and_diffs ~source_ledgers parent_directory =
2875+
Core.Unix.mkdir_p parent_directory ;
2876+
let genesis_ledger_data =
2877+
let directory_name = parent_directory ^/ "genesis_ledger" in
2878+
let root =
2879+
Ledger.Root.create_checkpoint_with_directory
2880+
source_ledgers.root_snarked_ledger ~directory_name
2881+
in
2882+
let diff = Ledger.all_accounts_on_masks source_ledgers.staged_ledger in
2883+
(root, diff)
2884+
in
2885+
let genesis_staking_ledger_data =
2886+
let directory_name = parent_directory ^/ "staking_ledger" in
2887+
match source_ledgers.staking_ledger with
2888+
| `Genesis _l ->
2889+
failwith "TODO!"
2890+
| `Root l ->
2891+
let root =
2892+
Ledger.Root.create_checkpoint_with_directory l ~directory_name
2893+
in
2894+
let diff = Ledger.Location.Map.empty in
2895+
(root, diff)
2896+
in
2897+
let genesis_next_epoch_ledger_data =
2898+
let directory_name = parent_directory ^/ "next_epoch_ledger" in
2899+
match source_ledgers.next_epoch_ledger with
2900+
| `Genesis _l ->
2901+
failwith "TODO!"
2902+
| `Root l ->
2903+
let root =
2904+
Ledger.Root.create_checkpoint_with_directory l ~directory_name
2905+
in
2906+
let diff = Ledger.Location.Map.empty in
2907+
(root, diff)
2908+
| `Uncommitted l ->
2909+
let root =
2910+
Ledger.Root.create_checkpoint_with_directory
2911+
source_ledgers.root_snarked_ledger ~directory_name
2912+
in
2913+
let diff = Ledger.all_accounts_on_masks l in
2914+
(root, diff)
2915+
in
2916+
( genesis_ledger_data
2917+
, genesis_staking_ledger_data
2918+
, genesis_next_epoch_ledger_data )
2919+
2920+
(** Generate the tar file and runtime ledger config for the given root
2921+
database, and close and delete the database *)
2922+
let generate_tar_and_config ~get_directory ~get_root_hash ~close_root ~logger
2923+
~target_dir ~ledger_name_prefix root =
2924+
let open Deferred.Or_error.Let_syntax in
2925+
let root_hash = get_root_hash root in
2926+
let ledger_dirname = get_directory root |> Option.value_exn in
2927+
let%bind tar_path =
2928+
Genesis_ledger_helper.Ledger.generate_tar ~logger ~target_dir
2929+
~ledger_name_prefix ~root_hash ~ledger_dirname ()
2930+
in
2931+
let%map s3_data_hash =
2932+
Genesis_ledger_helper.sha3_hash tar_path
2933+
|> Deferred.map ~f:Or_error.return
2934+
in
2935+
let config =
2936+
Runtime_config.ledger_of_hashes
2937+
~root_hash:(Mina_base.Ledger_hash.to_base58_check root_hash)
2938+
~s3_data_hash ()
2939+
in
2940+
close_root root ;
2941+
Mina_stdlib_unix.File_system.rmrf ledger_dirname ;
2942+
config
2943+
2944+
let generate_tars_and_configs ~get_directory ~get_root_hash ~close_root
2945+
~logger ~target_dir genesis_ledger genesis_staking_ledger
2946+
genesis_next_epoch_ledger =
2947+
let open Deferred.Or_error.Let_syntax in
2948+
Core.Unix.mkdir_p target_dir ;
2949+
let%bind genesis_ledger_config =
2950+
generate_tar_and_config ~get_directory ~get_root_hash ~close_root ~logger
2951+
~target_dir ~ledger_name_prefix:"genesis_ledger" genesis_ledger
2952+
in
2953+
let%bind genesis_staking_ledger_config =
2954+
generate_tar_and_config ~get_directory ~get_root_hash ~close_root ~logger
2955+
~target_dir ~ledger_name_prefix:"epoch_ledger" genesis_staking_ledger
2956+
in
2957+
let%map genesis_next_epoch_ledger_config =
2958+
generate_tar_and_config ~get_directory ~get_root_hash ~close_root ~logger
2959+
~target_dir ~ledger_name_prefix:"epoch_ledger" genesis_next_epoch_ledger
2960+
in
2961+
( genesis_ledger_config
2962+
, genesis_staking_ledger_config
2963+
, genesis_next_epoch_ledger_config )
2964+
2965+
let make_full_config ~genesis_state_timestamp ~global_slot_since_genesis
2966+
~state_hash ~blockchain_length ~staking_epoch_seed ~next_epoch_seed
2967+
( genesis_ledger_config
2968+
, genesis_staking_ledger_config
2969+
, genesis_next_epoch_ledger_config ) =
2970+
Runtime_config.make_automatic_fork_config ~genesis_state_timestamp
2971+
~genesis_ledger_config ~global_slot_since_genesis ~state_hash
2972+
~blockchain_length ~staking_ledger_config:genesis_staking_ledger_config
2973+
~staking_epoch_seed:(Epoch_seed.to_base58_check staking_epoch_seed)
2974+
~next_epoch_ledger_config:(Some genesis_next_epoch_ledger_config)
2975+
~next_epoch_seed:(Epoch_seed.to_base58_check next_epoch_seed)
2976+
2977+
let write_config_file ~filename daemon_config =
2978+
Async.Writer.save filename
2979+
~contents:(Yojson.Safe.to_string (Runtime_config.to_yojson daemon_config))
2980+
|> Deferred.map ~f:Or_error.return
2981+
2982+
let write_stable_config_directory ~logger ~genesis_state_timestamp
2983+
~global_slot_since_genesis ~state_hash ~staking_epoch_seed
2984+
~next_epoch_seed ~blockchain_length ~config_dir genesis_ledger
2985+
genesis_staking_ledger genesis_next_epoch_ledger =
2986+
let open Deferred.Or_error.Let_syntax in
2987+
[%log debug]
2988+
"Generating database files and daemon.json for stable hard fork config" ;
2989+
Core.Unix.mkdir_p config_dir ;
2990+
let genesis_dir = config_dir ^/ "genesis" in
2991+
let%bind genesis_config =
2992+
generate_tars_and_configs ~get_directory:Ledger.Db.get_directory
2993+
~get_root_hash:Ledger.Db.merkle_root ~close_root:Ledger.Db.close ~logger
2994+
~target_dir:genesis_dir genesis_ledger genesis_staking_ledger
2995+
genesis_next_epoch_ledger
2996+
in
2997+
write_config_file
2998+
~filename:(config_dir ^/ "daemon.json")
2999+
(make_full_config ~genesis_state_timestamp ~global_slot_since_genesis
3000+
~state_hash ~blockchain_length ~staking_epoch_seed ~next_epoch_seed
3001+
genesis_config )
3002+
3003+
let write_migrated_config_directory ~logger ~genesis_state_timestamp
3004+
~global_slot_since_genesis ~state_hash ~staking_epoch_seed
3005+
~next_epoch_seed ~blockchain_length ~config_dir genesis_ledger
3006+
genesis_staking_ledger genesis_next_epoch_ledger =
3007+
let open Deferred.Or_error.Let_syntax in
3008+
[%log debug]
3009+
"Generating database files and daemon.json for migrated hard fork config" ;
3010+
Core.Unix.mkdir_p config_dir ;
3011+
let genesis_dir = config_dir ^/ "genesis" in
3012+
let%bind genesis_config =
3013+
generate_tars_and_configs ~get_directory:Ledger.Hardfork_db.get_directory
3014+
~get_root_hash:Ledger.Hardfork_db.merkle_root
3015+
~close_root:Ledger.Hardfork_db.close ~logger ~target_dir:genesis_dir
3016+
genesis_ledger genesis_staking_ledger genesis_next_epoch_ledger
3017+
in
3018+
write_config_file
3019+
~filename:(config_dir ^/ "daemon.json")
3020+
(make_full_config ~genesis_state_timestamp ~global_slot_since_genesis
3021+
~state_hash ~blockchain_length ~staking_epoch_seed ~next_epoch_seed
3022+
genesis_config )
3023+
3024+
let genesis_timestamp_str ~hardfork_genesis_timestamp_offset block_timestamp =
3025+
block_timestamp |> Block_time.to_time_exn
3026+
|> Fn.flip Time.add hardfork_genesis_timestamp_offset
3027+
|> Time.to_string_iso8601_basic ~zone:Time.Zone.utc
3028+
3029+
let generate_hardfork_configs ~logger
3030+
~inputs:
3031+
{ source_ledgers
3032+
; global_slot_since_genesis
3033+
; state_hash
3034+
; staking_epoch_seed
3035+
; next_epoch_seed
3036+
; blockchain_length
3037+
; block_timestamp
3038+
} ~build_dir directory_name =
3039+
let open Deferred.Or_error.Let_syntax in
3040+
let migrate_and_apply (root, diff) =
3041+
let%map.Deferred root = Ledger.Root.make_converting root in
3042+
Ledger.Any_ledger.M.set_batch
3043+
(Ledger.Root.as_unmasked root)
3044+
(Map.to_alist diff) ;
3045+
let stable_db, migrated_db_opt =
3046+
Ledger.Root.unsafely_decompose_root root
3047+
in
3048+
let migrated_db =
3049+
migrated_db_opt
3050+
|> Option.value_exn
3051+
~message:"Invariant: root was already made converting"
3052+
in
3053+
(stable_db, migrated_db)
3054+
in
3055+
[%log debug] "Copying hard fork genesis ledger inputs" ;
3056+
let ( genesis_ledger_data
3057+
, genesis_staking_ledger_data
3058+
, genesis_next_epoch_ledger_data ) =
3059+
copy_genesis_roots_and_diffs ~source_ledgers build_dir
3060+
in
3061+
let%bind.Deferred genesis_ledger_legacy, genesis_ledger_migrated =
3062+
migrate_and_apply genesis_ledger_data
3063+
in
3064+
let%bind.Deferred ( genesis_staking_ledger_legacy
3065+
, genesis_staking_ledger_migrated ) =
3066+
migrate_and_apply genesis_staking_ledger_data
3067+
in
3068+
let%bind.Deferred ( genesis_next_epoch_ledger_legacy
3069+
, genesis_next_epoch_ledger_migrated ) =
3070+
migrate_and_apply genesis_next_epoch_ledger_data
3071+
in
3072+
(* TODO: the correct timestamp is actually the timestamp of the slot_tx_end plus the hardfork genesis offset *)
3073+
let genesis_state_timestamp =
3074+
genesis_timestamp_str
3075+
~hardfork_genesis_timestamp_offset:(Time.Span.of_int_sec 0)
3076+
block_timestamp
3077+
in
3078+
[%log debug] "Writing hard fork config directories" ;
3079+
let%bind () =
3080+
write_stable_config_directory ~logger ~genesis_state_timestamp
3081+
~global_slot_since_genesis ~state_hash ~staking_epoch_seed
3082+
~next_epoch_seed ~blockchain_length
3083+
~config_dir:(directory_name ^/ "fork_validation" ^/ "legacy")
3084+
genesis_ledger_legacy genesis_staking_ledger_legacy
3085+
genesis_next_epoch_ledger_legacy
3086+
in
3087+
let%bind () =
3088+
write_migrated_config_directory ~logger ~genesis_state_timestamp
3089+
~global_slot_since_genesis ~state_hash ~staking_epoch_seed
3090+
~next_epoch_seed ~blockchain_length ~config_dir:directory_name
3091+
genesis_ledger_migrated genesis_staking_ledger_migrated
3092+
genesis_next_epoch_ledger_migrated
3093+
in
3094+
return ()
3095+
3096+
let dump_reference_config ~breadcrumb_spec ~directory_name mina =
3097+
let open Deferred.Or_error.Let_syntax in
3098+
let logger = mina.config.logger in
3099+
Deferred.Or_error.try_with_join ~here:[%here]
3100+
@@ fun () ->
3101+
let%bind.Deferred dir_exists =
3102+
Mina_stdlib_unix.File_system.dir_exists directory_name
3103+
in
3104+
let%bind () =
3105+
if dir_exists then
3106+
Deferred.Or_error.error_string
3107+
"Requested config directory already exists"
3108+
else return ()
3109+
in
3110+
[%log debug] "Creating reference hard fork config in $directory_name"
3111+
~metadata:[ ("directory_name", `String directory_name) ] ;
3112+
let%bind.Deferred () =
3113+
Mina_stdlib_unix.File_system.create_dir directory_name
3114+
in
3115+
let%bind inputs = prepare_inputs ~breadcrumb_spec mina in
3116+
Mina_stdlib_unix.File_system.with_temp_dir (directory_name ^/ "_build")
3117+
~f:(fun build_dir ->
3118+
generate_hardfork_configs ~logger ~inputs ~build_dir directory_name )
28683119
end
28693120

28703121
let zkapp_cmd_limit t = t.config.zkapp_cmd_limit

src/lib/mina_lib/mina_lib.mli

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,10 +329,27 @@ module Hardfork_config : sig
329329
; staking_epoch_seed : Epoch_seed.t
330330
; next_epoch_seed : Epoch_seed.t
331331
; blockchain_length : Mina_numbers.Length.t
332+
; block_timestamp : Block_time.t
332333
}
333334

334335
val prepare_inputs :
335336
breadcrumb_spec:breadcrumb_spec -> mina_lib -> inputs Deferred.Or_error.t
337+
338+
(** Compute a full hard fork config (genesis ledger, genesis epoch ledgers,
339+
and node config) both without hard fork ledger migrations applied (the
340+
"legacy" format, compatible with the current daemon) and with the hard
341+
fork ledger migrations applied (the actual hard fork format, compatible
342+
with a hard fork daemon). The legacy format config will be saved in
343+
[daemon.legacy.json] and [genesis_legacy/] in [directory_name], and the
344+
hard fork format files will be saved in [daemon.json] and [genesis/] in
345+
that same directory. An empty [activated] file will be created in
346+
[directory_name] at the very end of this process to indicate that the
347+
config was generated successfully. *)
348+
val dump_reference_config :
349+
breadcrumb_spec:breadcrumb_spec
350+
-> directory_name:string
351+
-> mina_lib
352+
-> unit Deferred.Or_error.t
336353
end
337354

338355
val zkapp_cmd_limit : t -> int option ref

0 commit comments

Comments
 (0)