From 132a61aa0daf66e223aa5f6da6580bf61f0c5570 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 11 Jul 2024 10:37:49 +0200 Subject: [PATCH 01/12] lib_base: add bitset module from protocol Encoding changed to n, though. --- src/lib_base/bitset.ml | 74 +++++++++++++++++++++++++++++++++++++++ src/lib_base/bitset.mli | 76 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 src/lib_base/bitset.ml create mode 100644 src/lib_base/bitset.mli diff --git a/src/lib_base/bitset.ml b/src/lib_base/bitset.ml new file mode 100644 index 000000000000..a6894f9c1be2 --- /dev/null +++ b/src/lib_base/bitset.ml @@ -0,0 +1,74 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +open Error_monad + +type t = Z.t + +type error += Invalid_position of int + +let encoding = Data_encoding.n + +let empty = Z.zero + +let is_empty = Z.equal Z.zero + +let equal = Z.equal + +let mem field pos = + let open Result_syntax in + let* () = error_when Compare.Int.(pos < 0) (Invalid_position pos) in + return @@ Z.testbit field pos + +let add field pos = + let open Result_syntax in + let* () = error_when Compare.Int.(pos < 0) (Invalid_position pos) in + return @@ Z.logor field Z.(shift_left one pos) + +let remove field pos = + let open Result_syntax in + let* () = error_when Compare.Int.(pos < 0) (Invalid_position pos) in + return @@ Z.logand field Z.(lognot (shift_left one pos)) + +let from_list positions = List.fold_left_e add empty positions + +let to_list field = + let[@tailrec] rec to_list pos acc field = + if Z.equal Z.zero field then acc + else + let acc = if Z.testbit field 0 then pos :: acc else acc in + to_list (pos + 1) acc (Z.shift_right field 1) + in + to_list 0 [] field |> List.rev + +let fill ~length = + let open Result_syntax in + let* () = error_when Compare.Int.(length < 0) (Invalid_position length) in + return Z.(pred (shift_left one length)) + +let inter = Z.logand + +let diff b1 b2 = Z.logand b1 (Z.lognot b2) + +let () = + let open Data_encoding in + register_error_kind + `Permanent + ~id:"bitfield_invalid_position" + ~title:"Invalid bitfield’s position" + ~description:"Bitfields does not accept negative positions" + (obj1 (req "position" int31)) + (function Invalid_position i -> Some i | _ -> None) + (fun i -> Invalid_position i) + +let occupied_size_in_bits = Z.numbits + +let cardinal = + (* Cardinal of bitset is the hamming weight, i.e. the number of ones. *) + Z.popcount + +let to_z z = z diff --git a/src/lib_base/bitset.mli b/src/lib_base/bitset.mli new file mode 100644 index 000000000000..4bf58c716b76 --- /dev/null +++ b/src/lib_base/bitset.mli @@ -0,0 +1,76 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +open Error_monad + +(** A bitset is a compact structure to store a set of integers. *) +type t + +type error += Invalid_position of int + +val encoding : t Data_encoding.t + +(** A bitset encoding the empty set. *) +val empty : t + +(** [is_empty i] is [true] if [i] is empty. *) +val is_empty : t -> bool + +(** [equal i j] is [true] if [i] and [j] are identical. *) +val equal : t -> t -> bool + +(** [mem field i] returns [true] iff [i] has been added in [field]. + + This functions returns [Invalid_input i] if [i] is negative. *) +val mem : t -> int -> bool tzresult + +(** [add field i] returns a new bitset which contains [i] in + addition to the previous integers of [field]. + + This functions returns [Invalid_input i] if [i] is negative. *) +val add : t -> int -> t tzresult + +(** [remove field i] returns a new bitset in which [i] is + removed from [field]. + + This functions returns [Invalid_input i] if [i] is negative. *) +val remove : t -> int -> t tzresult + +(** [from_list positions] folds [add] over the [positions] starting from [empty]. + This function returns [Invalid_input i] if [i] is negative and appears in + [positions]. *) +val from_list : int list -> t tzresult + +(** [to_list t] returns the list of int in the bitset. *) +val to_list : t -> int list + +(** [fill ~length] is equivalent to setting all bits for positions in + [0, length - 1] to [one]. i.e., to [from_list (0 -- size -1)] or to + [(2 ^ length) - 1]. But it's more efficient than folding on individual + positions to set them. + + The function returns [Invalid_position length] if [length] is negative. +*) +val fill : length:int -> t tzresult + +(** [inter set_l set_r] returns [set] which is result of the + intersection of [set_l] and [set_r]. *) +val inter : t -> t -> t + +(** [diff set_l set_r] returns a [set] containing fiels in [set_l] + that are not in [set_r]. *) +val diff : t -> t -> t + +(** [occupied_size_in_bits bitset] returns the current number of bits + occupied by the [bitset]. *) +val occupied_size_in_bits : t -> int + +(** [cardinat bitset] returns the number of elements in the [bitsest]. *) +val cardinal : t -> int + +(** [to_z t] Returns the sum of powers of two of the given bitset. *) +val to_z : t -> Z.t -- GitLab From 21495dad92a942791a05f680e915c8d5e218ddfa Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 11 Jul 2024 17:38:42 +0200 Subject: [PATCH 02/12] Rollup node: structure for storing outbox messages on disk --- src/lib_smart_rollup_node/node_context.ml | 29 ++++++ src/lib_smart_rollup_node/node_context.mli | 25 +++++ src/lib_smart_rollup_node/store_v2.ml | 112 +++++++++++++++++++++ src/lib_smart_rollup_node/store_v2.mli | 30 ++++++ 4 files changed, 196 insertions(+) diff --git a/src/lib_smart_rollup_node/node_context.ml b/src/lib_smart_rollup_node/node_context.ml index 1ffbfbf86edd..f15ee3c3ec26 100644 --- a/src/lib_smart_rollup_node/node_context.ml +++ b/src/lib_smart_rollup_node/node_context.ml @@ -723,6 +723,35 @@ let save_messages {store; _} key ~predecessor messages = ~header:predecessor ~value:(messages :> string list) +let set_outbox_message_executed {store; _} ~outbox_level ~index = + Store.Outbox_messages.set_outbox_message_executed + store.outbox_messages + ~outbox_level + ~index + +let get_executable_pending_outbox_messages {store; lcc; current_protocol; _} = + let max_level = (Reference.get lcc).level in + let constants = (Reference.get current_protocol).constants.sc_rollup in + let min_level = + Int32.sub + max_level + (Int32.of_int + (constants.max_number_of_stored_cemented_commitments + * constants.commitment_period_in_blocks)) + in + Store.Outbox_messages.pending store.outbox_messages ~min_level ~max_level + +let get_unexecutable_pending_outbox_messages ({store; lcc; _} as node_ctxt) = + let open Lwt_result_syntax in + let* head = last_processed_head_opt node_ctxt in + let*? max_level = + match head with + | None -> error_with "No L2 head" + | Some h -> Ok h.header.level + in + let min_level = Int32.succ (Reference.get lcc).level in + Store.Outbox_messages.pending store.outbox_messages ~min_level ~max_level + let get_full_l2_block ?get_outbox_messages node_ctxt block_hash = let open Lwt_result_syntax in let* block = get_l2_block node_ctxt block_hash in diff --git a/src/lib_smart_rollup_node/node_context.mli b/src/lib_smart_rollup_node/node_context.mli index 5fb612e8e57a..99ff60d5ce7f 100644 --- a/src/lib_smart_rollup_node/node_context.mli +++ b/src/lib_smart_rollup_node/node_context.mli @@ -463,6 +463,31 @@ type proto_info = { (** Hash of the {e current} protocol for this level. *) } +(** {3 Outbox messages} *) + +(** Marks the outbox message at index for a given outbox level as executed in + the persistent storage. *) +val set_outbox_message_executed : + rw -> outbox_level:int32 -> index:int -> unit tzresult Lwt.t + +(** [register_new_outbox_messages node_ctxt ~outbox_level ~indexes] registers + the messages indexes for the [outbox_level]. If messages were already + registered for this level, they are overwritten. Messages added are first + marked unexecuted. *) +(** Returns the pending messages (i.e. unexecuted) that can now be executed. + The returned list contains outbox levels and indexes for each level (in + order). *) +val get_executable_pending_outbox_messages : + _ t -> (int32 * int list) list tzresult Lwt.t + +(** Returns the pending messages (i.e. unexecuted) that cannot be executed yet. + The returned list contains outbox levels and indexes for each level (in + order). *) +val get_unexecutable_pending_outbox_messages : + _ t -> (int32 * int list) list tzresult Lwt.t + +(** {3 Protocol} *) + (** [protocol_of_level_with_store store level] returns the protocol of block level [level]. *) val protocol_of_level_with_store : _ Store.t -> int32 -> proto_info tzresult Lwt.t diff --git a/src/lib_smart_rollup_node/store_v2.ml b/src/lib_smart_rollup_node/store_v2.ml index 3e511186bcea..7eea5ed43065 100644 --- a/src/lib_smart_rollup_node/store_v2.ml +++ b/src/lib_smart_rollup_node/store_v2.ml @@ -122,6 +122,106 @@ module Commitments = include Add_empty_header end) +module Outbox_messages = struct + type messages_per_level = {messages : Bitset.t; executed_messages : Bitset.t} + + module H = Hashtbl.Make (struct + include Int32 + + let hash = to_int + end) + + type pending_messages = messages_per_level H.t + + (* Relatively small so we keep all in memory. *) + include Indexed_store.Make_singleton (struct + type t = pending_messages + + let name = "outbox_messages" + + let encoding = + let open Data_encoding in + conv + (fun tbl -> H.to_seq tbl |> List.of_seq) + (fun l -> List.to_seq l |> H.of_seq) + @@ list + (conv + (fun (outbox_level, {messages; executed_messages}) -> + (outbox_level, (messages, executed_messages))) + (fun (outbox_level, (messages, executed_messages)) -> + (outbox_level, {messages; executed_messages})) + @@ merge_objs + (obj1 (req "outbox_level" int32)) + (obj2 + (req "messages" Bitset.encoding) + (req "executed_messages" Bitset.encoding))) + end) + + let by_outbox_level (t : _ t) ~outbox_level = + let open Lwt_result_syntax in + let* res = read t in + match res with + | None -> return_nil + | Some h -> ( + match H.find_opt h outbox_level with + | None -> return_nil + | Some {messages; executed_messages} -> + let indexes = Bitset.to_list messages in + List.map_e + (fun i -> + let open Result_syntax in + let* exec = Bitset.mem executed_messages i in + return (i, exec)) + indexes + |> Lwt.return) + + let pending (t : _ t) ~min_level ~max_level = + let open Lwt_result_syntax in + let* res = read t in + match res with + | None -> return_nil + | Some h -> + H.fold + (fun outbox_level {messages; executed_messages} acc -> + if outbox_level < min_level || outbox_level > max_level then acc + else + let pending_at_level = Bitset.diff messages executed_messages in + let l = Bitset.to_list pending_at_level in + if List.is_empty l then acc else (outbox_level, l) :: acc) + h + [] + |> List.fast_sort (fun (o1, _) (o2, _) -> Int32.compare o1 o2) + |> return + + let register_new_outbox_messages (t : _ t) ~outbox_level ~indexes = + let open Lwt_result_syntax in + let* res = read t in + let h = match res with None -> H.create 97 | Some h -> h in + let*? messages = Bitset.from_list indexes in + H.replace h outbox_level {messages; executed_messages = Bitset.empty} ; + write t h + + let set_outbox_message_executed (t : _ t) ~outbox_level ~index = + let open Lwt_result_syntax in + let* res = read t in + match res with + | None -> + (* Not tracking *) + return_unit + | Some h -> ( + match H.find h outbox_level with + | None -> + (* Not tracking *) + return_unit + | Some {messages; executed_messages} -> + let*? executed_messages = Bitset.add executed_messages index in + if Bitset.is_empty (Bitset.diff messages executed_messages) then + (* Clean outbox level when all messages are executed. *) + H.remove h outbox_level + else H.replace h outbox_level {messages; executed_messages} ; + write t h) +end + (** Single commitment for LCC. *) module Lcc = struct type lcc = {commitment : Commitment.Hash.t; level : int32} @@ -285,6 +385,7 @@ type 'a store = { inboxes : 'a Inboxes.t; commitments : 'a Commitments.t; commitments_published_at_level : 'a Commitments_published_at_level.t; + outbox_messages : 'a Outbox_messages.t; l2_head : 'a L2_head.t; last_finalized_level : 'a Last_finalized_level.t; lcc : 'a Lcc.t; @@ -311,6 +412,7 @@ let readonly inboxes; commitments; commitments_published_at_level; + outbox_messages; l2_head; last_finalized_level; lcc; @@ -331,6 +433,7 @@ let readonly commitments = Commitments.readonly commitments; commitments_published_at_level = Commitments_published_at_level.readonly commitments_published_at_level; + outbox_messages = Outbox_messages.readonly outbox_messages; l2_head = L2_head.readonly l2_head; last_finalized_level = Last_finalized_level.readonly last_finalized_level; lcc = Lcc.readonly lcc; @@ -352,6 +455,7 @@ let close inboxes; commitments; commitments_published_at_level; + outbox_messages = _; l2_head = _; last_finalized_level = _; lcc = _; @@ -402,6 +506,9 @@ let load (type a) (mode : a mode) ~index_buffer_size ~l2_blocks_cache_size mode ~path:(path "commitments_published_at_level") in + let* outbox_messages = + Outbox_messages.load mode ~path:(path "outbox_messages") + in let* l2_head = L2_head.load mode ~path:(path "l2_head") in let* last_finalized_level = Last_finalized_level.load mode ~path:(path "last_finalized_level") @@ -430,6 +537,7 @@ let load (type a) (mode : a mode) ~index_buffer_size ~l2_blocks_cache_size inboxes; commitments; commitments_published_at_level; + outbox_messages; l2_head; last_finalized_level; lcc; @@ -527,6 +635,7 @@ let gc inboxes; commitments; commitments_published_at_level; + outbox_messages = _; l2_head = _; last_finalized_level = _; lcc = _; @@ -561,6 +670,7 @@ let wait_gc_completion inboxes; commitments; commitments_published_at_level; + outbox_messages = _; l2_head = _; last_finalized_level = _; lcc = _; @@ -592,6 +702,7 @@ let is_gc_finished inboxes; commitments; commitments_published_at_level; + outbox_messages = _; l2_head = _; last_finalized_level = _; lcc = _; @@ -620,6 +731,7 @@ let cancel_gc inboxes; commitments; commitments_published_at_level; + outbox_messages = _; l2_head = _; last_finalized_level = _; lcc = _; diff --git a/src/lib_smart_rollup_node/store_v2.mli b/src/lib_smart_rollup_node/store_v2.mli index f854a058eb36..db2fa7a61c09 100644 --- a/src/lib_smart_rollup_node/store_v2.mli +++ b/src/lib_smart_rollup_node/store_v2.mli @@ -55,6 +55,35 @@ module Commitments : and type value := Octez_smart_rollup.Commitment.t and type header := unit +module Outbox_messages : sig + type +'a t + + val load : path:string -> 'a Store_sigs.mode -> 'a t tzresult Lwt.t + + val readonly : [> `Read] t -> [`Read] t + + val by_outbox_level : + [> `Read] t -> outbox_level:int32 -> (int * bool) list tzresult Lwt.t + + val pending : + [> `Read] t -> + min_level:int32 -> + max_level:int32 -> + (int32 * int list) list tzresult Lwt.t + + val register_new_outbox_messages : + [> `Read | `Write] t -> + outbox_level:int32 -> + indexes:int list -> + unit tzresult Lwt.t + + val set_outbox_message_executed : + [> `Read | `Write] t -> + outbox_level:int32 -> + index:int -> + unit tzresult Lwt.t +end + (** Storage containing the last cemented commitment. *) module Lcc : sig type lcc = {commitment : Commitment.Hash.t; level : int32} @@ -123,6 +152,7 @@ type +'a store = { inboxes : 'a Inboxes.t; commitments : 'a Commitments.t; commitments_published_at_level : 'a Commitments_published_at_level.t; + outbox_messages : 'a Outbox_messages.t; l2_head : 'a L2_head.t; last_finalized_level : 'a Last_finalized_level.t; lcc : 'a Lcc.t; -- GitLab From 43b7572b7132002bc6cb80bf6984581aabfbd1b6 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 11 Jul 2024 17:48:11 +0200 Subject: [PATCH 03/12] Rollup node/Alpha: register executed outbox messages --- .../lib_sc_rollup_node/daemon_helpers.ml | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/daemon_helpers.ml b/src/proto_alpha/lib_sc_rollup_node/daemon_helpers.ml index 027208a0ae58..b0c37467a82a 100644 --- a/src/proto_alpha/lib_sc_rollup_node/daemon_helpers.ml +++ b/src/proto_alpha/lib_sc_rollup_node/daemon_helpers.ml @@ -255,33 +255,37 @@ let process_included_l1_operation (type kind) ~catching_up Sc_rollup_node_errors.Exit_bond_recovered_bailout_mode | _ -> return_unit) | ( Sc_rollup_execute_outbox_message {output_proof; _}, - Sc_rollup_execute_outbox_message_result - {whitelist_update = Some whitelist_update; _} ) -> ( + Sc_rollup_execute_outbox_message_result {whitelist_update; _} ) -> ( + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let output_proof = + Data_encoding.Binary.of_string_exn + PVM.output_proof_encoding + output_proof + in + let Sc_rollup.{outbox_level; message_index; _} = + PVM.output_of_output_proof output_proof + in + let outbox_level = Raw_level.to_int32 outbox_level in + let message_index = Z.to_int message_index in + let* () = + Node_context.set_outbox_message_executed + node_ctxt + ~outbox_level + ~index:message_index + in match whitelist_update with - | Public -> + | None -> return_unit + | Some Public -> let () = Reference.set node_ctxt.private_info None in return_unit - | Private whitelist_update -> + | Some (Private whitelist_update) -> let () = Reference.map (Option.map (fun private_info -> - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let output_proof = - Data_encoding.Binary.of_string_exn - PVM.output_proof_encoding - output_proof - in - let Sc_rollup.{outbox_level; message_index; _} = - PVM.output_of_output_proof output_proof - in Node_context. { private_info with - last_whitelist_update = - { - outbox_level = Raw_level.to_int32 outbox_level; - message_index = Z.to_int message_index; - }; + last_whitelist_update = {outbox_level; message_index}; })) node_ctxt.private_info in -- GitLab From af1c94239ea79810d9100c19b28ea79bb6e69cb7 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 23 Aug 2024 10:56:25 +0200 Subject: [PATCH 04/12] Rollup node/Beta: register executed outbox messages Porting to proto beta 82ffc053fedfae51e0f499549e8e1b1d74f9b65f - Rollup node/Alpha: register executed outbox messages --- .../lib_sc_rollup_node/daemon_helpers.ml | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/proto_021_PsquebeC/lib_sc_rollup_node/daemon_helpers.ml b/src/proto_021_PsquebeC/lib_sc_rollup_node/daemon_helpers.ml index 027208a0ae58..b0c37467a82a 100644 --- a/src/proto_021_PsquebeC/lib_sc_rollup_node/daemon_helpers.ml +++ b/src/proto_021_PsquebeC/lib_sc_rollup_node/daemon_helpers.ml @@ -255,33 +255,37 @@ let process_included_l1_operation (type kind) ~catching_up Sc_rollup_node_errors.Exit_bond_recovered_bailout_mode | _ -> return_unit) | ( Sc_rollup_execute_outbox_message {output_proof; _}, - Sc_rollup_execute_outbox_message_result - {whitelist_update = Some whitelist_update; _} ) -> ( + Sc_rollup_execute_outbox_message_result {whitelist_update; _} ) -> ( + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let output_proof = + Data_encoding.Binary.of_string_exn + PVM.output_proof_encoding + output_proof + in + let Sc_rollup.{outbox_level; message_index; _} = + PVM.output_of_output_proof output_proof + in + let outbox_level = Raw_level.to_int32 outbox_level in + let message_index = Z.to_int message_index in + let* () = + Node_context.set_outbox_message_executed + node_ctxt + ~outbox_level + ~index:message_index + in match whitelist_update with - | Public -> + | None -> return_unit + | Some Public -> let () = Reference.set node_ctxt.private_info None in return_unit - | Private whitelist_update -> + | Some (Private whitelist_update) -> let () = Reference.map (Option.map (fun private_info -> - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let output_proof = - Data_encoding.Binary.of_string_exn - PVM.output_proof_encoding - output_proof - in - let Sc_rollup.{outbox_level; message_index; _} = - PVM.output_of_output_proof output_proof - in Node_context. { private_info with - last_whitelist_update = - { - outbox_level = Raw_level.to_int32 outbox_level; - message_index = Z.to_int message_index; - }; + last_whitelist_update = {outbox_level; message_index}; })) node_ctxt.private_info in -- GitLab From 0aa3a942e5647dbd90ebf3add56362ad89c5993c Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 11 Jul 2024 17:48:11 +0200 Subject: [PATCH 05/12] Rollup node/ParisC: register executed outbox messages --- .../lib_sc_rollup_node/daemon_helpers.ml | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/proto_020_PsParisC/lib_sc_rollup_node/daemon_helpers.ml b/src/proto_020_PsParisC/lib_sc_rollup_node/daemon_helpers.ml index 027208a0ae58..b0c37467a82a 100644 --- a/src/proto_020_PsParisC/lib_sc_rollup_node/daemon_helpers.ml +++ b/src/proto_020_PsParisC/lib_sc_rollup_node/daemon_helpers.ml @@ -255,33 +255,37 @@ let process_included_l1_operation (type kind) ~catching_up Sc_rollup_node_errors.Exit_bond_recovered_bailout_mode | _ -> return_unit) | ( Sc_rollup_execute_outbox_message {output_proof; _}, - Sc_rollup_execute_outbox_message_result - {whitelist_update = Some whitelist_update; _} ) -> ( + Sc_rollup_execute_outbox_message_result {whitelist_update; _} ) -> ( + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let output_proof = + Data_encoding.Binary.of_string_exn + PVM.output_proof_encoding + output_proof + in + let Sc_rollup.{outbox_level; message_index; _} = + PVM.output_of_output_proof output_proof + in + let outbox_level = Raw_level.to_int32 outbox_level in + let message_index = Z.to_int message_index in + let* () = + Node_context.set_outbox_message_executed + node_ctxt + ~outbox_level + ~index:message_index + in match whitelist_update with - | Public -> + | None -> return_unit + | Some Public -> let () = Reference.set node_ctxt.private_info None in return_unit - | Private whitelist_update -> + | Some (Private whitelist_update) -> let () = Reference.map (Option.map (fun private_info -> - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let output_proof = - Data_encoding.Binary.of_string_exn - PVM.output_proof_encoding - output_proof - in - let Sc_rollup.{outbox_level; message_index; _} = - PVM.output_of_output_proof output_proof - in Node_context. { private_info with - last_whitelist_update = - { - outbox_level = Raw_level.to_int32 outbox_level; - message_index = Z.to_int message_index; - }; + last_whitelist_update = {outbox_level; message_index}; })) node_ctxt.private_info in -- GitLab From 418529841c5e67efcdf5c5785b18d43d294cd910 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 11 Jul 2024 17:48:11 +0200 Subject: [PATCH 06/12] Rollup node/ParisB: register executed outbox messages --- .../lib_sc_rollup_node/daemon_helpers.ml | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/proto_019_PtParisB/lib_sc_rollup_node/daemon_helpers.ml b/src/proto_019_PtParisB/lib_sc_rollup_node/daemon_helpers.ml index 027208a0ae58..b0c37467a82a 100644 --- a/src/proto_019_PtParisB/lib_sc_rollup_node/daemon_helpers.ml +++ b/src/proto_019_PtParisB/lib_sc_rollup_node/daemon_helpers.ml @@ -255,33 +255,37 @@ let process_included_l1_operation (type kind) ~catching_up Sc_rollup_node_errors.Exit_bond_recovered_bailout_mode | _ -> return_unit) | ( Sc_rollup_execute_outbox_message {output_proof; _}, - Sc_rollup_execute_outbox_message_result - {whitelist_update = Some whitelist_update; _} ) -> ( + Sc_rollup_execute_outbox_message_result {whitelist_update; _} ) -> ( + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let output_proof = + Data_encoding.Binary.of_string_exn + PVM.output_proof_encoding + output_proof + in + let Sc_rollup.{outbox_level; message_index; _} = + PVM.output_of_output_proof output_proof + in + let outbox_level = Raw_level.to_int32 outbox_level in + let message_index = Z.to_int message_index in + let* () = + Node_context.set_outbox_message_executed + node_ctxt + ~outbox_level + ~index:message_index + in match whitelist_update with - | Public -> + | None -> return_unit + | Some Public -> let () = Reference.set node_ctxt.private_info None in return_unit - | Private whitelist_update -> + | Some (Private whitelist_update) -> let () = Reference.map (Option.map (fun private_info -> - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let output_proof = - Data_encoding.Binary.of_string_exn - PVM.output_proof_encoding - output_proof - in - let Sc_rollup.{outbox_level; message_index; _} = - PVM.output_of_output_proof output_proof - in Node_context. { private_info with - last_whitelist_update = - { - outbox_level = Raw_level.to_int32 outbox_level; - message_index = Z.to_int message_index; - }; + last_whitelist_update = {outbox_level; message_index}; })) node_ctxt.private_info in -- GitLab From 6c4f854427cda993a64c09af4a8150c232179b15 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 11 Jul 2024 17:48:11 +0200 Subject: [PATCH 07/12] Rollup node/Oxford: register executed outbox messages --- .../lib_sc_rollup_node/daemon_helpers.ml | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/daemon_helpers.ml b/src/proto_018_Proxford/lib_sc_rollup_node/daemon_helpers.ml index d239789a114d..b48493b230ed 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/daemon_helpers.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/daemon_helpers.ml @@ -281,33 +281,37 @@ let process_included_l1_operation (type kind) ~catching_up Sc_rollup_node_errors.Exit_bond_recovered_bailout_mode | _ -> return_unit) | ( Sc_rollup_execute_outbox_message {output_proof; _}, - Sc_rollup_execute_outbox_message_result - {whitelist_update = Some whitelist_update; _} ) -> ( + Sc_rollup_execute_outbox_message_result {whitelist_update; _} ) -> ( + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let output_proof = + Data_encoding.Binary.of_string_exn + PVM.output_proof_encoding + output_proof + in + let Sc_rollup.{outbox_level; message_index; _} = + PVM.output_of_output_proof output_proof + in + let outbox_level = Raw_level.to_int32 outbox_level in + let message_index = Z.to_int message_index in + let* () = + Node_context.set_outbox_message_executed + node_ctxt + ~outbox_level + ~index:message_index + in match whitelist_update with - | Public -> + | None -> return_unit + | Some Public -> let () = Reference.set node_ctxt.private_info None in return_unit - | Private whitelist_update -> + | Some (Private whitelist_update) -> let () = Reference.map (Option.map (fun private_info -> - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let output_proof = - Data_encoding.Binary.of_string_exn - PVM.output_proof_encoding - output_proof - in - let Sc_rollup.{outbox_level; message_index; _} = - PVM.output_of_output_proof output_proof - in Node_context. { private_info with - last_whitelist_update = - { - outbox_level = Raw_level.to_int32 outbox_level; - message_index = Z.to_int message_index; - }; + last_whitelist_update = {outbox_level; message_index}; })) node_ctxt.private_info in -- GitLab From 368eaf074d8e8140a137adefde6d0542367f9409 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 12 Jul 2024 10:51:42 +0200 Subject: [PATCH 08/12] Rollup node: register new outbox messages on head processing --- src/lib_smart_rollup_node/node_context.ml | 6 +++++ src/lib_smart_rollup_node/node_context.mli | 3 +++ .../rollup_node_daemon.ml | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/src/lib_smart_rollup_node/node_context.ml b/src/lib_smart_rollup_node/node_context.ml index f15ee3c3ec26..de6d5cde4dfc 100644 --- a/src/lib_smart_rollup_node/node_context.ml +++ b/src/lib_smart_rollup_node/node_context.ml @@ -729,6 +729,12 @@ let set_outbox_message_executed {store; _} ~outbox_level ~index = ~outbox_level ~index +let register_new_outbox_messages {store; _} ~outbox_level ~indexes = + Store.Outbox_messages.register_new_outbox_messages + store.outbox_messages + ~outbox_level + ~indexes + let get_executable_pending_outbox_messages {store; lcc; current_protocol; _} = let max_level = (Reference.get lcc).level in let constants = (Reference.get current_protocol).constants.sc_rollup in diff --git a/src/lib_smart_rollup_node/node_context.mli b/src/lib_smart_rollup_node/node_context.mli index 99ff60d5ce7f..2d49eb491b59 100644 --- a/src/lib_smart_rollup_node/node_context.mli +++ b/src/lib_smart_rollup_node/node_context.mli @@ -474,6 +474,9 @@ val set_outbox_message_executed : the messages indexes for the [outbox_level]. If messages were already registered for this level, they are overwritten. Messages added are first marked unexecuted. *) +val register_new_outbox_messages : + rw -> outbox_level:int32 -> indexes:int list -> unit tzresult Lwt.t + (** Returns the pending messages (i.e. unexecuted) that can now be executed. The returned list contains outbox levels and indexes for each level (in order). *) diff --git a/src/lib_smart_rollup_node/rollup_node_daemon.ml b/src/lib_smart_rollup_node/rollup_node_daemon.ml index 7f8467208bba..e9900fa8a250 100644 --- a/src/lib_smart_rollup_node/rollup_node_daemon.ml +++ b/src/lib_smart_rollup_node/rollup_node_daemon.ml @@ -112,6 +112,26 @@ let maybe_split_context node_ctxt commitment_hash head_level = Node_context.save_context_split_level node_ctxt head_level) else return_unit +(** Register outbox messages if any. *) +let register_outbox_messages (module Plugin : Protocol_plugin_sig.S) node_ctxt + ctxt level = + let open Lwt_result_syntax in + let*! outbox_messages = + let*! pvm_state = Context.PVMState.find ctxt in + match pvm_state with + | None -> Lwt.return_nil + | Some pvm_state -> + Plugin.Pvm.get_outbox_messages node_ctxt pvm_state ~outbox_level:level + in + match outbox_messages with + | [] -> return_unit + | _ -> + let indexes = List.map fst outbox_messages in + Node_context.register_new_outbox_messages + node_ctxt + ~outbox_level:level + ~indexes + (* Process a L1 that we have never seen and for which we have processed the predecessor. *) let process_unseen_head ({node_ctxt; _} as state) ~catching_up ~predecessor @@ -172,6 +192,7 @@ let process_unseen_head ({node_ctxt; _} as state) ~catching_up ~predecessor let+ pred = Node_context.get_l2_block node_ctxt predecessor.hash in Sc_rollup_block.most_recent_commitment pred.header in + let* () = register_outbox_messages state.plugin node_ctxt ctxt level in let header = Sc_rollup_block. { @@ -728,6 +749,7 @@ module Internal_for_tests = struct let+ pred = Node_context.get_l2_block node_ctxt predecessor.hash in Sc_rollup_block.most_recent_commitment pred.header in + let* () = register_outbox_messages (module Plugin) node_ctxt ctxt level in let header = Sc_rollup_block. { -- GitLab From 8460ab580e6839a5c7447c978f1b735e7d064e0a Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Mon, 15 Jul 2024 12:00:46 +0200 Subject: [PATCH 09/12] Rollup node/RPC: retrieve pending and executed outbox messages by index --- src/lib_smart_rollup/rollup_node_services.ml | 24 +++++++++ src/lib_smart_rollup_node/rpc_directory.ml | 53 ++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/lib_smart_rollup/rollup_node_services.ml b/src/lib_smart_rollup/rollup_node_services.ml index 0d8e159d8014..a1bd244165e1 100644 --- a/src/lib_smart_rollup/rollup_node_services.ml +++ b/src/lib_smart_rollup/rollup_node_services.ml @@ -205,6 +205,16 @@ module Encodings = struct (opt "first_published_at_level" int32) (opt "published_at_level" int32) + let outbox = + obj2 + (req "outbox_level" int32) + (req + "messages" + (list + (obj2 + (req "message_index" int31) + (opt "message" Outbox_message.summary_encoding)))) + let queued_message = obj2 (req "id" L2_message.Id.encoding) (req "message" L2_message.encoding) @@ -766,6 +776,20 @@ module Local = struct (Data_encoding.option Encodings.commitment_with_hash_and_level_infos) (path / "commitments" /: Arg.commitment_hash) + let outbox_pending_executable = + Tezos_rpc.Service.get_service + ~description:"Pending outbox messages which can be executed" + ~query:Tezos_rpc.Query.empty + ~output:(Data_encoding.list Encodings.outbox) + (path / "outbox" / "pending" / "executable") + + let outbox_pending_unexecutable = + Tezos_rpc.Service.get_service + ~description:"Pending outbox messages which cannot yet be executed" + ~query:Tezos_rpc.Query.empty + ~output:(Data_encoding.list Encodings.outbox) + (path / "outbox" / "pending" / "unexecutable") + let gc_info = Tezos_rpc.Service.get_service ~description:"Information about garbage collection" diff --git a/src/lib_smart_rollup_node/rpc_directory.ml b/src/lib_smart_rollup_node/rpc_directory.ml index 4ed856d92919..254a4ad4d165 100644 --- a/src/lib_smart_rollup_node/rpc_directory.ml +++ b/src/lib_smart_rollup_node/rpc_directory.ml @@ -371,6 +371,59 @@ let () = in return_some (commitment, hash, first_published, published) +let get_outbox_content (node_ctxt : _ Node_context.t) + (outbox_level, message_indexes) = + let open Lwt_result_syntax in + let* outbox = + let lcc = (Reference.get node_ctxt.lcc).level in + let ctxt_block_level = Int32.max lcc outbox_level in + let* ctxt_block_hash = + Node_context.hash_of_level_opt node_ctxt ctxt_block_level + in + match ctxt_block_hash with + | None -> return_nil + | Some ctxt_block_hash -> ( + let* ctxt = Node_context.checkout_context node_ctxt ctxt_block_hash in + let*! state = Context.PVMState.find ctxt in + match state with + | None -> return_nil + | Some state -> + let*? (module Plugin) = + Protocol_plugins.proto_plugin_for_protocol + (Reference.get node_ctxt.current_protocol).hash + in + let*! outbox = + Plugin.Pvm.get_outbox_messages node_ctxt state ~outbox_level + in + return outbox) + in + let messages = + List.rev_map + (fun i -> (i, List.assoc ~equal:Int.equal i outbox)) + message_indexes + |> List.rev + in + return (outbox_level, messages) + +let () = + Local_directory.register0 Rollup_node_services.Local.outbox_pending_executable + @@ fun node_ctxt () () -> + let open Lwt_result_syntax in + let* outbox_messages = + Node_context.get_executable_pending_outbox_messages node_ctxt + in + List.map_ep (get_outbox_content node_ctxt) outbox_messages + +let () = + Local_directory.register0 + Rollup_node_services.Local.outbox_pending_unexecutable + @@ fun node_ctxt () () -> + let open Lwt_result_syntax in + let* outbox_messages = + Node_context.get_unexecutable_pending_outbox_messages node_ctxt + in + List.map_ep (get_outbox_content node_ctxt) outbox_messages + let () = Local_directory.register0 Rollup_node_services.Local.gc_info @@ fun node_ctxt () () -> -- GitLab From 6348f99253bd73a9ab8eddd923d7fc47066591bc Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 30 Aug 2024 16:47:31 +0200 Subject: [PATCH 10/12] Rollup node/Context: function get which fails if PVM state does not exist --- src/lib_layer2_store/context.ml | 9 ++++++++ src/lib_layer2_store/context.mli | 4 ++++ .../rollup_node_daemon.ml | 7 ++---- src/lib_smart_rollup_node/rpc_directory.ml | 23 ++++++++----------- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/lib_layer2_store/context.ml b/src/lib_layer2_store/context.ml index cb160124ec93..526dce7c06ca 100644 --- a/src/lib_layer2_store/context.ml +++ b/src/lib_layer2_store/context.ml @@ -200,6 +200,15 @@ module PVMState = struct ~pvmstate ~impl_name) + let get ctxt = + let open Lwt_result_syntax in + let*! pvm_state = find ctxt in + match pvm_state with + | None -> + failwith + "Could not retrieve PVM state from context, this shouldn't happen." + | Some pvm_state -> return pvm_state + let lookup : value -> string list -> bytes option Lwt.t = fun (PVMState {pvm_context_impl = (module Pvm_Context_Impl); pvmstate; _}) path -> diff --git a/src/lib_layer2_store/context.mli b/src/lib_layer2_store/context.mli index e6d2986e046e..b2b969da9b85 100644 --- a/src/lib_layer2_store/context.mli +++ b/src/lib_layer2_store/context.mli @@ -159,6 +159,10 @@ module PVMState : sig (** [find context] returns the PVM state stored in the [context], if any. *) val find : 'a t -> value option Lwt.t + (** [get context] is the same as {!find} but fails if there is no PVM state + stored in the context. *) + val get : 'a t -> value tzresult Lwt.t + (** [lookup state path] returns the data stored for the path [path] in the PVM state [state]. *) val lookup : value -> string list -> bytes option Lwt.t diff --git a/src/lib_smart_rollup_node/rollup_node_daemon.ml b/src/lib_smart_rollup_node/rollup_node_daemon.ml index e9900fa8a250..476f5287d464 100644 --- a/src/lib_smart_rollup_node/rollup_node_daemon.ml +++ b/src/lib_smart_rollup_node/rollup_node_daemon.ml @@ -116,12 +116,9 @@ let maybe_split_context node_ctxt commitment_hash head_level = let register_outbox_messages (module Plugin : Protocol_plugin_sig.S) node_ctxt ctxt level = let open Lwt_result_syntax in + let* pvm_state = Context.PVMState.get ctxt in let*! outbox_messages = - let*! pvm_state = Context.PVMState.find ctxt in - match pvm_state with - | None -> Lwt.return_nil - | Some pvm_state -> - Plugin.Pvm.get_outbox_messages node_ctxt pvm_state ~outbox_level:level + Plugin.Pvm.get_outbox_messages node_ctxt pvm_state ~outbox_level:level in match outbox_messages with | [] -> return_unit diff --git a/src/lib_smart_rollup_node/rpc_directory.ml b/src/lib_smart_rollup_node/rpc_directory.ml index 254a4ad4d165..f29dc73143b4 100644 --- a/src/lib_smart_rollup_node/rpc_directory.ml +++ b/src/lib_smart_rollup_node/rpc_directory.ml @@ -382,20 +382,17 @@ let get_outbox_content (node_ctxt : _ Node_context.t) in match ctxt_block_hash with | None -> return_nil - | Some ctxt_block_hash -> ( + | Some ctxt_block_hash -> let* ctxt = Node_context.checkout_context node_ctxt ctxt_block_hash in - let*! state = Context.PVMState.find ctxt in - match state with - | None -> return_nil - | Some state -> - let*? (module Plugin) = - Protocol_plugins.proto_plugin_for_protocol - (Reference.get node_ctxt.current_protocol).hash - in - let*! outbox = - Plugin.Pvm.get_outbox_messages node_ctxt state ~outbox_level - in - return outbox) + let* state = Context.PVMState.get ctxt in + let*? (module Plugin) = + Protocol_plugins.proto_plugin_for_protocol + (Reference.get node_ctxt.current_protocol).hash + in + let*! outbox = + Plugin.Pvm.get_outbox_messages node_ctxt state ~outbox_level + in + return outbox in let messages = List.rev_map -- GitLab From 815f1b72b6396eeee1e962d8bcddf55b9dcf9561 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Mon, 12 Aug 2024 13:01:49 +0200 Subject: [PATCH 11/12] Rollup node: command to recompute outbox messages info in storage --- src/lib_smart_rollup_node/cli.ml | 3 + src/lib_smart_rollup_node/node_context.ml | 6 ++ src/lib_smart_rollup_node/node_context.mli | 7 +++ src/lib_smart_rollup_node/repair.ml | 71 +++++++++++++++++++++- src/lib_smart_rollup_node/store_v2.ml | 10 +++ src/lib_smart_rollup_node/store_v2.mli | 6 ++ 6 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/lib_smart_rollup_node/cli.ml b/src/lib_smart_rollup_node/cli.ml index be7d48574aee..539b3e374ea7 100644 --- a/src/lib_smart_rollup_node/cli.ml +++ b/src/lib_smart_rollup_node/cli.ml @@ -556,3 +556,6 @@ let bail_on_disagree_switch : (bool, Client_context.full) Tezos_clic.arg = "Make an observer rollup node bail when it sees a commitment it disagree \ with on L1." () + +let level_param next = + Tezos_clic.param ~name:"level" ~desc:"Level" positive_int32_parameter next diff --git a/src/lib_smart_rollup_node/node_context.ml b/src/lib_smart_rollup_node/node_context.ml index de6d5cde4dfc..6cec084ff32f 100644 --- a/src/lib_smart_rollup_node/node_context.ml +++ b/src/lib_smart_rollup_node/node_context.ml @@ -735,6 +735,12 @@ let register_new_outbox_messages {store; _} ~outbox_level ~indexes = ~outbox_level ~indexes +let register_missing_outbox_messages {store; _} ~outbox_level ~indexes = + Store.Outbox_messages.register_missing_outbox_messages + store.outbox_messages + ~outbox_level + ~indexes + let get_executable_pending_outbox_messages {store; lcc; current_protocol; _} = let max_level = (Reference.get lcc).level in let constants = (Reference.get current_protocol).constants.sc_rollup in diff --git a/src/lib_smart_rollup_node/node_context.mli b/src/lib_smart_rollup_node/node_context.mli index 2d49eb491b59..a90a3f46c19d 100644 --- a/src/lib_smart_rollup_node/node_context.mli +++ b/src/lib_smart_rollup_node/node_context.mli @@ -477,6 +477,13 @@ val set_outbox_message_executed : val register_new_outbox_messages : rw -> outbox_level:int32 -> indexes:int list -> unit tzresult Lwt.t +(** [register_missing_outbox_messages node_ctxt ~outbox_level ~indexes] + registers the messages indexes for the [outbox_level]. If messages were + already registered for this level, they are not overwritten. This function + is meant to be used to recompute missing outbox messages information. *) +val register_missing_outbox_messages : + rw -> outbox_level:int32 -> indexes:int list -> unit tzresult Lwt.t + (** Returns the pending messages (i.e. unexecuted) that can now be executed. The returned list contains outbox levels and indexes for each level (in order). *) diff --git a/src/lib_smart_rollup_node/repair.ml b/src/lib_smart_rollup_node/repair.ml index d5fae7b2b7d2..3a6cb3d49466 100644 --- a/src/lib_smart_rollup_node/repair.ml +++ b/src/lib_smart_rollup_node/repair.ml @@ -209,5 +209,74 @@ let repair_commitments_command = (fun data_dir protocol cctxt -> command_fix_commitments_for_protocol cctxt ~data_dir protocol) +let recompute_outbox_messages node_ctxt first_level = + let open Lwt_result_syntax in + let* l2_head = Node_context.last_processed_head_opt node_ctxt in + let l2_head = WithExceptions.Option.get ~loc:__LOC__ l2_head in + let* () = + when_ (first_level > l2_head.header.level) @@ fun () -> + failwith + "First level to recompute outbox messages is %ld but head is at %ld" + first_level + l2_head.header.level + in + let levels = + Stdlib.List.init + (Int32.to_int (Int32.sub l2_head.header.level first_level) + 1) + (fun x -> Int32.add (Int32.of_int x) first_level) + in + let lcc = Reference.get node_ctxt.lcc in + List.iter_es + (fun level -> + let* plugin = Protocol_plugins.proto_plugin_for_level node_ctxt level in + let (module Plugin) = plugin in + let ctxt_level = if level <= lcc.level then lcc.level else level in + let* ctxt_block = + Node_context.get_l2_block_by_level node_ctxt ctxt_level + in + let* ctxt = + Node_context.checkout_context node_ctxt ctxt_block.header.block_hash + in + let*! outbox_messages = + let*! pvm_state = Context.PVMState.find ctxt in + match pvm_state with + | None -> Lwt.return_nil + | Some pvm_state -> + Plugin.Pvm.get_outbox_messages + node_ctxt + pvm_state + ~outbox_level:level + in + match outbox_messages with + | [] -> return_unit + | _ -> + Format.eprintf + "%ld : %d outbox messages@." + level + (List.length outbox_messages) ; + let indexes = List.map fst outbox_messages in + Node_context.register_missing_outbox_messages + node_ctxt + ~outbox_level:level + ~indexes) + levels + +let command_recompute_outbox_messages cctxt ~data_dir level = + Snapshots.with_modify_data_dir cctxt ~data_dir ~apply_unsafe_patches:false + @@ fun node_ctxt ~head:_ -> recompute_outbox_messages node_ctxt level + +let recompute_outbox_messages_command = + let open Tezos_clic in + command + ~group + ~desc: + "Recompute outbox messages from a given level up to the current head. It \ + does not overwrite already stored outbox messages." + (args1 Cli.data_dir_arg) + (prefixes ["recompute"; "outbox"; "messages"; "from"] + @@ Cli.level_param @@ stop) + (fun data_dir level cctxt -> + command_recompute_outbox_messages cctxt ~data_dir level) + (** Commands exported by the [Repair] module. *) -let commands = [repair_commitments_command] +let commands = [repair_commitments_command; recompute_outbox_messages_command] diff --git a/src/lib_smart_rollup_node/store_v2.ml b/src/lib_smart_rollup_node/store_v2.ml index 7eea5ed43065..1c48a43e364a 100644 --- a/src/lib_smart_rollup_node/store_v2.ml +++ b/src/lib_smart_rollup_node/store_v2.ml @@ -201,6 +201,16 @@ module Outbox_messages = struct H.replace h outbox_level {messages; executed_messages = Bitset.empty} ; write t h + let register_missing_outbox_messages (t : _ t) ~outbox_level ~indexes = + let open Lwt_result_syntax in + let* res = read t in + let h = match res with None -> H.create 97 | Some h -> h in + let*? messages = Bitset.from_list indexes in + if H.mem h outbox_level then return_unit + else ( + H.replace h outbox_level {messages; executed_messages = Bitset.empty} ; + write t h) + let set_outbox_message_executed (t : _ t) ~outbox_level ~index = let open Lwt_result_syntax in let* res = read t in diff --git a/src/lib_smart_rollup_node/store_v2.mli b/src/lib_smart_rollup_node/store_v2.mli index db2fa7a61c09..c5e4938b0720 100644 --- a/src/lib_smart_rollup_node/store_v2.mli +++ b/src/lib_smart_rollup_node/store_v2.mli @@ -77,6 +77,12 @@ module Outbox_messages : sig indexes:int list -> unit tzresult Lwt.t + val register_missing_outbox_messages : + [> `Read | `Write] t -> + outbox_level:int32 -> + indexes:int list -> + unit tzresult Lwt.t + val set_outbox_message_executed : [> `Read | `Write] t -> outbox_level:int32 -> -- GitLab From 0046b174fc9e0a777562a2a99abb70563884b0e2 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 16 Aug 2024 17:14:12 +0200 Subject: [PATCH 12/12] Test: check outbox messages execution status in rollup node RPCs --- tezt/lib_tezos/sc_rollup_rpc.ml | 22 +++++++++++++++++++ tezt/lib_tezos/sc_rollup_rpc.mli | 10 +++++++++ tezt/tests/sc_rollup.ml | 36 ++++++++++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/tezt/lib_tezos/sc_rollup_rpc.ml b/tezt/lib_tezos/sc_rollup_rpc.ml index 3b6f5e53afde..75759789554f 100644 --- a/tezt/lib_tezos/sc_rollup_rpc.ml +++ b/tezt/lib_tezos/sc_rollup_rpc.ml @@ -312,3 +312,25 @@ let outbox_proof_simple ?(block = "head") ~outbox_level ~message_index () = (* There is 0x return in the proof hash as it is a hex *) let proof_hex = sf "0x%s" proof_string in Some {commitment_hash; proof = proof_hex}) + +type outbox_msg = {message_index : int; message : JSON.t} + +let outbox_list_of_json json = + let open JSON in + as_list json + |> List.map @@ fun json -> + let outbox_level = json |-> "outbox_level" |> as_int in + let msgs = + json |-> "messages" |> as_list + |> List.map @@ fun json -> + let message_index = json |-> "message_index" |> as_int in + let message = json |-> "message" in + {message_index; message} + in + (outbox_level, msgs) + +let get_local_outbox_pending_executable () = + make GET ["local"; "outbox"; "pending"; "executable"] outbox_list_of_json + +let get_local_outbox_pending_unexecutable () = + make GET ["local"; "outbox"; "pending"; "unexecutable"] outbox_list_of_json diff --git a/tezt/lib_tezos/sc_rollup_rpc.mli b/tezt/lib_tezos/sc_rollup_rpc.mli index 367fd613488f..ecfab55108ce 100644 --- a/tezt/lib_tezos/sc_rollup_rpc.mli +++ b/tezt/lib_tezos/sc_rollup_rpc.mli @@ -193,3 +193,13 @@ val outbox_proof_simple : message_index:int -> unit -> outbox_proof option RPC_core.t + +type outbox_msg = {message_index : int; message : JSON.t} + +(** RPC: [GET /local/outbox/pending/executable] *) +val get_local_outbox_pending_executable : + unit -> (int * outbox_msg list) list RPC_core.t + +(** RPC: [GET /local/outbox/pending/unexecutable] *) +val get_local_outbox_pending_unexecutable : + unit -> (int * outbox_msg list) list RPC_core.t diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index ba07406f41e0..4958c08852fa 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -4019,6 +4019,8 @@ let test_outbox_message_generic ?supports ?regression ?expected_error ?outbox_parameters_ty ?boot_sector ~input_message ~expected_storage ~kind ~message_kind = let commitment_period = 2 and challenge_window = 5 in + let outbox_level = 5 in + let message_index = 0 in let message_kind_s = match message_kind with `Internal -> "intern" | `External -> "extern" in @@ -4106,6 +4108,36 @@ let test_outbox_message_generic ?supports ?regression ?expected_error string ~error_msg:"Invalid contract storage: expecting '%R', got '%L'.") in + let check_outbox_execution outbox_level status = + let* _ = Sc_rollup_node.wait_sync rollup_node ~timeout:10. in + let* executable_outboxes = + Sc_rollup_node.RPC.call rollup_node + @@ Sc_rollup_rpc.get_local_outbox_pending_executable () + in + let* unexecutable_outboxes = + Sc_rollup_node.RPC.call rollup_node + @@ Sc_rollup_rpc.get_local_outbox_pending_unexecutable () + in + let get_nb l = + match List.assoc_opt outbox_level l with + | None -> 0 + | Some l -> List.length l + in + let executable = get_nb executable_outboxes in + let unexecutable = get_nb unexecutable_outboxes in + match (status, executable, unexecutable) with + | `Executable, 1, 0 -> unit + | `Unexecutable, 0, 1 -> unit + | _ -> + Test.fail + "Outbox level should be %s but there is %d executable, %d \ + unexecutable." + (match status with + | `Executable -> "executable" + | `Unexecutable -> "unexecutable") + executable + unexecutable + in let perform_rollup_execution_and_cement source_address target_address = let* payload = input_message protocol target_address in let* () = @@ -4138,13 +4170,12 @@ let test_outbox_message_generic ?supports ?regression ?expected_error in Check.((nb_outbox_transactions_in_block = [1]) (list int)) ~error_msg:"Block has %L outbox transactions but expected %R" ; + let* () = check_outbox_execution outbox_level `Unexecutable in let blocks_to_wait = 3 + (2 * commitment_period) + challenge_window - earliness in repeat blocks_to_wait @@ fun () -> Client.bake_for_and_wait client in - let outbox_level = 5 in - let message_index = 0 in let trigger_outbox_message_execution ?expected_l1_error address = let parameters = "37" in let check_expected_outbox () = @@ -4202,6 +4233,7 @@ let test_outbox_message_generic ?supports ?regression ?expected_error | Some {commitment_hash; proof}, None -> ( match expected_l1_error with | None -> + let* () = check_outbox_execution outbox_level `Executable in let*! () = Client.Sc_rollup.execute_outbox_message ~hooks -- GitLab