diff --git a/etherlink/CHANGES_KERNEL.md b/etherlink/CHANGES_KERNEL.md index 3f785099fa65a80f9de12b5264f4d38c1c80b3be..793a22f184afd0f6a22261f4c1891d6c478b9d47 100644 --- a/etherlink/CHANGES_KERNEL.md +++ b/etherlink/CHANGES_KERNEL.md @@ -1,6 +1,16 @@ # Changelog -## Version Next +## Version NEXT + +### Features + +### Bug fixes + +### Breaking changes + +### Internal + +## Version 20ab639f09a8c7c76f982383c3d9e1f831f38088 ### Features diff --git a/etherlink/CHANGES_NODE.md b/etherlink/CHANGES_NODE.md index 13ea4cbc6cfe3d4bd7b2dd91b081b32e680a8b9b..bd730730ca33bebf1e78f783c18d0e4524288b48 100644 --- a/etherlink/CHANGES_NODE.md +++ b/etherlink/CHANGES_NODE.md @@ -1,6 +1,16 @@ # Changelog -## Next +## Version for NEXT + +### Features + +### Bug fixes + +### Breaking changes + +### Internal + +## Version for kernel 20ab639f09a8c7c76f982383c3d9e1f831f38088 ### Features @@ -17,7 +27,6 @@ in a sqlite database (as a consquence, the node now depends on `libsqlite3`). (!11948) -### Internal ## Version for kernel c5969505b81b52a779270b69f48b6a66c84da429 diff --git a/etherlink/bin_node/lib_prod/blueprint_store.ml b/etherlink/bin_node/lib_prod/blueprint_store.ml deleted file mode 100644 index b1281ac4fa63299942bec6b1a44d0fd9cbf0b065..0000000000000000000000000000000000000000 --- a/etherlink/bin_node/lib_prod/blueprint_store.ml +++ /dev/null @@ -1,78 +0,0 @@ -(*****************************************************************************) -(* *) -(* SPDX-License-Identifier: MIT *) -(* Copyright (c) 2024 Nomadic Labs *) -(* *) -(*****************************************************************************) - -(* - - This implementation of the Blueprints Store saves the blueprint in a simple - file system hierarchy (to avoid over-populating one directory in particular). - - The [AAAABBBBCCCC]th blueprint will be stored under the path - [data_dir/blueprints/AAAA/BBBB/CCCC] in a binary format. - - *) - -open Ethereum_types -open Filename.Infix - -type t = string - -let make ~data_dir = data_dir // "blueprint" - -let kind_to_string ~kind = - match kind with `Execute -> "execute" | `Publish -> "publish" - -let blueprint_relative_path ~kind number = - let to_string i = Format.sprintf "%04i" i in - let share = 10_000 in - let base = number / share in - let file = number mod share |> to_string in - let sub_directory = base / share |> to_string in - let sub_sub_directory = base mod share |> to_string in - (kind_to_string ~kind // sub_directory // sub_sub_directory, file) - -type error += Cannot_store_blueprint - -let store store_path ~kind blueprint (Qty number) = - let open Lwt_syntax in - let number = Z.to_int number in - let str = - Data_encoding.Binary.to_string_exn - Blueprint_types.payload_encoding - blueprint - in - - let blueprint_directory, file = blueprint_relative_path ~kind number in - - let* _ = Lwt_utils_unix.create_dir (store_path // blueprint_directory) in - - let* res = - Lwt_utils_unix.( - with_open_out - (store_path // blueprint_directory // file) - (fun fd -> write_string fd str)) - in - - return (Result.map_error (fun _ -> [Cannot_store_blueprint]) res) - -let find store_path ~kind (Qty number) = - let open Lwt_syntax in - let number = Z.to_int number in - - let blueprint_directory, file = blueprint_relative_path ~kind number in - - Lwt.catch - (fun () -> - let* str = - Lwt_utils_unix.read_file (store_path // blueprint_directory // file) - in - - return - @@ Some - (Data_encoding.Binary.of_string_exn - Blueprint_types.payload_encoding - str)) - (fun _exn -> return_none) diff --git a/etherlink/bin_node/lib_prod/blueprint_store.mli b/etherlink/bin_node/lib_prod/blueprint_store.mli deleted file mode 100644 index aae0a1e4fff568a2784aa1ae935bd925476287e8..0000000000000000000000000000000000000000 --- a/etherlink/bin_node/lib_prod/blueprint_store.mli +++ /dev/null @@ -1,35 +0,0 @@ -(*****************************************************************************) -(* *) -(* SPDX-License-Identifier: MIT *) -(* Copyright (c) 2024 Nomadic Labs *) -(* *) -(*****************************************************************************) - -type t - -val make : data_dir:string -> t - -(** [store s ~kind blueprint number] saves the [blueprint] of height [number] - in the store [s]. - - The [kind] is used to differentiate between blueprints to publish and - to execute. See {!Sequencer_blueprint} for further details. - *) -val store : - t -> - kind:[< `Execute | `Publish] -> - Blueprint_types.payload -> - Ethereum_types.quantity -> - unit tzresult Lwt.t - -(** [find s ~kind number] tries to fetch the blueprint of height [number] in - the store [s], and returns [None] if it does not exist. - - The [kind] is used to differentiate between blueprints to publish and - to execute. See {!Sequencer_blueprint} for further details. -*) -val find : - t -> - kind:[< `Execute | `Publish] -> - Ethereum_types.quantity -> - Blueprint_types.payload option Lwt.t diff --git a/etherlink/bin_node/lib_prod/blueprints_publisher.ml b/etherlink/bin_node/lib_prod/blueprints_publisher.ml index bc68f2916af404cb0fc6c37cd7ca73facbda3e45..1ab72c4d2fad53dc6090d4647b7864280853ac46 100644 --- a/etherlink/bin_node/lib_prod/blueprints_publisher.ml +++ b/etherlink/bin_node/lib_prod/blueprints_publisher.ml @@ -7,14 +7,14 @@ type parameters = { rollup_node_endpoint : Uri.t; - store : Blueprint_store.t; + store : Store.t; max_blueprints_lag : int; max_blueprints_catchup : int; catchup_cooldown : int; } type state = { - store : Blueprint_store.t; + store : Store.t; rollup_node_endpoint : Uri.t; max_blueprints_lag : Z.t; max_blueprints_catchup : Z.t; @@ -95,7 +95,7 @@ module Worker = struct (* We do not check if we succeed or not: this will be done when new L2 heads come from the rollup node. *) witness_level self level ; - let* res = Rollup_node_services.publish ~rollup_node_endpoint payload in + let* res = Rollup_services.publish ~rollup_node_endpoint payload in match res with | Ok _ -> Blueprint_events.blueprint_injected level | Error _ -> @@ -105,12 +105,7 @@ module Worker = struct Blueprint_events.blueprint_injection_failed level let catch_up worker = - let open Lwt_syntax in - let rec range min max () = - if Z.Compare.(max <= min) then return (Lwt_seq.Cons (min, Lwt_seq.empty)) - else return (Lwt_seq.Cons (min, range (Z.succ min) max)) - in - + let open Lwt_result_syntax in (* We limit the maximum number of blueprints we send at once *) let upper_bound = Z.( @@ -119,36 +114,35 @@ module Worker = struct (latest_level_seen worker)) in - let* () = + let*! () = Blueprint_events.catching_up (latest_level_confirmed worker) upper_bound in - let to_catchup = range (latest_level_confirmed worker) upper_bound in - - let* () = - Lwt_seq.iter_s - (fun level -> - let* payload = - Blueprint_store.find - ~kind:`Publish - (blueprint_store worker) - (Qty level) - in - match payload with - | Some payload -> publish worker payload level - | None -> - let* () = Blueprint_events.missing_blueprint level in - Stdlib.failwith - Format.( - asprintf - "Blueprint for level %a missing from the store" - Z.pp_print - level)) - to_catchup + + let rec catching_up curr = + if Z.Compare.(curr <= upper_bound) then + let* payload = + Store.Publishable_blueprints.find (blueprint_store worker) (Qty curr) + in + match payload with + | Some payload -> + let*! () = publish worker payload curr in + catching_up Z.(succ curr) + | None -> + let*! () = Blueprint_events.missing_blueprint curr in + Stdlib.failwith + Format.( + asprintf + "Blueprint for level %a missing from the store" + Z.pp_print + curr) + else return_unit in + let* () = catching_up (latest_level_confirmed worker) in + (* We give ourselves a cooldown window Tezos blocks to inject everything *) set_cooldown worker (catchup_cooldown worker) ; - Lwt_result_syntax.return_unit + return_unit end type worker = Worker.infinite Worker.queue Worker.t diff --git a/etherlink/bin_node/lib_prod/blueprints_publisher.mli b/etherlink/bin_node/lib_prod/blueprints_publisher.mli index 1965b2d32831775b2364da67a8f9a1eb4a617b07..f51e0a7e5fa1a5ba8eedc649a513b83d8927a8a3 100644 --- a/etherlink/bin_node/lib_prod/blueprints_publisher.mli +++ b/etherlink/bin_node/lib_prod/blueprints_publisher.mli @@ -10,7 +10,7 @@ val start : max_blueprints_lag:int -> max_blueprints_catchup:int -> catchup_cooldown:int -> - Blueprint_store.t -> + Store.t -> unit tzresult Lwt.t val shutdown : unit -> unit Lwt.t diff --git a/etherlink/bin_node/lib_prod/delayed_inbox.ml b/etherlink/bin_node/lib_prod/delayed_inbox.ml index 1c78f970bcdd71ffded1915c9a4df0ccd0e484de..7d90e4f3782067726875457fa666aa57344aaf99 100644 --- a/etherlink/bin_node/lib_prod/delayed_inbox.ml +++ b/etherlink/bin_node/lib_prod/delayed_inbox.ml @@ -66,7 +66,7 @@ module Worker = Worker.MakeSingle (Name) (Request) (Types) type worker = Worker.infinite Worker.queue Worker.t let subkeys_from_rollup_node path level rollup_node_endpoint = - let open Rollup_node_services in + let open Rollup_services in call_service ~base:rollup_node_endpoint durable_state_subkeys @@ -75,7 +75,7 @@ let subkeys_from_rollup_node path level rollup_node_endpoint = () let read_from_rollup_node path level rollup_node_endpoint = - let open Rollup_node_services in + let open Rollup_services in call_service ~base:rollup_node_endpoint durable_state_value diff --git a/etherlink/bin_node/lib_prod/dune b/etherlink/bin_node/lib_prod/dune index b1a45ef2c66893f15c7f746027f8a6d57014bb65..213c06430e21c6ccd65ffa0046aed2ebb6d9d597 100644 --- a/etherlink/bin_node/lib_prod/dune +++ b/etherlink/bin_node/lib_prod/dune @@ -16,6 +16,10 @@ octez-evm-node-libs.evm_node_lib_prod_encoding lwt-watcher lwt-exit + caqti + caqti-lwt + caqti-lwt.unix + caqti-driver-sqlite3 octez-shell-libs.client-base octez-evm-node-libs.evm_node_config octez-libs.context.sigs diff --git a/etherlink/bin_node/lib_prod/durable_storage_path.ml b/etherlink/bin_node/lib_prod/durable_storage_path.ml index b0b30c96db1f617f83bc3712003f06cdf6d45b56..534978e0984c1c513e7045fa398ee62d138111b7 100644 --- a/etherlink/bin_node/lib_prod/durable_storage_path.ml +++ b/etherlink/bin_node/lib_prod/durable_storage_path.ml @@ -93,3 +93,11 @@ module Delayed_transaction = struct let transaction (Hash (Hex tx_hash)) = hashes ^ "/" ^ tx_hash ^ "/data" end + +module Evm_events = struct + let events = EVM.make "/events" + + let length = events ^ "/" ^ "length" + + let nth_event i = events ^ "/" ^ string_of_int i +end diff --git a/etherlink/bin_node/lib_prod/durable_storage_path.mli b/etherlink/bin_node/lib_prod/durable_storage_path.mli index 61462e1616eeec8e0de807c30fd84c0daa4611e9..829e20e7f4f34a4ea3e9f49aeb643e5e7466ff53 100644 --- a/etherlink/bin_node/lib_prod/durable_storage_path.mli +++ b/etherlink/bin_node/lib_prod/durable_storage_path.mli @@ -72,3 +72,14 @@ module Delayed_transaction : sig (** Path to the delayed transaction. *) val transaction : hash -> path end + +module Evm_events : sig + (** Path to the list of events of the kernel. *) + val events : path + + (** Path to the length. *) + val length : path + + (** Path to the nth event of the kernel. *) + val nth_event : int -> path +end diff --git a/etherlink/bin_node/lib_prod/encodings/ethereum_types.ml b/etherlink/bin_node/lib_prod/encodings/ethereum_types.ml index 249d915b8d6910ce60936ff6c8d1953bcb2fd35f..ff52ee60b3c5b5712f0aa6532610d91db3958600 100644 --- a/etherlink/bin_node/lib_prod/encodings/ethereum_types.ml +++ b/etherlink/bin_node/lib_prod/encodings/ethereum_types.ml @@ -165,6 +165,8 @@ let decode_address bytes = Address (decode_hex bytes) let decode_number bytes = Bytes.to_string bytes |> Z.of_bits |> quantity_of_z +let encode_number (Qty v) = Z.to_bits v |> Bytes.of_string + let decode_hash bytes = Hash (decode_hex bytes) let pad_to_n_bytes_le bytes length = @@ -1098,10 +1100,30 @@ let filter_topic_encoding = (fun l -> Or l); ] +type filter_address = Single of address | Vec of address list + +let filter_address_encoding = + let open Data_encoding in + union + [ + case + ~title:"single" + (Tag 0) + address_encoding + (function Single address -> Some address | _ -> None) + (fun address -> Single address); + case + ~title:"vec" + (Tag 1) + (list address_encoding) + (function Vec l -> Some l | _ -> None) + (fun l -> Vec l); + ] + type filter = { from_block : block_param option; to_block : block_param option; - address : address option; + address : filter_address option; topics : filter_topic option list option; block_hash : block_hash option; } @@ -1116,7 +1138,7 @@ let filter_encoding = (obj5 (opt "fromBlock" block_param_encoding) (opt "toBlock" block_param_encoding) - (opt "address" address_encoding) + (opt "address" filter_address_encoding) (opt "topics" (list @@ option filter_topic_encoding)) (opt "blockHash" block_hash_encoding)) @@ -1191,7 +1213,7 @@ module Delayed_transaction = struct let of_bytes hash bytes = match bytes |> Rlp.decode with - | Ok Rlp.(List [List [Value tag; content]; _timestamp]) -> ( + | Ok Rlp.(List [List [Value tag; content]; _timestamp; _level]) -> ( match (Bytes.to_string tag, content) with | "\x01", Rlp.Value raw_tx -> let hash = @@ -1214,3 +1236,49 @@ module Delayed_transaction = struct let pp_short fmt {hash = Hash (Hex h); _} = Format.pp_print_string fmt h end + +module Upgrade = struct + type t = {hash : hash; timestamp : quantity} + + let of_rlp = function + | Rlp.List [Value hash_bytes; Value timestamp] -> + let hash = + hash_bytes |> Bytes.to_string |> Hex.of_string |> Hex.show + |> hash_of_string + in + let timestamp = decode_number timestamp in + Some {hash; timestamp} + | _ -> None + + let of_bytes bytes = + match bytes |> Rlp.decode with Ok rlp -> of_rlp rlp | _ -> None + + let to_bytes {hash; timestamp} = + let hash_bytes = hash_to_bytes hash |> String.to_bytes in + let timestamp_bytes = encode_number timestamp in + Rlp.(encode (List [Value hash_bytes; Value timestamp_bytes])) +end + +module Evm_events = struct + type t = Upgrade_event of Upgrade.t + + let of_bytes bytes = + match bytes |> Rlp.decode with + | Ok (Rlp.List [Value tag; rlp_content]) -> ( + match Bytes.to_string tag with + | "\x01" -> + let upgrade = Upgrade.of_rlp rlp_content in + Option.map (fun u -> Upgrade_event u) upgrade + | _ -> None) + | _ -> None + + let pp fmt = function + | Upgrade_event {hash; timestamp = Qty timestamp} -> + Format.fprintf + fmt + "upgrade:@ hash %a,@ timestamp %a" + pp_hash + hash + Z.pp_print + timestamp +end diff --git a/etherlink/bin_node/lib_prod/evm_context.ml b/etherlink/bin_node/lib_prod/evm_context.ml index eca0eb7708006b1a8ac99c165a03f5c0c96b1056..34775fdb3a4aea80576a8f62231321a706fb11e7 100644 --- a/etherlink/bin_node/lib_prod/evm_context.ml +++ b/etherlink/bin_node/lib_prod/evm_context.ml @@ -14,109 +14,43 @@ type t = { mutable next_blueprint_number : Ethereum_types.quantity; mutable current_block_hash : Ethereum_types.block_hash; blueprint_watcher : Blueprint_types.t Lwt_watcher.input; -} - -type metadata = { - checkpoint : Context_hash.t; - next_blueprint_number : Ethereum_types.quantity; - current_block_hash : Ethereum_types.block_hash; + store : Store.t; } let store_path ~data_dir = Filename.Infix.(data_dir // "store") -let metadata_path ~data_dir = Filename.Infix.(data_dir // "metadata") - -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)) - -let store_metadata ~data_dir metadata = - let json = Data_encoding.Json.construct metadata_encoding metadata in - Lwt_utils_unix.Json.write_file (metadata_path ~data_dir) json - -type old_metadata = { - checkpoint : Context_hash.t; - next_blueprint_number : Ethereum_types.quantity; -} - -let old_metadata_encoding = - let open Data_encoding in - conv - (fun {checkpoint; next_blueprint_number} -> - (checkpoint, next_blueprint_number)) - (fun (checkpoint, next_blueprint_number) -> - {checkpoint; next_blueprint_number}) - (obj2 - (req "checkpoint" Context_hash.encoding) - (req "next_blueprint_number" Ethereum_types.quantity_encoding)) - -let load_metadata ~data_dir index = +let load ~data_dir index = let open Lwt_result_syntax in - let path = metadata_path ~data_dir in - let*! exists = Lwt_unix.file_exists path in - if exists then - let* content = Lwt_utils_unix.Json.read_file path in - Lwt.catch - (fun () -> - let {checkpoint; next_blueprint_number; current_block_hash} = - Data_encoding.Json.destruct metadata_encoding content - in - let*! context = Irmin_context.checkout_exn index checkpoint in - return (context, next_blueprint_number, current_block_hash, true)) - (fun _exn -> - let {checkpoint; next_blueprint_number} = - Data_encoding.Json.destruct old_metadata_encoding content - in - let*! context = Irmin_context.checkout_exn index checkpoint in - let*! evm_state = Irmin_context.PVMState.get context in - let* current_block_hash = Evm_state.current_block_hash evm_state in - return (context, next_blueprint_number, current_block_hash, true)) - else - let context = Irmin_context.empty index in - return - ( context, - Ethereum_types.Qty Z.zero, - Ethereum_types.genesis_parent_hash, - false ) + let* store = Store.init ~data_dir in + let* latest = Store.Context_hashes.find_latest store in + match latest with + | Some (Qty latest_blueprint_number, checkpoint) -> + let*! context = Irmin_context.checkout_exn index checkpoint in + let*! evm_state = Irmin_context.PVMState.get context in + let+ current_block_hash = Evm_state.current_block_hash evm_state in + ( store, + context, + Ethereum_types.Qty Z.(succ latest_blueprint_number), + current_block_hash, + true ) + | None -> + let context = Irmin_context.empty index in + return + ( store, + context, + Ethereum_types.Qty Z.zero, + Ethereum_types.genesis_parent_hash, + false ) -let commit (ctxt : t) evm_state = +let commit ~number (ctxt : t) evm_state = let open Lwt_result_syntax in let*! context = Irmin_context.PVMState.set ctxt.context evm_state in let*! checkpoint = Irmin_context.commit context in - let* () = - store_metadata - ~data_dir:ctxt.data_dir - { - checkpoint; - next_blueprint_number = ctxt.next_blueprint_number; - current_block_hash = ctxt.current_block_hash; - } - in ctxt.context <- context ; - return_unit + Store.Context_hashes.store ctxt.store number checkpoint let evm_state {context; _} = Irmin_context.PVMState.get context -let store_blueprint ctxt number blueprint = - Blueprint_store.store - (Blueprint_store.make ~data_dir:ctxt.data_dir) - number - blueprint - -let find_blueprint ctxt number = - Blueprint_store.find - ~kind:`Execute - (Blueprint_store.make ~data_dir:ctxt.data_dir) - number - let execution_config ctxt = Config.config ~preimage_directory:ctxt.preimages @@ -124,15 +58,12 @@ let execution_config ctxt = ~destination:ctxt.smart_rollup_address () -let execute = - let perform_commit = commit in - fun ?(commit = false) ctxt inbox -> - let open Lwt_result_syntax in - let config = execution_config ctxt in - let*! evm_state = evm_state ctxt in - let* evm_state = Evm_state.execute ~config evm_state inbox in - let* () = when_ commit (fun () -> perform_commit ctxt evm_state) in - return (ctxt, evm_state) +let execute ?wasm_entrypoint ctxt inbox = + let open Lwt_result_syntax in + let config = execution_config ctxt in + let*! evm_state = evm_state ctxt in + let* evm_state = Evm_state.execute ?wasm_entrypoint ~config evm_state inbox in + return (ctxt, evm_state) type error += Cannot_apply_blueprint of {local_state_level : Z.t} @@ -168,12 +99,15 @@ let apply_blueprint ctxt payload = | Apply_success (evm_state, Block_height blueprint_number, current_block_hash) when Z.equal blueprint_number next -> let* () = - store_blueprint ~kind:`Execute ctxt payload (Qty blueprint_number) + Store.Executable_blueprints.store + ctxt.store + (Qty blueprint_number) + payload in ctxt.next_blueprint_number <- Qty (Z.succ blueprint_number) ; ctxt.current_block_hash <- current_block_hash ; let*! () = Blueprint_events.blueprint_applied blueprint_number in - let* () = commit ctxt evm_state in + let* () = commit ~number:(Qty blueprint_number) ctxt evm_state in Lwt_watcher.notify ctxt.blueprint_watcher {number = Qty blueprint_number; payload} ; @@ -189,13 +123,15 @@ let apply_and_publish_blueprint (ctxt : t) (blueprint : Sequencer_blueprint.t) = let (Qty level) = ctxt.next_blueprint_number in let* ctxt = apply_blueprint ctxt blueprint.to_execute in let* () = - store_blueprint ~kind:`Publish ctxt blueprint.to_publish (Qty level) + Store.Publishable_blueprints.store + ctxt.store + (Qty level) + blueprint.to_publish in let* () = Blueprints_publisher.publish level blueprint.to_publish in return ctxt -let init ?(genesis_timestamp = Helpers.now ()) ?produce_genesis_with - ?kernel_path ~data_dir ~preimages ~smart_rollup_address () = +let init ?kernel_path ~data_dir ~preimages ~smart_rollup_address () = let open Lwt_result_syntax in let* index = Irmin_context.load ~cache_size:100_000 Read_write (store_path ~data_dir) @@ -203,8 +139,8 @@ let init ?(genesis_timestamp = Helpers.now ()) ?produce_genesis_with let destination = Tezos_crypto.Hashed.Smart_rollup_address.of_string_exn smart_rollup_address in - let* context, next_blueprint_number, current_block_hash, loaded = - load_metadata ~data_dir index + let* store, context, next_blueprint_number, current_block_hash, loaded = + load ~data_dir index in let ctxt = { @@ -216,42 +152,29 @@ let init ?(genesis_timestamp = Helpers.now ()) ?produce_genesis_with next_blueprint_number; current_block_hash; blueprint_watcher = Lwt_watcher.create_input (); + store; } in - let* ctxt = + + let* () = match kernel_path with - | Some kernel -> ( + | Some kernel -> if loaded then let*! () = Events.ignored_kernel_arg () in - return ctxt + return_unit else let* evm_state = Evm_state.init ~kernel in - let* () = commit ctxt evm_state in - match produce_genesis_with with - | Some (cctxt, sequencer_key) -> - (* Create the first empty block. *) - let* genesis = - Sequencer_blueprint.create - ~cctxt - ~sequencer_key - ~timestamp:genesis_timestamp - ~smart_rollup_address - ~transactions:[] - ~delayed_transactions:[] - ~number:Ethereum_types.(Qty Z.zero) - ~parent_hash:Ethereum_types.genesis_parent_hash - in - apply_and_publish_blueprint ctxt genesis - | None -> return ctxt) + (* The state prior to the genesis blueprint is indexed by [-1] *) + commit ~number:(Qty Z.(pred zero)) ctxt evm_state | None -> - if loaded then return ctxt + if loaded then return_unit else failwith "Cannot compute the initial EVM state without the path to the \ initial kernel" in - return ctxt + return (ctxt, loaded) let init_from_rollup_node ~data_dir ~rollup_node_data_dir = let open Lwt_result_syntax in @@ -292,6 +215,7 @@ let init_from_rollup_node ~data_dir ~rollup_node_data_dir = Irmin_context.checkout_exn evm_node_index checkpoint in let*! evm_state = Irmin_context.PVMState.get evm_node_context in + (* Assert we can read the current blueprint number *) let* current_blueprint_number = let*! current_blueprint_number_opt = Evm_state.inspect evm_state Durable_storage_path.Block.current_number @@ -300,35 +224,34 @@ let init_from_rollup_node ~data_dir ~rollup_node_data_dir = | Some bytes -> return (Bytes.to_string bytes |> Z.of_bits) | None -> failwith "The blueprint number was not found" in - let* current_block_hash = + (* Assert we can read the current block hash *) + let* () = let*! current_block_hash_opt = Evm_state.inspect evm_state Durable_storage_path.Block.current_hash in match current_block_hash_opt with - | Some bytes -> - return - (Ethereum_types.block_hash_of_string (Hex.of_bytes bytes |> Hex.show)) + | Some _bytes -> return_unit | None -> failwith "The block hash was not found" in - let next_blueprint_number = - Ethereum_types.Qty Z.(add one current_blueprint_number) + (* Init the store *) + let* store = Store.init ~data_dir in + let* () = + Store.Context_hashes.store store (Qty current_blueprint_number) checkpoint in - store_metadata - ~data_dir - {checkpoint; next_blueprint_number; current_block_hash} + return_unit -let execute_and_inspect ~input ctxt = +let execute_and_inspect ?wasm_entrypoint ~input ctxt = let open Lwt_result_syntax in let config = execution_config ctxt in let*! evm_state = evm_state ctxt in - Evm_state.execute_and_inspect ~config ~input evm_state + Evm_state.execute_and_inspect ?wasm_entrypoint ~config ~input evm_state let last_produced_blueprint (ctxt : t) = - let open Lwt_syntax in + let open Lwt_result_syntax in let (Qty next) = ctxt.next_blueprint_number in let current = Ethereum_types.Qty Z.(pred next) in - let* blueprint = find_blueprint ctxt current in + let* blueprint = Store.Executable_blueprints.find ctxt.store current in match blueprint with | Some blueprint -> - return_ok Blueprint_types.{number = current; payload = blueprint} + return Blueprint_types.{number = current; payload = blueprint} | None -> failwith "Could not fetch the last produced blueprint" diff --git a/etherlink/bin_node/lib_prod/evm_context.mli b/etherlink/bin_node/lib_prod/evm_context.mli index cd71f4fef6b564cd94be14a85a0b1bf704c97af5..7188156f27f3190633cd2a4ab2609481fbb9bfc0 100644 --- a/etherlink/bin_node/lib_prod/evm_context.mli +++ b/etherlink/bin_node/lib_prod/evm_context.mli @@ -16,25 +16,22 @@ type t = { mutable current_block_hash : Ethereum_types.block_hash; (** Hash of the latest processed block *) blueprint_watcher : Blueprint_types.t Lwt_watcher.input; + store : Store.t; } (** [init ~data_dir ~preimages ~smart_rollup_address ()] creates a context where it initializes the {!type-index}, and use a checkpoint mechanism to load the latest {!type-store} if any. - If the context does not already exist, [kernel_path] is required. - Additionally, if [produce_genesis_with] is set, this function also - produces and publishes the genesis blueprint (optionally set to - [genesis_timestamp]). *) + Returns an additional boolean telling if the context was loaded from disk + ([true]) or was initialized from scratch ([false]). *) val init : - ?genesis_timestamp:Time.Protocol.t -> - ?produce_genesis_with:Client_context.wallet * Client_keys.sk_uri -> ?kernel_path:string -> data_dir:string -> preimages:string -> smart_rollup_address:string -> unit -> - t tzresult Lwt.t + (t * bool) tzresult Lwt.t (** [init_from_rollup_node ~data_dir ~rollup_node_data_dir ~inspect_current_blueprint_number] @@ -44,34 +41,38 @@ val init : val init_from_rollup_node : data_dir:string -> rollup_node_data_dir:string -> unit tzresult Lwt.t -(** [commit ctxt evm_state] updates the [evm_state] in [ctxt], commits - to disk the changes, and update the checkpoint. *) -val commit : t -> Evm_state.t -> unit tzresult Lwt.t +(** [commit ~number ctxt evm_state] updates the [evm_state] resulting from the + application of the [number]th blueprint in [ctxt], commits to disk the + changes, and update the checkpoint. *) +val commit : + number:Ethereum_types.quantity -> t -> Evm_state.t -> unit tzresult Lwt.t (** [evm_state ctxt] returns the freshest EVM state stored under [ctxt]. *) val evm_state : t -> Evm_state.t Lwt.t -(** [execute ?commit ctxt messages] executes [messages] on the freshest - EVM state stored in [ctxt]. +(** [execute ?wasm_entrypoint ?commit ctxt messages] executes the + [wasm_entrypoint] function with [messages] in the inbox of the freshest EVM + state stored in [ctxt]. - If [commit = true], the resulting EVM state is committed in [ctxt] (that - is, it becomes the freshest one). *) + If [wasm_entrypoint] is omitted, the [kernel_run] function of the kernel is + executed. *) val execute : - ?commit:bool -> + ?wasm_entrypoint:string -> t -> [< `Input of string] list -> (t * Evm_state.t) tzresult Lwt.t (** [execute_and_inspect ~input ctxt] executes [input] using the freshest EVM - state, and returns [input.insights_requests]. *) + state, and returns [input.insights_requests]. + + If [wasm_entrypoint] is omitted, the [kernel_run] function of the kernel is + executed. *) val execute_and_inspect : + ?wasm_entrypoint:string -> input:Simulation.Encodings.simulate_input -> t -> bytes option list tzresult Lwt.t -val find_blueprint : - t -> Ethereum_types.quantity -> Blueprint_types.payload option Lwt.t - val last_produced_blueprint : t -> Blueprint_types.t tzresult Lwt.t (** [apply_blueprint ctxt blueprint] applies [blueprint] in the freshest EVM diff --git a/etherlink/bin_node/lib_prod/evm_events_follower.ml b/etherlink/bin_node/lib_prod/evm_events_follower.ml new file mode 100644 index 0000000000000000000000000000000000000000..08e7e20665b9eb360a155ff48277d3570f4a54c9 --- /dev/null +++ b/etherlink/bin_node/lib_prod/evm_events_follower.ml @@ -0,0 +1,199 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +type parameters = { + rollup_node_endpoint : Uri.t; + backend : (module Services_backend_sig.S); +} + +module StringSet = Set.Make (String) + +module Types = struct + type state = parameters + + type nonrec parameters = parameters +end + +module Name = struct + (* We only have a single events follower in the evm node *) + type t = unit + + let encoding = Data_encoding.unit + + let base = ["evm_node"; "prod"; "events_follower"; "worker"] + + let pp _ _ = () + + let equal () () = true +end + +module Request = struct + type ('a, 'b) t = New_rollup_node_block : Int32.t -> (unit, error trace) t + + type view = View : _ t -> view + + let view (req : _ t) = View req + + let encoding = + let open Data_encoding in + union + [ + case + (Tag 0) + ~title:"New_rollup_node_block" + (obj2 + (req "request" (constant "new_rollup_node_block")) + (req "rollup_head" int32)) + (function + | View (New_rollup_node_block rollup_head) -> Some ((), rollup_head)) + (fun ((), rollup_head) -> View (New_rollup_node_block rollup_head)); + ] + + let pp ppf (View r) = + match r with + | New_rollup_node_block rollup_head -> + Format.fprintf ppf "New_rollup_node_block (level %ld)" rollup_head +end + +module Worker = Worker.MakeSingle (Name) (Request) (Types) + +type worker = Worker.infinite Worker.queue Worker.t + +let read_from_rollup_node path level rollup_node_endpoint = + let open Rollup_services in + call_service + ~base:rollup_node_endpoint + durable_state_value + ((), Block_id.Level level) + {key = path} + () + +let on_new_event ({backend; _} : Types.state) event = + let open Ethereum_types in + let (module Backend) = backend in + match event with + | Evm_events.Upgrade_event upgrade -> + let payload = Upgrade.to_bytes upgrade |> String.of_bytes in + Backend.inject_kernel_upgrade ~payload + +let fetch_event ({rollup_node_endpoint; _} : Types.state) rollup_block_lvl + event_index = + let open Lwt_result_syntax in + let path = Durable_storage_path.Evm_events.nth_event event_index in + let* bytes_opt = + read_from_rollup_node path rollup_block_lvl rollup_node_endpoint + in + let event_opt = Option.bind bytes_opt Ethereum_types.Evm_events.of_bytes in + let*! () = + if Option.is_none event_opt then + Evm_events_follower_events.unreadable_event (event_index, rollup_block_lvl) + else Lwt.return_unit + in + return event_opt + +let on_new_head ({rollup_node_endpoint; _} as state : Types.state) + rollup_block_lvl = + let open Lwt_result_syntax in + let* nb_of_events_bytes = + read_from_rollup_node + Durable_storage_path.Evm_events.length + rollup_block_lvl + rollup_node_endpoint + in + match nb_of_events_bytes with + | None -> return_unit + | Some nb_of_events_bytes -> + let (Qty nb_of_events) = + Ethereum_types.decode_number nb_of_events_bytes + in + let nb_of_events = Z.to_int nb_of_events in + let* events = + List.init_ep + ~when_negative_length: + (error_of_fmt + "Internal error: the rollup node advertised a negative length \ + for the events stream") + nb_of_events + (fetch_event state rollup_block_lvl) + in + List.iter_es + (function + | None -> return_unit | Some event -> on_new_event state event) + events + +module Handlers = struct + type self = worker + + let on_request : + type r request_error. + worker -> (r, request_error) Request.t -> (r, request_error) result Lwt.t + = + fun worker request -> + let open Lwt_result_syntax in + match request with + | Request.New_rollup_node_block rollup_block_lvl -> + protect @@ fun () -> + let* () = on_new_head (Worker.state worker) rollup_block_lvl in + return_unit + + type launch_error = error trace + + let on_launch _w () (parameters : Types.parameters) = + let state = parameters in + Lwt_result_syntax.return state + + let on_error (type a b) _w _st (_r : (a, b) Request.t) (_errs : b) : + unit tzresult Lwt.t = + Lwt_result_syntax.return_unit + + let on_completion _ _ _ _ = Lwt.return_unit + + let on_no_request _ = Lwt.return_unit + + let on_close _ = Lwt.return_unit +end + +let table = Worker.create_table Queue + +let worker_promise, worker_waker = Lwt.task () + +type error += No_worker + +let worker = + lazy + (match Lwt.state worker_promise with + | Lwt.Return worker -> Ok worker + | Lwt.Fail e -> Error (TzTrace.make @@ error_of_exn e) + | Lwt.Sleep -> Error (TzTrace.make No_worker)) + +let start parameters = + let open Lwt_result_syntax in + let*! () = Evm_events_follower_events.started () in + let+ worker = Worker.launch table () parameters (module Handlers) in + Lwt.wakeup worker_waker worker + +let shutdown () = + let open Lwt_syntax in + let w = Lazy.force worker in + match w with + | Error _ -> + (* There is no events follower, nothing to do *) + Lwt.return_unit + | Ok w -> + let* () = Evm_events_follower_events.shutdown () in + Worker.shutdown w + +let worker_add_request ~request : unit tzresult Lwt.t = + let open Lwt_result_syntax in + match Lazy.force worker with + | Ok w -> + let*! (_pushed : bool) = Worker.Queue.push_request w request in + return_unit + | Error e -> Lwt.return (Error e) + +let new_rollup_block rollup_level = + worker_add_request ~request:(New_rollup_node_block rollup_level) diff --git a/etherlink/bin_node/lib_prod/evm_events_follower.mli b/etherlink/bin_node/lib_prod/evm_events_follower.mli new file mode 100644 index 0000000000000000000000000000000000000000..57d8cc3fe0d1d9cd63a08ffa2473eb727e1ff296 --- /dev/null +++ b/etherlink/bin_node/lib_prod/evm_events_follower.mli @@ -0,0 +1,24 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +type parameters = { + rollup_node_endpoint : Uri.t; + (** Rollup node endpoint used to monitor kernel events. *) + backend : (module Services_backend_sig.S); + (** Local backend to propagate events in local state. *) +} + +(** [start parameters] starts the events follower. *) +val start : parameters -> unit tzresult Lwt.t + +(** [shutdown ()] stops the events follower. *) +val shutdown : unit -> unit Lwt.t + +(** [new_rollup_block rollup_level] tells the worker that a new L2 + head has been published and that the rollup head is now + [rollup_level]. *) +val new_rollup_block : Int32.t -> unit tzresult Lwt.t diff --git a/etherlink/bin_node/lib_prod/evm_events_follower_events.ml b/etherlink/bin_node/lib_prod/evm_events_follower_events.ml new file mode 100644 index 0000000000000000000000000000000000000000..ef01d095c24acb8699f1cf44cdbadde3e5032437 --- /dev/null +++ b/etherlink/bin_node/lib_prod/evm_events_follower_events.ml @@ -0,0 +1,46 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +module Event = struct + open Internal_event.Simple + + let section = Events.section + + let started = + declare_0 + ~section + ~name:"evm_events_follower_started" + ~msg:"Evm events follower has been started" + ~level:Notice + () + + let unreadable_event = + declare_2 + ~section + ~name:"evm_events_unreadable_event" + ~msg:"Evm events follower could not parse event {index} of level {level}" + ~level:Error + ("index", Data_encoding.int31) + ("level", Data_encoding.int32) + + let pp_int32 fmt i = Format.fprintf fmt "%ld" i + + let shutdown = + declare_0 + ~section + ~name:"shutting_down_evm_events_follower" + ~msg:"Stopping the evm events follower" + ~level:Notice + () +end + +let started = Internal_event.Simple.emit Event.started + +let shutdown = Internal_event.Simple.emit Event.shutdown + +let unreadable_event (index, level) = + Internal_event.Simple.emit Event.unreadable_event (index, level) diff --git a/etherlink/bin_node/lib_prod/evm_services.ml b/etherlink/bin_node/lib_prod/evm_services.ml index 79b6028a4c1a37cb1115cb6fd416108c0d26edc5..0c3b64906976bacc50c9e1bc01d62fdf8289ce02 100644 --- a/etherlink/bin_node/lib_prod/evm_services.ml +++ b/etherlink/bin_node/lib_prod/evm_services.ml @@ -62,12 +62,15 @@ let create_blueprint_watcher_service (ctxt : Evm_context.t) from_level = let current_request = !next_level_requested in (next_level_requested := Z.(succ current_request)) ; let* blueprint = - Evm_context.find_blueprint ctxt (Qty current_request) + Store.Executable_blueprints.find ctxt.store (Qty current_request) in match blueprint with - | Some payload -> + | Ok (Some payload) -> return_some Blueprint_types.{number = Qty current_request; payload} - | None -> return_none) + | Ok None -> return_none + | Error _ -> + Stdlib.failwith + "Something went wrong when trying to fetch a blueprint") else Lwt_stream.get blueprint_stream in Tezos_rpc.Answer.return_stream {next; shutdown} @@ -77,12 +80,12 @@ let register_get_smart_rollup_address_service ctxt dir = let open Lwt_syntax in return_ok ctxt.Evm_context.smart_rollup_address) -let register_get_blueprint_service ctxt dir = +let register_get_blueprint_service (ctxt : Evm_context.t) dir = Directory.opt_register1 dir get_blueprint_service (fun level () () -> - let open Lwt_syntax in + let open Lwt_result_syntax in let number = Ethereum_types.Qty (Z.of_int64 level) in - let* blueprint = Evm_context.find_blueprint ctxt number in - return_ok blueprint) + let* blueprint = Store.Executable_blueprints.find ctxt.store number in + return blueprint) let register_blueprint_watcher_service ctxt dir = Directory.gen_register0 dir blueprint_watcher_service (fun level () -> diff --git a/etherlink/bin_node/lib_prod/evm_state.ml b/etherlink/bin_node/lib_prod/evm_state.ml index 1fd1316038cfe45c7debb72c680063319959c525..63001b87cc2cf583b23b2e327a2c9ec3cbcc8649 100644 --- a/etherlink/bin_node/lib_prod/evm_state.ml +++ b/etherlink/bin_node/lib_prod/evm_state.ml @@ -32,18 +32,13 @@ module Wasm_utils = Wasm_utils.Make (Tezos_tree_encoding.Encodings_util.Make (Bare_context)) module Wasm = Wasm_debugger.Make (Wasm_utils) -let execute ~config evm_state inbox = +let execute ?(wasm_entrypoint = Tezos_scoru_wasm.Constants.wasm_entrypoint) + ~config evm_state inbox = let open Lwt_result_syntax in let inbox = List.map (function `Input s -> s) inbox in let inbox = List.to_seq [inbox] in let* evm_state, _, _, _ = - Wasm.Commands.eval - ~wasm_entrypoint:Tezos_scoru_wasm.Constants.wasm_entrypoint - 0l - inbox - config - Inbox - evm_state + Wasm.Commands.eval ~wasm_entrypoint 0l inbox config Inbox evm_state in return evm_state @@ -88,7 +83,7 @@ let current_block_hash evm_state = | Some h -> return (decode_block_hash h) | None -> return genesis_parent_hash -let execute_and_inspect ~config +let execute_and_inspect ?wasm_entrypoint ~config ~input:Simulation.Encodings.{messages; insight_requests; _} ctxt = let open Lwt_result_syntax in let keys = @@ -102,7 +97,7 @@ let execute_and_inspect ~config in (* Messages from simulation requests are already valid inputs. *) let messages = List.map (fun s -> `Input s) messages in - let* evm_state = execute ~config ctxt messages in + let* evm_state = execute ?wasm_entrypoint ~config ctxt messages in let*! values = List.map_p (fun key -> inspect evm_state key) keys in return values @@ -118,7 +113,13 @@ let apply_blueprint ~config evm_state (blueprint : Blueprint_types.payload) = blueprint in let*! (Block_height before_height) = current_block_height evm_state in - let* evm_state = execute ~config evm_state exec_inputs in + let* evm_state = + execute + ~wasm_entrypoint:Tezos_scoru_wasm.Constants.wasm_entrypoint + ~config + evm_state + exec_inputs + in let*! (Block_height after_height) = current_block_height evm_state in let* block_hash = current_block_hash evm_state in if Z.(equal (succ before_height) after_height) then diff --git a/etherlink/bin_node/lib_prod/evm_state.mli b/etherlink/bin_node/lib_prod/evm_state.mli index 67969c7ddcc33ed001a9155113815067f7342dd8..60730ccccb684222837427dad5017a24f0ec64c6 100644 --- a/etherlink/bin_node/lib_prod/evm_state.mli +++ b/etherlink/bin_node/lib_prod/evm_state.mli @@ -7,10 +7,15 @@ type t = Irmin_context.PVMState.value -(** [execute ~config evm_state messages] executes [messages] on the local - [evm_state]. *) +(** [execute ~wasm_entrypoint ~config evm_state messages] executes the + [wasm_entrypoint] function (default to [kernel_run]) with [messages] within + the inbox of [evm_state]. *) val execute : - config:Config.config -> t -> [< `Input of string] list -> t tzresult Lwt.t + ?wasm_entrypoint:string -> + config:Config.config -> + t -> + [< `Input of string] list -> + t tzresult Lwt.t (** [init ~kernel] initializes the local [evm_state] with [kernel]. *) val init : kernel:string -> t tzresult Lwt.t @@ -23,9 +28,11 @@ val modify : key:string -> value:string -> t -> t Lwt.t [evm_state], if any. *) val inspect : t -> string -> bytes option Lwt.t -(** [execute_and_inspect ~config ~input evm_state] executes [input] within - [evm_state], and returns [input.insights_requests]. *) +(** [execute_and_inspect ?wasm_entrypoint ~config ~input evm_state] executes + the [wasm_entrypoint] function (default to [kernel_run]) with [input] + within the inbox of [evm_state], and returns [input.insights_requests]. *) val execute_and_inspect : + ?wasm_entrypoint:string -> config:Config.config -> input:Simulation.Encodings.simulate_input -> t -> diff --git a/etherlink/bin_node/lib_prod/filter_helpers.ml b/etherlink/bin_node/lib_prod/filter_helpers.ml index 019cd7e8bd9947690798e6ed5ce4c0c8747f42ad..48dbafcced9161c7bf32ab59b4162f81a1b0fd60 100644 --- a/etherlink/bin_node/lib_prod/filter_helpers.ml +++ b/etherlink/bin_node/lib_prod/filter_helpers.ml @@ -27,6 +27,8 @@ open Ethereum_types to blocks that have at least a log that matches with the pattern. For this reason, we decide to ignore [Or] patterns in the bloom filter "heuristic" (including all topics would break the previous property). + The same is done for addresses, as a filter can match against a list + of them. If this becomes a serious bottleneck, we could keep a collection of bloom filters to represent the disjunction. *) @@ -44,7 +46,7 @@ type valid_filter = { to_block : block_height; bloom : Ethbloom.t; topics : filter_topic option list; - address : address option; + address : address list; } module Event = struct @@ -53,7 +55,7 @@ module Event = struct let incompatible_block_params = Internal_event.Simple.declare_0 ~section - ~name:"incompatible_block_params_dev" + ~name:"incompatible_block_params_prod" ~msg:"block_hash field cannot be set when from_block and to_block are set" ~level:Error () @@ -61,7 +63,7 @@ module Event = struct let block_range_too_large = Internal_event.Simple.declare_0 ~section - ~name:"block_range_too_large_dev" + ~name:"block_range_too_large_prod" ~msg:"Requested block range is above the maximum" ~level:Error () @@ -69,7 +71,7 @@ module Event = struct let topic_list_too_large = Internal_event.Simple.declare_0 ~section - ~name:"topic_list_too_large_dev" + ~name:"topic_list_too_large_prod" ~msg:"Topic list length should be at most 4" ~level:Error () @@ -77,7 +79,7 @@ module Event = struct let receipt_not_found = Internal_event.Simple.declare_1 ~section - ~name:"receipt_not_found_dev" + ~name:"receipt_not_found_prod" ~msg:"Receipt not found for {tx_hash}" ~level:Error ("tx_hash", hash_encoding) @@ -85,7 +87,7 @@ module Event = struct let too_many_logs = Internal_event.Simple.declare_0 ~section - ~name:"too_many_logs_dev" + ~name:"too_many_logs_prod" ~msg:"Too many logs requested" ~level:Error () @@ -142,7 +144,9 @@ let validate_range log_filter_config let make_bloom (filter : filter) = let bloom = Ethbloom.make () in Option.iter - (fun (Address address) -> Ethbloom.accrue ~input:address bloom) + (function + | Single (Address address) -> Ethbloom.accrue ~input:address bloom + | _ -> ()) filter.address ; Option.iter (List.iter (function @@ -176,13 +180,17 @@ let validate_filter log_filter_config in let*?? () = validate_topics filter in let bloom = make_bloom filter in + let address = + Option.map (function Single a -> [a] | Vec l -> l) filter.address + |> Option.value ~default:[] + in return_some { from_block; to_block; bloom; topics = Option.value ~default:[] filter.topics; - address = filter.address; + address; } let hex_to_bytes h = hex_to_bytes h |> Bytes.of_string @@ -211,7 +219,7 @@ let match_filter_topics (filter : valid_filter) (log_topics : hash list) : bool (* Checks if a filter's address matches a log's address *) let match_filter_address (filter : valid_filter) (address : address) : bool = - Option.fold ~none:true ~some:(( = ) address) filter.address + List.is_empty filter.address || List.mem ~equal:( = ) address filter.address (* Apply a filter on one log *) let filter_one_log : valid_filter -> transaction_log -> filter_changes option = diff --git a/etherlink/bin_node/lib_prod/observer.ml b/etherlink/bin_node/lib_prod/observer.ml index 53a18eeb2509dde65bb5845eab7e07c117ca3b2f..f3814285e90fe855cf0a9a834c1592a943412faf 100644 --- a/etherlink/bin_node/lib_prod/observer.ml +++ b/etherlink/bin_node/lib_prod/observer.ml @@ -9,6 +9,8 @@ open Ethereum_types module MakeBackend (Ctxt : sig val ctxt : Evm_context.t + + val evm_node_endpoint : Uri.t end) : Services_backend_sig.Backend = struct module READER = struct let read path = @@ -43,10 +45,53 @@ end) : Services_backend_sig.Backend = struct module Publisher = struct type messages = TxEncoder.messages - let publish_messages ~timestamp:_ ~smart_rollup_address:_ ~messages:_ = - (* TODO: https://gitlab.com/tezos/tezos/-/issues/6882 - Forward transactions *) - failwith "Forwarding transactions is not implemented yet" + let check_response = + let open Rpc_encodings.JSONRPC in + let open Lwt_result_syntax in + function + | {value = Ok _; _} -> return_unit + | {value = Error {message; _}; _} -> + failwith "Send_raw_transaction failed with message \"%s\"" message + + let check_batched_response = + let open Services in + function + | Batch l -> List.iter_es check_response l + | Singleton r -> check_response r + + let send_raw_transaction_method txn = + let open Rpc_encodings in + let message = + Hex.of_string txn |> Hex.show |> Ethereum_types.hex_of_string + in + JSONRPC. + { + method_ = Send_raw_transaction.method_; + parameters = + Some + (Data_encoding.Json.construct + Send_raw_transaction.input_encoding + message); + id = None; + } + + let publish_messages ~timestamp:_ ~smart_rollup_address:_ ~messages = + let open Rollup_services in + let open Lwt_result_syntax in + let methods = List.map send_raw_transaction_method messages in + + let* response = + call_service + ~base:Ctxt.evm_node_endpoint + (Services.dispatch_service ~path:Resto.Path.root) + () + () + (Batch methods) + in + + let* () = check_batched_response response in + + return_unit end module SimulatorBackend = struct @@ -67,12 +112,15 @@ end) : Services_backend_sig.Backend = struct ~value:payload evm_state in - let* () = Evm_context.commit Ctxt.ctxt evm_state in + let (Qty next) = Ctxt.ctxt.next_blueprint_number in + let* () = + Evm_context.commit ~number:(Qty Z.(pred next)) Ctxt.ctxt evm_state + in return_unit end let on_new_blueprint (ctxt : Evm_context.t) (blueprint : Blueprint_types.t) = - let (Qty level) = ctxt.next_blueprint_number in + let (Qty level) = blueprint.number in let (Qty number) = ctxt.next_blueprint_number in if Z.(equal level number) then Evm_context.apply_blueprint ctxt blueprint.payload @@ -85,6 +133,9 @@ let main (ctxt : Evm_context.t) ~evm_node_endpoint = match candidate with | Some blueprint -> let* ctxt = on_new_blueprint ctxt blueprint in + let* _ = + Tx_pool.produce_block ~force:false ~timestamp:(Helpers.now ()) + in loop ctxt stream | None -> return_unit in @@ -101,5 +152,7 @@ let main (ctxt : Evm_context.t) ~evm_node_endpoint = module Make (Ctxt : sig val ctxt : Evm_context.t + + val evm_node_endpoint : Uri.t end) : Services_backend_sig.S = Services_backend_sig.Make (MakeBackend (Ctxt)) diff --git a/etherlink/bin_node/lib_prod/observer.mli b/etherlink/bin_node/lib_prod/observer.mli index af61822b1d78735d067a71810dd01ec6ef4c1459..190f5e98422c41981b64342b8b85ff1bff6eb5d9 100644 --- a/etherlink/bin_node/lib_prod/observer.mli +++ b/etherlink/bin_node/lib_prod/observer.mli @@ -11,4 +11,6 @@ val main : Evm_context.t -> evm_node_endpoint:Uri.t -> unit tzresult Lwt.t module Make (Ctxt : sig val ctxt : Evm_context.t + + val evm_node_endpoint : Uri.t end) : Services_backend_sig.S diff --git a/etherlink/bin_node/lib_prod/rollup_node.ml b/etherlink/bin_node/lib_prod/rollup_node.ml index 7021540b800fa62d0b836b82900c2bac92965eee..94f3247d857914f5b662ace26b33230ce7d9831a 100644 --- a/etherlink/bin_node/lib_prod/rollup_node.ml +++ b/etherlink/bin_node/lib_prod/rollup_node.ml @@ -26,7 +26,7 @@ (* *) (*****************************************************************************) -open Rollup_node_services +open Rollup_services open Transaction_format module MakeBackend (Base : sig diff --git a/etherlink/bin_node/lib_prod/rollup_node_follower.ml b/etherlink/bin_node/lib_prod/rollup_node_follower.ml index 2cb8bae27074190162334eb43bc41ccac332b39e..03eb52e5791ec46e9db21743ec82e3ba19e16f58 100644 --- a/etherlink/bin_node/lib_prod/rollup_node_follower.ml +++ b/etherlink/bin_node/lib_prod/rollup_node_follower.ml @@ -86,7 +86,7 @@ let worker = | Lwt.Sleep -> Error (TzTrace.make No_l2_block_follower)) let read_from_rollup_node path level rollup_node_endpoint = - let open Rollup_node_services in + let open Rollup_services in call_service ~base:rollup_node_endpoint durable_state_value @@ -113,6 +113,7 @@ let process_new_block ~rollup_node_endpoint block = let open Lwt_syntax in let finalized_level = Sc_rollup_block.(Int32.(sub block.header.level 2l)) in let* _ = Delayed_inbox.new_rollup_block finalized_level in + let* _ = Evm_events_follower.new_rollup_block finalized_level in let* () = advertize_blueprints_publisher rollup_node_endpoint finalized_level in @@ -143,7 +144,7 @@ let start ({rollup_node_endpoint} as parameters) = Lwt.dont_wait (fun () -> let*! stream = - Rollup_node_services.make_streamed_call ~rollup_node_endpoint + Rollup_services.make_streamed_call ~rollup_node_endpoint in process_rollup_node_stream ~stream worker) (fun _ -> ()) diff --git a/etherlink/bin_node/lib_prod/rollup_node_services.ml b/etherlink/bin_node/lib_prod/rollup_services.ml similarity index 96% rename from etherlink/bin_node/lib_prod/rollup_node_services.ml rename to etherlink/bin_node/lib_prod/rollup_services.ml index 1e85b1f5f187bfa87ea1ca45793e9c09d4e19ede..76f02278d95aef550cd53872bcc326140cb47a31 100644 --- a/etherlink/bin_node/lib_prod/rollup_node_services.ml +++ b/etherlink/bin_node/lib_prod/rollup_services.ml @@ -8,6 +8,14 @@ (* *) (*****************************************************************************) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/6953 + + Make the sequencer node resilient to rollup node disconnect. + + RPC failures makes the sequencer stop or maybe fails to parse + specific element. +*) + open Tezos_rpc open Path diff --git a/etherlink/bin_node/lib_prod/sequencer.ml b/etherlink/bin_node/lib_prod/sequencer.ml index b728ca5384c71393315b4d1af8f5a09988e98afe..742530028cfdd17b424f81d9d5f5aab7f5ae1a9a 100644 --- a/etherlink/bin_node/lib_prod/sequencer.ml +++ b/etherlink/bin_node/lib_prod/sequencer.ml @@ -87,7 +87,10 @@ end) : Services_backend_sig.Backend = struct ~value:payload evm_state in - let* _ctxt = Evm_context.commit Ctxt.ctxt evm_state in + let (Qty next) = Ctxt.ctxt.next_blueprint_number in + let* () = + Evm_context.commit ~number:(Qty Z.(pred next)) Ctxt.ctxt evm_state + in return_unit end @@ -110,6 +113,7 @@ let install_finalizer_seq server private_server = let* () = Events.shutdown_rpc_server ~private_:true in let* () = Tx_pool.shutdown () in let* () = Rollup_node_follower.shutdown () in + let* () = Evm_events_follower.shutdown () in let* () = Blueprints_publisher.shutdown () in let* () = Delayed_inbox.shutdown () in return_unit @@ -214,13 +218,21 @@ let loop_sequencer : loop now let main ~data_dir ~rollup_node_endpoint ~max_blueprints_lag - ~max_blueprints_catchup ~catchup_cooldown ?genesis_timestamp ~cctxt - ~sequencer ~(configuration : Configuration.sequencer Configuration.t) - ?kernel () = + ~max_blueprints_catchup ~catchup_cooldown + ?(genesis_timestamp = Helpers.now ()) ~cctxt ~sequencer + ~(configuration : Configuration.sequencer Configuration.t) ?kernel () = let open Lwt_result_syntax in let open Configuration in let* smart_rollup_address = - Rollup_node_services.smart_rollup_address rollup_node_endpoint + Rollup_services.smart_rollup_address rollup_node_endpoint + in + let* ctxt, loaded = + Evm_context.init + ?kernel_path:kernel + ~data_dir + ~preimages:configuration.mode.preimages + ~smart_rollup_address + () in let* () = Blueprints_publisher.start @@ -228,18 +240,27 @@ let main ~data_dir ~rollup_node_endpoint ~max_blueprints_lag ~max_blueprints_lag ~max_blueprints_catchup ~catchup_cooldown - (Blueprint_store.make ~data_dir) + ctxt.store in + let* ctxt = - Evm_context.init - ?genesis_timestamp - ~produce_genesis_with:(cctxt, sequencer) - ~data_dir - ?kernel_path:kernel - ~preimages:configuration.mode.preimages - ~smart_rollup_address - () + if not loaded then + (* Create the first empty block. *) + let* genesis = + Sequencer_blueprint.create + ~cctxt + ~sequencer_key:sequencer + ~timestamp:genesis_timestamp + ~smart_rollup_address + ~transactions:[] + ~delayed_transactions:[] + ~number:Ethereum_types.(Qty Z.zero) + ~parent_hash:Ethereum_types.genesis_parent_hash + in + Evm_context.apply_and_publish_blueprint ctxt genesis + else return ctxt in + let module Sequencer = Make (struct let ctxt = ctxt @@ -254,6 +275,10 @@ let main ~data_dir ~rollup_node_endpoint ~max_blueprints_lag let* () = Delayed_inbox.start {rollup_node_endpoint; delayed_inbox_interval = 1} in + let* () = + Evm_events_follower.start + {rollup_node_endpoint; backend = (module Sequencer)} + in let* () = Rollup_node_follower.start {rollup_node_endpoint} in let directory = Services.directory configuration ((module Sequencer), smart_rollup_address) diff --git a/etherlink/bin_node/lib_prod/sequencer_blueprint.ml b/etherlink/bin_node/lib_prod/sequencer_blueprint.ml index e88fb9f3c76cc301cb07061901a8932d30a6d689..965513539ebe5f7ca21a00a4e8d8810e436e4909 100644 --- a/etherlink/bin_node/lib_prod/sequencer_blueprint.ml +++ b/etherlink/bin_node/lib_prod/sequencer_blueprint.ml @@ -157,7 +157,6 @@ let create ~cctxt ~sequencer_key ~timestamp ~smart_rollup_address ~number rlp_sequencer_blueprint) |> return in - let* to_publish = List.mapi_ep (message_from_chunk nb_chunks_publish) to_publish_chunks in diff --git a/etherlink/bin_node/lib_prod/sequencer_blueprint.mli b/etherlink/bin_node/lib_prod/sequencer_blueprint.mli index acaeccc44752eeee96a45b6c30bdc9ef70f519df..e58842598c7e9ca27a7561911b39c10f48d43c40 100644 --- a/etherlink/bin_node/lib_prod/sequencer_blueprint.mli +++ b/etherlink/bin_node/lib_prod/sequencer_blueprint.mli @@ -28,7 +28,7 @@ type t = { valid list of external messages inputs to put in the inbox. *) val create : - cctxt:Client_context.wallet -> + cctxt:#Client_context.wallet -> sequencer_key:Client_keys.sk_uri -> timestamp:Time.Protocol.t -> smart_rollup_address:string -> diff --git a/etherlink/bin_node/lib_prod/simulation.ml b/etherlink/bin_node/lib_prod/simulation.ml index 78462fec200c195363a0a37f81bb6394266a70df..655ef4f1a62aa9092cc5e36398636f5aaac1bb01 100644 --- a/etherlink/bin_node/lib_prod/simulation.ml +++ b/etherlink/bin_node/lib_prod/simulation.ml @@ -289,7 +289,10 @@ let gas_estimation Encodings.{success; result; gas} = The extra gas units, i.e. 2300, will be refunded. *) let simulated_amount = Z.(add simulated_amount (of_int 2300)) in - + (* add a safety margin of 2%, sufficient to cover a 1/64th difference *) + let simulated_amount = + Z.(add simulated_amount (cdiv simulated_amount (of_int 50))) + in return (Ok (quantity_of_z simulated_amount)) | Some false, Some result, _ -> let error_msg = Bytes.to_string result in diff --git a/etherlink/bin_node/lib_prod/simulator.ml b/etherlink/bin_node/lib_prod/simulator.ml index a26a6518cd4c5e755201dde1e9efe5a9994571fc..adc4c34da78b27dddc8e3350e6ff21b604abe97a 100644 --- a/etherlink/bin_node/lib_prod/simulator.ml +++ b/etherlink/bin_node/lib_prod/simulator.ml @@ -11,6 +11,9 @@ module type SimulationBackend = sig Simulation.Encodings.insights tzresult Lwt.t end +(* This value is a hard maximum used by estimateGas. Set at Int64.max_int / 2 *) +let max_gas_limit = Z.of_int64 0x3FFFFFFFFFFFFFFFL + module Make (SimulationBackend : SimulationBackend) = struct let simulate_call call = let open Lwt_result_syntax in @@ -34,7 +37,7 @@ module Make (SimulationBackend : SimulationBackend) = struct in Simulation.call_result results - let estimate_gas call = + let call_estimate_gas call = let open Lwt_result_syntax in let*? messages = Simulation.encode call in let insight_requests = @@ -56,6 +59,30 @@ module Make (SimulationBackend : SimulationBackend) = struct in Simulation.gas_estimation results + let rec confirm_gas (call : Ethereum_types.call) gas = + let open Ethereum_types in + let open Lwt_result_syntax in + let double (Qty z) = Qty Z.(mul (of_int 2) z) in + let reached_max (Qty z) = z >= max_gas_limit in + let new_call = {call with gas = Some gas} in + let* result = call_estimate_gas new_call in + match result with + | Error _ -> + (* TODO: https://gitlab.com/tezos/tezos/-/issues/6984 + All errors should not be treated the same *) + let new_gas = double gas in + if reached_max new_gas then + failwith "Gas estimate reached max gas limit." + else confirm_gas call new_gas + | Ok _ -> return (Ok gas) + + let estimate_gas call = + let open Lwt_result_syntax in + let* res = call_estimate_gas call in + match res with + | Ok gas -> confirm_gas call gas + | Error e -> failwith "Couldn't estimate gas: %s" e + let is_tx_valid tx_raw = let open Lwt_result_syntax in let*? messages = Simulation.encode_tx tx_raw in diff --git a/etherlink/bin_node/lib_prod/store.ml b/etherlink/bin_node/lib_prod/store.ml new file mode 100644 index 0000000000000000000000000000000000000000..297e4b05d9b284d836e9d693d4e2ebe3bbda0f50 --- /dev/null +++ b/etherlink/bin_node/lib_prod/store.ml @@ -0,0 +1,179 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +open Filename.Infix + +type t = {db_uri : Uri.t} + +module Q = struct + open Caqti_request.Infix + open Caqti_type.Std + open Ethereum_types + + let level = + custom + ~encode:(fun (Qty x) -> Ok Z.(to_int x)) + ~decode:(fun x -> Ok (Qty Z.(of_int x))) + int + + let payload = + custom + ~encode:(fun payload -> + Ok + (Data_encoding.Binary.to_string_exn + Blueprint_types.payload_encoding + payload)) + ~decode:(fun bytes -> + Option.to_result ~none:"Not a valid blueprint payload" + @@ Data_encoding.Binary.of_string_opt + Blueprint_types.payload_encoding + bytes) + string + + let context_hash = + custom + ~encode:(fun hash -> Ok (Context_hash.to_b58check hash)) + ~decode:(fun bytes -> + Option.to_result ~none:"Not a valid b58check encoded hash" + @@ Context_hash.of_b58check_opt bytes) + string + + module Executable_blueprints = struct + let create_table = + (unit ->. unit) + @@ {eos| + CREATE TABLE executable_blueprints ( + id SERIAL PRIMARY KEY, + payload BLOB NOT NULL + ) + |eos} + + let insert = + (t2 level payload ->. unit) + @@ {eos|INSERT INTO executable_blueprints (id, payload) VALUES (?, ?)|eos} + + let select = + (level ->? payload) + @@ {eos|SELECT (payload) FROM executable_blueprints WHERE id = ?|eos} + end + + module Publishable_blueprints = struct + let create_table = + (unit ->. unit) + @@ {eos| + CREATE TABLE publishable_blueprints ( + id SERIAL PRIMARY KEY, + payload BLOB NOT NULL + ) + |eos} + + let insert = + (t2 level payload ->. unit) + @@ {eos|INSERT INTO publishable_blueprints (id, payload) VALUES (?, ?)|eos} + + let select = + (level ->? payload) + @@ {eos|SELECT (payload) FROM publishable_blueprints WHERE id = ?|eos} + end + + module Context_hashes = struct + let create_table = + (unit ->. unit) + @@ {eos| + CREATE TABLE context_hashes ( + id SERIAL PRIMARY KEY, + context_hash VARCHAR(52) NOT NULL + ) + |eos} + + let insert = + (t2 level context_hash ->. unit) + @@ {eos|REPLACE INTO context_hashes (id, context_hash) VALUES (?, ?)|eos} + + let select = + (level ->? context_hash) + @@ {eos|SELECT (context_hash) FROM context_hashes WHERE id = ?|eos} + + let get_latest = + (unit ->? t2 level context_hash) + @@ {eos|SELECT id, context_hash FROM context_hashes ORDER BY id DESC LIMIT 1|eos} + end +end + +let with_caqti_error p = + let open Lwt_syntax in + let* p in + match p with + | Ok p -> return_ok p + | Error err -> failwith "Caqti: %a" Caqti_error.pp err + +let init ~data_dir = + let open Lwt_result_syntax in + with_caqti_error + @@ + let path = data_dir // "store.sqlite" in + let*! exists = Lwt_unix.file_exists path in + let uri = Uri.of_string Format.(sprintf "sqlite3:%s" path) in + let* () = + when_ (not exists) @@ fun () -> + Caqti_lwt_unix.with_connection + uri + (fun (module Db : Caqti_lwt.CONNECTION) -> + let* () = Db.exec Q.Publishable_blueprints.create_table () in + let* () = Db.exec Q.Executable_blueprints.create_table () in + let* () = Db.exec Q.Context_hashes.create_table () in + return_unit) + in + return {db_uri = uri} + +module Executable_blueprints = struct + let store {db_uri} number blueprint = + with_caqti_error + @@ Caqti_lwt_unix.with_connection db_uri + @@ fun (module Db : Caqti_lwt.CONNECTION) -> + Db.exec Q.Executable_blueprints.insert (number, blueprint) + + let find {db_uri} number = + with_caqti_error + @@ Caqti_lwt_unix.with_connection db_uri + @@ fun (module Db : Caqti_lwt.CONNECTION) -> + Db.find_opt Q.Executable_blueprints.select number +end + +module Publishable_blueprints = struct + let store {db_uri} number blueprint = + with_caqti_error + @@ Caqti_lwt_unix.with_connection db_uri + @@ fun (module Db : Caqti_lwt.CONNECTION) -> + Db.exec Q.Publishable_blueprints.insert (number, blueprint) + + let find {db_uri} number = + with_caqti_error + @@ Caqti_lwt_unix.with_connection db_uri + @@ fun (module Db : Caqti_lwt.CONNECTION) -> + Db.find_opt Q.Publishable_blueprints.select number +end + +module Context_hashes = struct + let store {db_uri} number hash = + with_caqti_error + @@ Caqti_lwt_unix.with_connection db_uri + @@ fun (module Db : Caqti_lwt.CONNECTION) -> + Db.exec Q.Context_hashes.insert (number, hash) + + let find {db_uri} number = + with_caqti_error + @@ Caqti_lwt_unix.with_connection db_uri + @@ fun (module Db : Caqti_lwt.CONNECTION) -> + Db.find_opt Q.Context_hashes.select number + + let find_latest {db_uri} = + with_caqti_error + @@ Caqti_lwt_unix.with_connection db_uri + @@ fun (module Db : Caqti_lwt.CONNECTION) -> + Db.find_opt Q.Context_hashes.get_latest () +end diff --git a/etherlink/bin_node/lib_prod/store.mli b/etherlink/bin_node/lib_prod/store.mli new file mode 100644 index 0000000000000000000000000000000000000000..5514b0a1ba493d24d281d06c4cbac7a9565d6584 --- /dev/null +++ b/etherlink/bin_node/lib_prod/store.mli @@ -0,0 +1,51 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +type t + +(** [init ~data_dir] returns a handler to the EVM node store located under + [data_dir]. If no store is located in [data_dir], an empty store is + created. Also returns if the store was created ([true]) or was already + existing ([false]). *) +val init : data_dir:string -> t tzresult Lwt.t + +module Executable_blueprints : sig + val store : + t -> + Ethereum_types.quantity -> + Blueprint_types.payload -> + unit tzresult Lwt.t + + val find : + t -> + Ethereum_types.quantity -> + Blueprint_types.payload option tzresult Lwt.t +end + +module Publishable_blueprints : sig + val store : + t -> + Ethereum_types.quantity -> + Blueprint_types.payload -> + unit tzresult Lwt.t + + val find : + t -> + Ethereum_types.quantity -> + Blueprint_types.payload option tzresult Lwt.t +end + +module Context_hashes : sig + val store : + t -> Ethereum_types.quantity -> Context_hash.t -> unit tzresult Lwt.t + + val find : + t -> Ethereum_types.quantity -> Context_hash.t option tzresult Lwt.t + + val find_latest : + t -> (Ethereum_types.quantity * Context_hash.t) option tzresult Lwt.t +end diff --git a/etherlink/bin_node/lib_prod/tx_pool.ml b/etherlink/bin_node/lib_prod/tx_pool.ml index 2dfe12f206ec5862e0d929e6e3a78870b24c4c32..df77ff14a57b1056acc3386ccddd1bcda415acd3 100644 --- a/etherlink/bin_node/lib_prod/tx_pool.ml +++ b/etherlink/bin_node/lib_prod/tx_pool.ml @@ -122,7 +122,7 @@ module Pool = struct aux current_nonce user_transactions |> Ethereum_types.quantity_of_z end -type mode = Proxy of {rollup_node_endpoint : Uri.t} | Sequencer +type mode = Proxy of {rollup_node_endpoint : Uri.t} | Sequencer | Observer type parameters = { rollup_node : (module Services_backend_sig.S); @@ -391,6 +391,19 @@ let on_head ~force ~timestamp state = module Handlers = struct type self = worker + let observer_self_inject_request w = + let open Lwt_result_syntax in + let state = Worker.state w in + match state.mode with + | Observer -> + let*! _ = + Worker.Queue.push_request + w + (Request.Inject_transactions (false, Helpers.now ())) + in + return_unit + | Sequencer | Proxy _ -> return_unit + let on_request : type r request_error. worker -> (r, request_error) Request.t -> (r, request_error) result Lwt.t @@ -399,7 +412,11 @@ module Handlers = struct let state = Worker.state w in match request with | Request.Add_transaction transaction -> - protect @@ fun () -> on_transaction state transaction + protect @@ fun () -> + let open Lwt_result_syntax in + let* res = on_transaction state transaction in + let* () = observer_self_inject_request w in + return res | Request.Inject_transactions (force, timestamp) -> protect @@ fun () -> on_head ~force ~timestamp state @@ -473,10 +490,10 @@ let start ({mode; _} as parameters) = match mode with | Proxy {rollup_node_endpoint} -> let*! stream_l2 = - Rollup_node_services.make_streamed_call ~rollup_node_endpoint + Rollup_services.make_streamed_call ~rollup_node_endpoint in subscribe_l2_block ~stream_l2 worker - | Sequencer -> Lwt.return_unit) + | Sequencer | Observer -> Lwt.return_unit) (fun _ -> ()) in Lwt.wakeup worker_waker worker diff --git a/etherlink/bin_node/lib_prod/tx_pool.mli b/etherlink/bin_node/lib_prod/tx_pool.mli index 5b20782c806c98ba696309ad65b5dd3174b6d9b5..f3dadc37aee4526bf21abcf43496b459888d209a 100644 --- a/etherlink/bin_node/lib_prod/tx_pool.mli +++ b/etherlink/bin_node/lib_prod/tx_pool.mli @@ -8,7 +8,7 @@ (* TODO: https://gitlab.com/tezos/tezos/-/issues/6672 It should be created by the configuration, or at least using values of the configuration. *) -type mode = Proxy of {rollup_node_endpoint : Uri.t} | Sequencer +type mode = Proxy of {rollup_node_endpoint : Uri.t} | Sequencer | Observer type parameters = { rollup_node : (module Services_backend_sig.S); (** The backend RPC module. *) diff --git a/etherlink/bin_node/main.ml b/etherlink/bin_node/main.ml index baa23f1e3a8d41eddb5b2a682a2b69902b7f03d6..7d7db199195174820f7b6a7a67ec6728f32e5d28 100644 --- a/etherlink/bin_node/main.ml +++ b/etherlink/bin_node/main.ml @@ -170,7 +170,7 @@ let rollup_node_config_prod ~rollup_node_endpoint ~keep_alive = let* smart_rollup_address = fetch_smart_rollup_address ~keep_alive - Rollup_node_services.smart_rollup_address + Rollup_services.smart_rollup_address rollup_node_endpoint in return diff --git a/etherlink/kernel_evm/kernel/src/migration.rs b/etherlink/kernel_evm/kernel/src/migration.rs index 686f592f7b54408d4159cf34650bb6f85a5a8052..8cc6fdaaf6ef4a7024c94665fbe751eac1d3e5e0 100644 --- a/etherlink/kernel_evm/kernel/src/migration.rs +++ b/etherlink/kernel_evm/kernel/src/migration.rs @@ -5,9 +5,7 @@ // SPDX-License-Identifier: MIT use crate::error::Error; use crate::error::UpgradeProcessError::Fallback; -use crate::storage::{ - read_storage_version, store_storage_version, KERNEL_GOVERNANCE, STORAGE_VERSION, -}; +use crate::storage::{read_storage_version, store_storage_version, STORAGE_VERSION}; use tezos_smart_rollup_host::runtime::Runtime; pub enum MigrationStatus { @@ -16,13 +14,6 @@ pub enum MigrationStatus { Done, } -fn add_kernel_governance(host: &mut impl Runtime) -> Result<(), Error> { - let contract_b58 = "KT1Nn8bjcPSpg7EUHcZwYPCcfT1W8Z5Q8ug5"; - let bytes = contract_b58.as_bytes(); - host.store_write_all(&KERNEL_GOVERNANCE, bytes) - .map_err(Into::into) -} - // The workflow for migration is the following: // // - bump `storage::STORAGE_VERSION` by one @@ -45,9 +36,6 @@ fn migration(host: &mut Host) -> anyhow::Result let current_version = read_storage_version(host)?; if STORAGE_VERSION == current_version + 1 { // MIGRATION CODE - START - // raise da fee from 2 to 4 mutez/byte - crate::storage::store_da_fee(host, crate::fees::DA_FEE_PER_BYTE.into())?; - add_kernel_governance(host)?; // MIGRATION CODE - END store_storage_version(host, STORAGE_VERSION)?; return Ok(MigrationStatus::Done); diff --git a/etherlink/kernel_evm/kernel/src/storage.rs b/etherlink/kernel_evm/kernel/src/storage.rs index e912ed785ad7c5b17d34e50ff8c96f3efa46cede..77cbc2de9084e71a0d708df6ee370d1c1adf36ac 100644 --- a/etherlink/kernel_evm/kernel/src/storage.rs +++ b/etherlink/kernel_evm/kernel/src/storage.rs @@ -37,7 +37,7 @@ const KERNEL_VERSION_PATH: RefPath = RefPath::assert_from(b"/kernel_version"); const TICKETER: RefPath = RefPath::assert_from(b"/ticketer"); pub const ADMIN: RefPath = RefPath::assert_from(b"/admin"); const SEQUENCER_ADMIN: RefPath = RefPath::assert_from(b"/sequencer_admin"); -pub const KERNEL_GOVERNANCE: RefPath = RefPath::assert_from(b"/kernel_governance"); +const KERNEL_GOVERNANCE: RefPath = RefPath::assert_from(b"/kernel_governance"); const DELAYED_BRIDGE: RefPath = RefPath::assert_from(b"/delayed_bridge"); // Path to the block in progress, used between reboots diff --git a/etherlink/kernel_evm/kernel/tests/resources/ghostnet_evm_kernel.wasm b/etherlink/kernel_evm/kernel/tests/resources/ghostnet_evm_kernel.wasm index d853aa882deab0f75ccc6d5937b3bb20d1b4f6cf..f34de2480560c0a392884a542ed06bf3f5d57147 100755 Binary files a/etherlink/kernel_evm/kernel/tests/resources/ghostnet_evm_kernel.wasm and b/etherlink/kernel_evm/kernel/tests/resources/ghostnet_evm_kernel.wasm differ diff --git a/etherlink/tezt/tests/evm_rollup.ml b/etherlink/tezt/tests/evm_rollup.ml index 1994c1ea2c262d8e709c95aba37c677fe4655509..e986239784deacb6a7eede73d4aee89428764b72 100644 --- a/etherlink/tezt/tests/evm_rollup.ml +++ b/etherlink/tezt/tests/evm_rollup.ml @@ -2659,6 +2659,8 @@ let gen_kernel_migration_test ?config ?(admin = Constant.bootstrap5) ~scenario_prior ~scenario_after protocol = let* evm_setup = setup_evm_kernel + ~da_fee_per_byte:Wei.zero + ~minimum_base_fee_per_gas:(Wei.of_string "21000") ?config ~kernel_installee:Constant.WASM.ghostnet_evm_kernel ~admin:(Some admin) @@ -3151,55 +3153,11 @@ let test_transaction_storage_before_and_after_migration = in gen_kernel_migration_test ~config ~scenario_prior ~scenario_after protocol -let test_migration_sets_kernel_governance = - Protocol.register_test - ~__FILE__ - ~tags:["evm"; "migration"; "kernel_governance"] - ~uses:(fun _protocol -> - [ - Constant.octez_smart_rollup_node; - Constant.octez_evm_node; - Constant.smart_rollup_installer; - Constant.WASM.evm_kernel; - Constant.WASM.ghostnet_evm_kernel; - ]) - ~title:"Migration sets kernel_governance contract" - @@ fun protocol -> - let scenario_prior ~evm_setup:_ = return () in - let scenario_after ~evm_setup ~sanity_check:() = - let* kernel_governance_contract = - Sc_rollup_node.RPC.call - evm_setup.sc_rollup_node - ~rpc_hooks:Tezos_regression.rpc_hooks - @@ Sc_rollup_rpc.get_global_block_durable_state_value - ~pvm_kind - ~operation:Sc_rollup_rpc.Value - ~key:Durable_storage_path.kernel_governance - () - in - let kernel_governance_contract = - Option.map - (fun hex -> - let hex = `Hex hex in - Hex.to_string hex) - kernel_governance_contract - in - Check.( - (kernel_governance_contract = Some "KT1Nn8bjcPSpg7EUHcZwYPCcfT1W8Z5Q8ug5") - (option string)) - ~error_msg: - "The migration should have set the kernel_governance contract, got %L \ - expected %R" ; - unit - in - gen_kernel_migration_test ~scenario_prior ~scenario_after protocol - let register_evm_migration ~protocols = test_kernel_migration protocols ; test_deposit_before_and_after_migration protocols ; test_block_storage_before_and_after_migration protocols ; - test_transaction_storage_before_and_after_migration protocols ; - test_migration_sets_kernel_governance protocols + test_transaction_storage_before_and_after_migration protocols let block_transaction_count_by ~by arg = let method_ = "eth_getBlockTransactionCountBy" ^ by_block_arg_string by in diff --git a/etherlink/tezt/tests/expected/evm_rollup.ml/Alpha- Regression test for Ghostnet kernel.out b/etherlink/tezt/tests/expected/evm_rollup.ml/Alpha- Regression test for Ghostnet kernel.out index 17ef8fb1813a8b0276034a49488e1f47f94666c6..c4be452e0f5fdcdb9ce5835f51fa4b43c15cce2a 100644 --- a/etherlink/tezt/tests/expected/evm_rollup.ml/Alpha- Regression test for Ghostnet kernel.out +++ b/etherlink/tezt/tests/expected/evm_rollup.ml/Alpha- Regression test for Ghostnet kernel.out @@ -1 +1 @@ -c5969505b81b52a779270b69f48b6a66c84da429 +20ab639f09a8c7c76f982383c3d9e1f831f38088 diff --git a/manifest/main.ml b/manifest/main.ml index f3e30a653fbbc157f4e97ab62598383c772dac71..600cb6c18e77a2b86cb6fa72c873acff201b3cc7 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -8411,6 +8411,10 @@ let evm_node_lib_prod = evm_node_lib_prod_encoding |> open_; lwt_watcher; lwt_exit; + caqti; + caqti_lwt; + caqti_lwt_unix; + caqti_sqlite; octez_client_base |> open_; evm_node_config |> open_; octez_context_sigs; diff --git a/opam/octez-evm-node-libs.opam b/opam/octez-evm-node-libs.opam index 20b8f8ecdb53769996b4fdc024f2d7ea3e9ccabd..a2648fc3bc597dcae14facee5bd3b6962918a1c6 100644 --- a/opam/octez-evm-node-libs.opam +++ b/opam/octez-evm-node-libs.opam @@ -16,12 +16,12 @@ depends: [ "octez-version" "lwt-watcher" { = "0.2" } "lwt-exit" - "octez-shell-libs" - "octez-l2-libs" - "octez-smart-rollup-wasm-debugger-lib" "caqti" "caqti-lwt" { >= "2.0.1" } "caqti-driver-sqlite3" { >= "2.0.1" } + "octez-shell-libs" + "octez-l2-libs" + "octez-smart-rollup-wasm-debugger-lib" ] build: [ ["rm" "-r" "vendors" "contrib"]