diff --git a/etherlink/bin_node/lib_dev/events.ml b/etherlink/bin_node/lib_dev/events.ml index e7bd48f542cd2a2f0c5af5ad7afb7632625f7c56..a880dd4acbe269a3b62a7af5518ae6b792768f60 100644 --- a/etherlink/bin_node/lib_dev/events.ml +++ b/etherlink/bin_node/lib_dev/events.ml @@ -63,6 +63,15 @@ let event_callback_log = ("method", Data_encoding.string) ("body", Data_encoding.string) +let migrating_data_dir = + Internal_event.Simple.declare_2 + ~section + ~name:"migration_data_dir" + ~msg:"Migrating from {before} to {after}" + ~level:Notice + ("before", Data_encoding.int8) + ("after", Data_encoding.int8) + let received_upgrade payload = emit received_upgrade payload let ignored_kernel_arg () = emit ignored_kernel_arg () @@ -75,3 +84,5 @@ let shutdown_rpc_server ~private_ = let shutdown_node ~exit_status = emit event_shutdown_node exit_status let callback_log ~uri ~meth ~body = emit event_callback_log (uri, meth, body) + +let migrating_data_dir ~before ~after = emit migrating_data_dir (before, after) diff --git a/etherlink/bin_node/lib_dev/events.mli b/etherlink/bin_node/lib_dev/events.mli index ade6c96e406146af7b27f8d62b95eb270bdcd962..051640a4a3b25f155cbfa0987e6ceffde90d0263 100644 --- a/etherlink/bin_node/lib_dev/events.mli +++ b/etherlink/bin_node/lib_dev/events.mli @@ -35,3 +35,7 @@ val shutdown_node : exit_status:int -> unit Lwt.t (** [callback_log ~uri ~meth ~body] is used as the debug event used as callback for resto to logs the requests. *) val callback_log : uri:string -> meth:string -> body:string -> unit Lwt.t + +(** [migrating_data_dir ~before ~after] advertises that the node is running + a data dir migration from [before] to [after]. *) +val migrating_data_dir : before:int -> after:int -> unit Lwt.t diff --git a/etherlink/bin_node/lib_dev/evm_context.ml b/etherlink/bin_node/lib_dev/evm_context.ml index 671a34b27234535927132a95cb08c2961e032b3c..e9c4e75604492538c23ec85dbb37eba76c7a8d07 100644 --- a/etherlink/bin_node/lib_dev/evm_context.ml +++ b/etherlink/bin_node/lib_dev/evm_context.ml @@ -17,6 +17,58 @@ type t = { store : Store.t; } +module Version = struct + type version = V0 | V1 + + let current = V0 + + let encoding = + let open Data_encoding in + union + [ + case + ~title:"v0" + (Tag 0) + unit + (function V0 -> Some () | _ -> None) + (fun () -> V0); + case + ~title:"v1" + (Tag 1) + unit + (function V1 -> Some () | _ -> None) + (fun () -> V1); + ] + + let to_int = function V0 -> 0 | V1 -> 1 + + let path data_dir = Filename.Infix.(data_dir // "version") + + type error += Cannot_store_version + + let set data_dir version = + let open Lwt_syntax in + let path = path data_dir in + let* res = + Lwt_utils_unix.with_open_out ~overwrite:true path (fun fd -> + let content = Data_encoding.Binary.to_string_exn encoding version in + Lwt_utils_unix.write_string fd content) + in + return (Result.map_error (fun _ -> [Cannot_store_version]) res) + + let get data_dir = + let open Lwt_result_syntax in + let path = path data_dir in + let*! version_exists = Lwt_unix.file_exists path in + if version_exists then + let*! content = Lwt_utils_unix.read_file path in + let version = Data_encoding.Binary.of_string_exn encoding content in + return version + else + let* () = set data_dir current in + return current +end + let store_path ~data_dir = Filename.Infix.(data_dir // "store") let load ~data_dir index = @@ -131,8 +183,65 @@ let apply_and_publish_blueprint (ctxt : t) (blueprint : Sequencer_blueprint.t) = let* () = Blueprints_publisher.publish level blueprint.to_publish in return ctxt +module Migration = struct + type metadata = { + checkpoint : Context_hash.t; + next_blueprint_number : Ethereum_types.quantity; + current_block_hash : Ethereum_types.block_hash; + } + + let metadata_encoding = + let open Data_encoding in + conv + (fun {checkpoint; next_blueprint_number; current_block_hash} -> + (checkpoint, next_blueprint_number, current_block_hash)) + (fun (checkpoint, next_blueprint_number, current_block_hash) -> + {checkpoint; next_blueprint_number; current_block_hash}) + (obj3 + (req "checkpoint" Context_hash.encoding) + (req "next_blueprint_number" Ethereum_types.quantity_encoding) + (req "current_block_hash" Ethereum_types.block_hash_encoding)) +end + +let migrate_if_needed ~data_dir version = + let open Lwt_result_syntax in + match version with + | Version.V0 -> + let open Filename.Infix in + let after = Version.V1 in + let*! () = + Events.migrating_data_dir + ~before:(Version.to_int version) + ~after:(Version.to_int after) + in + let metadata_path = data_dir // "metadata" in + let*! exists = Lwt_unix.file_exists metadata_path in + if exists then + let*! contents = Lwt_utils_unix.read_file metadata_path in + let json = + match Data_encoding.Json.from_string contents with + | Ok json -> json + | Error _ -> Stdlib.failwith "Cannot parse metadata file" + in + let metadata = + Data_encoding.Json.(destruct Migration.metadata_encoding json) + in + let current = + match metadata.Migration.next_blueprint_number with + | Qty x -> Ethereum_types.Qty Z.(pred x) + in + let* store = Store.init ~data_dir in + let* () = + Store.Context_hashes.store store current metadata.checkpoint + in + Version.set data_dir after + else return_unit + | V1 -> return_unit + let init ?kernel_path ~data_dir ~preimages ~smart_rollup_address () = let open Lwt_result_syntax in + let* version = Version.get data_dir in + let* () = migrate_if_needed ~data_dir version in let* index = Irmin_context.load ~cache_size:100_000 Read_write (store_path ~data_dir) in diff --git a/etherlink/bin_node/main.ml b/etherlink/bin_node/main.ml index baa23f1e3a8d41eddb5b2a682a2b69902b7f03d6..a1dd2641c6fd33b7dc1c28e6021f7613577e7196 100644 --- a/etherlink/bin_node/main.ml +++ b/etherlink/bin_node/main.ml @@ -1032,61 +1032,6 @@ let init_from_rollup_node_command = ~data_dir ~rollup_node_data_dir) -module Migration = struct - type metadata = { - checkpoint : Context_hash.t; - next_blueprint_number : Ethereum_types.quantity; - current_block_hash : Ethereum_types.block_hash; - } - - let metadata_encoding = - let open Data_encoding in - conv - (fun {checkpoint; next_blueprint_number; current_block_hash} -> - (checkpoint, next_blueprint_number, current_block_hash)) - (fun (checkpoint, next_blueprint_number, current_block_hash) -> - {checkpoint; next_blueprint_number; current_block_hash}) - (obj3 - (req "checkpoint" Context_hash.encoding) - (req "next_blueprint_number" Ethereum_types.quantity_encoding) - (req "current_block_hash" Ethereum_types.block_hash_encoding)) -end - -let migrate_metadata_command = - let open Tezos_clic in - command - ~desc:"Migrate the old metadata json file to the new sqlite database" - (args1 data_dir_arg) - (prefixes ["migrate"; "metadata"; "file"] @@ stop) - (fun data_dir () -> - let open Filename.Infix in - let open Lwt_result_syntax in - let metadata_path = data_dir // "metadata" in - let*! exists = Lwt_unix.file_exists metadata_path in - if exists then - let*! contents = Lwt_utils_unix.read_file metadata_path in - let json = - match Data_encoding.Json.from_string contents with - | Ok json -> json - | Error _ -> Stdlib.failwith "Cannot parse metadata file" - in - let metadata = - Data_encoding.Json.(destruct Migration.metadata_encoding json) - in - let current = - match metadata.Migration.next_blueprint_number with - | Qty x -> Ethereum_types.Qty Z.(pred x) - in - let* store = Evm_node_lib_dev.Store.init ~data_dir in - let* () = - Evm_node_lib_dev.Store.Context_hashes.store - store - current - metadata.checkpoint - in - return_unit - else return_unit) - let dump_to_rlp = let open Tezos_clic in let open Lwt_result_syntax in @@ -1139,7 +1084,6 @@ let commands = chunker_command; make_upgrade_command; init_from_rollup_node_command; - migrate_metadata_command; dump_to_rlp; ]