From 2ae7a18406c333e95247dbce21520356d277b0f0 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 3 Nov 2022 11:58:17 +0100 Subject: [PATCH 01/21] SCORU/Node: remove functor from Interpreter_events --- .../bin_sc_rollup_node/fueled_pvm.ml | 6 +- .../bin_sc_rollup_node/interpreter.ml | 16 +-- .../bin_sc_rollup_node/interpreter_event.ml | 98 ++++++++----------- 3 files changed, 49 insertions(+), 71 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml index a39c5144b496..971f4db6c0dc 100644 --- a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml @@ -44,10 +44,8 @@ module type S = sig (state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult Lwt.t end -module Make - (PVM : Pvm.S) - (Interpreter_event : Interpreter_event.S with type state := PVM.state) - (F : Fuel.S) : S with type state = PVM.state and type fuel = F.t = struct +module Make (PVM : Pvm.S) (F : Fuel.S) : + S with type state = PVM.state and type fuel = F.t = struct type state = PVM.state type fuel = F.t diff --git a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml index 2421f68454b0..e5d4e9d8adb8 100644 --- a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml +++ b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml @@ -49,13 +49,8 @@ end module Make (PVM : Pvm.S) : S with module PVM = PVM = struct module PVM = PVM - - module Interpreter_event : Interpreter_event.S with type state := PVM.state = - Interpreter_event.Make (PVM) - - module Accounted_pvm = - Fueled_pvm.Make (PVM) (Interpreter_event) (Fuel.Accounted) - module Free_pvm = Fueled_pvm.Make (PVM) (Interpreter_event) (Fuel.Free) + module Accounted_pvm = Fueled_pvm.Make (PVM) (Fuel.Accounted) + module Free_pvm = Fueled_pvm.Make (PVM) (Fuel.Free) (** [metadata node_ctxt] creates a {Sc_rollup.Metadata.t} using the information stored in [node_ctxt]. *) @@ -189,8 +184,13 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct {num_messages; num_ticks; initial_tick} in (* Produce events. *) + let*! state_hash = PVM.state_hash state in let*! () = - Interpreter_event.transitioned_pvm inbox_level state num_messages + Interpreter_event.transitioned_pvm + inbox_level + state_hash + last_tick + num_messages in return_unit diff --git a/src/proto_alpha/bin_sc_rollup_node/interpreter_event.ml b/src/proto_alpha/bin_sc_rollup_node/interpreter_event.ml index 4ebe754da6a3..49152eed397a 100644 --- a/src/proto_alpha/bin_sc_rollup_node/interpreter_event.ml +++ b/src/proto_alpha/bin_sc_rollup_node/interpreter_event.ml @@ -25,71 +25,51 @@ open Protocol.Alpha_context.Sc_rollup -module type S = sig - type state +module Simple = struct + include Internal_event.Simple - (** [transition_pvm inbox_level hash n] emits the event that a PVM + let section = ["sc_rollup_node"; "interpreter"] + + let transitioned_pvm = + declare_4 + ~section + ~name:"sc_rollup_node_interpreter_transitioned_pvm" + ~msg: + "Transitioned PVM at inbox level {inbox_level} to {state_hash} at tick \ + {ticks} with {num_messages} messages" + ~level:Notice + ("inbox_level", Protocol.Alpha_context.Raw_level.encoding) + ("state_hash", State_hash.encoding) + ("ticks", Tick.encoding) + ("num_messages", Data_encoding.z) + + let intended_failure = + declare_4 + ~section + ~name:"sc_rollup_node_interpreter_intended_failure" + ~msg: + "Intended failure at level {level} for message indexed {message_index} \ + and at the tick {message_tick} of message processing (internal = \ + {internal})." + ~level:Notice + ("level", Data_encoding.int31) + ("message_index", Data_encoding.int31) + ("message_tick", Data_encoding.int64) + ("internal", Data_encoding.bool) +end + +(** [transition_pvm inbox_level hash tick n] emits the event that a PVM transition is leading to the state of the given [hash] by - processing [n] messages. *) - val transitioned_pvm : - Protocol.Alpha_context.Raw_level.t -> state -> Z.t -> unit Lwt.t + processing [n] messages at [tick]. *) +let transitioned_pvm inbox_level hash tick num_messages = + Simple.(emit transitioned_pvm (inbox_level, hash, tick, num_messages)) - (** [intended_failure level message_index message_tick internal] emits +(** [intended_failure level message_index message_tick internal] emits the event that an intended failure has been injected at some given [level], during the processing of a given [message_index] and at tick [message_tick] during this message processing. [internal] is [true] if the failure is injected in a PVM internal step. [internal] is [false] if the failure is injected in the input to the PVM. *) - val intended_failure : - level:int -> - message_index:int -> - message_tick:int64 -> - internal:bool -> - unit Lwt.t -end - -module Make (PVM : Pvm.S) : S with type state := PVM.state = struct - module Simple = struct - include Internal_event.Simple - - let section = ["sc_rollup_node"; PVM.name; "interpreter"] - - let transitioned_pvm = - declare_4 - ~section - ~name:"sc_rollup_node_interpreter_transitioned_pvm" - ~msg: - "Transitioned PVM at inbox level {inbox_level} to {state_hash} at \ - tick {ticks} with {num_messages} messages" - ~level:Notice - ("inbox_level", Protocol.Alpha_context.Raw_level.encoding) - ("state_hash", State_hash.encoding) - ("ticks", Tick.encoding) - ("num_messages", Data_encoding.z) - - let intended_failure = - declare_4 - ~section - ~name:"sc_rollup_node_interpreter_intended_failure" - ~msg: - "Intended failure at level {level} for message indexed \ - {message_index} and at the tick {message_tick} of message \ - processing (internal = {internal})." - ~level:Notice - ("level", Data_encoding.int31) - ("message_index", Data_encoding.int31) - ("message_tick", Data_encoding.int64) - ("internal", Data_encoding.bool) - end - - let transitioned_pvm inbox_level state num_messages = - let open Lwt_syntax in - let* hash = PVM.state_hash state in - let* ticks = PVM.get_tick state in - Simple.(emit transitioned_pvm (inbox_level, hash, ticks, num_messages)) - - let intended_failure ~level ~message_index ~message_tick ~internal = - Simple.( - emit intended_failure (level, message_index, message_tick, internal)) -end +let intended_failure ~level ~message_index ~message_tick ~internal = + Simple.(emit intended_failure (level, message_index, message_tick, internal)) -- GitLab From 66bb844f1238d0101073c538c758800bf3680acb Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 3 Nov 2022 12:00:23 +0100 Subject: [PATCH 02/21] SCORU/Node: instantiated fueled pvms --- src/proto_alpha/bin_sc_rollup_node/fuel.ml | 32 +- .../bin_sc_rollup_node/fueled_pvm.ml | 458 +++++++++--------- .../bin_sc_rollup_node/interpreter.ml | 5 +- 3 files changed, 255 insertions(+), 240 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/fuel.ml b/src/proto_alpha/bin_sc_rollup_node/fuel.ml index dd662dac1d1e..73a6f6c0dc9e 100644 --- a/src/proto_alpha/bin_sc_rollup_node/fuel.ml +++ b/src/proto_alpha/bin_sc_rollup_node/fuel.ml @@ -26,34 +26,35 @@ module type S = sig type t - (** [consume consumption fuel] consumes the [consumption] amount from the - original [fuel] - It returns - None when the [consumption] is greater than the original [fuel] or - Some remaining fuel - *) + (** [consume consumption fuel] consumes the [consumption] amount from the + original [fuel]. It returns [None] when the [consumption] is greater + than the original [fuel] or [Some remaining_fuel]. *) val consume : t -> t -> t option (** The amount of fuel required to run one PVM tick. - - one_tick_consumption = of_ticks 1L + {[ + one_tick_consumption = of_ticks 1L + ]} *) val one_tick_consumption : t - (** [of_ticks ticks] gives the amount of fuel required to execute the amount of [ticks] - *) + (** [of_ticks ticks] gives the amount of fuel required to execute the amount + of [ticks]. *) val of_ticks : int64 -> t val is_empty : t -> bool - (** The maximum number of ticks that can be executed with the given amount of fuel - - max_ticks . of_ticks = id - of_ticks . max_ticks = id - *) + (** The maximum number of ticks that can be executed with the given amount of + fuel. + {[ + max_ticks ∘ of_ticks = Fun.id + of_ticks ∘ max_ticks = Fun.id + ]} + *) val max_ticks : t -> int64 end +(** Free fuel where consumption has no effect. *) module Free : S = struct type t = Free @@ -68,6 +69,7 @@ module Free : S = struct let max_ticks _ = Int64.max_int end +(** Accounted fuel where each tick consumes one unit of fuel. *) module Accounted : S = struct type t = int64 diff --git a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml index 971f4db6c0dc..d7e4ceb0d265 100644 --- a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml @@ -44,246 +44,258 @@ module type S = sig (state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult Lwt.t end -module Make (PVM : Pvm.S) (F : Fuel.S) : - S with type state = PVM.state and type fuel = F.t = struct - type state = PVM.state +module Make (PVM : Pvm.S) = struct + module Make_fueled (F : Fuel.S) : + S with type state = PVM.state and type fuel = F.t = struct + type state = PVM.state - type fuel = F.t + type fuel = F.t - let continue_with_fuel consumption initial_fuel state f = - let open Delayed_write_monad.Lwt_result_syntax in - match F.consume consumption initial_fuel with - | None -> return (state, 0L) - | Some fuel_left -> f fuel_left state + let continue_with_fuel consumption initial_fuel state f = + let open Delayed_write_monad.Lwt_result_syntax in + match F.consume consumption initial_fuel with + | None -> return (state, 0L) + | Some fuel_left -> f fuel_left state - exception Error_wrapper of tztrace + exception Error_wrapper of tztrace - (** [eval_until_input ~metadata level message_index ~fuel start_tick - failing_ticks state] advances a PVM [state] until it wants more - inputs or there are no more [fuel] (if [Some fuel] is - specified). The evaluation is running under the processing of - some [message_index] at a given [level] and this is the - [start_tick] of this message processing. If some [failing_ticks] - are planned by the loser mode, they will be made. *) - let eval_until_input ~metadata ~dal_attestation_lag data_dir store level - message_index ~fuel start_tick failing_ticks state = - let open Lwt_result_syntax in - let open Delayed_write_monad.Lwt_result_syntax in - let module Builtins = struct - let reveal_preimage hash = - let hash = - (* The 32-byte payload represents the encoded [Reveal_hash.t]. We must - decode it properly, instead of converting it byte-for-byte. *) - Tezos_webassembly_interpreter.Reveal.reveal_hash_to_string hash - |> Data_encoding.Binary.of_string_exn Sc_rollup.Reveal_hash.encoding - in - let*! data = Reveals.get ~data_dir ~pvm_name:PVM.name ~hash in - match data with - | Error error -> - (* The [Error_wrapper] must be caught upstream and converted into a - tzresult. *) - Lwt.fail (Error_wrapper error) - | Ok data -> Lwt.return data + (** [eval_until_input ~metadata level message_index ~fuel start_tick + failing_ticks state] advances a PVM [state] until it wants more + inputs or there are no more [fuel] (if [Some fuel] is + specified). The evaluation is running under the processing of + some [message_index] at a given [level] and this is the + [start_tick] of this message processing. If some [failing_ticks] + are planned by the loser mode, they will be made. *) + let eval_until_input ~metadata ~dal_attestation_lag data_dir store level + message_index ~fuel start_tick failing_ticks state = + let open Lwt_result_syntax in + let open Delayed_write_monad.Lwt_result_syntax in + let module Builtins = struct + let reveal_preimage hash = + let hash = + (* The 32-byte payload represents the encoded [Reveal_hash.t]. We must + decode it properly, instead of converting it byte-for-byte. *) + Tezos_webassembly_interpreter.Reveal.reveal_hash_to_string hash + |> Data_encoding.Binary.of_string_exn Sc_rollup.Reveal_hash.encoding + in + let*! data = Reveals.get ~data_dir ~pvm_name:PVM.name ~hash in + match data with + | Error error -> + (* The [Error_wrapper] must be caught upstream and converted into a + tzresult. *) + Lwt.fail (Error_wrapper error) + | Ok data -> Lwt.return data - let reveal_metadata () = - Lwt.return - (Data_encoding.Binary.to_string_exn - Sc_rollup.Metadata.encoding - metadata) - end in - let builtins = (module Builtins : Tezos_scoru_wasm.Builtins.S) in - let eval_tick fuel tick failing_ticks state = - let max_steps = F.max_ticks fuel in - let normal_eval state = - Lwt.catch - (fun () -> - let*! state, executed_ticks = - PVM.eval_many ~builtins ~max_steps state - in - return (state, executed_ticks, failing_ticks)) - (function - | Error_wrapper error -> Lwt.return (Error error) | exn -> raise exn) - in - let failure_insertion_eval state failing_ticks' = - let*! () = - Interpreter_event.intended_failure - ~level - ~message_index - ~message_tick:tick - ~internal:true + let reveal_metadata () = + Lwt.return + (Data_encoding.Binary.to_string_exn + Sc_rollup.Metadata.encoding + metadata) + end in + let builtins = (module Builtins : Tezos_scoru_wasm.Builtins.S) in + let eval_tick fuel tick failing_ticks state = + let max_steps = F.max_ticks fuel in + let normal_eval state = + Lwt.catch + (fun () -> + let*! state, executed_ticks = + PVM.eval_many ~builtins ~max_steps state + in + return (state, executed_ticks, failing_ticks)) + (function + | Error_wrapper error -> Lwt.return (Error error) + | exn -> raise exn) + in + let failure_insertion_eval state failing_ticks' = + let*! () = + Interpreter_event.intended_failure + ~level + ~message_index + ~message_tick:tick + ~internal:true + in + let*! state = PVM.Internal_for_tests.insert_failure state in + return (state, 1L, failing_ticks') in - let*! state = PVM.Internal_for_tests.insert_failure state in - return (state, 1L, failing_ticks') + match failing_ticks with + | xtick :: failing_ticks' when xtick = tick -> + failure_insertion_eval state failing_ticks' + | _ -> normal_eval state in - match failing_ticks with - | xtick :: failing_ticks' when xtick = tick -> - failure_insertion_eval state failing_ticks' - | _ -> normal_eval state - in - let rec go (fuel : fuel) current_tick failing_ticks state = - let*! input_request = PVM.is_input_state state in - if F.is_empty fuel then return (state, fuel, current_tick, failing_ticks) - else - match input_request with - | No_input_required -> - let>* next_state, executed_ticks, failing_ticks = - eval_tick fuel current_tick failing_ticks state - in - go - fuel - (Int64.add current_tick executed_ticks) - failing_ticks - next_state - | Needs_reveal (Reveal_raw_data hash) -> ( - let* data = Reveals.get ~data_dir ~pvm_name:PVM.name ~hash in - let*! next_state = PVM.set_input (Reveal (Raw_data data)) state in - match F.consume F.one_tick_consumption fuel with - | None -> return (state, fuel, current_tick, failing_ticks) - | Some fuel -> - go fuel (Int64.succ current_tick) failing_ticks next_state) - | Needs_reveal Reveal_metadata -> ( - let*! next_state = - PVM.set_input (Reveal (Metadata metadata)) state - in - match F.consume F.one_tick_consumption fuel with - | None -> return (state, fuel, current_tick, failing_ticks) - | Some fuel -> - go fuel (Int64.succ current_tick) failing_ticks next_state) - | Needs_reveal (Request_dal_page page_id) -> ( - let>* content_opt = - Dal_pages_request.page_content ~dal_attestation_lag store page_id - in - let*! next_state = - PVM.set_input (Reveal (Dal_page content_opt)) state - in - match F.consume F.one_tick_consumption fuel with - | None -> return (state, fuel, current_tick, failing_ticks) - | Some fuel -> - go fuel (Int64.succ current_tick) failing_ticks next_state) - | Initial | First_after _ -> - return (state, fuel, current_tick, failing_ticks) - in - go fuel start_tick failing_ticks state + let rec go (fuel : fuel) current_tick failing_ticks state = + let*! input_request = PVM.is_input_state state in + if F.is_empty fuel then return (state, fuel, current_tick, failing_ticks) + else + match input_request with + | No_input_required -> + let>* next_state, executed_ticks, failing_ticks = + eval_tick fuel current_tick failing_ticks state + in + go + fuel + (Int64.add current_tick executed_ticks) + failing_ticks + next_state + | Needs_reveal (Reveal_raw_data hash) -> ( + let* data = Reveals.get ~data_dir ~pvm_name:PVM.name ~hash in + let*! next_state = PVM.set_input (Reveal (Raw_data data)) state in + match F.consume F.one_tick_consumption fuel with + | None -> return (state, fuel, current_tick, failing_ticks) + | Some fuel -> + go fuel (Int64.succ current_tick) failing_ticks next_state) + | Needs_reveal Reveal_metadata -> ( + let*! next_state = + PVM.set_input (Reveal (Metadata metadata)) state + in + match F.consume F.one_tick_consumption fuel with + | None -> return (state, fuel, current_tick, failing_ticks) + | Some fuel -> + go fuel (Int64.succ current_tick) failing_ticks next_state) + | Needs_reveal (Request_dal_page page_id) -> ( + let>* content_opt = + Dal_pages_request.page_content + ~dal_attestation_lag + store + page_id + in + let*! next_state = + PVM.set_input (Reveal (Dal_page content_opt)) state + in + match F.consume F.one_tick_consumption fuel with + | None -> return (state, fuel, current_tick, failing_ticks) + | Some fuel -> + go fuel (Int64.succ current_tick) failing_ticks next_state) + | Initial | First_after _ -> + return (state, fuel, current_tick, failing_ticks) + in + go fuel start_tick failing_ticks state - (** [mutate input] corrupts the payload of [input] for testing purposes. *) - let mutate input = - let payload = Sc_rollup.Inbox_message.unsafe_of_string "0xC4C4" in - {input with Sc_rollup.payload} + (** [mutate input] corrupts the payload of [input] for testing purposes. *) + let mutate input = + let payload = Sc_rollup.Inbox_message.unsafe_of_string "0xC4C4" in + {input with Sc_rollup.payload} - (** [feed_input ~metadata level message_index ~fuel ~failing_ticks state + (** [feed_input ~metadata level message_index ~fuel ~failing_ticks state input] feeds [input] (that has a given [message_index] in inbox of [level]) to the PVM in order to advance [state] to the next step that requires an input. This function is controlled by some [fuel] and may introduce intended failures at some given [failing_ticks]. *) - let feed_input ~metadata ~dal_attestation_lag data_dir store level - message_index ~fuel ~failing_ticks state input = - let open Lwt_result_syntax in - let open Delayed_write_monad.Lwt_result_syntax in - let>* state, fuel, tick, failing_ticks = - eval_until_input - ~metadata - ~dal_attestation_lag - data_dir - store - level - message_index - ~fuel - 0L - failing_ticks - state - in - let consumption = F.of_ticks tick in - continue_with_fuel consumption fuel state @@ fun fuel state -> - let>* input, failing_ticks = - match failing_ticks with - | xtick :: failing_ticks' -> - if xtick = tick then - let*! () = - Interpreter_event.intended_failure - ~level - ~message_index - ~message_tick:tick - ~internal:false - in - return (mutate input, failing_ticks') - else return (input, failing_ticks) - | _ -> return (input, failing_ticks) - in - let*! state = PVM.set_input (Inbox_message input) state in - let>* state, _fuel, tick, _failing_ticks = - eval_until_input - ~metadata - ~dal_attestation_lag - data_dir - store - level - message_index - ~fuel - tick - failing_ticks - state - in - return (state, tick) + let feed_input ~metadata ~dal_attestation_lag data_dir store level + message_index ~fuel ~failing_ticks state input = + let open Lwt_result_syntax in + let open Delayed_write_monad.Lwt_result_syntax in + let>* state, fuel, tick, failing_ticks = + eval_until_input + ~metadata + ~dal_attestation_lag + data_dir + store + level + message_index + ~fuel + 0L + failing_ticks + state + in + let consumption = F.of_ticks tick in + continue_with_fuel consumption fuel state @@ fun fuel state -> + let>* input, failing_ticks = + match failing_ticks with + | xtick :: failing_ticks' -> + if xtick = tick then + let*! () = + Interpreter_event.intended_failure + ~level + ~message_index + ~message_tick:tick + ~internal:false + in + return (mutate input, failing_ticks') + else return (input, failing_ticks) + | _ -> return (input, failing_ticks) + in + let*! state = PVM.set_input (Inbox_message input) state in + let>* state, _fuel, tick, _failing_ticks = + eval_until_input + ~metadata + ~dal_attestation_lag + data_dir + store + level + message_index + ~fuel + tick + failing_ticks + state + in + return (state, tick) - let eval_block_inbox ~metadata ~dal_attestation_lag ~fuel - (Node_context.{data_dir; store; loser_mode; _} as node_context) hash - (state : state) : - (state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult - Lwt.t = - let open Lwt_result_syntax in - let open Delayed_write_monad.Lwt_result_syntax in - (* Obtain inbox and its messages for this block. *) - let*! inbox = Store.Inboxes.find store hash in - match inbox with - | None -> - (* A level with no messages for use. Skip it. *) - let* level = State.level_of_hash store hash in - return (state, Z.zero, Raw_level.of_int32_exn level, fuel) - | Some inbox -> - let inbox_level = Inbox.inbox_level inbox in - let*! messages = Store.Messages.get store hash in - (* TODO: #2717 - The length of messages here can potentially overflow the [int] returned from [List.length]. - *) - let num_messages = List.length messages |> Z.of_int in + let eval_block_inbox ~metadata ~dal_attestation_lag ~fuel + Node_context.{data_dir; store; loser_mode; _} hash (state : state) : + (state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult + Lwt.t = + let open Lwt_result_syntax in + let open Delayed_write_monad.Lwt_result_syntax in + (* Obtain inbox and its messages for this block. *) + let*! inbox = Store.Inboxes.find store hash in + match inbox with + | None -> + (* A level with no messages for use. Skip it. *) + let* level = State.level_of_hash store hash in + return (state, Z.zero, Raw_level.of_int32_exn level, fuel) + | Some inbox -> + let inbox_level = Inbox.inbox_level inbox in + let*! messages = Store.Messages.get store hash in + (* TODO: #2717 + The length of messages here can potentially overflow the [int] returned from [List.length]. + *) + let num_messages = List.length messages |> Z.of_int in - let feed_message (message_counter : int) (state, fuel) - (message : Sc_rollup.Inbox_message.t) = - let*? payload = - Sc_rollup.Inbox_message.( - message |> serialize |> Environment.wrap_tzresult) - in - let input = - Sc_rollup. - {inbox_level; message_counter = Z.of_int message_counter; payload} - in - let level = Raw_level.to_int32 inbox_level |> Int32.to_int in + let feed_message (message_counter : int) (state, fuel) + (message : Sc_rollup.Inbox_message.t) = + let*? payload = + Sc_rollup.Inbox_message.( + message |> serialize |> Environment.wrap_tzresult) + in + let input = + Sc_rollup. + { + inbox_level; + message_counter = Z.of_int message_counter; + payload; + } + in + let level = Raw_level.to_int32 inbox_level |> Int32.to_int in - let failing_ticks = - Loser_mode.is_failure - loser_mode - ~level - ~message_index:message_counter + let failing_ticks = + Loser_mode.is_failure + loser_mode + ~level + ~message_index:message_counter + in + let>* state, executed_ticks = + feed_input + ~metadata + ~dal_attestation_lag + data_dir + store + level + message_counter + ~fuel + ~failing_ticks + state + input + in + return (state, F.of_ticks executed_ticks) in - let>* state, executed_ticks = - feed_input - ~metadata - ~dal_attestation_lag - data_dir - node_context - level - message_counter - ~fuel - ~failing_ticks - state - input + (* Iterate the PVM state with all the messages for this level. *) + let>* state, fuel = + list_fold_left_i_es feed_message (state, fuel) messages in - return (state, F.of_ticks executed_ticks) - in - (* Iterate the PVM state with all the messages for this level. *) - let>* state, fuel = - list_fold_left_i_es feed_message (state, fuel) messages - in - return (state, num_messages, inbox_level, fuel) + return (state, num_messages, inbox_level, fuel) + end + + module Free = Make_fueled (Fuel.Free) + module Accounted = Make_fueled (Fuel.Accounted) end diff --git a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml index e5d4e9d8adb8..574835e4ccad 100644 --- a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml +++ b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml @@ -49,8 +49,9 @@ end module Make (PVM : Pvm.S) : S with module PVM = PVM = struct module PVM = PVM - module Accounted_pvm = Fueled_pvm.Make (PVM) (Fuel.Accounted) - module Free_pvm = Fueled_pvm.Make (PVM) (Fuel.Free) + module Fueled_pvm = Fueled_pvm.Make (PVM) + module Accounted_pvm = Fueled_pvm.Accounted + module Free_pvm = Fueled_pvm.Free (** [metadata node_ctxt] creates a {Sc_rollup.Metadata.t} using the information stored in [node_ctxt]. *) -- GitLab From 574e6c23783bc19642f4f6f741a0d56a3f294158 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 3 Nov 2022 11:37:17 +0100 Subject: [PATCH 03/21] SCORU/Node: expose evaluation function for external messages --- .../bin_sc_rollup_node/fueled_pvm.ml | 171 ++++++++++-------- .../bin_sc_rollup_node/interpreter.ml | 21 --- .../bin_sc_rollup_node/node_context.ml | 5 + .../bin_sc_rollup_node/node_context.mli | 4 + .../bin_sc_rollup_node/refutation_game.ml | 2 +- 5 files changed, 110 insertions(+), 93 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml index d7e4ceb0d265..91072bc52a72 100644 --- a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml @@ -34,14 +34,31 @@ module type S = sig type fuel + type eval_result = {state : state; remaining_fuel : fuel; num_ticks : Z.t} + + (** [eval_block_inbox ~fuel node_ctxt block_hash state] evaluates the + [messages] for the inbox of block [block_hash] in the given [state] of the + PVM and returns the evaluation results containing the new state, the + number of messages, the inbox level and the remaining fuel. *) val eval_block_inbox : - metadata:Sc_rollup.Metadata.t -> - dal_attestation_lag:int -> fuel:fuel -> _ Node_context.t -> Tezos_crypto.Block_hash.t -> state -> (state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult Lwt.t + + (** [eval_messages ~fuel node_ctxt state inbox_level messages] evaluates the + [messages] for inbox level [inbox_level] in the given [state] of the PVM + and returns the evaluation results containing the new state, the remaining + fuel, and the number of ticks for the evaluation + of this message. *) + val eval_messages : + fuel:fuel -> + _ Node_context.t -> + state -> + Raw_level.t -> + Sc_rollup.Inbox_message.t list -> + eval_result Node_context.delayed_write tzresult Lwt.t end module Make (PVM : Pvm.S) = struct @@ -51,6 +68,8 @@ module Make (PVM : Pvm.S) = struct type fuel = F.t + type eval_result = {state : state; remaining_fuel : fuel; num_ticks : Z.t} + let continue_with_fuel consumption initial_fuel state f = let open Delayed_write_monad.Lwt_result_syntax in match F.consume consumption initial_fuel with @@ -59,17 +78,20 @@ module Make (PVM : Pvm.S) = struct exception Error_wrapper of tztrace - (** [eval_until_input ~metadata level message_index ~fuel start_tick - failing_ticks state] advances a PVM [state] until it wants more - inputs or there are no more [fuel] (if [Some fuel] is - specified). The evaluation is running under the processing of - some [message_index] at a given [level] and this is the - [start_tick] of this message processing. If some [failing_ticks] - are planned by the loser mode, they will be made. *) - let eval_until_input ~metadata ~dal_attestation_lag data_dir store level - message_index ~fuel start_tick failing_ticks state = + (** [eval_until_input node_ctxt level message_index ~fuel start_tick + failing_ticks state] advances a PVM [state] until it wants more inputs or + there are no more [fuel] (if [Some fuel] is specified). The evaluation is + running under the processing of some [message_index] at a given [level] + and this is the [start_tick] of this message processing. If some + [failing_ticks] are planned by the loser mode, they will be made. *) + let eval_until_input node_ctxt level message_index ~fuel start_tick + failing_ticks state = let open Lwt_result_syntax in let open Delayed_write_monad.Lwt_result_syntax in + let metadata = Node_context.metadata node_ctxt in + let dal_attestation_lag = + node_ctxt.protocol_constants.parametric.dal.attestation_lag + in let module Builtins = struct let reveal_preimage hash = let hash = @@ -137,7 +159,12 @@ module Make (PVM : Pvm.S) = struct failing_ticks next_state | Needs_reveal (Reveal_raw_data hash) -> ( - let* data = Reveals.get ~data_dir ~pvm_name:PVM.name ~hash in + let* data = + Reveals.get + ~data_dir:node_ctxt.data_dir + ~pvm_name:PVM.name + ~hash + in let*! next_state = PVM.set_input (Reveal (Raw_data data)) state in match F.consume F.one_tick_consumption fuel with | None -> return (state, fuel, current_tick, failing_ticks) @@ -155,7 +182,7 @@ module Make (PVM : Pvm.S) = struct let>* content_opt = Dal_pages_request.page_content ~dal_attestation_lag - store + node_ctxt page_id in let*! next_state = @@ -175,22 +202,19 @@ module Make (PVM : Pvm.S) = struct let payload = Sc_rollup.Inbox_message.unsafe_of_string "0xC4C4" in {input with Sc_rollup.payload} - (** [feed_input ~metadata level message_index ~fuel ~failing_ticks state - input] feeds [input] (that has a given [message_index] in inbox - of [level]) to the PVM in order to advance [state] to the next - step that requires an input. This function is controlled by - some [fuel] and may introduce intended failures at some given - [failing_ticks]. *) - let feed_input ~metadata ~dal_attestation_lag data_dir store level - message_index ~fuel ~failing_ticks state input = + (** [feed_input node_ctxt level message_index ~fuel ~failing_ticks state + input] feeds [input] (that has a given [message_index] in inbox + of [level]) to the PVM in order to advance [state] to the next + step that requires an input. This function is controlled by + some [fuel] and may introduce intended failures at some given + [failing_ticks]. *) + let feed_input node_ctxt level message_index ~fuel ~failing_ticks state + input = let open Lwt_result_syntax in let open Delayed_write_monad.Lwt_result_syntax in let>* state, fuel, tick, failing_ticks = eval_until_input - ~metadata - ~dal_attestation_lag - data_dir - store + node_ctxt level message_index ~fuel @@ -218,10 +242,7 @@ module Make (PVM : Pvm.S) = struct let*! state = PVM.set_input (Inbox_message input) state in let>* state, _fuel, tick, _failing_ticks = eval_until_input - ~metadata - ~dal_attestation_lag - data_dir - store + node_ctxt level message_index ~fuel @@ -231,8 +252,43 @@ module Make (PVM : Pvm.S) = struct in return (state, tick) - let eval_block_inbox ~metadata ~dal_attestation_lag ~fuel - Node_context.{data_dir; store; loser_mode; _} hash (state : state) : + let eval_messages ~fuel node_ctxt state inbox_level messages = + let open Lwt_result_syntax in + let open Delayed_write_monad.Lwt_result_syntax in + let level = Raw_level.to_int32 inbox_level |> Int32.to_int in + (* Iterate the PVM state with all the messages. *) + list_fold_left_i_es + (fun message_counter (state, fuel) message -> + let*? payload = + Sc_rollup.Inbox_message.( + message |> serialize |> Environment.wrap_tzresult) + in + let input = + Sc_rollup. + {inbox_level; message_counter = Z.of_int message_counter; payload} + in + let failing_ticks = + Loser_mode.is_failure + node_ctxt.Node_context.loser_mode + ~level + ~message_index:message_counter + in + let>* state, executed_ticks = + feed_input + node_ctxt + level + message_counter + ~fuel + ~failing_ticks + state + input + in + return (state, F.of_ticks executed_ticks)) + (state, fuel) + messages + + let eval_block_inbox ~fuel (Node_context.{store; _} as node_ctxt) hash + (state : state) : (state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult Lwt.t = let open Lwt_result_syntax in @@ -251,49 +307,22 @@ module Make (PVM : Pvm.S) = struct The length of messages here can potentially overflow the [int] returned from [List.length]. *) let num_messages = List.length messages |> Z.of_int in - - let feed_message (message_counter : int) (state, fuel) - (message : Sc_rollup.Inbox_message.t) = - let*? payload = - Sc_rollup.Inbox_message.( - message |> serialize |> Environment.wrap_tzresult) - in - let input = - Sc_rollup. - { - inbox_level; - message_counter = Z.of_int message_counter; - payload; - } - in - let level = Raw_level.to_int32 inbox_level |> Int32.to_int in - - let failing_ticks = - Loser_mode.is_failure - loser_mode - ~level - ~message_index:message_counter - in - let>* state, executed_ticks = - feed_input - ~metadata - ~dal_attestation_lag - data_dir - store - level - message_counter - ~fuel - ~failing_ticks - state - input - in - return (state, F.of_ticks executed_ticks) - in - (* Iterate the PVM state with all the messages for this level. *) + (* Evaluate all the messages for this level. *) let>* state, fuel = - list_fold_left_i_es feed_message (state, fuel) messages + eval_messages ~fuel node_ctxt state inbox_level messages in return (state, num_messages, inbox_level, fuel) + + let eval_messages ~fuel node_ctxt state inbox_level messages = + let open Lwt_result_syntax in + let open Delayed_write_monad.Lwt_result_syntax in + let*! initial_tick = PVM.get_tick state in + let>* state, remaining_fuel = + eval_messages ~fuel node_ctxt state inbox_level messages + in + let*! final_tick = PVM.get_tick state in + let num_ticks = Sc_rollup.Tick.distance initial_tick final_tick in + return {state; remaining_fuel; num_ticks} end module Free = Make_fueled (Fuel.Free) diff --git a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml index 574835e4ccad..247fa0120310 100644 --- a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml +++ b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml @@ -29,8 +29,6 @@ open Alpha_context module type S = sig module PVM : Pvm.S - val metadata : _ Node_context.t -> Sc_rollup.Metadata.t - (** [process_head node_ctxt head] interprets the messages associated with a [head] from a chain [event]. This requires the inbox to be updated beforehand. *) @@ -53,13 +51,6 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct module Accounted_pvm = Fueled_pvm.Accounted module Free_pvm = Fueled_pvm.Free - (** [metadata node_ctxt] creates a {Sc_rollup.Metadata.t} using the information - stored in [node_ctxt]. *) - let metadata (node_ctxt : _ Node_context.t) = - let address = node_ctxt.rollup_address in - let origination_level = node_ctxt.genesis_info.Sc_rollup.Commitment.level in - Sc_rollup.Metadata.{address; origination_level} - (** [get_boot_sector block_hash node_ctxt] fetches the operations in the [block_hash] and looks for the bootsector used to originate the rollup we're following. @@ -133,14 +124,8 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct let open Lwt_result_syntax in (* Retrieve the previous PVM state from store. *) let* ctxt, predecessor_state = state_of_head node_ctxt ctxt predecessor in - let metadata = metadata node_ctxt in - let dal_attestation_lag = - node_ctxt.protocol_constants.parametric.dal.attestation_lag - in let* eval_result = Free_pvm.eval_block_inbox - ~metadata - ~dal_attestation_lag ~fuel:(Fuel.Free.of_ticks 0L) node_ctxt hash @@ -241,14 +226,8 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct ctxt Layer1.{hash = predecessor_hash; level = pred_level} in - let metadata = metadata node_ctxt in - let dal_attestation_lag = - node_ctxt.protocol_constants.parametric.dal.attestation_lag - in let>* state, _counter, _level, _fuel = Accounted_pvm.eval_block_inbox - ~metadata - ~dal_attestation_lag ~fuel:(Fuel.Accounted.of_ticks tick_distance) node_ctxt hash diff --git a/src/proto_alpha/bin_sc_rollup_node/node_context.ml b/src/proto_alpha/bin_sc_rollup_node/node_context.ml index d666f9a2a93f..062ba1365261 100644 --- a/src/proto_alpha/bin_sc_rollup_node/node_context.ml +++ b/src/proto_alpha/bin_sc_rollup_node/node_context.ml @@ -106,6 +106,11 @@ let checkout_context node_ctxt block_hash = (block_hash, Some (Context.hash_to_raw_string context_hash))) | Some ctxt -> return ctxt +let metadata node_ctxt = + let address = node_ctxt.rollup_address in + let origination_level = node_ctxt.genesis_info.Sc_rollup.Commitment.level in + Sc_rollup.Metadata.{address; origination_level} + let readonly (node_ctxt : _ t) = { node_ctxt with diff --git a/src/proto_alpha/bin_sc_rollup_node/node_context.mli b/src/proto_alpha/bin_sc_rollup_node/node_context.mli index 6ff286da6e4b..2d04267b78f8 100644 --- a/src/proto_alpha/bin_sc_rollup_node/node_context.mli +++ b/src/proto_alpha/bin_sc_rollup_node/node_context.mli @@ -105,6 +105,10 @@ val init : val checkout_context : 'a t -> Tezos_crypto.Block_hash.t -> 'a Context.t tzresult Lwt.t +(** [metadata node_ctxt] creates a {Sc_rollup.Metadata.t} using the information + stored in [node_ctxt]. *) +val metadata : _ t -> Sc_rollup.Metadata.t + (** [readonly node_ctxt] returns a read only version of the node context [node_ctxt]. *) val readonly : _ t -> ro diff --git a/src/proto_alpha/bin_sc_rollup_node/refutation_game.ml b/src/proto_alpha/bin_sc_rollup_node/refutation_game.ml index 1a3836d4abed..2975aa1ee542 100644 --- a/src/proto_alpha/bin_sc_rollup_node/refutation_game.ml +++ b/src/proto_alpha/bin_sc_rollup_node/refutation_game.ml @@ -243,7 +243,7 @@ module Make (Interpreter : Interpreter.S) : let page_info = page_info end end in - let metadata = Interpreter.metadata node_ctxt in + let metadata = Node_context.metadata node_ctxt in let* proof = trace (Sc_rollup_node_errors.Cannot_produce_proof -- GitLab From d68d014e5978dcf54afe4126e29bea2d5e6a617f Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 3 Nov 2022 13:24:43 +0100 Subject: [PATCH 04/21] SCORU/Node: RPC to simulate PVM evaluation of input messages --- .../bin_sc_rollup_node/RPC_server.ml | 39 ++++++++++++++- .../bin_sc_rollup_node/RPC_server.mli | 5 +- .../bin_sc_rollup_node/components.ml | 2 +- .../bin_sc_rollup_node/fueled_pvm.ml | 26 ++++++---- .../bin_sc_rollup_node/interpreter.ml | 6 +++ .../lib_sc_rollup/sc_rollup_services.ml | 48 +++++++++++++++++++ 6 files changed, 110 insertions(+), 16 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml index 4ad0a4e9a253..77f1553a1dee 100644 --- a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml +++ b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml @@ -229,9 +229,10 @@ module Common = struct state.num_ticks end -module Make (PVM : Pvm.S) = struct - module PVM = PVM +module Make (Interpreter : Interpreter.S) = struct + module PVM = Interpreter.PVM module Outbox = Outbox.Make (PVM) + module Free_pvm = Interpreter.Free_pvm let get_state (node_ctxt : _ Node_context.t) block_hash = let open Lwt_result_syntax in @@ -239,6 +240,35 @@ module Make (PVM : Pvm.S) = struct let*! state = PVM.State.find ctxt in match state with None -> failwith "No state" | Some state -> return state + let simulate_messages node_ctxt block messages = + let open Lwt_result_syntax in + let open Alpha_context in + let messages = + List.map (fun m -> Sc_rollup.Inbox_message.External m) messages + in + let* state = get_state node_ctxt block in + let* level = State.level_of_hash node_ctxt.store block in + let inbox_level = Raw_level.of_int32_exn (Int32.succ level) in + let* Free_pvm.{state; num_ticks; _} = + Free_pvm.eval_messages + ~fuel:(Fuel.Free.of_ticks 0L) + node_ctxt + state + inbox_level + messages + in + let*! outbox = PVM.get_outbox state in + let output = + List.filter + (fun Sc_rollup.{outbox_level; _} -> outbox_level = inbox_level) + outbox + in + let*! state_hash = PVM.state_hash state in + let*! status = PVM.get_status state in + let status = PVM.string_of_status status in + return + Sc_rollup_services.{state_hash; status; output; inbox_level; num_ticks} + let () = Block_directory.register0 Sc_rollup_services.Global.Block.total_ticks @@ fun (node_ctxt, block) () () -> @@ -343,6 +373,11 @@ module Make (PVM : Pvm.S) = struct Sc_rollup_services.Global.Helpers.outbox_proof @@ fun node_ctxt output () -> Outbox.proof_of_output node_ctxt output + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.simulate + @@ fun (node_ctxt, block) () messages -> + simulate_messages node_ctxt block messages + let register node_ctxt = List.fold_left (fun dir f -> Tezos_rpc.Directory.merge dir (f node_ctxt)) diff --git a/src/proto_alpha/bin_sc_rollup_node/RPC_server.mli b/src/proto_alpha/bin_sc_rollup_node/RPC_server.mli index 2bb1a448b1ec..9ba5b19c2dbf 100644 --- a/src/proto_alpha/bin_sc_rollup_node/RPC_server.mli +++ b/src/proto_alpha/bin_sc_rollup_node/RPC_server.mli @@ -25,9 +25,8 @@ open Tezos_rpc_http_server -(** Functor to construct an RPC server for a given PVM, i.e. the PVM for the - rollup of the current node. *) -module Make (PVM : Pvm.S) : sig +(** Functor to construct an RPC server for a given PVM with interpreter. *) +module Make (Interpreter : Interpreter.S) : sig (** [start node_ctxt config] starts an RPC server listening for requests on the port [config.rpc_port] and address [config.rpc_addr]. *) val start : diff --git a/src/proto_alpha/bin_sc_rollup_node/components.ml b/src/proto_alpha/bin_sc_rollup_node/components.ml index 394b62ed6413..6612ae8c3849 100644 --- a/src/proto_alpha/bin_sc_rollup_node/components.ml +++ b/src/proto_alpha/bin_sc_rollup_node/components.ml @@ -28,7 +28,7 @@ module Make (PVM : Pvm.S) = struct module PVM = PVM module Interpreter = Interpreter.Make (PVM) module Commitment = Commitment.Make (PVM) - module RPC_server = RPC_server.Make (PVM) + module RPC_server = RPC_server.Make (Interpreter) module Refutation_game = Refutation_game.Make (Interpreter) end diff --git a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml index 91072bc52a72..fbdf931b9bc8 100644 --- a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml @@ -30,11 +30,11 @@ open Protocol open Alpha_context module type S = sig - type state + module PVM : Pvm.S type fuel - type eval_result = {state : state; remaining_fuel : fuel; num_ticks : Z.t} + type eval_result = {state : PVM.state; remaining_fuel : fuel; num_ticks : Z.t} (** [eval_block_inbox ~fuel node_ctxt block_hash state] evaluates the [messages] for the inbox of block [block_hash] in the given [state] of the @@ -44,8 +44,9 @@ module type S = sig fuel:fuel -> _ Node_context.t -> Tezos_crypto.Block_hash.t -> - state -> - (state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult Lwt.t + PVM.state -> + (PVM.state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult + Lwt.t (** [eval_messages ~fuel node_ctxt state inbox_level messages] evaluates the [messages] for inbox level [inbox_level] in the given [state] of the PVM @@ -55,7 +56,7 @@ module type S = sig val eval_messages : fuel:fuel -> _ Node_context.t -> - state -> + PVM.state -> Raw_level.t -> Sc_rollup.Inbox_message.t list -> eval_result Node_context.delayed_write tzresult Lwt.t @@ -63,12 +64,16 @@ end module Make (PVM : Pvm.S) = struct module Make_fueled (F : Fuel.S) : - S with type state = PVM.state and type fuel = F.t = struct - type state = PVM.state + S with module PVM = PVM and type fuel = F.t = struct + module PVM = PVM type fuel = F.t - type eval_result = {state : state; remaining_fuel : fuel; num_ticks : Z.t} + type eval_result = { + state : PVM.state; + remaining_fuel : fuel; + num_ticks : Z.t; + } let continue_with_fuel consumption initial_fuel state f = let open Delayed_write_monad.Lwt_result_syntax in @@ -288,8 +293,9 @@ module Make (PVM : Pvm.S) = struct messages let eval_block_inbox ~fuel (Node_context.{store; _} as node_ctxt) hash - (state : state) : - (state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult + (state : PVM.state) : + (PVM.state * Z.t * Raw_level.t * fuel) Node_context.delayed_write + tzresult Lwt.t = let open Lwt_result_syntax in let open Delayed_write_monad.Lwt_result_syntax in diff --git a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml index 247fa0120310..1c5f4482f480 100644 --- a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml +++ b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml @@ -29,6 +29,12 @@ open Alpha_context module type S = sig module PVM : Pvm.S + module Accounted_pvm : + Fueled_pvm.S with module PVM = PVM and type fuel = Fuel.Accounted.t + + module Free_pvm : + Fueled_pvm.S with module PVM = PVM and type fuel = Fuel.Free.t + (** [process_head node_ctxt head] interprets the messages associated with a [head] from a chain [event]. This requires the inbox to be updated beforehand. *) diff --git a/src/proto_alpha/lib_sc_rollup/sc_rollup_services.ml b/src/proto_alpha/lib_sc_rollup/sc_rollup_services.ml index 0dbbca4a226a..1789a9a383aa 100644 --- a/src/proto_alpha/lib_sc_rollup/sc_rollup_services.ml +++ b/src/proto_alpha/lib_sc_rollup/sc_rollup_services.ml @@ -48,6 +48,14 @@ open Alpha_context See below for a more detailed explanation. *) +type eval_result = { + state_hash : Sc_rollup.State_hash.t; + status : string; + output : Sc_rollup.output list; + inbox_level : Raw_level.t; + num_ticks : Z.t; +} + module Encodings = struct open Data_encoding @@ -58,6 +66,33 @@ module Encodings = struct (opt "published_at_level" Raw_level.encoding) let hex_string = conv Bytes.of_string Bytes.to_string bytes + + let eval_result = + conv + (fun {state_hash; status; output; inbox_level; num_ticks} -> + (state_hash, status, output, inbox_level, num_ticks)) + (fun (state_hash, status, output, inbox_level, num_ticks) -> + {state_hash; status; output; inbox_level; num_ticks}) + @@ obj5 + (req + "state_hash" + Sc_rollup.State_hash.encoding + ~description: + "Hash of the state after execution of the PVM on the input \ + messages") + (req "status" string ~description:"Status of the PVM after evaluation") + (req + "output" + (list Sc_rollup.output_encoding) + ~description:"Output produced by evaluation of the messages") + (req + "inbox_level" + Raw_level.encoding + ~description:"Level of the inbox that would contain these messages") + (req + "num_ticks" + z + ~description:"Ticks taken by the PVM for evaluating the messages") end module Arg = struct @@ -303,6 +338,19 @@ module Global = struct ~output:Data_encoding.(list Sc_rollup.output_encoding) (path / "outbox") + let simulate = + Tezos_rpc.Service.post_service + ~description:"Simulate messages evaluation by the PVM" + ~query:Tezos_rpc.Query.empty + ~input: + Data_encoding.( + def + "messages" + ~description:"Input messages for simulation" + (list Encodings.hex_string)) + ~output:Encodings.eval_result + (path / "simulate") + let dal_slots = Tezos_rpc.Service.get_service ~description:"Availability slots for a given block" -- GitLab From d415746e703b8358a1c6d81cee1aa8b0c2717335 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 3 Nov 2022 13:32:33 +0100 Subject: [PATCH 05/21] SCORU/client: RPC post commands --- .../bin_sc_rollup_client/commands.ml | 85 ++++++++++++++++--- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_client/commands.ml b/src/proto_alpha/bin_sc_rollup_client/commands.ml index 4d77f875aece..cd492698bdde 100644 --- a/src/proto_alpha/bin_sc_rollup_client/commands.ml +++ b/src/proto_alpha/bin_sc_rollup_client/commands.ml @@ -197,23 +197,82 @@ let get_output_message_encoding () = | Error _ -> cctxt#message "Error while encoding outbox message.") >>= fun () -> return_unit) -(** [call_get cctxt raw_url] executes a GET RPC call against the [raw_url]. *) -let call_get (cctxt : #Configuration.sc_client_context) raw_url = +let call ?body meth raw_url (cctxt : #Configuration.sc_client_context) = let open Lwt_result_syntax in - let meth = `GET in let uri = Uri.of_string raw_url in - let* answer = cctxt#generic_media_type_call meth uri in + let body = + (* This code is similar to a piece of code in [fill_in] + function. An RPC is declared as POST, PATCH or PUT, but the + body is not given. In that case, the body should be an empty + JSON object. *) + match (meth, body) with + | _, Some _ -> body + | `DELETE, None | `GET, None -> None + | `PATCH, None | `PUT, None | `POST, None -> Some (`O []) + in + let* answer = cctxt#generic_media_type_call ?body meth uri in let*! () = display_answer cctxt answer in return_unit -let rpc_get_command = - Tezos_clic.command - ~desc:"Call an RPC with the GET method." - Tezos_clic.no_options - (Tezos_clic.prefixes ["rpc"; "get"] - @@ Tezos_clic.string ~name:"url" ~desc:"the RPC URL" - @@ Tezos_clic.stop) - (fun () url cctxt -> call_get cctxt url) +let call_with_json meth raw_url json (cctxt : #Configuration.sc_client_context) + = + match Ezjsonm.value_from_string_result json with + | Error err -> + cctxt#error + "Failed to parse the provided json: %s\n%!" + (Ezjsonm.read_error_description err) + | Ok body -> call meth ~body raw_url cctxt + +let call_with_file_or_json meth url maybe_file + (cctxt : #Configuration.sc_client_context) = + let open Lwt_result_syntax in + let* json = + match TzString.split ':' ~limit:1 maybe_file with + | ["file"; filename] -> + Lwt.catch + (fun () -> + Lwt_result.ok @@ Lwt_io.(with_file ~mode:Input filename read)) + (fun exn -> failwith "cannot read file (%s)" (Printexc.to_string exn)) + | _ -> return maybe_file + in + call_with_json meth url json cctxt + +let rpc_commands () = + let open Tezos_clic in + let group = {name = "rpc"; title = "Commands for the low level RPC layer"} in + [ + command + ~group + ~desc:"Call an RPC with the GET method." + no_options + (prefixes ["rpc"; "get"] @@ string ~name:"url" ~desc:"the RPC URL" @@ stop) + (fun () -> call `GET); + command + ~group + ~desc:"Call an RPC with the POST method." + no_options + (prefixes ["rpc"; "post"] + @@ string ~name:"url" ~desc:"the RPC URL" + @@ stop) + (fun () -> call `POST); + command + ~group + ~desc: + "Call an RPC with the POST method, providing input data via the \ + command line." + no_options + (prefixes ["rpc"; "post"] + @@ string ~name:"url" ~desc:"the RPC URL" + @@ prefix "with" + @@ string + ~name:"input" + ~desc: + "the raw JSON input to the RPC\n\ + For instance, use `{}` to send the empty document.\n\ + Alternatively, use `file:path` to read the JSON data from a file." + @@ stop) + (fun () -> call_with_file_or_json `POST); + ] module Keys = struct open Tezos_client_base.Client_keys @@ -272,9 +331,9 @@ let all () = get_state_value_command (); get_output_proof (); get_output_message_encoding (); - rpc_get_command; Keys.generate_keys (); Keys.list_keys (); Keys.show_address (); Keys.import_secret_key (); ] + @ rpc_commands () -- GitLab From 87766ba160767bc52b8c8c3475a4b5c983bb41b3 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 30 Sep 2022 17:03:48 +0200 Subject: [PATCH 06/21] Proto/Alpha: expose required part of Sc_rollup.Inbox in Alpha_context This exposes number_of_messages_during_commitment_period --- src/proto_alpha/lib_protocol/alpha_context.mli | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 40733c2cbc48..79968e2a575d 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -3290,6 +3290,8 @@ module Sc_rollup : sig val inbox_level : t -> Raw_level.t + val number_of_messages_during_commitment_period : t -> int64 + type history_proof val equal_history_proof : history_proof -> history_proof -> bool -- GitLab From 4145a86965d328001b577c27ca5ea930442a63ea Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 30 Sep 2022 17:05:11 +0200 Subject: [PATCH 07/21] SCORU/Node: More checks on simulation RPC - Check that we have not reached the maximum number of messages for the period in which we are doing the simulation - Check emptiness --- .../bin_sc_rollup_node/RPC_server.ml | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml index 77f1553a1dee..39c48358aeb7 100644 --- a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml +++ b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml @@ -243,12 +243,49 @@ module Make (Interpreter : Interpreter.S) = struct let simulate_messages node_ctxt block messages = let open Lwt_result_syntax in let open Alpha_context in + let*? () = + error_when + (messages = []) + (Environment.wrap_tzerror Sc_rollup_errors.Sc_rollup_add_zero_messages) + in let messages = List.map (fun m -> Sc_rollup.Inbox_message.External m) messages in let* state = get_state node_ctxt block in let* level = State.level_of_hash node_ctxt.store block in let inbox_level = Raw_level.of_int32_exn (Int32.succ level) in + let* inbox = Inbox.inbox_of_hash node_ctxt block in + let max_messages = + node_ctxt.protocol_constants.parametric.sc_rollup + .max_number_of_messages_per_commitment_period |> Int64.of_int + in + (* TODO: https://gitlab.com/tezos/tezos/-/issues/3978 + The number of messages during commitment period is broken with the + unique inbox. *) + (* + let commitment_period = + node_ctxt.protocol_constants.parametric.sc_rollup + .commitment_period_in_blocks |> Int32.of_int + in + let inbox = + Sc_rollup.Inbox.refresh_commitment_period + ~commitment_period + ~level:inbox_level + inbox + in + *) + let inbox_nb_messages = + Int64.add + (Sc_rollup.Inbox.number_of_messages_during_commitment_period inbox) + (Int64.of_int (List.length messages)) + in + let*? () = + error_when + Compare.Int64.(inbox_nb_messages > max_messages) + (Environment.wrap_tzerror + Sc_rollup_errors + .Sc_rollup_max_number_of_messages_reached_for_commitment_period) + in let* Free_pvm.{state; num_ticks; _} = Free_pvm.eval_messages ~fuel:(Fuel.Free.of_ticks 0L) -- GitLab From 0c6936b0e04bf722fba061343621701f4abab401 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 3 Nov 2022 13:37:05 +0100 Subject: [PATCH 08/21] SCORU/Node: expose Inbox.add_messages --- src/proto_alpha/bin_sc_rollup_node/inbox.ml | 74 ++++++++++---------- src/proto_alpha/bin_sc_rollup_node/inbox.mli | 13 ++++ 2 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/inbox.ml b/src/proto_alpha/bin_sc_rollup_node/inbox.ml index 45beefd267d9..43c7a48f880b 100644 --- a/src/proto_alpha/bin_sc_rollup_node/inbox.ml +++ b/src/proto_alpha/bin_sc_rollup_node/inbox.ml @@ -29,7 +29,7 @@ open Protocol open Alpha_context -let lift = Lwt.map Environment.wrap_tzresult +let lift promise = Lwt.map Environment.wrap_tzresult promise module State = struct let add_messages = Store.Messages.add @@ -159,6 +159,39 @@ let same_inbox_as_layer_1 node_ctxt head_hash inbox = (Sc_rollup.Inbox.equal layer1_inbox inbox) (Sc_rollup_node_errors.Inconsistent_inbox {layer1_inbox; inbox}) +let add_messages (node_ctxt : _ Node_context.t) ctxt level inbox history + messages = + let open Lwt_result_syntax in + let*! messages_tree = Context.MessageTrees.find ctxt in + lift + @@ + if messages = [] then return (history, inbox, ctxt) + else + let*? messages = List.map_e Sc_rollup.Inbox_message.serialize messages in + (* TODO: https://gitlab.com/tezos/tezos/-/issues/3978 + The number of messages during commitment period is broken with the + unique inbox. *) + (* + let commitment_period = + node_ctxt.protocol_constants.parametric.sc_rollup + .commitment_period_in_blocks |> Int32.of_int + in + let inbox = + Sc_rollup.Inbox.refresh_commitment_period ~commitment_period ~level inbox + in + *) + let* messages_tree, history, inbox = + Context.Inbox.add_messages + node_ctxt.context + history + inbox + level + messages + messages_tree + in + let*! ctxt = Context.MessageTrees.set ctxt messages_tree in + return (history, inbox, ctxt) + let process_head (node_ctxt : _ Node_context.t) Layer1.({level; hash = head_hash} as head) = let open Lwt_result_syntax in @@ -181,49 +214,16 @@ let process_head (node_ctxt : _ Node_context.t) let* predecessor = Layer1.get_predecessor node_ctxt.l1_ctxt head in let* inbox = State.inbox_of_head node_ctxt predecessor in let* history = State.history_of_head node_ctxt predecessor in + let*? level = Environment.wrap_tzresult @@ Raw_level.of_int32 level in let* ctxt = - if level <= Raw_level.to_int32 node_ctxt.Node_context.genesis_info.level - then + if Raw_level.(level <= node_ctxt.Node_context.genesis_info.level) then (* This is before we have interpreted the boot sector, so we start with an empty context in genesis *) return (Context.empty node_ctxt.context) else Node_context.checkout_context node_ctxt predecessor.hash in - let*! messages_tree = Context.MessageTrees.find ctxt in let* history, inbox, ctxt = - lift - @@ let*? level = Raw_level.of_int32 level in - let*? messages = - List.map_e Sc_rollup.Inbox_message.serialize messages - in - if messages = [] then return (history, inbox, ctxt) - else - (* TODO: https://gitlab.com/tezos/tezos/-/issues/3978 - - The number of messages during commitment period is broken with the - unique inbox. *) - (* let commitment_period = - * node_ctxt.protocol_constants.parametric.sc_rollup - * .commitment_period_in_blocks |> Int32.of_int - * in - * let inbox = - * Sc_rollup.Inbox.refresh_commitment_period - * ~commitment_period - * ~level - * inbox - * in *) - let* messages_tree, history, inbox = - Context.Inbox.add_messages - node_ctxt.context - history - inbox - level - messages - messages_tree - in - - let*! ctxt = Context.MessageTrees.set ctxt messages_tree in - return (history, inbox, ctxt) + add_messages node_ctxt ctxt level inbox history messages in let* () = same_inbox_as_layer_1 node_ctxt head_hash inbox in let*! () = State.add_inbox node_ctxt.store head_hash inbox in diff --git a/src/proto_alpha/bin_sc_rollup_node/inbox.mli b/src/proto_alpha/bin_sc_rollup_node/inbox.mli index 24457aadba25..ab6ed5b2a24d 100644 --- a/src/proto_alpha/bin_sc_rollup_node/inbox.mli +++ b/src/proto_alpha/bin_sc_rollup_node/inbox.mli @@ -56,3 +56,16 @@ val history_of_hash : (** [start ()] initializes the inbox to track the messages being published. *) val start : unit -> unit Lwt.t + +(** [add_messages node_ctxt ctxt inbox_level inbox history messages] adds + [messages] to the [inbox] whose history is [history]. The new inbox level is + given as [inbox_level]. It takes a context [ctxt] and returns a new + [history], [inbox] and context [ctxt] with an updated message tree. *) +val add_messages : + Node_context.rw -> + 'a Context.t -> + Raw_level.t -> + Sc_rollup.Inbox.t -> + Sc_rollup.Inbox.History.t -> + Sc_rollup.Inbox_message.t list -> + (Sc_rollup.Inbox.History.t * Sc_rollup.Inbox.t * 'a Context.t) tzresult Lwt.t -- GitLab From 330d253db916f97d395bcf0ac007e543e77a36e6 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 3 Nov 2022 15:33:06 +0100 Subject: [PATCH 09/21] SCORU/Node: Dedicated simulation module with simulation state --- .../bin_sc_rollup_node/RPC_server.ml | 52 +------- src/proto_alpha/bin_sc_rollup_node/inbox.ml | 4 + src/proto_alpha/bin_sc_rollup_node/inbox.mli | 10 ++ .../bin_sc_rollup_node/interpreter.ml | 9 ++ .../bin_sc_rollup_node/simulation.ml | 112 ++++++++++++++++++ .../bin_sc_rollup_node/simulation.mli | 55 +++++++++ 6 files changed, 196 insertions(+), 46 deletions(-) create mode 100644 src/proto_alpha/bin_sc_rollup_node/simulation.ml create mode 100644 src/proto_alpha/bin_sc_rollup_node/simulation.mli diff --git a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml index 39c48358aeb7..d41fc6a54351 100644 --- a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml +++ b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml @@ -233,6 +233,7 @@ module Make (Interpreter : Interpreter.S) = struct module PVM = Interpreter.PVM module Outbox = Outbox.Make (PVM) module Free_pvm = Interpreter.Free_pvm + module Simulation = Simulation.Make (Interpreter) let get_state (node_ctxt : _ Node_context.t) block_hash = let open Lwt_result_syntax in @@ -243,56 +244,15 @@ module Make (Interpreter : Interpreter.S) = struct let simulate_messages node_ctxt block messages = let open Lwt_result_syntax in let open Alpha_context in - let*? () = - error_when - (messages = []) - (Environment.wrap_tzerror Sc_rollup_errors.Sc_rollup_add_zero_messages) + let* level = State.level_of_hash node_ctxt.Node_context.store block in + let* sim = + Simulation.start_simulation node_ctxt Layer1.{hash = block; level} in let messages = List.map (fun m -> Sc_rollup.Inbox_message.External m) messages in - let* state = get_state node_ctxt block in - let* level = State.level_of_hash node_ctxt.store block in - let inbox_level = Raw_level.of_int32_exn (Int32.succ level) in - let* inbox = Inbox.inbox_of_hash node_ctxt block in - let max_messages = - node_ctxt.protocol_constants.parametric.sc_rollup - .max_number_of_messages_per_commitment_period |> Int64.of_int - in - (* TODO: https://gitlab.com/tezos/tezos/-/issues/3978 - The number of messages during commitment period is broken with the - unique inbox. *) - (* - let commitment_period = - node_ctxt.protocol_constants.parametric.sc_rollup - .commitment_period_in_blocks |> Int32.of_int - in - let inbox = - Sc_rollup.Inbox.refresh_commitment_period - ~commitment_period - ~level:inbox_level - inbox - in - *) - let inbox_nb_messages = - Int64.add - (Sc_rollup.Inbox.number_of_messages_during_commitment_period inbox) - (Int64.of_int (List.length messages)) - in - let*? () = - error_when - Compare.Int64.(inbox_nb_messages > max_messages) - (Environment.wrap_tzerror - Sc_rollup_errors - .Sc_rollup_max_number_of_messages_reached_for_commitment_period) - in - let* Free_pvm.{state; num_ticks; _} = - Free_pvm.eval_messages - ~fuel:(Fuel.Free.of_ticks 0L) - node_ctxt - state - inbox_level - messages + let* {state; inbox_level; _}, num_ticks = + Simulation.simulate_messages node_ctxt sim messages in let*! outbox = PVM.get_outbox state in let output = diff --git a/src/proto_alpha/bin_sc_rollup_node/inbox.ml b/src/proto_alpha/bin_sc_rollup_node/inbox.ml index 43c7a48f880b..278f776f450e 100644 --- a/src/proto_alpha/bin_sc_rollup_node/inbox.ml +++ b/src/proto_alpha/bin_sc_rollup_node/inbox.ml @@ -241,4 +241,8 @@ let history_of_hash node_ctxt hash = let* level = State.level_of_hash node_ctxt.Node_context.store hash in State.history_of_head node_ctxt {hash; level} +let inbox_of_head = State.inbox_of_head + +let history_of_head = State.history_of_head + let start () = Inbox_event.starting () diff --git a/src/proto_alpha/bin_sc_rollup_node/inbox.mli b/src/proto_alpha/bin_sc_rollup_node/inbox.mli index ab6ed5b2a24d..12873db7bc7b 100644 --- a/src/proto_alpha/bin_sc_rollup_node/inbox.mli +++ b/src/proto_alpha/bin_sc_rollup_node/inbox.mli @@ -54,6 +54,16 @@ val history_of_hash : Tezos_crypto.Block_hash.t -> Sc_rollup.Inbox.History.t tzresult Lwt.t +(** [inbox_of_head node_ctxt block_head] returns the rollup inbox at the end of + the given validation of [block_head]. *) +val inbox_of_head : + _ Node_context.t -> Layer1.head -> Sc_rollup.Inbox.t tzresult Lwt.t + +(** [history_of_head node_ctxt block_head] returns the rollup inbox history at + the end of the given validation of [block_head]. *) +val history_of_head : + _ Node_context.t -> Layer1.head -> Sc_rollup.Inbox.History.t tzresult Lwt.t + (** [start ()] initializes the inbox to track the messages being published. *) val start : unit -> unit Lwt.t diff --git a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml index 1c5f4482f480..42347dbe8e2c 100644 --- a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml +++ b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml @@ -49,6 +49,15 @@ module type S = sig Sc_rollup.Tick.t -> Raw_level.t -> (PVM.state * PVM.hash) option tzresult Lwt.t + + (** [state_of_head node_ctxt ctxt head] returns the state corresponding to the + block [head], or the state at rollup genesis if the block is before the + rollup origination. *) + val state_of_head : + 'a Node_context.t -> + 'a Context.t -> + Layer1.head -> + ('a Context.t * PVM.state) tzresult Lwt.t end module Make (PVM : Pvm.S) : S with module PVM = PVM = struct diff --git a/src/proto_alpha/bin_sc_rollup_node/simulation.ml b/src/proto_alpha/bin_sc_rollup_node/simulation.ml new file mode 100644 index 000000000000..83267fe04ea9 --- /dev/null +++ b/src/proto_alpha/bin_sc_rollup_node/simulation.ml @@ -0,0 +1,112 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +open Protocol +open Alpha_context + +module type S = sig + module PVM : Pvm.S + + type t = { + ctxt : Context.ro; + inbox_level : Raw_level.t; + nb_messages : int64; + state : PVM.state; + } + + val start_simulation : Node_context.ro -> Layer1.head -> t tzresult Lwt.t + + val simulate_messages : + Node_context.ro -> + t -> + Sc_rollup.Inbox_message.t list -> + (t * Z.t) tzresult Lwt.t +end + +module Make (Interpreter : Interpreter.S) : + S with module PVM = Interpreter.PVM = struct + module PVM = Interpreter.PVM + module Fueled_pvm = Interpreter.Free_pvm + + type t = { + ctxt : Context.ro; + inbox_level : Raw_level.t; + nb_messages : int64; + state : PVM.state; + } + + let start_simulation node_ctxt (Layer1.{hash; level} as head) = + let open Lwt_result_syntax in + let*? level = Environment.wrap_tzresult @@ Raw_level.of_int32 level in + let* ctxt = + if Raw_level.(level <= node_ctxt.Node_context.genesis_info.level) then + (* This is before we have interpreted the boot sector, so we start + with an empty context in genesis *) + return (Context.empty node_ctxt.context) + else Node_context.checkout_context node_ctxt hash + in + let* inbox = Inbox.inbox_of_head node_ctxt head in + let+ ctxt, state = Interpreter.state_of_head node_ctxt ctxt head in + let nb_messages = + Sc_rollup.Inbox.number_of_messages_during_commitment_period inbox + in + let inbox_level = Raw_level.succ level in + {ctxt; nb_messages; state; inbox_level} + + let simulate_messages (node_ctxt : Node_context.ro) + {ctxt; nb_messages; state; inbox_level} messages = + let open Lwt_result_syntax in + (* Build new inbox *) + let*? () = + error_when + (messages = []) + (Environment.wrap_tzerror Sc_rollup_errors.Sc_rollup_add_zero_messages) + in + let max_messages = + node_ctxt.protocol_constants.parametric.sc_rollup + .max_number_of_messages_per_commitment_period |> Int64.of_int + in + let nb_messages = + Int64.add nb_messages (Int64.of_int (List.length messages)) + in + let*? () = + error_when + Compare.Int64.(nb_messages > max_messages) + (Environment.wrap_tzerror + Sc_rollup_errors + .Sc_rollup_max_number_of_messages_reached_for_commitment_period) + in + (* Build new state *) + let* Fueled_pvm.{state; num_ticks; _} = + Fueled_pvm.eval_messages + ~fuel:(Fuel.Free.of_ticks 0L) + node_ctxt + state + inbox_level + messages + in + let*! ctxt = PVM.State.set ctxt state in + return ({ctxt; nb_messages; state; inbox_level}, num_ticks) +end diff --git a/src/proto_alpha/bin_sc_rollup_node/simulation.mli b/src/proto_alpha/bin_sc_rollup_node/simulation.mli new file mode 100644 index 000000000000..cba98d33bd3b --- /dev/null +++ b/src/proto_alpha/bin_sc_rollup_node/simulation.mli @@ -0,0 +1,55 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +open Protocol.Alpha_context + +module type S = sig + module PVM : Pvm.S + + (** Type of the state for a simulation. *) + type t = { + ctxt : Context.ro; + inbox_level : Raw_level.t; + nb_messages : int64; + state : PVM.state; + } + + (** [start_simulation node_ctxt block] starts a new simulation {e on top} of + [block], i.e. for an hypothetical new inbox (level). *) + val start_simulation : Node_context.ro -> Layer1.head -> t tzresult Lwt.t + + (** [simulate_messages node_ctxt ?fuel sim messages] runs a simulation of new + [messages] in the given simulation (state) [sim] and returns a new + simulation state, the remaining fuel (when [?fuel] is provided) and the + number of ticks that happened. *) + val simulate_messages : + Node_context.ro -> + t -> + Sc_rollup.Inbox_message.t list -> + (t * Z.t) tzresult Lwt.t +end + +(** Functor to construct a simulator for a given PVM with interpreter. *) +module Make (Interpreter : Interpreter.S) : S with module PVM = Interpreter.PVM -- GitLab From 0bb3f9cc464775c26e1acbfe03599f41dc23be52 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 4 Oct 2022 10:14:14 +0200 Subject: [PATCH 10/21] SCORU/Node: add mli for Interpreter --- .../bin_sc_rollup_node/interpreter.ml | 9 --- .../bin_sc_rollup_node/interpreter.mli | 63 +++++++++++++++++++ 2 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 src/proto_alpha/bin_sc_rollup_node/interpreter.mli diff --git a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml index 42347dbe8e2c..711bf576cddd 100644 --- a/src/proto_alpha/bin_sc_rollup_node/interpreter.ml +++ b/src/proto_alpha/bin_sc_rollup_node/interpreter.ml @@ -35,24 +35,15 @@ module type S = sig module Free_pvm : Fueled_pvm.S with module PVM = PVM and type fuel = Fuel.Free.t - (** [process_head node_ctxt head] interprets the messages associated - with a [head] from a chain [event]. This requires the inbox to be updated - beforehand. *) val process_head : Node_context.rw -> Context.rw -> Layer1.head -> unit tzresult Lwt.t - (** [state_of_tick node_ctxt tick level] returns [Some (state, hash)] - for a given [tick] if this [tick] happened before - [level]. Otherwise, returns [None].*) val state_of_tick : _ Node_context.t -> Sc_rollup.Tick.t -> Raw_level.t -> (PVM.state * PVM.hash) option tzresult Lwt.t - (** [state_of_head node_ctxt ctxt head] returns the state corresponding to the - block [head], or the state at rollup genesis if the block is before the - rollup origination. *) val state_of_head : 'a Node_context.t -> 'a Context.t -> diff --git a/src/proto_alpha/bin_sc_rollup_node/interpreter.mli b/src/proto_alpha/bin_sc_rollup_node/interpreter.mli new file mode 100644 index 000000000000..aa415432c95b --- /dev/null +++ b/src/proto_alpha/bin_sc_rollup_node/interpreter.mli @@ -0,0 +1,63 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 TriliTech *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +open Protocol.Alpha_context + +module type S = sig + module PVM : Pvm.S + + module Accounted_pvm : + Fueled_pvm.S with module PVM = PVM and type fuel = Fuel.Accounted.t + + module Free_pvm : + Fueled_pvm.S with module PVM = PVM and type fuel = Fuel.Free.t + + (** [process_head node_ctxt head] interprets the messages associated + with a [head] from a chain [event]. This requires the inbox to be updated + beforehand. *) + val process_head : + Node_context.rw -> Context.rw -> Layer1.head -> unit tzresult Lwt.t + + (** [state_of_tick node_ctxt tick level] returns [Some (state, hash)] + for a given [tick] if this [tick] happened before + [level]. Otherwise, returns [None].*) + val state_of_tick : + _ Node_context.t -> + Sc_rollup.Tick.t -> + Raw_level.t -> + (PVM.state * PVM.hash) option tzresult Lwt.t + + (** [state_of_head node_ctxt ctxt head] returns the state corresponding to the + block [head], or the state at rollup genesis if the block is before the + rollup origination. *) + val state_of_head : + _ Node_context.t -> + Context.t -> + Layer1.head -> + (Context.t * PVM.state) tzresult Lwt.t +end + +(** Functor to construct an interpreter for a given PVM. *) +module Make (PVM : Pvm.S) : S with module PVM = PVM -- GitLab From eeb36adbbd84fe041b754b80d1c5552ea41edc86 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 4 Oct 2022 10:17:49 +0200 Subject: [PATCH 11/21] SCORU/Node: add mli for Outbox --- src/proto_alpha/bin_sc_rollup_node/outbox.ml | 2 - src/proto_alpha/bin_sc_rollup_node/outbox.mli | 37 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 src/proto_alpha/bin_sc_rollup_node/outbox.mli diff --git a/src/proto_alpha/bin_sc_rollup_node/outbox.ml b/src/proto_alpha/bin_sc_rollup_node/outbox.ml index 9f2420f9b12e..27c1f166d986 100644 --- a/src/proto_alpha/bin_sc_rollup_node/outbox.ml +++ b/src/proto_alpha/bin_sc_rollup_node/outbox.ml @@ -23,8 +23,6 @@ (* *) (*****************************************************************************) -(** This module provides helper to interact with PVM outboxes. *) - open Node_context open Protocol.Alpha_context diff --git a/src/proto_alpha/bin_sc_rollup_node/outbox.mli b/src/proto_alpha/bin_sc_rollup_node/outbox.mli new file mode 100644 index 000000000000..7b59aae86f09 --- /dev/null +++ b/src/proto_alpha/bin_sc_rollup_node/outbox.mli @@ -0,0 +1,37 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** This module provides helper to interact with PVM outboxes. *) + +open Protocol.Alpha_context + +module Make (PVM : Pvm.S) : sig + (** [proof_of_output node_ctxt output] returns the last cemented commitment + hash and the proof of the output in the LCC. *) + val proof_of_output : + Node_context.t -> + Sc_rollup.output -> + (Store.Last_cemented_commitment_hash.value * string) tzresult Lwt.t +end -- GitLab From 1abc74806b3f3814683d20ab289f1e2b59b7f1f4 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 11 Oct 2022 15:19:51 +0200 Subject: [PATCH 12/21] SCORU/Node: fix chunks over sized --- src/proto_alpha/bin_sc_rollup_node/reveals.ml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/reveals.ml b/src/proto_alpha/bin_sc_rollup_node/reveals.ml index 4f0523afefa3..48b8d22f5cdd 100644 --- a/src/proto_alpha/bin_sc_rollup_node/reveals.ml +++ b/src/proto_alpha/bin_sc_rollup_node/reveals.ml @@ -150,10 +150,15 @@ module Arith = struct let chunk = make_chunk () in Buffer.add_string buf token ; (chunk :: chunks, len)) - else ( - if Buffer.length buf > 0 then Buffer.add_char buf ' ' ; + else + let len = + if Buffer.length buf > 0 then ( + Buffer.add_char buf ' ' ; + len + 1) + else len + in Buffer.add_string buf token ; - (chunks, size + len))) + (chunks, size + len)) ([], 0) tokens in -- GitLab From 954b6fb743dbfe9ae770aa44a20c826913177fa0 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 11 Oct 2022 16:10:42 +0200 Subject: [PATCH 13/21] SCORU/Node: build reveals chunks from string --- .../main_sc_rollup_node_alpha.ml | 14 ++-- src/proto_alpha/bin_sc_rollup_node/reveals.ml | 72 ++++++++++++++----- .../bin_sc_rollup_node/reveals.mli | 22 ++++-- 3 files changed, 82 insertions(+), 26 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml b/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml index f91d067aff60..943ca558767d 100644 --- a/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml +++ b/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml @@ -205,6 +205,12 @@ let pvm_name_arg = ~default:"arith" Client_proto_args.string_parameter +let pvm_kind_arg = + Clic.map_arg pvm_name_arg ~f:(fun _ name -> + match Protocol.Alpha_context.Sc_rollup.Kind.of_name name with + | None -> failwith "Invalid PVM name %s" name + | Some kind -> return kind) + let group = { Tezos_clic.name = "sc_rollup.node"; @@ -331,11 +337,11 @@ let import_command = let open Tezos_clic in command ~group - ~desc:"Run the rollup daemon." - (args3 data_dir_arg filename_arg pvm_name_arg) + ~desc:"Import data to be used in reveal ticks." + (args3 data_dir_arg filename_arg pvm_kind_arg) (prefixes ["import"] @@ stop) - (fun (data_dir, filename, pvm_name) cctxt -> - let hash = Reveals.import ~data_dir ~filename ~pvm_name in + (fun (data_dir, filename, pvm_kind) cctxt -> + let hash = Reveals.import ~data_dir pvm_kind ~filename in cctxt#message "%a" Protocol.Alpha_context.Sc_rollup.Reveal_hash.pp hash >>= return) diff --git a/src/proto_alpha/bin_sc_rollup_node/reveals.ml b/src/proto_alpha/bin_sc_rollup_node/reveals.ml index 48b8d22f5cdd..814df7082a91 100644 --- a/src/proto_alpha/bin_sc_rollup_node/reveals.ml +++ b/src/proto_alpha/bin_sc_rollup_node/reveals.ml @@ -65,6 +65,8 @@ let () = | Could_not_open_preimage_file filename -> Some filename | _ -> None) (fun filename -> Could_not_open_preimage_file filename) +type source = String of string | File of string + let file_contents filename = let open Lwt_result_syntax in Lwt.catch @@ -105,15 +107,27 @@ module Arith = struct let pvm_name = Protocol.Alpha_context.Sc_rollup.ArithPVM.Protocol_implementation.name - let rev_chunks_of_file filename = + type input = + | String of {s : string; mutable pos : int; len : int} + | Input of in_channel + + let rev_chunks_of_input input = (* FIXME: https://gitlab.com/tezos/tezos/-/issues/3853 Can be made more efficient. *) - let get_char cin = try Some (input_char cin) with End_of_file -> None in + let get_char () = + match input with + | Input cin -> ( try Some (input_char cin) with End_of_file -> None) + | String ({s; pos; len} as sin) -> + if pos >= len then None + else ( + sin.pos <- pos + 1 ; + Some s.[pos]) + in let buf = Buffer.create 31 in let tokens = - let cin = open_in filename in + (* let cin = open_in filename in *) let rec aux tokens = - match get_char cin with + match get_char () with | None -> List.rev @@ @@ -130,7 +144,6 @@ module Arith = struct aux tokens in let tokens = aux [] in - close_in cin ; tokens in let limit = @@ -177,24 +190,47 @@ module Arith = struct | Some h -> Format.asprintf "%s hash:%a" chunk Reveal_hash.pp h in let hash = Reveal_hash.hash_string [cell] in - aux (Some hash) ((cell, hash) :: linked_chunks) rev_chunks + aux (Some hash) ((hash, cell) :: linked_chunks) rev_chunks in aux None [] rev_chunks - let import data_dir filename = - ensure_dir_exists data_dir pvm_name ; - let rev_chunks = rev_chunks_of_file filename in + let input_of_source = function + | File filename -> Input (open_in filename) + | String s -> String {s; pos = 0; len = String.length s} + + let close_input = function String _ -> () | Input cin -> close_in cin + + let chunkify source = + let input = input_of_source source in + let rev_chunks = rev_chunks_of_input input in let linked_hashed_chunks = link_rev_chunks rev_chunks in + close_input input ; + linked_hashed_chunks + + let import data_dir source = + ensure_dir_exists data_dir pvm_name ; + let linked_hashed_chunks = chunkify source in List.iter - (fun (data, hash) -> save_string (path data_dir pvm_name hash) data) + (fun (hash, data) -> save_string (path data_dir pvm_name hash) data) linked_hashed_chunks ; - Stdlib.List.hd linked_hashed_chunks |> snd + Stdlib.List.hd linked_hashed_chunks |> fst + + let chunkify source = + let linked_hashed_chunks = chunkify source in + let chunks_map = + linked_hashed_chunks |> List.to_seq + |> Protocol.Alpha_context.Sc_rollup.Reveal_hash.Map.of_seq + in + (chunks_map, Stdlib.List.hd linked_hashed_chunks |> fst) end -let import ~data_dir ~pvm_name ~filename = - if - String.equal - pvm_name - Protocol.Alpha_context.Sc_rollup.ArithPVM.Protocol_implementation.name - then Arith.import data_dir filename - else Stdlib.failwith "Not supported yet" +let import ~data_dir (pvm_kind : Protocol.Alpha_context.Sc_rollup.Kind.t) + ~filename = + match pvm_kind with + | Example_arith -> Arith.import data_dir (File filename) + | Wasm_2_0_0 -> Stdlib.failwith "Not supported yet" + +let chunkify (pvm_kind : Protocol.Alpha_context.Sc_rollup.Kind.t) source = + match pvm_kind with + | Example_arith -> Arith.chunkify source + | Wasm_2_0_0 -> Stdlib.failwith "Not supported yet" diff --git a/src/proto_alpha/bin_sc_rollup_node/reveals.mli b/src/proto_alpha/bin_sc_rollup_node/reveals.mli index 10abf2d3d7f0..5bc0351b784e 100644 --- a/src/proto_alpha/bin_sc_rollup_node/reveals.mli +++ b/src/proto_alpha/bin_sc_rollup_node/reveals.mli @@ -50,6 +50,12 @@ open Protocol.Alpha_context +(** Source of data *) +type source = + | String of string (** A string containing the whole data *) + | File of string + (** A file name whose associated file contains the whole data *) + (** [get ~data_dir ~pvm_name ~hash] retrieves the data associated with the reveal hash [hash] from disk. May fail with: {ul @@ -66,11 +72,19 @@ val get : hash:Sc_rollup.Reveal_hash.t -> string tzresult Lwt.t -(** [import ~data_dir ~pvm_name ~filename] turns the content of ~filename - into a chunk of pages of (at most) 4KB, returning the hash of the first - chunk. *) +(** [import ~data_dir pvm_kind ~filename] turns the content of ~filename into a + chunk of pages of (at most) 4KB and stores them on disk in [data_dir]. It + returns the hash of the first chunk. *) val import : data_dir:string -> - pvm_name:string -> + Sc_rollup.Kind.t -> filename:string -> Sc_rollup.Reveal_hash.t + +(** [chunkify pvm_kind source] turns the content of ~filename into a chunk of + pages of (at most) 4KB. It returns the map of chunks and the hash of the + first chunk. *) +val chunkify : + Sc_rollup.Kind.t -> + source -> + string Sc_rollup.Reveal_hash.Map.t * Sc_rollup.Reveal_hash.t -- GitLab From bd44c3a7b8276ac1005770b8da2da0fb2163c001 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 11 Oct 2022 16:58:28 +0200 Subject: [PATCH 14/21] SCORU/Node: expose PVM kind --- src/proto_alpha/bin_sc_rollup_node/arith_pvm.ml | 6 +++++- src/proto_alpha/bin_sc_rollup_node/pvm.ml | 3 +++ src/proto_alpha/bin_sc_rollup_node/wasm_2_0_0_pvm.ml | 7 ++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/arith_pvm.ml b/src/proto_alpha/bin_sc_rollup_node/arith_pvm.ml index 946a347b06da..881417dcc1d3 100644 --- a/src/proto_alpha/bin_sc_rollup_node/arith_pvm.ml +++ b/src/proto_alpha/bin_sc_rollup_node/arith_pvm.ml @@ -45,7 +45,11 @@ module Arith_proof_format = end) module Impl : Pvm.S = struct - include Sc_rollup.ArithPVM.Make (Arith_proof_format) + module PVM = Sc_rollup.ArithPVM.Make (Arith_proof_format) + include PVM + + let kind = Sc_rollup.Kind.of_pvm (module PVM) + module State = Context.PVMState let string_of_status status = diff --git a/src/proto_alpha/bin_sc_rollup_node/pvm.ml b/src/proto_alpha/bin_sc_rollup_node/pvm.ml index f47b30948aa5..37a10fcf3229 100644 --- a/src/proto_alpha/bin_sc_rollup_node/pvm.ml +++ b/src/proto_alpha/bin_sc_rollup_node/pvm.ml @@ -34,6 +34,9 @@ module type S = sig with type context = Context.rw_index and type hash = Sc_rollup.State_hash.t + (** Kind of the PVM (same as {!name}). *) + val kind : Sc_rollup.Kind.t + (** [get_tick state] gets the total tick counter for the given PVM state. *) val get_tick : state -> Sc_rollup.Tick.t Lwt.t diff --git a/src/proto_alpha/bin_sc_rollup_node/wasm_2_0_0_pvm.ml b/src/proto_alpha/bin_sc_rollup_node/wasm_2_0_0_pvm.ml index 92dde90354fc..45fc6c234f82 100644 --- a/src/proto_alpha/bin_sc_rollup_node/wasm_2_0_0_pvm.ml +++ b/src/proto_alpha/bin_sc_rollup_node/wasm_2_0_0_pvm.ml @@ -64,7 +64,12 @@ module Make_backend (Tree : TreeS) = struct end module Impl : Pvm.S = struct - include Sc_rollup.Wasm_2_0_0PVM.Make (Make_backend) (Wasm_2_0_0_proof_format) + module PVM = + Sc_rollup.Wasm_2_0_0PVM.Make (Make_backend) (Wasm_2_0_0_proof_format) + include PVM + + let kind = Sc_rollup.Kind.of_pvm (module PVM) + module State = Context.PVMState let string_of_status : status -> string = function -- GitLab From 6c5dd5db8ecd9ecc6d378db5e14dfe6a76910475 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 11 Oct 2022 16:59:14 +0200 Subject: [PATCH 15/21] SCORU/Node: allow to provide revelation data in simulate RPC --- .../bin_sc_rollup_node/RPC_server.ml | 28 +++++-- .../bin_sc_rollup_node/fueled_pvm.ml | 84 ++++++++++++------- .../bin_sc_rollup_node/interpreter.mli | 6 +- .../main_sc_rollup_node_alpha.ml | 2 +- src/proto_alpha/bin_sc_rollup_node/outbox.mli | 2 +- src/proto_alpha/bin_sc_rollup_node/reveals.ml | 5 +- .../bin_sc_rollup_node/simulation.ml | 25 ++++-- .../bin_sc_rollup_node/simulation.mli | 15 +++- .../lib_sc_rollup/sc_rollup_services.ml | 26 ++++-- 9 files changed, 135 insertions(+), 58 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml index d41fc6a54351..97a3c4c9a2f4 100644 --- a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml +++ b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml @@ -241,12 +241,30 @@ module Make (Interpreter : Interpreter.S) = struct let*! state = PVM.State.find ctxt in match state with None -> failwith "No state" | Some state -> return state - let simulate_messages node_ctxt block messages = + let simulate_messages (node_ctxt : Node_context.ro) block ~reveal_pages + messages = let open Lwt_result_syntax in let open Alpha_context in - let* level = State.level_of_hash node_ctxt.Node_context.store block in + let reveal_map = + match reveal_pages with + | Some pages -> + let map = + List.fold_left + (fun map page -> + let hash = Sc_rollup.Reveal_hash.hash_string [page] in + Sc_rollup.Reveal_hash.Map.add hash page map) + Sc_rollup.Reveal_hash.Map.empty + pages + in + Some map + | None -> None + in + let* level = State.level_of_hash node_ctxt.store block in let* sim = - Simulation.start_simulation node_ctxt Layer1.{hash = block; level} + Simulation.start_simulation + node_ctxt + ~reveal_map + Layer1.{hash = block; level} in let messages = List.map (fun m -> Sc_rollup.Inbox_message.External m) messages @@ -372,8 +390,8 @@ module Make (Interpreter : Interpreter.S) = struct let () = Block_directory.register0 Sc_rollup_services.Global.Block.simulate - @@ fun (node_ctxt, block) () messages -> - simulate_messages node_ctxt block messages + @@ fun (node_ctxt, block) () {messages; reveal_pages} -> + simulate_messages node_ctxt block ~reveal_pages messages let register node_ctxt = List.fold_left diff --git a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml index fbdf931b9bc8..b43106d455b3 100644 --- a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml @@ -48,12 +48,15 @@ module type S = sig (PVM.state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult Lwt.t - (** [eval_messages ~fuel node_ctxt state inbox_level messages] evaluates the - [messages] for inbox level [inbox_level] in the given [state] of the PVM - and returns the evaluation results containing the new state, the remaining - fuel, and the number of ticks for the evaluation - of this message. *) + (** [eval_messages ?reveal_map ~fuel node_ctxt state + inbox_level messages] evaluates the [messages] for inbox level + [inbox_level] in the given [state] of the PVM and returns the evaluation + results containing the new state, the remaining fuel, and the number of + ticks for the evaluation of these messages. When + [reveal_map] is provided, it is used as an additional source of data for + revelation ticks. *) val eval_messages : + ?reveal_map:string Sc_rollup.Reveal_hash.Map.t -> fuel:fuel -> _ Node_context.t -> PVM.state -> @@ -75,6 +78,16 @@ module Make (PVM : Pvm.S) = struct num_ticks : Z.t; } + let get_reveal ~data_dir reveal_map hash = + let found_in_map = + match reveal_map with + | None -> None + | Some map -> Sc_rollup.Reveal_hash.Map.find_opt hash map + in + match found_in_map with + | Some data -> return data + | None -> Reveals.get ~data_dir ~pvm_name:PVM.name ~hash + let continue_with_fuel consumption initial_fuel state f = let open Delayed_write_monad.Lwt_result_syntax in match F.consume consumption initial_fuel with @@ -83,14 +96,15 @@ module Make (PVM : Pvm.S) = struct exception Error_wrapper of tztrace - (** [eval_until_input node_ctxt level message_index ~fuel start_tick - failing_ticks state] advances a PVM [state] until it wants more inputs or - there are no more [fuel] (if [Some fuel] is specified). The evaluation is - running under the processing of some [message_index] at a given [level] - and this is the [start_tick] of this message processing. If some - [failing_ticks] are planned by the loser mode, they will be made. *) - let eval_until_input node_ctxt level message_index ~fuel start_tick - failing_ticks state = + (** [eval_until_input node_ctxt reveal_map level message_index ~fuel + start_tick failing_ticks state] advances a PVM [state] until it wants + more inputs or there are no more [fuel] (if [Some fuel] is + specified). The evaluation is running under the processing of some + [message_index] at a given [level] and this is the [start_tick] of this + message processing. If some [failing_ticks] are planned by the loser + mode, they will be made. *) + let eval_until_input node_ctxt reveal_map level message_index ~fuel + start_tick failing_ticks state = let open Lwt_result_syntax in let open Delayed_write_monad.Lwt_result_syntax in let metadata = Node_context.metadata node_ctxt in @@ -105,7 +119,9 @@ module Make (PVM : Pvm.S) = struct Tezos_webassembly_interpreter.Reveal.reveal_hash_to_string hash |> Data_encoding.Binary.of_string_exn Sc_rollup.Reveal_hash.encoding in - let*! data = Reveals.get ~data_dir ~pvm_name:PVM.name ~hash in + let*! data = + get_reveal ~data_dir:node_ctxt.data_dir reveal_map hash + in match data with | Error error -> (* The [Error_wrapper] must be caught upstream and converted into a @@ -165,10 +181,7 @@ module Make (PVM : Pvm.S) = struct next_state | Needs_reveal (Reveal_raw_data hash) -> ( let* data = - Reveals.get - ~data_dir:node_ctxt.data_dir - ~pvm_name:PVM.name - ~hash + get_reveal ~data_dir:node_ctxt.data_dir reveal_map hash in let*! next_state = PVM.set_input (Reveal (Raw_data data)) state in match F.consume F.one_tick_consumption fuel with @@ -207,19 +220,20 @@ module Make (PVM : Pvm.S) = struct let payload = Sc_rollup.Inbox_message.unsafe_of_string "0xC4C4" in {input with Sc_rollup.payload} - (** [feed_input node_ctxt level message_index ~fuel ~failing_ticks state - input] feeds [input] (that has a given [message_index] in inbox - of [level]) to the PVM in order to advance [state] to the next - step that requires an input. This function is controlled by - some [fuel] and may introduce intended failures at some given - [failing_ticks]. *) - let feed_input node_ctxt level message_index ~fuel ~failing_ticks state - input = + (** [feed_input node_ctxt reveal_map level message_index ~fuel + ~failing_ticks state input] feeds [input] (that has a given + [message_index] in inbox of [level]) to the PVM in order to advance + [state] to the next step that requires an input. This function is + controlled by some [fuel] and may introduce intended failures at some + given [failing_ticks]. *) + let feed_input node_ctxt reveal_map level message_index ~fuel ~failing_ticks + state input = let open Lwt_result_syntax in let open Delayed_write_monad.Lwt_result_syntax in let>* state, fuel, tick, failing_ticks = eval_until_input node_ctxt + reveal_map level message_index ~fuel @@ -248,6 +262,7 @@ module Make (PVM : Pvm.S) = struct let>* state, _fuel, tick, _failing_ticks = eval_until_input node_ctxt + reveal_map level message_index ~fuel @@ -257,7 +272,7 @@ module Make (PVM : Pvm.S) = struct in return (state, tick) - let eval_messages ~fuel node_ctxt state inbox_level messages = + let eval_messages ~reveal_map ~fuel node_ctxt state inbox_level messages = let open Lwt_result_syntax in let open Delayed_write_monad.Lwt_result_syntax in let level = Raw_level.to_int32 inbox_level |> Int32.to_int in @@ -281,6 +296,7 @@ module Make (PVM : Pvm.S) = struct let>* state, executed_ticks = feed_input node_ctxt + reveal_map level message_counter ~fuel @@ -315,16 +331,22 @@ module Make (PVM : Pvm.S) = struct let num_messages = List.length messages |> Z.of_int in (* Evaluate all the messages for this level. *) let>* state, fuel = - eval_messages ~fuel node_ctxt state inbox_level messages + eval_messages + ~reveal_map:None + ~fuel + node_ctxt + state + inbox_level + messages in return (state, num_messages, inbox_level, fuel) - let eval_messages ~fuel node_ctxt state inbox_level messages = + let eval_messages ?reveal_map ~fuel node_ctxt state inbox_level messages = let open Lwt_result_syntax in let open Delayed_write_monad.Lwt_result_syntax in let*! initial_tick = PVM.get_tick state in - let>* state, remaining_fuel = - eval_messages ~fuel node_ctxt state inbox_level messages + let*> state, remaining_fuel = + eval_messages ~reveal_map ~fuel node_ctxt state inbox_level messages in let*! final_tick = PVM.get_tick state in let num_ticks = Sc_rollup.Tick.distance initial_tick final_tick in diff --git a/src/proto_alpha/bin_sc_rollup_node/interpreter.mli b/src/proto_alpha/bin_sc_rollup_node/interpreter.mli index aa415432c95b..9f3fbc27871e 100644 --- a/src/proto_alpha/bin_sc_rollup_node/interpreter.mli +++ b/src/proto_alpha/bin_sc_rollup_node/interpreter.mli @@ -53,10 +53,10 @@ module type S = sig block [head], or the state at rollup genesis if the block is before the rollup origination. *) val state_of_head : - _ Node_context.t -> - Context.t -> + 'a Node_context.t -> + 'a Context.t -> Layer1.head -> - (Context.t * PVM.state) tzresult Lwt.t + ('a Context.t * PVM.state) tzresult Lwt.t end (** Functor to construct an interpreter for a given PVM. *) diff --git a/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml b/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml index 943ca558767d..67ae2b862f37 100644 --- a/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml +++ b/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml @@ -206,7 +206,7 @@ let pvm_name_arg = Client_proto_args.string_parameter let pvm_kind_arg = - Clic.map_arg pvm_name_arg ~f:(fun _ name -> + Tezos_clic.map_arg pvm_name_arg ~f:(fun _ name -> match Protocol.Alpha_context.Sc_rollup.Kind.of_name name with | None -> failwith "Invalid PVM name %s" name | Some kind -> return kind) diff --git a/src/proto_alpha/bin_sc_rollup_node/outbox.mli b/src/proto_alpha/bin_sc_rollup_node/outbox.mli index 7b59aae86f09..d061ca3397c1 100644 --- a/src/proto_alpha/bin_sc_rollup_node/outbox.mli +++ b/src/proto_alpha/bin_sc_rollup_node/outbox.mli @@ -31,7 +31,7 @@ module Make (PVM : Pvm.S) : sig (** [proof_of_output node_ctxt output] returns the last cemented commitment hash and the proof of the output in the LCC. *) val proof_of_output : - Node_context.t -> + Node_context.rw -> Sc_rollup.output -> (Store.Last_cemented_commitment_hash.value * string) tzresult Lwt.t end diff --git a/src/proto_alpha/bin_sc_rollup_node/reveals.ml b/src/proto_alpha/bin_sc_rollup_node/reveals.ml index 814df7082a91..afe3b80d3f23 100644 --- a/src/proto_alpha/bin_sc_rollup_node/reveals.ml +++ b/src/proto_alpha/bin_sc_rollup_node/reveals.ml @@ -228,7 +228,10 @@ let import ~data_dir (pvm_kind : Protocol.Alpha_context.Sc_rollup.Kind.t) ~filename = match pvm_kind with | Example_arith -> Arith.import data_dir (File filename) - | Wasm_2_0_0 -> Stdlib.failwith "Not supported yet" + | Wasm_2_0_0 -> + (* TODO: https://gitlab.com/tezos/tezos/-/issues/4067 + Add support for multiple revelation data serialization schemes *) + Stdlib.failwith "Not supported yet" let chunkify (pvm_kind : Protocol.Alpha_context.Sc_rollup.Kind.t) source = match pvm_kind with diff --git a/src/proto_alpha/bin_sc_rollup_node/simulation.ml b/src/proto_alpha/bin_sc_rollup_node/simulation.ml index 83267fe04ea9..0284385c6e6c 100644 --- a/src/proto_alpha/bin_sc_rollup_node/simulation.ml +++ b/src/proto_alpha/bin_sc_rollup_node/simulation.ml @@ -27,16 +27,24 @@ open Protocol open Alpha_context module type S = sig - module PVM : Pvm.S + module Interpreter : Interpreter.S + + module PVM = Interpreter.PVM + module Fueled_pvm = Interpreter.Free_pvm type t = { ctxt : Context.ro; inbox_level : Raw_level.t; nb_messages : int64; state : PVM.state; + reveal_map : string Sc_rollup.Reveal_hash.Map.t option; } - val start_simulation : Node_context.ro -> Layer1.head -> t tzresult Lwt.t + val start_simulation : + Node_context.ro -> + reveal_map:string Sc_rollup.Reveal_hash.Map.t option -> + Layer1.head -> + t tzresult Lwt.t val simulate_messages : Node_context.ro -> @@ -46,7 +54,8 @@ module type S = sig end module Make (Interpreter : Interpreter.S) : - S with module PVM = Interpreter.PVM = struct + S with module Interpreter = Interpreter = struct + module Interpreter = Interpreter module PVM = Interpreter.PVM module Fueled_pvm = Interpreter.Free_pvm @@ -55,9 +64,10 @@ module Make (Interpreter : Interpreter.S) : inbox_level : Raw_level.t; nb_messages : int64; state : PVM.state; + reveal_map : string Sc_rollup.Reveal_hash.Map.t option; } - let start_simulation node_ctxt (Layer1.{hash; level} as head) = + let start_simulation node_ctxt ~reveal_map (Layer1.{hash; level} as head) = let open Lwt_result_syntax in let*? level = Environment.wrap_tzresult @@ Raw_level.of_int32 level in let* ctxt = @@ -73,10 +83,10 @@ module Make (Interpreter : Interpreter.S) : Sc_rollup.Inbox.number_of_messages_during_commitment_period inbox in let inbox_level = Raw_level.succ level in - {ctxt; nb_messages; state; inbox_level} + {ctxt; nb_messages; state; inbox_level; reveal_map} let simulate_messages (node_ctxt : Node_context.ro) - {ctxt; nb_messages; state; inbox_level} messages = + {ctxt; nb_messages; state; inbox_level; reveal_map} messages = let open Lwt_result_syntax in (* Build new inbox *) let*? () = @@ -101,6 +111,7 @@ module Make (Interpreter : Interpreter.S) : (* Build new state *) let* Fueled_pvm.{state; num_ticks; _} = Fueled_pvm.eval_messages + ?reveal_map ~fuel:(Fuel.Free.of_ticks 0L) node_ctxt state @@ -108,5 +119,5 @@ module Make (Interpreter : Interpreter.S) : messages in let*! ctxt = PVM.State.set ctxt state in - return ({ctxt; nb_messages; state; inbox_level}, num_ticks) + return ({ctxt; nb_messages; state; inbox_level; reveals_map}, num_ticks) end diff --git a/src/proto_alpha/bin_sc_rollup_node/simulation.mli b/src/proto_alpha/bin_sc_rollup_node/simulation.mli index cba98d33bd3b..4d411352c466 100644 --- a/src/proto_alpha/bin_sc_rollup_node/simulation.mli +++ b/src/proto_alpha/bin_sc_rollup_node/simulation.mli @@ -26,7 +26,10 @@ open Protocol.Alpha_context module type S = sig - module PVM : Pvm.S + module Interpreter : Interpreter.S + + module PVM = Interpreter.PVM + module Fueled_pvm = Interpreter.Free_pvm (** Type of the state for a simulation. *) type t = { @@ -34,11 +37,16 @@ module type S = sig inbox_level : Raw_level.t; nb_messages : int64; state : PVM.state; + reveal_map : string Sc_rollup.Reveal_hash.Map.t option; } (** [start_simulation node_ctxt block] starts a new simulation {e on top} of [block], i.e. for an hypothetical new inbox (level). *) - val start_simulation : Node_context.ro -> Layer1.head -> t tzresult Lwt.t + val start_simulation : + Node_context.ro -> + reveal_map:string Sc_rollup.Reveal_hash.Map.t option -> + Layer1.head -> + t tzresult Lwt.t (** [simulate_messages node_ctxt ?fuel sim messages] runs a simulation of new [messages] in the given simulation (state) [sim] and returns a new @@ -52,4 +60,5 @@ module type S = sig end (** Functor to construct a simulator for a given PVM with interpreter. *) -module Make (Interpreter : Interpreter.S) : S with module PVM = Interpreter.PVM +module Make (Interpreter : Interpreter.S) : + S with module Interpreter = Interpreter diff --git a/src/proto_alpha/lib_sc_rollup/sc_rollup_services.ml b/src/proto_alpha/lib_sc_rollup/sc_rollup_services.ml index 1789a9a383aa..f19b52b5f2bd 100644 --- a/src/proto_alpha/lib_sc_rollup/sc_rollup_services.ml +++ b/src/proto_alpha/lib_sc_rollup/sc_rollup_services.ml @@ -56,6 +56,11 @@ type eval_result = { num_ticks : Z.t; } +type simulate_input = { + messages : string list; + reveal_pages : string list option; +} + module Encodings = struct open Data_encoding @@ -93,6 +98,20 @@ module Encodings = struct "num_ticks" z ~description:"Ticks taken by the PVM for evaluating the messages") + + let simulate_input = + conv + (fun {messages; reveal_pages} -> (messages, reveal_pages)) + (fun (messages, reveal_pages) -> {messages; reveal_pages}) + @@ obj2 + (req + "messages" + (list hex_string) + ~description:"Input messages for simulation") + (opt + "reveal_pages" + (list hex_string) + ~description:"Pages (at most 4kB) to be used for revelation ticks") end module Arg = struct @@ -342,12 +361,7 @@ module Global = struct Tezos_rpc.Service.post_service ~description:"Simulate messages evaluation by the PVM" ~query:Tezos_rpc.Query.empty - ~input: - Data_encoding.( - def - "messages" - ~description:"Input messages for simulation" - (list Encodings.hex_string)) + ~input:Encodings.simulate_input ~output:Encodings.eval_result (path / "simulate") -- GitLab From c126da947b577bc3b95f6ffeb85858ad744a7fe8 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 21 Oct 2022 12:03:05 +0200 Subject: [PATCH 16/21] SCORU/Node: Better errors for revelation data importing --- .../main_sc_rollup_node_alpha.ml | 9 ++-- src/proto_alpha/bin_sc_rollup_node/reveals.ml | 42 ++++++++++++++++--- .../bin_sc_rollup_node/reveals.mli | 4 +- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml b/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml index 67ae2b862f37..7341a72bf374 100644 --- a/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml +++ b/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml @@ -341,9 +341,12 @@ let import_command = (args3 data_dir_arg filename_arg pvm_kind_arg) (prefixes ["import"] @@ stop) (fun (data_dir, filename, pvm_kind) cctxt -> - let hash = Reveals.import ~data_dir pvm_kind ~filename in - cctxt#message "%a" Protocol.Alpha_context.Sc_rollup.Reveal_hash.pp hash - >>= return) + let open Lwt_result_syntax in + let*? hash = Reveals.import ~data_dir pvm_kind ~filename in + let*! () = + cctxt#message "%a" Protocol.Alpha_context.Sc_rollup.Reveal_hash.pp hash + in + return_unit) let sc_rollup_commands () = List.map diff --git a/src/proto_alpha/bin_sc_rollup_node/reveals.ml b/src/proto_alpha/bin_sc_rollup_node/reveals.ml index afe3b80d3f23..8642a5da8154 100644 --- a/src/proto_alpha/bin_sc_rollup_node/reveals.ml +++ b/src/proto_alpha/bin_sc_rollup_node/reveals.ml @@ -28,6 +28,8 @@ module Reveal_hash = Protocol.Alpha_context.Sc_rollup.Reveal_hash type error += | Wrong_hash of {found : Reveal_hash.t; expected : Reveal_hash.t} | Could_not_open_preimage_file of String.t + | Empty_reveal_data + | PVM_not_supported of Protocol.Alpha_context.Sc_rollup.Kind.t let () = register_error_kind @@ -63,7 +65,31 @@ let () = Data_encoding.(obj1 (req "hash" string)) (function | Could_not_open_preimage_file filename -> Some filename | _ -> None) - (fun filename -> Could_not_open_preimage_file filename) + (fun filename -> Could_not_open_preimage_file filename) ; + register_error_kind + `Permanent + ~id:"sc_rollup_node_empty_reveal_data" + ~title:"Empty revelation data" + ~description:"Empty revelation data." + ~pp:(fun ppf () -> + Format.pp_print_string ppf "Tried to import or use empty revelation data.") + Data_encoding.unit + (function Empty_reveal_data -> Some () | _ -> None) + (fun () -> Empty_reveal_data) ; + register_error_kind + `Permanent + ~id:"sc_rollup_node_reveal_data_not_supported" + ~title:"Revelation data not supported for PVM" + ~description:"Revelation data not supported for PVM." + ~pp:(fun ppf kind -> + Format.fprintf + ppf + "Revelation data not supported for PVM %s" + (Protocol.Alpha_context.Sc_rollup.Kind.name_of kind)) + Data_encoding.( + obj1 (req "pvm_kind" Protocol.Alpha_context.Sc_rollup.Kind.encoding)) + (function PVM_not_supported k -> Some k | _ -> None) + (fun k -> PVM_not_supported k) type source = String of string | File of string @@ -207,21 +233,27 @@ module Arith = struct close_input input ; linked_hashed_chunks + let first_hash = function + | (hash, _) :: _ -> ok hash + | [] -> error Empty_reveal_data + let import data_dir source = ensure_dir_exists data_dir pvm_name ; let linked_hashed_chunks = chunkify source in List.iter (fun (hash, data) -> save_string (path data_dir pvm_name hash) data) linked_hashed_chunks ; - Stdlib.List.hd linked_hashed_chunks |> fst + first_hash linked_hashed_chunks let chunkify source = + let open Result_syntax in let linked_hashed_chunks = chunkify source in let chunks_map = linked_hashed_chunks |> List.to_seq |> Protocol.Alpha_context.Sc_rollup.Reveal_hash.Map.of_seq in - (chunks_map, Stdlib.List.hd linked_hashed_chunks |> fst) + let+ hash = first_hash linked_hashed_chunks in + (chunks_map, hash) end let import ~data_dir (pvm_kind : Protocol.Alpha_context.Sc_rollup.Kind.t) @@ -231,9 +263,9 @@ let import ~data_dir (pvm_kind : Protocol.Alpha_context.Sc_rollup.Kind.t) | Wasm_2_0_0 -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/4067 Add support for multiple revelation data serialization schemes *) - Stdlib.failwith "Not supported yet" + error (PVM_not_supported pvm_kind) let chunkify (pvm_kind : Protocol.Alpha_context.Sc_rollup.Kind.t) source = match pvm_kind with | Example_arith -> Arith.chunkify source - | Wasm_2_0_0 -> Stdlib.failwith "Not supported yet" + | Wasm_2_0_0 -> error (PVM_not_supported pvm_kind) diff --git a/src/proto_alpha/bin_sc_rollup_node/reveals.mli b/src/proto_alpha/bin_sc_rollup_node/reveals.mli index 5bc0351b784e..bdffe3bc9618 100644 --- a/src/proto_alpha/bin_sc_rollup_node/reveals.mli +++ b/src/proto_alpha/bin_sc_rollup_node/reveals.mli @@ -79,7 +79,7 @@ val import : data_dir:string -> Sc_rollup.Kind.t -> filename:string -> - Sc_rollup.Reveal_hash.t + Sc_rollup.Reveal_hash.t tzresult (** [chunkify pvm_kind source] turns the content of ~filename into a chunk of pages of (at most) 4KB. It returns the map of chunks and the hash of the @@ -87,4 +87,4 @@ val import : val chunkify : Sc_rollup.Kind.t -> source -> - string Sc_rollup.Reveal_hash.Map.t * Sc_rollup.Reveal_hash.t + (string Sc_rollup.Reveal_hash.Map.t * Sc_rollup.Reveal_hash.t) tzresult -- GitLab From 710adc6556bfc54983624d80549c3edd14fbf774 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 25 Oct 2022 16:30:49 +0200 Subject: [PATCH 17/21] SCORU/Node: add Start_of_level and End_of_level messages in simulation --- .../bin_sc_rollup_node/RPC_server.ml | 6 +- .../bin_sc_rollup_node/fueled_pvm.ml | 35 ++++-- .../bin_sc_rollup_node/simulation.ml | 113 ++++++++++++++---- .../bin_sc_rollup_node/simulation.mli | 16 ++- 4 files changed, 133 insertions(+), 37 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml index 97a3c4c9a2f4..004e24add74c 100644 --- a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml +++ b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml @@ -269,9 +269,13 @@ module Make (Interpreter : Interpreter.S) = struct let messages = List.map (fun m -> Sc_rollup.Inbox_message.External m) messages in - let* {state; inbox_level; _}, num_ticks = + let* sim, num_ticks_0 = Simulation.simulate_messages node_ctxt sim messages in + let* {state; inbox_level; _}, num_ticks_end = + Simulation.end_simulation node_ctxt sim + in + let num_ticks = Z.(num_ticks_0 + num_ticks_end) in let*! outbox = PVM.get_outbox state in let output = List.filter diff --git a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml index b43106d455b3..284ea35f6ab0 100644 --- a/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_alpha/bin_sc_rollup_node/fueled_pvm.ml @@ -48,17 +48,19 @@ module type S = sig (PVM.state * Z.t * Raw_level.t * fuel) Node_context.delayed_write tzresult Lwt.t - (** [eval_messages ?reveal_map ~fuel node_ctxt state + (** [eval_messages ?reveal_map ~fuel node_ctxt ~message_counter_offset state inbox_level messages] evaluates the [messages] for inbox level [inbox_level] in the given [state] of the PVM and returns the evaluation results containing the new state, the remaining fuel, and the number of - ticks for the evaluation of these messages. When + ticks for the evaluation of these messages. [message_counter_offset] is + used when we evaluate partial inboxes, such as during simulation. When [reveal_map] is provided, it is used as an additional source of data for revelation ticks. *) val eval_messages : ?reveal_map:string Sc_rollup.Reveal_hash.Map.t -> fuel:fuel -> _ Node_context.t -> + message_counter_offset:int -> PVM.state -> Raw_level.t -> Sc_rollup.Inbox_message.t list -> @@ -272,7 +274,8 @@ module Make (PVM : Pvm.S) = struct in return (state, tick) - let eval_messages ~reveal_map ~fuel node_ctxt state inbox_level messages = + let eval_messages ~reveal_map ~fuel node_ctxt ~message_counter_offset state + inbox_level messages = let open Lwt_result_syntax in let open Delayed_write_monad.Lwt_result_syntax in let level = Raw_level.to_int32 inbox_level |> Int32.to_int in @@ -283,22 +286,21 @@ module Make (PVM : Pvm.S) = struct Sc_rollup.Inbox_message.( message |> serialize |> Environment.wrap_tzresult) in - let input = - Sc_rollup. - {inbox_level; message_counter = Z.of_int message_counter; payload} - in + let message_index = message_counter_offset + message_counter in + let message_counter = Z.of_int message_index in + let input = Sc_rollup.{inbox_level; message_counter; payload} in let failing_ticks = Loser_mode.is_failure node_ctxt.Node_context.loser_mode ~level - ~message_index:message_counter + ~message_index in let>* state, executed_ticks = feed_input node_ctxt reveal_map level - message_counter + message_index ~fuel ~failing_ticks state @@ -335,18 +337,27 @@ module Make (PVM : Pvm.S) = struct ~reveal_map:None ~fuel node_ctxt + ~message_counter_offset:0 state inbox_level messages in return (state, num_messages, inbox_level, fuel) - let eval_messages ?reveal_map ~fuel node_ctxt state inbox_level messages = + let eval_messages ?reveal_map ~fuel node_ctxt ~message_counter_offset state + inbox_level messages = let open Lwt_result_syntax in let open Delayed_write_monad.Lwt_result_syntax in let*! initial_tick = PVM.get_tick state in - let*> state, remaining_fuel = - eval_messages ~reveal_map ~fuel node_ctxt state inbox_level messages + let>* state, remaining_fuel = + eval_messages + ~reveal_map + ~fuel + node_ctxt + ~message_counter_offset + state + inbox_level + messages in let*! final_tick = PVM.get_tick state in let num_ticks = Sc_rollup.Tick.distance initial_tick final_tick in diff --git a/src/proto_alpha/bin_sc_rollup_node/simulation.ml b/src/proto_alpha/bin_sc_rollup_node/simulation.ml index 0284385c6e6c..e092917bb390 100644 --- a/src/proto_alpha/bin_sc_rollup_node/simulation.ml +++ b/src/proto_alpha/bin_sc_rollup_node/simulation.ml @@ -32,12 +32,16 @@ module type S = sig module PVM = Interpreter.PVM module Fueled_pvm = Interpreter.Free_pvm + type level_position = Start | Middle | End + type t = { ctxt : Context.ro; inbox_level : Raw_level.t; - nb_messages : int64; state : PVM.state; reveal_map : string Sc_rollup.Reveal_hash.Map.t option; + nb_messages_period : int64; + nb_messages_inbox : int; + level_position : level_position; } val start_simulation : @@ -51,6 +55,8 @@ module type S = sig t -> Sc_rollup.Inbox_message.t list -> (t * Z.t) tzresult Lwt.t + + val end_simulation : Node_context.ro -> t -> (t * Z.t) tzresult Lwt.t end module Make (Interpreter : Interpreter.S) : @@ -59,19 +65,29 @@ module Make (Interpreter : Interpreter.S) : module PVM = Interpreter.PVM module Fueled_pvm = Interpreter.Free_pvm + type level_position = Start | Middle | End + type t = { ctxt : Context.ro; inbox_level : Raw_level.t; - nb_messages : int64; state : PVM.state; reveal_map : string Sc_rollup.Reveal_hash.Map.t option; + nb_messages_period : int64; + nb_messages_inbox : int; + level_position : level_position; } let start_simulation node_ctxt ~reveal_map (Layer1.{hash; level} as head) = let open Lwt_result_syntax in let*? level = Environment.wrap_tzresult @@ Raw_level.of_int32 level in + let*? () = + error_unless + Raw_level.(level >= node_ctxt.Node_context.genesis_info.level) + (Exn (Failure "Cannot simulate before origination level")) + in + let first_inbox_level = Raw_level.succ node_ctxt.genesis_info.level in let* ctxt = - if Raw_level.(level <= node_ctxt.Node_context.genesis_info.level) then + if Raw_level.(level < first_inbox_level) then (* This is before we have interpreted the boot sector, so we start with an empty context in genesis *) return (Context.empty node_ctxt.context) @@ -79,45 +95,102 @@ module Make (Interpreter : Interpreter.S) : in let* inbox = Inbox.inbox_of_head node_ctxt head in let+ ctxt, state = Interpreter.state_of_head node_ctxt ctxt head in - let nb_messages = + let nb_messages_period = Sc_rollup.Inbox.number_of_messages_during_commitment_period inbox in let inbox_level = Raw_level.succ level in - {ctxt; nb_messages; state; inbox_level; reveal_map} + { + ctxt; + state; + inbox_level; + reveal_map; + nb_messages_period; + nb_messages_inbox = 0; + level_position = Start; + } + + let simulate_messages_no_checks (node_ctxt : Node_context.ro) + ({ + ctxt; + state; + inbox_level; + reveal_map; + nb_messages_period; + nb_messages_inbox; + level_position = _; + } as sim) messages = + let open Lwt_result_syntax in + (* Build new state *) + let* eval_result = + Fueled_pvm.eval_messages + ?reveal_map + ~fuel:(Fuel.Free.of_ticks 0L) + node_ctxt + ~message_counter_offset:nb_messages_inbox + state + inbox_level + messages + in + let Fueled_pvm.{state; num_ticks; _} = + Delayed_write_monad.ignore eval_result + in + let*! ctxt = PVM.State.set ctxt state in + let nb_messages = List.length messages in + let nb_messages_inbox = nb_messages_inbox + nb_messages in + let nb_messages_period = + Int64.add nb_messages_period (Int64.of_int nb_messages) + in + return + ({sim with ctxt; state; nb_messages_period; nb_messages_inbox}, num_ticks) - let simulate_messages (node_ctxt : Node_context.ro) - {ctxt; nb_messages; state; inbox_level; reveal_map} messages = + let simulate_messages (node_ctxt : Node_context.ro) sim messages = let open Lwt_result_syntax in (* Build new inbox *) + let*? () = + error_when + (sim.level_position = End) + (Exn (Failure "Level for simulation is ended")) + in let*? () = error_when (messages = []) (Environment.wrap_tzerror Sc_rollup_errors.Sc_rollup_add_zero_messages) in + let messages = + if sim.level_position = Start then + Sc_rollup.Inbox_message.Internal Start_of_level :: messages + else messages + in let max_messages = node_ctxt.protocol_constants.parametric.sc_rollup .max_number_of_messages_per_commitment_period |> Int64.of_int + |> Int64.pred (* To account for End_of_level message *) in - let nb_messages = - Int64.add nb_messages (Int64.of_int (List.length messages)) + let nb_messages_period = + Int64.add sim.nb_messages_period (Int64.of_int (List.length messages)) in let*? () = error_when - Compare.Int64.(nb_messages > max_messages) + Compare.Int64.(nb_messages_period > max_messages) (Environment.wrap_tzerror Sc_rollup_errors .Sc_rollup_max_number_of_messages_reached_for_commitment_period) in - (* Build new state *) - let* Fueled_pvm.{state; num_ticks; _} = - Fueled_pvm.eval_messages - ?reveal_map - ~fuel:(Fuel.Free.of_ticks 0L) + let+ sim, num_ticks = simulate_messages_no_checks node_ctxt sim messages in + ({sim with level_position = Middle}, num_ticks) + + let end_simulation node_ctxt sim = + let open Lwt_result_syntax in + let*? () = + error_when + (sim.level_position = End) + (Exn (Failure "Level for simulation is ended")) + in + let+ sim, num_ticks = + simulate_messages_no_checks node_ctxt - state - inbox_level - messages + sim + [Sc_rollup.Inbox_message.Internal End_of_level] in - let*! ctxt = PVM.State.set ctxt state in - return ({ctxt; nb_messages; state; inbox_level; reveals_map}, num_ticks) + ({sim with level_position = End}, num_ticks) end diff --git a/src/proto_alpha/bin_sc_rollup_node/simulation.mli b/src/proto_alpha/bin_sc_rollup_node/simulation.mli index 4d411352c466..9d25bceb4ebd 100644 --- a/src/proto_alpha/bin_sc_rollup_node/simulation.mli +++ b/src/proto_alpha/bin_sc_rollup_node/simulation.mli @@ -31,24 +31,28 @@ module type S = sig module PVM = Interpreter.PVM module Fueled_pvm = Interpreter.Free_pvm + type level_position = Start | Middle | End + (** Type of the state for a simulation. *) type t = { ctxt : Context.ro; inbox_level : Raw_level.t; - nb_messages : int64; state : PVM.state; reveal_map : string Sc_rollup.Reveal_hash.Map.t option; + nb_messages_period : int64; + nb_messages_inbox : int; + level_position : level_position; } - (** [start_simulation node_ctxt block] starts a new simulation {e on top} of - [block], i.e. for an hypothetical new inbox (level). *) + (** [start_simulation node_ctxt reveal_source block] starts a new simulation + {e on top} of [block], i.e. for an hypothetical new inbox (level). *) val start_simulation : Node_context.ro -> reveal_map:string Sc_rollup.Reveal_hash.Map.t option -> Layer1.head -> t tzresult Lwt.t - (** [simulate_messages node_ctxt ?fuel sim messages] runs a simulation of new + (** [simulate_messages node_ctxt sim messages] runs a simulation of new [messages] in the given simulation (state) [sim] and returns a new simulation state, the remaining fuel (when [?fuel] is provided) and the number of ticks that happened. *) @@ -57,6 +61,10 @@ module type S = sig t -> Sc_rollup.Inbox_message.t list -> (t * Z.t) tzresult Lwt.t + + (** [end_simulation node_ctxt sim] adds and [End_of_level] message and marks + the simulation as ended. *) + val end_simulation : Node_context.ro -> t -> (t * Z.t) tzresult Lwt.t end (** Functor to construct a simulator for a given PVM with interpreter. *) -- GitLab From 522db77e2a5cd8338ce53a04192dc3df3b92e2a1 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 29 Sep 2022 18:04:05 +0200 Subject: [PATCH 18/21] Tezt/Tezos: call simulation of messages for sc rollup client --- tezt/lib_tezos/sc_rollup_client.ml | 52 +++++++++++++++++++++++++++++ tezt/lib_tezos/sc_rollup_client.mli | 20 +++++++++++ 2 files changed, 72 insertions(+) diff --git a/tezt/lib_tezos/sc_rollup_client.ml b/tezt/lib_tezos/sc_rollup_client.ml index 7c76ef36a5cf..cdd1e2134b28 100644 --- a/tezt/lib_tezos/sc_rollup_client.ml +++ b/tezt/lib_tezos/sc_rollup_client.ml @@ -40,6 +40,14 @@ type commitment = { type slot_header = {level : int; commitment : string; index : int} +type simulation_result = { + state_hash : string; + status : string; + output : JSON.t; + inbox_level : int; + num_ticks : int; +} + let commitment_from_json json = if JSON.is_null json then None else @@ -181,6 +189,16 @@ let rpc_get ?hooks sc_client path = let* output = Process.check_and_read_stdout process in return (JSON.parse ~origin:(Client.string_of_path path ^ " response") output) +let rpc_post ?hooks sc_client path data = + let process = + spawn_command + ?hooks + sc_client + ["rpc"; "post"; Client.string_of_path path; "with"; JSON.encode data] + in + let* output = Process.check_and_read_stdout process in + return (JSON.parse ~origin:(Client.string_of_path path ^ " response") output) + let ticks ?hooks ?(block = "head") sc_client = let open Lwt.Syntax in let+ res = rpc_get ?hooks sc_client ["global"; "block"; block; "ticks"] in @@ -252,6 +270,40 @@ let dal_downloaded_confirmed_slot_pages ?hooks ?(block = "head") sc_client = in (index, contents)) +let simulate ?hooks ?(block = "head") sc_client ?(reveal_pages = []) messages = + let open Lwt.Syntax in + let messages_json = + `A (List.map (fun s -> `String Hex.(of_string s |> show)) messages) + in + let reveal_json = + match reveal_pages with + | [] -> [] + | pages -> + [ + ( "reveal_pages", + `A (List.map (fun s -> `String Hex.(of_string s |> show)) pages) ); + ] + in + let data = + `O (("messages", messages_json) :: reveal_json) + |> JSON.annotate ~origin:"simulation data" + in + let+ obj = + rpc_post + ?hooks + sc_client + ["global"; "block"; block; "simulate"] + data + in + JSON. + { + state_hash = obj |> get "state_hash" |> as_string; + status = obj |> get "status" |> as_string; + output = obj |> get "output"; + inbox_level = obj |> get "inbox_level" |> as_int; + num_ticks = obj |> get "num_ticks" |> as_string |> int_of_string; + } + let spawn_generate_keys ?hooks ?(force = false) ~alias sc_client = spawn_command ?hooks diff --git a/tezt/lib_tezos/sc_rollup_client.mli b/tezt/lib_tezos/sc_rollup_client.mli index eb234144053c..519f6a52dd73 100644 --- a/tezt/lib_tezos/sc_rollup_client.mli +++ b/tezt/lib_tezos/sc_rollup_client.mli @@ -35,6 +35,14 @@ type commitment = { type slot_header = {level : int; commitment : string; index : int} +type simulation_result = { + state_hash : string; + status : string; + output : JSON.t; + inbox_level : int; + num_ticks : int; +} + (** [create ?name ?path ?base_dir ?path node] returns a fresh client identified by a specified [name], logging in [color], executing the program at [path], storing local information in [base_dir], and @@ -142,6 +150,18 @@ val dal_slot_headers : val dal_downloaded_confirmed_slot_pages : ?hooks:Process.hooks -> ?block:string -> t -> (int * string list) list Lwt.t +(** [simulate ?block client ?reveal_pages messages] simulates the evaluation of + input [messages] for the rollup PVM at [block] (default + ["head"]). [reveal_pages] can be used to provide data to be used for the + revelation ticks. *) +val simulate : + ?hooks:Process_hooks.t -> + ?block:string -> + t -> + ?reveal_pages:string list -> + string list -> + simulation_result Lwt.t + (** [generate_keys ~alias client] generates new unencrypted keys for [alias]. *) val generate_keys : ?hooks:Process.hooks -> ?force:bool -> alias:string -> t -> unit Lwt.t -- GitLab From 0245595120b08cc4cb6a970129cc959c0a7cbaba Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 29 Sep 2022 18:04:28 +0200 Subject: [PATCH 19/21] Test/Tezt: test simulation of messages with RPC --- tezt/tests/sc_rollup.ml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index 2bf5f087b206..4722dc7dd482 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -787,6 +787,32 @@ let sc_rollup_node_handles_chain_reorg sc_rollup_node _rollup_client _sc_rollup let* _ = Sc_rollup_node.wait_for_level ~timeout:3. sc_rollup_node 5 in unit +(* Simulation of messages *) +let sc_rollup_node_simulate sc_rollup_node sc_rollup_client _sc_rollup node + client = + let level = Node.get_level node in + let* () = Sc_rollup_node.run sc_rollup_node [] in + let msg1 = "3 3 + out" in + let* sim_result = + Sc_rollup_client.simulate + sc_rollup_client + ~reveal_pages: + (List.init 12 (fun j -> + String.init 1024 (fun i -> Char.chr (i * j mod 256)))) + [msg1] + in + let* () = send_message client (to_text_messages_arg [msg1]) in + let* _ = + Sc_rollup_node.wait_for_level ~timeout:3. sc_rollup_node (level + 1) + in + let* real_state_hash = Sc_rollup_client.state_hash sc_rollup_client in + let* real_outbox = Sc_rollup_client.outbox sc_rollup_client ~block:"head" in + Check.((sim_result.state_hash = real_state_hash) string) + ~error_msg:"Simulated resulting state hash is %L but should have been %R" ; + Check.((JSON.encode sim_result.output = real_outbox) string) + ~error_msg:"Simulated resulting outbox is %L but should have been %R" ; + return () + (* One can retrieve the list of originated SCORUs. ----------------------------------------------- *) @@ -3249,6 +3275,11 @@ let register ~kind ~protocols = ~variant:"handles_chain_reorg" sc_rollup_node_handles_chain_reorg protocols ; + test_rollup_inbox_of_rollup_node + ~kind + "simulation" + sc_rollup_node_simulate + protocols ; test_rollup_node_boots_into_initial_state protocols ~kind ; test_rollup_node_advances_pvm_state protocols ~kind ~internal:false ; test_rollup_node_advances_pvm_state protocols ~kind ~internal:true ; -- GitLab From 0097254e910872252c6b9317da5b208e43f960f8 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 30 Sep 2022 17:02:12 +0200 Subject: [PATCH 20/21] Tezt/tezos: use Runnable in Sc_rollup_client --- tezt/lib_tezos/sc_rollup_client.ml | 193 ++++++++++++---------------- tezt/lib_tezos/sc_rollup_client.mli | 49 +++++-- tezt/long_tests/sc_rollup.ml | 10 +- tezt/tests/dal.ml | 8 +- tezt/tests/sc_rollup.ml | 100 +++++++------- 5 files changed, 179 insertions(+), 181 deletions(-) diff --git a/tezt/lib_tezos/sc_rollup_client.ml b/tezt/lib_tezos/sc_rollup_client.ml index cdd1e2134b28..b6d73983ef52 100644 --- a/tezt/lib_tezos/sc_rollup_client.ml +++ b/tezt/lib_tezos/sc_rollup_client.ml @@ -23,6 +23,8 @@ (* *) (*****************************************************************************) +open Runnable.Syntax + type t = { name : string; path : string; @@ -95,29 +97,27 @@ let endpoint_arg sc_client = ["--endpoint"; Sc_rollup_node.endpoint sc_client.sc_node] let spawn_command ?hooks sc_client command = - Process.spawn - ~name:sc_client.name - ~color:sc_client.color - ?hooks - sc_client.path - (base_dir_arg sc_client @ endpoint_arg sc_client @ command) + let process = + Process.spawn + ~name:sc_client.name + ~color:sc_client.color + ?hooks + sc_client.path + (base_dir_arg sc_client @ endpoint_arg sc_client @ command) + in + Runnable.{value = process; run = Process.check_and_read_stdout} let sc_rollup_address ?hooks sc_client = - let* out = - spawn_command ?hooks sc_client ["get"; "sc"; "rollup"; "address"] - |> Process.check_and_read_stdout - in - return (String.trim out) + spawn_command ?hooks sc_client ["get"; "sc"; "rollup"; "address"] + |> Runnable.map String.trim let state_value ?hooks ?(block = "head") sc_client ~key = - let* out = - spawn_command - ?hooks - sc_client - ["get"; "state"; "value"; "for"; key; "--block"; block] - |> Process.check_and_read_stdout - in - return (Scanf.sscanf (String.trim out) "%S" (fun s -> s) |> String.to_bytes) + spawn_command + ?hooks + sc_client + ["get"; "state"; "value"; "for"; key; "--block"; block] + |> Runnable.map @@ fun out -> + Scanf.sscanf (String.trim out) "%S" (fun s -> s) |> String.to_bytes type transaction = { destination : string; @@ -141,7 +141,7 @@ type outbox_proof = {commitment_hash : string; proof : string} let outbox_proof_batch ?hooks ?expected_error sc_client ~message_index ~outbox_level batch = - let process = + let*? process = spawn_command ?hooks sc_client @@ -183,95 +183,75 @@ let outbox_proof_single ?hooks ?expected_error ?entrypoint sc_client [{destination; entrypoint; parameters}] let rpc_get ?hooks sc_client path = - let process = - spawn_command ?hooks sc_client ["rpc"; "get"; Client.string_of_path path] - in - let* output = Process.check_and_read_stdout process in - return (JSON.parse ~origin:(Client.string_of_path path ^ " response") output) + spawn_command ?hooks sc_client ["rpc"; "get"; Client.string_of_path path] + |> Runnable.map @@ fun output -> + JSON.parse ~origin:(Client.string_of_path path ^ " response") output let rpc_post ?hooks sc_client path data = - let process = - spawn_command - ?hooks - sc_client - ["rpc"; "post"; Client.string_of_path path; "with"; JSON.encode data] - in - let* output = Process.check_and_read_stdout process in - return (JSON.parse ~origin:(Client.string_of_path path ^ " response") output) + spawn_command + ?hooks + sc_client + ["rpc"; "post"; Client.string_of_path path; "with"; JSON.encode data] + |> Runnable.map @@ fun output -> + JSON.parse ~origin:(Client.string_of_path path ^ " response") output let ticks ?hooks ?(block = "head") sc_client = - let open Lwt.Syntax in - let+ res = rpc_get ?hooks sc_client ["global"; "block"; block; "ticks"] in - JSON.as_int res + let res = rpc_get ?hooks sc_client ["global"; "block"; block; "ticks"] in + Runnable.map JSON.as_int res let total_ticks ?hooks ?(block = "head") sc_client = - let open Lwt.Syntax in - let+ res = - rpc_get ?hooks sc_client ["global"; "block"; block; "total_ticks"] - in - JSON.as_int res + rpc_get ?hooks sc_client ["global"; "block"; block; "total_ticks"] + |> Runnable.map JSON.as_int let state_hash ?hooks ?(block = "head") sc_client = - let open Lwt.Syntax in - let+ res = - rpc_get ?hooks sc_client ["global"; "block"; block; "state_hash"] - in - JSON.as_string res + rpc_get ?hooks sc_client ["global"; "block"; block; "state_hash"] + |> Runnable.map JSON.as_string let status ?hooks ?(block = "head") sc_client = - let open Lwt.Syntax in - let+ res = rpc_get ?hooks sc_client ["global"; "block"; block; "status"] in - JSON.as_string res + rpc_get ?hooks sc_client ["global"; "block"; block; "status"] + |> Runnable.map JSON.as_string let outbox ?hooks ?(block = "cemented") sc_client = - let open Lwt.Syntax in - let+ res = rpc_get ?hooks sc_client ["global"; "block"; block; "outbox"] in - JSON.encode res + rpc_get ?hooks sc_client ["global"; "block"; block; "outbox"] let last_stored_commitment ?hooks sc_client = - let open Lwt.Syntax in - let+ json = rpc_get ?hooks sc_client ["global"; "last_stored_commitment"] in - commitment_with_hash_and_level_from_json json + rpc_get ?hooks sc_client ["global"; "last_stored_commitment"] + |> Runnable.map commitment_with_hash_and_level_from_json let last_published_commitment ?hooks sc_client = - let open Lwt.Syntax in - let+ json = rpc_get ?hooks sc_client ["local"; "last_published_commitment"] in - commitment_with_hash_and_level_from_json json + rpc_get ?hooks sc_client ["local"; "last_published_commitment"] + |> Runnable.map commitment_with_hash_and_level_from_json let dal_slot_headers ?hooks ?(block = "head") sc_client = - let open Lwt.Syntax in - let+ json = - rpc_get ?hooks sc_client ["global"; "block"; block; "dal"; "slot_headers"] - in - JSON.( - as_list json - |> List.map (fun obj -> - { - level = obj |> get "level" |> as_int; - commitment = obj |> get "commitment" |> as_string; - index = obj |> get "index" |> as_int; - })) + rpc_get ?hooks sc_client ["global"; "block"; block; "dal"; "slot_headers"] + |> Runnable.map (fun json -> + JSON.( + as_list json + |> List.map (fun obj -> + { + level = obj |> get "level" |> as_int; + commitment = obj |> get "commitment" |> as_string; + index = obj |> get "index" |> as_int; + }))) let dal_downloaded_confirmed_slot_pages ?hooks ?(block = "head") sc_client = - let open Lwt.Syntax in - let+ json = - rpc_get - ?hooks - sc_client - ["global"; "block"; block; "dal"; "confirmed_slot_pages"] - in - JSON.as_list json - |> List.map (fun obj -> - let index = obj |> JSON.get "index" |> JSON.as_int in - let contents = - obj |> JSON.get "contents" |> JSON.as_list - |> List.map (fun page -> - page |> JSON.as_string |> fun s -> Hex.to_string (`Hex s)) - in - (index, contents)) + rpc_get + ?hooks + sc_client + ["global"; "block"; block; "dal"; "confirmed_slot_pages"] + |> Runnable.map (fun json -> + JSON.as_list json + |> List.map (fun obj -> + let index = obj |> JSON.get "index" |> JSON.as_int in + let contents = + obj |> JSON.get "contents" |> JSON.as_list + |> List.map (fun page -> + page |> JSON.as_string |> fun s -> + Hex.to_string (`Hex s)) + in + (index, contents))) let simulate ?hooks ?(block = "head") sc_client ?(reveal_pages = []) messages = - let open Lwt.Syntax in let messages_json = `A (List.map (fun s -> `String Hex.(of_string s |> show)) messages) in @@ -288,21 +268,16 @@ let simulate ?hooks ?(block = "head") sc_client ?(reveal_pages = []) messages = `O (("messages", messages_json) :: reveal_json) |> JSON.annotate ~origin:"simulation data" in - let+ obj = - rpc_post - ?hooks - sc_client - ["global"; "block"; block; "simulate"] - data - in - JSON. - { - state_hash = obj |> get "state_hash" |> as_string; - status = obj |> get "status" |> as_string; - output = obj |> get "output"; - inbox_level = obj |> get "inbox_level" |> as_int; - num_ticks = obj |> get "num_ticks" |> as_string |> int_of_string; - } + rpc_post ?hooks sc_client ["global"; "block"; block; "simulate"] data + |> Runnable.map (fun obj -> + JSON. + { + state_hash = obj |> get "state_hash" |> as_string; + status = obj |> get "status" |> as_string; + output = obj |> get "output"; + inbox_level = obj |> get "inbox_level" |> as_int; + num_ticks = obj |> get "num_ticks" |> as_string |> int_of_string; + }) let spawn_generate_keys ?hooks ?(force = false) ~alias sc_client = spawn_command @@ -311,7 +286,8 @@ let spawn_generate_keys ?hooks ?(force = false) ~alias sc_client = (["gen"; "unencrypted"; "keys"; alias] @ if force then ["--force"] else []) let generate_keys ?hooks ?force ~alias sc_client = - spawn_generate_keys ?hooks ?force ~alias sc_client |> Process.check + let*? process = spawn_generate_keys ?hooks ?force ~alias sc_client in + Process.check process let spawn_list_keys ?hooks sc_client = spawn_command ?hooks sc_client ["list"; "keys"] @@ -334,18 +310,14 @@ let parse_list_keys output = | Some l -> l let list_keys ?hooks sc_client = - let* out = - spawn_list_keys ?hooks sc_client |> Process.check_and_read_stdout - in + let*! out = spawn_list_keys ?hooks sc_client in return (parse_list_keys out) let spawn_show_address ?hooks ~alias sc_client = spawn_command ?hooks sc_client ["show"; "address"; alias] let show_address ?hooks ~alias sc_client = - let* out = - spawn_show_address ?hooks ~alias sc_client |> Process.check_and_read_stdout - in + let*! out = spawn_show_address ?hooks ~alias sc_client in return (Account.parse_client_output_aggregate ~alias ~client_output:out) let spawn_import_secret_key ?hooks ?(force = false) @@ -361,4 +333,5 @@ let spawn_import_secret_key ?hooks ?(force = false) @ if force then ["--force"] else []) let import_secret_key ?hooks ?force key sc_client = - spawn_import_secret_key ?hooks ?force key sc_client |> Process.check + let*? process = spawn_import_secret_key ?hooks ?force key sc_client in + Process.check process diff --git a/tezt/lib_tezos/sc_rollup_client.mli b/tezt/lib_tezos/sc_rollup_client.mli index 519f6a52dd73..8d0e005dbd54 100644 --- a/tezt/lib_tezos/sc_rollup_client.mli +++ b/tezt/lib_tezos/sc_rollup_client.mli @@ -57,35 +57,48 @@ val create : (** [sc_rollup_address client] returns the smart contract rollup address of the node associated to the [client]. *) -val sc_rollup_address : ?hooks:Process.hooks -> t -> string Lwt.t +val sc_rollup_address : ?hooks:Process.hooks -> t -> string Runnable.process (** [rpc_get client path] issues a GET request for [path]. *) -val rpc_get : ?hooks:Process.hooks -> t -> Client.path -> JSON.t Lwt.t +val rpc_get : + ?hooks:Process.hooks -> t -> Client.path -> JSON.t Runnable.process + +(** [rpc_post client path data] issues a POST request for [path] with [data]. *) +val rpc_post : + ?hooks:Process.hooks -> t -> Client.path -> JSON.t -> JSON.t Runnable.process (** [total_ticks ?block client] gets the total number of ticks for the PVM. *) -val total_ticks : ?hooks:Process.hooks -> ?block:string -> t -> int Lwt.t +val total_ticks : + ?hooks:Process.hooks -> ?block:string -> t -> int Runnable.process (** [ticks ?block client] gets the number of ticks for the PVM for the [block] (default ["head"]). *) -val ticks : ?hooks:Process.hooks -> ?block:string -> t -> int Lwt.t +val ticks : ?hooks:Process.hooks -> ?block:string -> t -> int Runnable.process (** [state_hash ?block client] gets the corresponding PVM state hash for the [block] (default ["head"]). *) -val state_hash : ?hooks:Process.hooks -> ?block:string -> t -> string Lwt.t +val state_hash : + ?hooks:Process.hooks -> ?block:string -> t -> string Runnable.process (** [state_value ?block client key] gets the corresponding PVM state value mapped to [key] for the [block] (default ["head"]). *) val state_value : - ?hooks:Process.hooks -> ?block:string -> t -> key:string -> bytes Lwt.t + ?hooks:Process.hooks -> + ?block:string -> + t -> + key:string -> + bytes Runnable.process (** [status ?block client] gets the corresponding PVM status for the [block] (default ["head"]). *) -val status : ?hooks:Process.hooks -> ?block:string -> t -> string Lwt.t +val status : + ?hooks:Process.hooks -> ?block:string -> t -> string Runnable.process (** [outbox ?block client] gets the rollup outbox for the [block] (default ["cemented"] which is the block corresponding to the last cemented level). *) -val outbox : ?hooks:Process.hooks -> ?block:string -> t -> string Lwt.t +val outbox : + ?hooks:Process.hooks -> ?block:string -> t -> JSON.t Runnable.process type outbox_proof = {commitment_hash : string; proof : string} @@ -133,22 +146,32 @@ val commitment_with_hash_and_level_from_json : (** [last_stored_commitment client] gets the last commitment with its hash stored by the rollup node. *) val last_stored_commitment : - ?hooks:Process.hooks -> t -> (string * commitment * int option) option Lwt.t + ?hooks:Process.hooks -> + t -> + (string * commitment * int option) option Runnable.process (** [last_published_commitment client] gets the last commitment published by the rollup node, with its hash and level when the commitment was first published. *) val last_published_commitment : - ?hooks:Process.hooks -> t -> (string * commitment * int option) option Lwt.t + ?hooks:Process.hooks -> + t -> + (string * commitment * int option) option Runnable.process (** [dal_slot_headers ?block client] returns the dal slot headers of the [block] (default ["head"]). *) val dal_slot_headers : - ?hooks:Process.hooks -> ?block:string -> t -> slot_header list Lwt.t + ?hooks:Process.hooks -> + ?block:string -> + t -> + slot_header list Runnable.process (** [dal_downloaded_confirmed_slot_pages ?block client] returns the confirmed slots downloaded after processing the [block] (default ["head"]). *) val dal_downloaded_confirmed_slot_pages : - ?hooks:Process.hooks -> ?block:string -> t -> (int * string list) list Lwt.t + ?hooks:Process.hooks -> + ?block:string -> + t -> + (int * string list) list Runnable.process (** [simulate ?block client ?reveal_pages messages] simulates the evaluation of input [messages] for the rollup PVM at [block] (default @@ -160,7 +183,7 @@ val simulate : t -> ?reveal_pages:string list -> string list -> - simulation_result Lwt.t + simulation_result Runnable.process (** [generate_keys ~alias client] generates new unencrypted keys for [alias]. *) val generate_keys : diff --git a/tezt/long_tests/sc_rollup.ml b/tezt/long_tests/sc_rollup.ml index 1a89c281bd9f..5a619152e60a 100644 --- a/tezt/long_tests/sc_rollup.ml +++ b/tezt/long_tests/sc_rollup.ml @@ -192,10 +192,10 @@ let test_rollup_node_advances_pvm_state protocols ~test_name ~boot_sector in (* Called with monotonically increasing [i] *) let test_message i = - let* prev_state_hash = + let*! prev_state_hash = Sc_rollup_client.state_hash ~hooks sc_rollup_client in - let* prev_ticks = Sc_rollup_client.total_ticks ~hooks sc_rollup_client in + let*! prev_ticks = Sc_rollup_client.total_ticks ~hooks sc_rollup_client in let message = sf "%d %d + value" i ((i + 2) * 2) in let* () = match forwarder with @@ -220,7 +220,7 @@ let test_rollup_node_advances_pvm_state protocols ~test_name ~boot_sector let* () = match kind with | "arith" -> - let* encoded_value = + let*! encoded_value = Sc_rollup_client.state_value ~hooks sc_rollup_client @@ -254,12 +254,12 @@ let test_rollup_node_advances_pvm_state protocols ~test_name ~boot_sector | _otherwise -> raise (Invalid_argument kind) in - let* state_hash = Sc_rollup_client.state_hash ~hooks sc_rollup_client in + let*! state_hash = Sc_rollup_client.state_hash ~hooks sc_rollup_client in Check.(state_hash <> prev_state_hash) Check.string ~error_msg:"State hash has not changed (%L <> %R)" ; - let* ticks = Sc_rollup_client.total_ticks ~hooks sc_rollup_client in + let*! ticks = Sc_rollup_client.total_ticks ~hooks sc_rollup_client in Check.(ticks >= prev_ticks) Check.int ~error_msg:"Tick counter did not advance (%L >= %R)" ; diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index ad20b9c3da5f..847eb04695ec 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -977,7 +977,7 @@ let rollup_node_stores_dal_slots ?expand_test _protocol dal_node sc_rollup_node let* slots_published_level = Sc_rollup_node.wait_for_level sc_rollup_node (init_level + 1) in - let* slots_headers = + let*! slots_headers = Sc_rollup_client.dal_slot_headers ~hooks sc_rollup_client in let commitments = @@ -1016,7 +1016,7 @@ let rollup_node_stores_dal_slots ?expand_test _protocol dal_node sc_rollup_node "Current level has moved past slot attestation level (current = %L, \ expected = %R)" ; (* 7. Check that no slots have been downloaded *) - let* downloaded_confirmed_slots = + let*! downloaded_confirmed_slots = Sc_rollup_client.dal_downloaded_confirmed_slot_pages ~hooks sc_rollup_client in let expected_number_of_downloaded_or_unconfirmed_slots = 0 in @@ -1047,7 +1047,7 @@ let rollup_node_stores_dal_slots ?expand_test _protocol dal_node sc_rollup_node expected = %R)" ; (* 9. Wait for the rollup node to download the attested slots. *) let confirmed_level_as_string = Int.to_string slot_confirmed_level in - let* downloaded_confirmed_slots = + let*! downloaded_confirmed_slots = Sc_rollup_client.dal_downloaded_confirmed_slot_pages ~block:confirmed_level_as_string sc_rollup_client @@ -1145,7 +1145,7 @@ let rollup_node_interprets_dal_pages client sc_rollup sc_rollup_node = let* _lvl = Sc_rollup_node.wait_for_level ~timeout:120. sc_rollup_node (level + 1) in - let* encoded_value = + let*! encoded_value = Sc_rollup_client.state_value ~hooks sc_rollup_client ~key:"vars/value" in match Data_encoding.(Binary.of_bytes int31) @@ encoded_value with diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index 4722dc7dd482..cc7afd58c355 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -421,7 +421,7 @@ let test_rollup_node_running ~kind = failwith "Please install curl" | Some sc_rollup_from_rpc -> JSON.as_string sc_rollup_from_rpc in - let* sc_rollup_from_client = + let*! sc_rollup_from_client = Sc_rollup_client.sc_rollup_address ~hooks rollup_client in if sc_rollup_from_rpc <> sc_rollup then @@ -793,7 +793,7 @@ let sc_rollup_node_simulate sc_rollup_node sc_rollup_client _sc_rollup node let level = Node.get_level node in let* () = Sc_rollup_node.run sc_rollup_node [] in let msg1 = "3 3 + out" in - let* sim_result = + let*! sim_result = Sc_rollup_client.simulate sc_rollup_client ~reveal_pages: @@ -805,11 +805,11 @@ let sc_rollup_node_simulate sc_rollup_node sc_rollup_client _sc_rollup node let* _ = Sc_rollup_node.wait_for_level ~timeout:3. sc_rollup_node (level + 1) in - let* real_state_hash = Sc_rollup_client.state_hash sc_rollup_client in - let* real_outbox = Sc_rollup_client.outbox sc_rollup_client ~block:"head" in + let*! real_state_hash = Sc_rollup_client.state_hash sc_rollup_client in + let*! real_outbox = Sc_rollup_client.outbox sc_rollup_client ~block:"head" in Check.((sim_result.state_hash = real_state_hash) string) ~error_msg:"Simulated resulting state hash is %L but should have been %R" ; - Check.((JSON.encode sim_result.output = real_outbox) string) + Check.((JSON.encode sim_result.output = JSON.encode real_outbox) string) ~error_msg:"Simulated resulting outbox is %L but should have been %R" ; return () @@ -876,12 +876,12 @@ let test_rollup_node_boots_into_initial_state ~kind = Check.int ~error_msg:"Current level has moved past origination level (%L = %R)" ; - let* ticks = Sc_rollup_client.total_ticks ~hooks sc_rollup_client in + let*! ticks = Sc_rollup_client.total_ticks ~hooks sc_rollup_client in Check.(ticks = 0) Check.int ~error_msg:"Unexpected initial tick count (%L = %R)" ; - let* status = Sc_rollup_client.status ~hooks sc_rollup_client in + let*! status = Sc_rollup_client.status ~hooks sc_rollup_client in let expected_status = match kind with | "arith" -> "Halted" @@ -939,10 +939,10 @@ let test_rollup_node_advances_pvm_state ?regression ~title ?boot_sector in (* Called with monotonically increasing [i] *) let test_message i = - let* prev_state_hash = + let*! prev_state_hash = Sc_rollup_client.state_hash ~hooks sc_rollup_client in - let* prev_ticks = Sc_rollup_client.total_ticks ~hooks sc_rollup_client in + let*! prev_ticks = Sc_rollup_client.total_ticks ~hooks sc_rollup_client in let message = sf "%d %d + value" i ((i + 2) * 2) in let* () = match forwarder with @@ -969,7 +969,7 @@ let test_rollup_node_advances_pvm_state ?regression ~title ?boot_sector let* () = match kind with | "arith" -> - let* encoded_value = + let*! encoded_value = Sc_rollup_client.state_value ~hooks sc_rollup_client @@ -1003,12 +1003,12 @@ let test_rollup_node_advances_pvm_state ?regression ~title ?boot_sector | _otherwise -> raise (Invalid_argument kind) in - let* state_hash = Sc_rollup_client.state_hash ~hooks sc_rollup_client in + let*! state_hash = Sc_rollup_client.state_hash ~hooks sc_rollup_client in Check.(state_hash <> prev_state_hash) Check.string ~error_msg:"State hash has not changed (%L <> %R)" ; - let* ticks = Sc_rollup_client.total_ticks ~hooks sc_rollup_client in + let*! ticks = Sc_rollup_client.total_ticks ~hooks sc_rollup_client in Check.(ticks >= prev_ticks) Check.int ~error_msg:"Tick counter did not advance (%L >= %R)" ; @@ -1187,7 +1187,7 @@ let commitment_stored sc_rollup_node sc_rollup_client sc_rollup _node client = sc_rollup_node store_commitment_level in - let* stored_commitment = + let*! stored_commitment = Sc_rollup_client.last_stored_commitment ~hooks sc_rollup_client in let stored_inbox_level = Option.map inbox_level stored_commitment in @@ -1197,7 +1197,7 @@ let commitment_stored sc_rollup_node sc_rollup_client sc_rollup _node client = "Commitment has been stored at a level different than expected (%L = %R)" ; (* Bake one level for commitment to be included *) let* () = Client.bake_for_and_wait client in - let* published_commitment = + let*! published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client in check_commitment_eq @@ -1254,17 +1254,19 @@ let mode_publish mode publishes sc_rollup_node sc_rollup_client sc_rollup node let* _ = Sc_rollup_node.wait_for_level sc_rollup_node level and* _ = Sc_rollup_node.wait_for_level sc_rollup_other_node level in Log.info "Both rollup nodes have reached level %d." level ; - let* state_hash = Sc_rollup_client.state_hash ~hooks sc_rollup_client - and* state_hash_other = + let state_hash = Sc_rollup_client.state_hash ~hooks sc_rollup_client + and state_hash_other = Sc_rollup_client.state_hash ~hooks sc_rollup_other_client in + let*! state_hash = state_hash in + let*! state_hash_other = state_hash_other in Check.((state_hash = state_hash_other) string) ~error_msg: "State hash of other rollup node is %R but the first rollup node has %L" ; - let* published_commitment = + let*! published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client in - let* other_published_commitment = + let*! other_published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_other_client in if published_commitment = None then @@ -1320,7 +1322,7 @@ let commitment_not_stored_if_non_final sc_rollup_node sc_rollup_client sc_rollup sc_rollup_node (store_commitment_level + levels_to_finalize) in - let* commitment = + let*! commitment = Sc_rollup_client.last_stored_commitment ~hooks sc_rollup_client in let stored_inbox_level = Option.map inbox_level commitment in @@ -1328,7 +1330,7 @@ let commitment_not_stored_if_non_final sc_rollup_node sc_rollup_client sc_rollup (Check.option Check.int) ~error_msg: "Commitment has been stored at a level different than expected (%L = %R)" ; - let* commitment = + let*! commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client in let published_inbox_level = Option.map inbox_level commitment in @@ -1389,7 +1391,7 @@ let commitments_messages_reset kind sc_rollup_node sc_rollup_client sc_rollup sc_rollup_node (init_level + (2 * levels_to_commitment) + block_finality_time) in - let* stored_commitment = + let*! stored_commitment = Sc_rollup_client.last_stored_commitment ~hooks sc_rollup_client in let stored_inbox_level = Option.map inbox_level stored_commitment in @@ -1412,7 +1414,7 @@ let commitments_messages_reset kind sc_rollup_node sc_rollup_client sc_rollup ~error_msg: "Number of ticks processed by commitment is different from the number \ of ticks expected (%L = %R)") ; - let* published_commitment = + let*! published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client in check_commitment_eq @@ -1494,10 +1496,10 @@ let commitment_stored_robust_to_failures sc_rollup_node sc_rollup_client sc_rollup_node' level_commitment_is_stored in - let* stored_commitment = + let*! stored_commitment = Sc_rollup_client.last_stored_commitment ~hooks sc_rollup_client in - let* stored_commitment' = + let*! stored_commitment' = Sc_rollup_client.last_stored_commitment ~hooks sc_rollup_client' in check_commitment_eq @@ -1604,7 +1606,7 @@ let commitments_reorgs ~kind sc_rollup_node sc_rollup_client sc_rollup node sc_rollup_node (init_level + levels_to_commitment + block_finality_time) in - let* stored_commitment = + let*! stored_commitment = Sc_rollup_client.last_stored_commitment ~hooks sc_rollup_client in let stored_inbox_level = Option.map inbox_level stored_commitment in @@ -1635,7 +1637,7 @@ let commitments_reorgs ~kind sc_rollup_node sc_rollup_client sc_rollup node ~error_msg: "Number of ticks processed by commitment is different from the number \ of ticks expected (%L = %R)") ; - let* published_commitment = + let*! published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client in check_commitment_eq @@ -1741,10 +1743,10 @@ let commitment_before_lcc_not_published sc_rollup_node sc_rollup_client sc_rollup_node (commitment_inbox_level + block_finality_time) in - let* rollup_node1_stored_commitment = + let*! rollup_node1_stored_commitment = Sc_rollup_client.last_stored_commitment ~hooks sc_rollup_client in - let* rollup_node1_published_commitment = + let*! rollup_node1_published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client in let () = @@ -1821,7 +1823,7 @@ let commitment_before_lcc_not_published sc_rollup_node sc_rollup_client Check.int ~error_msg:"Current level has moved past cementation inbox level (%L = %R)" ; (* Check that no commitment was published. *) - let* rollup_node2_last_published_commitment = + let*! rollup_node2_last_published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client' in let rollup_node2_last_published_commitment_inbox_level = @@ -1836,7 +1838,7 @@ let commitment_before_lcc_not_published sc_rollup_node sc_rollup_client in (* Check that the commitment stored by the second rollup node is the same commmitment stored by the first rollup node. *) - let* rollup_node2_stored_commitment = + let*! rollup_node2_stored_commitment = Sc_rollup_client.last_stored_commitment ~hooks sc_rollup_client' in let () = @@ -1858,7 +1860,7 @@ let commitment_before_lcc_not_published sc_rollup_node sc_rollup_client sc_rollup_node' (level_after_cementation + commitment_period) in - let* rollup_node2_last_published_commitment = + let*! rollup_node2_last_published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client' in let rollup_node2_last_published_commitment_inbox_level = @@ -1919,7 +1921,7 @@ let first_published_level_is_global sc_rollup_node sc_rollup_client sc_rollup sc_rollup_node (commitment_inbox_level + block_finality_time) in - let* rollup_node1_published_commitment = + let*! rollup_node1_published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client in Check.( @@ -1934,7 +1936,7 @@ let first_published_level_is_global sc_rollup_node sc_rollup_client sc_rollup let* commitment_publish_level = Sc_rollup_node.wait_for_level sc_rollup_node (commitment_finalized_level + 1) in - let* rollup_node1_published_commitment = + let*! rollup_node1_published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client in Check.( @@ -1967,7 +1969,7 @@ let first_published_level_is_global sc_rollup_node sc_rollup_client sc_rollup Check.int ~error_msg:"Current level has moved past cementation inbox level (%L = %R)" ; (* Check that no commitment was published. *) - let* rollup_node2_published_commitment = + let*! rollup_node2_published_commitment = Sc_rollup_client.last_published_commitment ~hooks sc_rollup_client' in check_commitment_eq @@ -2205,7 +2207,7 @@ let test_rollup_arith_uses_reveals ~kind = Sc_rollup_node.wait_for_level ~timeout:120. sc_rollup_node (level + 1) in - let* encoded_value = + let*! encoded_value = Sc_rollup_client.state_value ~hooks sc_rollup_client ~key:"vars/value" in let value = @@ -2469,7 +2471,7 @@ let test_refutation_scenario ?commitment_period ?challenge_window ~variant ~kind ~error_msg:"expecting loss for dishonest participant = %R, got %L") ; Log.info "Checking that we can still retrieve state from rollup node" ; (* This is a way to make sure the rollup node did not crash *) - let* _value = Sc_rollup_client.state_hash ~hooks sc_client1 in + let*! _value = Sc_rollup_client.state_hash ~hooks sc_client1 in unit let rec swap i l = @@ -3076,8 +3078,8 @@ let test_outbox_message_generic ?regression ?expected_error ~skip ~earliness repeat blocks_to_wait @@ fun () -> Client.bake_for client in let trigger_outbox_message_execution address = - let* outbox = Sc_rollup_client.outbox sc_client in - Log.info "Outbox is %s" outbox ; + let*! outbox = Sc_rollup_client.outbox sc_client in + Log.info "Outbox is %s" (JSON.encode outbox) ; let* answer = let message_index = 0 in let outbox_level = 4 in @@ -3163,7 +3165,7 @@ let test_rpcs ~kind = } @@ fun sc_rollup_node sc_client sc_rollup node client -> let* () = Sc_rollup_node.run sc_rollup_node [] in - let* sc_rollup_address = + let*! sc_rollup_address = Sc_rollup_client.rpc_get ~hooks sc_client ["global"; "sc_rollup_address"] in let sc_rollup_address = JSON.as_string sc_rollup_address in @@ -3177,7 +3179,7 @@ let test_rpcs ~kind = Sc_rollup_node.wait_for_level ~timeout:3.0 sc_rollup_node (level + n) in let* l1_block_hash = RPC.Client.call client @@ RPC.get_chain_block_hash () in - let* l2_block_hash = + let*! l2_block_hash = Sc_rollup_client.rpc_get ~hooks sc_client @@ -3189,13 +3191,13 @@ let test_rpcs ~kind = let* l1_block_hash = RPC.Client.call client @@ RPC.get_chain_block_hash ~block:"5" () in - let* l2_block_hash = + let*! l2_block_hash = Sc_rollup_client.rpc_get ~hooks sc_client ["global"; "block"; "5"; "hash"] in let l2_block_hash = JSON.as_string l2_block_hash in Check.((l1_block_hash = l2_block_hash) string) ~error_msg:"Block 5 on L1 is %L where as on L2 it is %R" ; - let* l2_finalied_block_level = + let*! l2_finalied_block_level = Sc_rollup_client.rpc_get ~hooks sc_client @@ -3204,7 +3206,7 @@ let test_rpcs ~kind = let l2_finalied_block_level = JSON.as_int l2_finalied_block_level in Check.((l2_finalied_block_level = level + n - 2) int) ~error_msg:"Finalized block is %L but should be %R" ; - let* l2_num_messages = + let*! l2_num_messages = Sc_rollup_client.rpc_get ~hooks sc_client @@ -3213,34 +3215,34 @@ let test_rpcs ~kind = let l2_num_messages = JSON.as_int l2_num_messages in Check.((l2_num_messages = batch_size + 2) int) ~error_msg:"Number of messages of head is %L but should be %R" ; - let* _status = + let*! _status = Sc_rollup_client.rpc_get ~hooks sc_client ["global"; "block"; "head"; "status"] in - let* _ticks = + let*! _ticks = Sc_rollup_client.rpc_get ~hooks sc_client ["global"; "block"; "head"; "ticks"] in - let* _state_hash = + let*! _state_hash = Sc_rollup_client.rpc_get ~hooks sc_client ["global"; "block"; "head"; "state_hash"] in - let* _outbox = + let*! _outbox = Sc_rollup_client.rpc_get ~hooks sc_client ["global"; "block"; "head"; "outbox"] in - let* _head = + let*! _head = Sc_rollup_client.rpc_get ~hooks sc_client ["global"; "tezos_head"] in - let* _level = + let*! _level = Sc_rollup_client.rpc_get ~hooks sc_client ["global"; "tezos_level"] in unit -- GitLab From 69a340a2b6dcf988ecaaf766d765bfb5e8a3a73d Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 30 Sep 2022 17:24:37 +0200 Subject: [PATCH 21/21] Tezt/Tezt: test simulation errors --- tezt/tests/sc_rollup.ml | 54 ++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index cc7afd58c355..1333be547b23 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -156,10 +156,14 @@ let register_test ?(regression = false) ~__FILE__ ~tags ~title f = if regression then Protocol.register_regression_test ~__FILE__ ~title ~tags f else Protocol.register_test ~__FILE__ ~title ~tags f -let setup_l1 ?commitment_period ?challenge_window ?timeout protocol = +let setup_l1 ?commitment_period ?challenge_window + ?max_number_of_messages_per_commitment_period ?timeout protocol = let parameters = make_parameter "sc_rollup_commitment_period_in_blocks" commitment_period @ make_parameter "sc_rollup_challenge_window_in_blocks" challenge_window + @ make_parameter + "sc_rollup_max_number_of_messages_per_commitment_period" + max_number_of_messages_per_commitment_period @ make_parameter "sc_rollup_timeout_period_in_blocks" timeout @ [(["sc_rollup_enable"], `Bool true)] in @@ -250,7 +254,8 @@ let test_l1_scenario ?regression ~kind ?boot_sector ?commitment_period scenario sc_rollup tezos_node tezos_client let test_full_scenario ?regression ~kind ?boot_sector ?commitment_period - ?challenge_window ?timeout {variant; tags; description} scenario = + ?challenge_window ?timeout ?max_number_of_messages_per_commitment_period + {variant; tags; description} scenario = let tags = kind :: "rollup_node" :: tags in register_test ?regression @@ -266,7 +271,12 @@ let test_full_scenario ?regression ~kind ?boot_sector ?commitment_period | None -> "")) @@ fun protocol -> let* tezos_node, tezos_client = - setup_l1 ?commitment_period ?challenge_window ?timeout protocol + setup_l1 + ?commitment_period + ?challenge_window + ?timeout + ?max_number_of_messages_per_commitment_period + protocol in let* rollup_node, rollup_client, sc_rollup = setup_rollup ~kind ?boot_sector tezos_node tezos_client @@ -656,11 +666,14 @@ let fetch_messages_from_block client = tree which must have the same root hash as the one stored by the protocol in the context. *) -let test_rollup_inbox_of_rollup_node ~variant scenario ~kind = +let test_rollup_inbox_of_rollup_node + ?max_number_of_messages_per_commitment_period ?(extra_tags = []) ~variant + scenario ~kind = test_full_scenario + ?max_number_of_messages_per_commitment_period { variant = Some variant; - tags = ["inbox"]; + tags = ["inbox"] @ extra_tags; description = "maintenance of inbox in the rollup node"; } ~kind @@ -811,7 +824,22 @@ let sc_rollup_node_simulate sc_rollup_node sc_rollup_client _sc_rollup node ~error_msg:"Simulated resulting state hash is %L but should have been %R" ; Check.((JSON.encode sim_result.output = JSON.encode real_outbox) string) ~error_msg:"Simulated resulting outbox is %L but should have been %R" ; - return () + let*? sim_result = Sc_rollup_client.simulate sc_rollup_client [] in + let* () = + Process.check_error ~msg:(rex "Tried to add zero messages") sim_result + in + let* constants = get_sc_rollup_constants client in + let too_many_msgs = + List.init + constants.max_number_of_messages_per_commitment_period + (Fun.const "") + in + (* In the context of "head", there is already one message in the inbox, so if + we add max messages, we'll be over the limit. *) + let*? sim_result = Sc_rollup_client.simulate sc_rollup_client too_many_msgs in + Process.check_error + ~msg:(rex "Maximum number of messages reached for commitment period") + sim_result (* One can retrieve the list of originated SCORUs. ----------------------------------------------- @@ -2105,7 +2133,7 @@ let test_rollup_origination_boot_sector ~boot_sector ~kind = let init_hash = JSON.(init_commitment |-> "compressed_state" |> as_string) in let* () = Sc_rollup_node.run rollup_node [] in let* _ = Sc_rollup_node.wait_for_level ~timeout:3. rollup_node init_level in - let* node_state_hash = Sc_rollup_client.state_hash ~hooks rollup_client in + let*! node_state_hash = Sc_rollup_client.state_hash ~hooks rollup_client in Check.( (init_hash = node_state_hash) string @@ -3277,11 +3305,6 @@ let register ~kind ~protocols = ~variant:"handles_chain_reorg" sc_rollup_node_handles_chain_reorg protocols ; - test_rollup_inbox_of_rollup_node - ~kind - "simulation" - sc_rollup_node_simulate - protocols ; test_rollup_node_boots_into_initial_state protocols ~kind ; test_rollup_node_advances_pvm_state protocols ~kind ~internal:false ; test_rollup_node_advances_pvm_state protocols ~kind ~internal:true ; @@ -3415,6 +3438,13 @@ let register ~protocols = ~boot_sector2:"31" ~kind:"arith" protocols ; + test_rollup_inbox_of_rollup_node + ~kind:"arith" + ~variant:"simulation" + ~extra_tags:["simulation"] + ~max_number_of_messages_per_commitment_period:300 + sc_rollup_node_simulate + protocols ; (* Specific Wasm PVM tezts *) test_rollup_node_run_with_kernel protocols -- GitLab