Skip to content

Commit 2c885cc

Browse files
committed
Add client method to generate hard fork config
1 parent fafaa0b commit 2c885cc

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
@@ -138,7 +138,9 @@ module Make
138138
val create_checkpoint_with_directory :
139139
t -> directory_name:string -> t
140140

141-
(** Convert a root backed by a [Config.Stable_db] to *)
141+
(** Convert a root backed by a [Config.Stable_db] to one backed by a
142+
[Config.Converting_db] by gradually migrating the stable database. Does
143+
nothing if the backing is already [Config.Converting_db]. *)
142144
val make_converting : t -> t Async.Deferred.t
143145

144146
(** 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
@@ -2828,6 +2828,7 @@ module Hardfork_config = struct
28282828
; staking_epoch_seed : Epoch_seed.t
28292829
; next_epoch_seed : Epoch_seed.t
28302830
; blockchain_length : Mina_numbers.Length.t
2831+
; block_timestamp : Block_time.t
28312832
}
28322833

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

28653116
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
@@ -327,10 +327,27 @@ module Hardfork_config : sig
327327
; staking_epoch_seed : Epoch_seed.t
328328
; next_epoch_seed : Epoch_seed.t
329329
; blockchain_length : Mina_numbers.Length.t
330+
; block_timestamp : Block_time.t
330331
}
331332

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

336353
val zkapp_cmd_limit : t -> int option ref

0 commit comments

Comments
 (0)