From e8e2b6570bd86f315d9d26b8adca322e7b325664 Mon Sep 17 00:00:00 2001 From: Antoine Lanco Date: Thu, 14 Nov 2024 11:43:35 +0100 Subject: [PATCH 1/4] EVM/Kernel: Add flush_delayed_inbox event --- etherlink/kernel_evm/kernel/src/event.rs | 26 ++++++++++++++++++-- etherlink/kernel_evm/kernel/src/stage_one.rs | 14 ++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/etherlink/kernel_evm/kernel/src/event.rs b/etherlink/kernel_evm/kernel/src/event.rs index 0a20b5a7c027..8aa7ba6c8f39 100644 --- a/etherlink/kernel_evm/kernel/src/event.rs +++ b/etherlink/kernel_evm/kernel/src/event.rs @@ -1,24 +1,35 @@ // SPDX-FileCopyrightText: 2024 Nomadic Labs +// SPDX-FileCopyrightText: 2024 Functori // // SPDX-License-Identifier: MIT use crate::{inbox::Transaction, storage, upgrade}; use primitive_types::{H256, U256}; use rlp::{Encodable, RlpStream}; -use tezos_ethereum::rlp_helpers::append_u256_le; +use tezos_ethereum::rlp_helpers::{append_timestamp, append_u256_le}; use tezos_evm_runtime::runtime::Runtime; +use tezos_smart_rollup_encoding::timestamp::Timestamp; pub const UPGRADE_TAG: u8 = 0x01; pub const SEQUENCER_UPGRADE_TAG: u8 = 0x02; pub const BLUEPRINT_APPLIED_TAG: u8 = 0x03; pub const NEW_DELAYED_TRANSACTION_TAG: u8 = 0x04; +pub const FLUSH_DELAYED_INBOX: u8 = 0x05; #[derive(Debug, PartialEq, Clone)] pub enum Event { Upgrade(upgrade::KernelUpgrade), SequencerUpgrade(upgrade::SequencerUpgrade), - BlueprintApplied { number: U256, hash: H256 }, + BlueprintApplied { + number: U256, + hash: H256, + }, NewDelayedTransaction(Box), + FlushDelayedInbox { + hashes: Vec, + timestamp: Timestamp, + level: U256, + }, } impl Encodable for Event { @@ -43,6 +54,17 @@ impl Encodable for Event { stream.append(&NEW_DELAYED_TRANSACTION_TAG); stream.append(txn); } + Event::FlushDelayedInbox { + hashes, + timestamp, + level, + } => { + stream.append(&FLUSH_DELAYED_INBOX); + stream.begin_list(3); + stream.append_list(hashes); + append_timestamp(stream, *timestamp); + stream.append(level); + } } } } diff --git a/etherlink/kernel_evm/kernel/src/stage_one.rs b/etherlink/kernel_evm/kernel/src/stage_one.rs index cb60c0d542c6..e547fd49e632 100644 --- a/etherlink/kernel_evm/kernel/src/stage_one.rs +++ b/etherlink/kernel_evm/kernel/src/stage_one.rs @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2023 Nomadic Labs +// SPDX-FileCopyrightText: 2024 Functori // // SPDX-License-Identifier: MIT @@ -9,9 +10,11 @@ use crate::configuration::{ }; use crate::current_timestamp; use crate::delayed_inbox::DelayedInbox; +use crate::event::Event; use crate::inbox::{read_proxy_inbox, read_sequencer_inbox}; use crate::inbox::{ProxyInboxContent, StageOneStatus}; use anyhow::Ok; +use primitive_types::H256; use std::ops::Add; use tezos_crypto_rs::hash::ContractKt1Hash; use tezos_ethereum::block::L2Block; @@ -80,13 +83,22 @@ fn fetch_delayed_transactions( } }; + let hashes: Vec = timed_out.iter().map(|x| H256::from(x.tx_hash)).collect(); + // Create a new blueprint with the timed out transactions let blueprint = Blueprint { transactions: timed_out, timestamp, }; + let level = base.add(offset); + Event::FlushDelayedInbox { + hashes, + timestamp, + level, + } + .store(host)?; // Store the blueprint. - store_immediate_blueprint(host, blueprint, base.add(offset))?; + store_immediate_blueprint(host, blueprint, level)?; offset += 1; } -- GitLab From 75c9a2e07a7dc0f6dea43b0d8dd92c86130f24d2 Mon Sep 17 00:00:00 2001 From: Antoine Lanco Date: Fri, 15 Nov 2024 09:30:45 +0100 Subject: [PATCH 2/4] EVM/Node: Add flush_delayed_inbox decoding --- .../bin_node/lib_dev/encodings/evm_events.ml | 50 +++++++++++++++++++ .../bin_node/lib_dev/encodings/evm_events.mli | 5 ++ .../bin_node/lib_dev/encodings/helpers.ml | 13 +++++ etherlink/bin_node/lib_dev/evm_context.ml | 7 +++ .../lib_dev/evm_events_follower_events.ml | 14 ++++++ 5 files changed, 89 insertions(+) diff --git a/etherlink/bin_node/lib_dev/encodings/evm_events.ml b/etherlink/bin_node/lib_dev/encodings/evm_events.ml index d69f8f1d7307..b853baf3bf30 100644 --- a/etherlink/bin_node/lib_dev/encodings/evm_events.ml +++ b/etherlink/bin_node/lib_dev/encodings/evm_events.ml @@ -197,11 +197,43 @@ module Blueprint_applied = struct (tup2 z string) end +module Flushed_blueprint = struct + type t = {hashes : hash list; timestamp : Time.Protocol.t; level : quantity} + + let of_rlp = function + | Rlp.List [List hashes; Value timestamp; Value level] -> + let hashes = + Helpers.fold_left_option + (fun acc -> function + | Rlp.Value i -> Some (decode_hash i :: acc) + | _ -> None) + [] + hashes + in + Option.map + (fun hashes -> + { + hashes; + timestamp = timestamp_of_bytes timestamp; + level = decode_number_le level; + }) + hashes + | _ -> None + + let encoding = + let open Data_encoding in + conv + (fun {hashes; timestamp; level = Qty level} -> (hashes, timestamp, level)) + (fun (hashes, timestamp, level) -> {hashes; timestamp; level = Qty level}) + (tup3 (Data_encoding.list hash_encoding) Time.Protocol.encoding z) +end + type t = | Upgrade_event of Upgrade.t | Sequencer_upgrade_event of Sequencer_upgrade.t | Blueprint_applied of Blueprint_applied.t | New_delayed_transaction of Delayed_transaction.t + | Flush_delayed_inbox of Flushed_blueprint.t let of_bytes bytes = match bytes |> Rlp.decode with @@ -225,6 +257,9 @@ let of_bytes bytes = in Option.map (fun u -> New_delayed_transaction u) transaction | _ -> None) + | "\x05" -> + let flushed_blueprint = Flushed_blueprint.of_rlp rlp_content in + Option.map (fun u -> Flush_delayed_inbox u) flushed_blueprint | _ -> None) | _ -> None @@ -260,6 +295,14 @@ let pp fmt = function "New delayed transaction:@ %a" Delayed_transaction.pp_short delayed_transaction + | Flush_delayed_inbox {hashes = _; timestamp; level = Qty level} -> + Format.fprintf + fmt + "Flush delayed inbox:@,timestamp:%a@, level: %a" + Time.Protocol.pp + timestamp + Z.pp_print + level let encoding = let open Data_encoding in @@ -302,4 +345,11 @@ let encoding = | _ -> None) ~inj:(fun delayed_transaction -> New_delayed_transaction delayed_transaction); + case + ~kind:"flush_delayed_inbox" + ~tag:4 + ~event_encoding:Flushed_blueprint.encoding + ~proj:(function + | Flush_delayed_inbox blueprint -> Some blueprint | _ -> None) + ~inj:(fun blueprint -> Flush_delayed_inbox blueprint); ] diff --git a/etherlink/bin_node/lib_dev/encodings/evm_events.mli b/etherlink/bin_node/lib_dev/encodings/evm_events.mli index fde2a9bbcb35..9ccd8bc4f3af 100644 --- a/etherlink/bin_node/lib_dev/encodings/evm_events.mli +++ b/etherlink/bin_node/lib_dev/encodings/evm_events.mli @@ -54,11 +54,16 @@ module Blueprint_applied : sig type t = {number : quantity; hash : block_hash} end +module Flushed_blueprint : sig + type t = {hashes : hash list; timestamp : Time.Protocol.t; level : quantity} +end + type t = | Upgrade_event of Upgrade.t | Sequencer_upgrade_event of Sequencer_upgrade.t | Blueprint_applied of Blueprint_applied.t | New_delayed_transaction of Delayed_transaction.t + | Flush_delayed_inbox of Flushed_blueprint.t val pp : Format.formatter -> t -> unit diff --git a/etherlink/bin_node/lib_dev/encodings/helpers.ml b/etherlink/bin_node/lib_dev/encodings/helpers.ml index acf8d24593a7..f15c0c01fcd7 100644 --- a/etherlink/bin_node/lib_dev/encodings/helpers.ml +++ b/etherlink/bin_node/lib_dev/encodings/helpers.ml @@ -36,3 +36,16 @@ let encoding_with_optional_last_param encoding second_param_encoding (fun (t, _) -> Some t) (fun t -> (t, default_second_param)); ] + +(* A variation of List.fold_left where the function f returns an option. + If f returns None, the fold stops and returns None. + If f returns Some, the fold continues with the updated accumulator. + This is a lazy fold_left to avoid iterating over the entire list unnecessarily. *) +let fold_left_option f acc l = + let rec aux f acc l = + match (acc, l) with + | None, _ -> None + | _, [] -> acc + | Some acc, hd :: tl -> aux f (f acc hd) tl + in + aux f (Some acc) l diff --git a/etherlink/bin_node/lib_dev/evm_context.ml b/etherlink/bin_node/lib_dev/evm_context.ml index 0ed785cdd662..123aa5d6bb9c 100644 --- a/etherlink/bin_node/lib_dev/evm_context.ml +++ b/etherlink/bin_node/lib_dev/evm_context.ml @@ -599,6 +599,13 @@ module State = struct on_new_delayed_transaction ~delayed_transaction evm_state in return (evm_state, on_success) + | Flush_delayed_inbox flushed_blueprint -> + let*! () = + Evm_events_follower_events.flush_delayed_inbox + ~timestamp:flushed_blueprint.timestamp + flushed_blueprint.level + in + return (evm_state, on_success) let current_blueprint_number ctxt = let (Qty next_blueprint_number) = ctxt.session.next_blueprint_number in diff --git a/etherlink/bin_node/lib_dev/evm_events_follower_events.ml b/etherlink/bin_node/lib_dev/evm_events_follower_events.ml index d35331fc1c97..864dd1d9819c 100644 --- a/etherlink/bin_node/lib_dev/evm_events_follower_events.ml +++ b/etherlink/bin_node/lib_dev/evm_events_follower_events.ml @@ -139,6 +139,17 @@ module Event = struct multiple RPCs fetching." ~level:Warning () + + let event_flush_delayed_inbox = + declare_2 + ~section + ~name:"flush_delayed_inbox" + ~msg: + "The delayed inbox has been flushed in a blueprint at level {level} (timestamp: \ + {timestamp})." + ~level:Notice + ("timestamp", Time.Protocol.encoding) + ("level", Data_encoding.n) end let started = Internal_event.Simple.emit Event.started @@ -175,3 +186,6 @@ let unexpected_number_of_events ~expected ~fetched = (expected, fetched) let fallback () = Internal_event.Simple.emit Event.fallback () + +let flush_delayed_inbox ~timestamp Ethereum_types.(Qty level) = + Internal_event.Simple.emit Event.event_flush_delayed_inbox (timestamp, level) -- GitLab From 39191059e66c8f6fe8d6d3c72b435bc35b7777d2 Mon Sep 17 00:00:00 2001 From: Antoine Lanco Date: Fri, 15 Nov 2024 13:54:31 +0100 Subject: [PATCH 3/4] Tezt: flush delayed inbox tezt --- etherlink/tezt/lib/evm_node.ml | 4 ++ etherlink/tezt/lib/evm_node.mli | 2 + etherlink/tezt/tests/evm_sequencer.ml | 57 +++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/etherlink/tezt/lib/evm_node.ml b/etherlink/tezt/lib/evm_node.ml index fb567066ef87..a36583cf061a 100644 --- a/etherlink/tezt/lib/evm_node.ml +++ b/etherlink/tezt/lib/evm_node.ml @@ -376,6 +376,10 @@ let wait_for_rollup_node_follower_disabled ?timeout evm_node = wait_for_event ?timeout evm_node ~event:"rollup_node_follower_disabled.v0" @@ Fun.const (Some ()) +let wait_for_flush_delayed_inbox ?timeout evm_node = + wait_for_event ?timeout evm_node ~event:"flush_delayed_inbox.v0" + @@ Fun.const (Some ()) + let wait_for_blueprint_invalid ?timeout evm_node = wait_for_event ?timeout evm_node ~event:"blueprint_invalid.v0" @@ Fun.const (Some ()) diff --git a/etherlink/tezt/lib/evm_node.mli b/etherlink/tezt/lib/evm_node.mli index 78011b0b7f37..f5f1cb8c8fb6 100644 --- a/etherlink/tezt/lib/evm_node.mli +++ b/etherlink/tezt/lib/evm_node.mli @@ -217,6 +217,8 @@ val wait_for_block_producer_tx_injected : ?timeout:float -> t -> string Lwt.t val wait_for_retrying_connect : ?timeout:float -> t -> unit Lwt.t +val wait_for_flush_delayed_inbox : ?timeout:float -> t -> unit Lwt.t + val wait_for_rollup_node_follower_connection_acquired : ?timeout:float -> t -> unit Lwt.t diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index 862c35438ede..4c4b844dab72 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -2937,6 +2937,62 @@ let test_legacy_deposits_dispatched_after_kernel_upgrade = unit +let test_delayed_inbox_flushing_event = + register_all + ~delayed_inbox_timeout:3 + ~delayed_inbox_min_levels:1 + ~da_fee:arb_da_fee_for_delayed_inbox + ~tags:["evm"; "sequencer"; "delayed_inbox"; "timeout"; "flush"] + ~title:"Flush delayed inbox event" + ~use_dal:Register_without_feature + ~use_threshold_encryption:Register_without_feature + ~kernels:[Latest] + @@ fun { + client; + l1_contracts; + sc_rollup_address; + sc_rollup_node; + sequencer; + observer; + proxy; + _; + } + _protocol -> + (* Kill the sequencer *) + let* () = Evm_node.terminate sequencer in + let* _ = next_rollup_node_level ~sc_rollup_node ~client in + let _ = Rpc.block_number proxy in + (* This is a transfer from Eth_account.bootstrap_accounts.(0) to + Eth_account.bootstrap_accounts.(1). *) + let* raw_transfer = + Cast.craft_tx + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~chain_id:1337 + ~nonce:0 + ~gas_price:1_000_000_000 + ~gas:23_300 + ~value:(Wei.of_eth_int 1) + ~address:Eth_account.bootstrap_accounts.(1).address + () + in + let* _hash = + send_raw_transaction_to_delayed_inbox + ~sc_rollup_node + ~client + ~l1_contracts + ~sc_rollup_address + raw_transfer + in + (* Bake a few blocks, should be enough for the tx to time out and be + forced *) + let wait_for_flush = Evm_node.wait_for_flush_delayed_inbox observer in + let* _ = + repeat 5 (fun () -> + let* _ = next_rollup_node_level ~sc_rollup_node ~client in + unit) + and* () = wait_for_flush in + unit + let test_delayed_transfer_timeout = register_all ~delayed_inbox_timeout:3 @@ -7009,6 +7065,7 @@ let () = test_delayed_transfer_timeout_fails_l1_levels protocols ; test_forced_blueprint_takes_pred_timestamp protocols ; test_forced_blueprint_takes_l1_timestamp protocols ; + test_delayed_inbox_flushing_event protocols ; test_delayed_inbox_flushing protocols ; test_no_automatic_block_production protocols ; test_non_increasing_timestamp protocols ; -- GitLab From aafb0f21d440f019aa46b8a3d038f973915f5529 Mon Sep 17 00:00:00 2001 From: Antoine Lanco Date: Fri, 22 Nov 2024 12:10:36 +0100 Subject: [PATCH 4/4] EVM/Node: Add fail to decode evm_event event --- .../bin_node/lib_dev/encodings/evm_events.ml | 95 ++++++++++++++----- .../bin_node/lib_dev/encodings/evm_events.mli | 2 +- .../bin_node/lib_dev/evm_events_follower.ml | 10 +- .../lib_dev/evm_events_follower_events.ml | 4 +- 4 files changed, 81 insertions(+), 30 deletions(-) diff --git a/etherlink/bin_node/lib_dev/encodings/evm_events.ml b/etherlink/bin_node/lib_dev/encodings/evm_events.ml index b853baf3bf30..5446209bb40e 100644 --- a/etherlink/bin_node/lib_dev/encodings/evm_events.ml +++ b/etherlink/bin_node/lib_dev/encodings/evm_events.ml @@ -9,6 +9,41 @@ open Ethereum_types +module Event = struct + open Internal_event.Simple + + let section = ["evm_node"; "dev"] + + let fail_decode_evm_event event = + declare_1 + ~section + ~name:(Format.sprintf "fail_decode_%s" event) + ~msg:(Format.sprintf "Fail to decode %s {tag}" event) + ~level:Error + ("tag", Data_encoding.string) +end + +let fail_decode_event tag = + match tag with + | "\x01" -> + let event = "kernel_upgrade" in + Internal_event.Simple.emit (Event.fail_decode_evm_event event) tag + | "\x02" -> + let event = "sequencer_upgrade" in + Internal_event.Simple.emit (Event.fail_decode_evm_event event) tag + | "\x03" -> + let event = "blueprint_applied" in + Internal_event.Simple.emit (Event.fail_decode_evm_event event) tag + | "\x04" -> + let event = "new_delayed_transaction" in + Internal_event.Simple.emit (Event.fail_decode_evm_event event) tag + | "\x05" -> + let event = "flush_delayed_inbox" in + Internal_event.Simple.emit (Event.fail_decode_evm_event event) tag + | _ -> + let event = "unknown" in + Internal_event.Simple.emit (Event.fail_decode_evm_event event) tag + module Delayed_transaction = struct type kind = Transaction | Deposit | Fa_deposit @@ -236,32 +271,42 @@ type t = | Flush_delayed_inbox of Flushed_blueprint.t let of_bytes bytes = + let open Lwt_syntax in 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 - | "\x02" -> - let sequencer_upgrade = Sequencer_upgrade.of_rlp rlp_content in - Option.map (fun u -> Sequencer_upgrade_event u) sequencer_upgrade - | "\x03" -> - let blueprint_applied = Blueprint_applied.of_rlp rlp_content in - Option.map (fun u -> Blueprint_applied u) blueprint_applied - | "\x04" -> ( - match rlp_content with - | List [Value hash; transaction_content] -> - let hash = decode_hash hash in - let transaction = - Delayed_transaction.of_rlp_content hash transaction_content - in - Option.map (fun u -> New_delayed_transaction u) transaction - | _ -> None) - | "\x05" -> - let flushed_blueprint = Flushed_blueprint.of_rlp rlp_content in - Option.map (fun u -> Flush_delayed_inbox u) flushed_blueprint - | _ -> None) - | _ -> None + | Ok (Rlp.List [Value tag; rlp_content]) -> + let string_tag = Bytes.to_string tag in + let event = + match string_tag with + | "\x01" -> + let upgrade = Upgrade.of_rlp rlp_content in + Option.map (fun u -> Upgrade_event u) upgrade + | "\x02" -> + let sequencer_upgrade = Sequencer_upgrade.of_rlp rlp_content in + Option.map (fun u -> Sequencer_upgrade_event u) sequencer_upgrade + | "\x03" -> + let blueprint_applied = Blueprint_applied.of_rlp rlp_content in + Option.map (fun u -> Blueprint_applied u) blueprint_applied + | "\x04" -> ( + match rlp_content with + | List [Value hash; transaction_content] -> + let hash = decode_hash hash in + let transaction = + Delayed_transaction.of_rlp_content hash transaction_content + in + Option.map (fun u -> New_delayed_transaction u) transaction + | _ -> None) + | "\x05" -> + let flushed_blueprint = Flushed_blueprint.of_rlp rlp_content in + Option.map (fun u -> Flush_delayed_inbox u) flushed_blueprint + | _ -> None + in + + let* () = + if Option.is_none event then fail_decode_event string_tag + else return_unit + in + return event + | _ -> return_none let pp fmt = function | Upgrade_event {hash; timestamp} -> diff --git a/etherlink/bin_node/lib_dev/encodings/evm_events.mli b/etherlink/bin_node/lib_dev/encodings/evm_events.mli index 9ccd8bc4f3af..bbf54c084c79 100644 --- a/etherlink/bin_node/lib_dev/encodings/evm_events.mli +++ b/etherlink/bin_node/lib_dev/encodings/evm_events.mli @@ -69,4 +69,4 @@ val pp : Format.formatter -> t -> unit val encoding : t Data_encoding.t -val of_bytes : bytes -> t option +val of_bytes : bytes -> t option Lwt.t diff --git a/etherlink/bin_node/lib_dev/evm_events_follower.ml b/etherlink/bin_node/lib_dev/evm_events_follower.ml index 041052925a69..bce185c26e83 100644 --- a/etherlink/bin_node/lib_dev/evm_events_follower.ml +++ b/etherlink/bin_node/lib_dev/evm_events_follower.ml @@ -54,7 +54,12 @@ let fetch_event ({rollup_node_endpoint; keep_alive; _} : Types.state) let* bytes_opt = read_from_rollup_node ~keep_alive path rollup_block_lvl rollup_node_endpoint in - let event_opt = Option.bind bytes_opt Evm_events.of_bytes in + let*! event_opt = + match bytes_opt with + | Some bytes -> Evm_events.of_bytes bytes + | None -> Lwt.return_none + in + let open Lwt_result_syntax in let*! () = if Option.is_none event_opt then Evm_events_follower_events.unreadable_event (event_index, rollup_block_lvl) @@ -125,7 +130,8 @@ let fetch_events_at_once let*! () = Evm_events_follower_events.unexpected_key index in return_none | Some event_index -> ( - match Evm_events.of_bytes value with + let* event = Evm_events.of_bytes value in + match event with | None -> return_none | Some event -> return_some (event_index, event)))) bindings diff --git a/etherlink/bin_node/lib_dev/evm_events_follower_events.ml b/etherlink/bin_node/lib_dev/evm_events_follower_events.ml index 864dd1d9819c..b68da9bb7773 100644 --- a/etherlink/bin_node/lib_dev/evm_events_follower_events.ml +++ b/etherlink/bin_node/lib_dev/evm_events_follower_events.ml @@ -145,8 +145,8 @@ module Event = struct ~section ~name:"flush_delayed_inbox" ~msg: - "The delayed inbox has been flushed in a blueprint at level {level} (timestamp: \ - {timestamp})." + "The delayed inbox has been flushed in a blueprint at level {level} \ + (timestamp: {timestamp})." ~level:Notice ("timestamp", Time.Protocol.encoding) ("level", Data_encoding.n) -- GitLab