diff --git a/src/lib_protocol_environment/environment_V6.ml b/src/lib_protocol_environment/environment_V6.ml index f38743620f8c41fa2defe0136b385e951a57150b..8c61ec5abfaba326ae27b343823aaef40499802b 100644 --- a/src/lib_protocol_environment/environment_V6.ml +++ b/src/lib_protocol_environment/environment_V6.ml @@ -1092,7 +1092,7 @@ struct let+ payload = Wasm.get_output {outbox_level; message_index} tree in match payload with Some payload -> payload | None -> "" - let convert_input : Tezos_scoru_wasm.Wasm_pvm_sig.input_info -> input = + let convert_input : Tezos_scoru_wasm.Wasm_pvm_state.input_info -> input = function | {inbox_level; message_counter} -> let inbox_level = diff --git a/src/lib_protocol_environment/environment_V7.ml b/src/lib_protocol_environment/environment_V7.ml index c7ba16ff74016fc37c08c8d4982e8e434fdff2c1..b77c5e9c06c79632dfc512b4e2ca87c69658faad 100644 --- a/src/lib_protocol_environment/environment_V7.ml +++ b/src/lib_protocol_environment/environment_V7.ml @@ -104,8 +104,8 @@ module type T = sig and type Dal.commitment = Tezos_crypto_dal.Cryptobox.Verifier.commitment and type Bounded.Non_negative_int32.t = Tezos_base.Bounded.Non_negative_int32.t - and type Wasm_2_0_0.input = Tezos_scoru_wasm.Wasm_pvm_sig.input_info - and type Wasm_2_0_0.output = Tezos_scoru_wasm.Wasm_pvm_sig.output_info + and type Wasm_2_0_0.input = Tezos_scoru_wasm.Wasm_pvm_state.input_info + and type Wasm_2_0_0.output = Tezos_scoru_wasm.Wasm_pvm_state.output_info type error += Ecoproto_error of Error_monad.error @@ -1038,12 +1038,12 @@ struct end module Wasm_2_0_0 = struct - type input = Tezos_scoru_wasm.Wasm_pvm_sig.input_info = { + type input = Tezos_scoru_wasm.Wasm_pvm_state.input_info = { inbox_level : Bounded.Non_negative_int32.t; message_counter : Z.t; } - type output = Tezos_scoru_wasm.Wasm_pvm_sig.output_info = { + type output = Tezos_scoru_wasm.Wasm_pvm_state.output_info = { outbox_level : Bounded.Non_negative_int32.t; message_index : Z.t; } diff --git a/src/lib_protocol_environment/environment_V7.mli b/src/lib_protocol_environment/environment_V7.mli index 0ce2fc9c4d7c6c2335c48348a6d9c04996213a7f..8170178e6d573746bd8f43e6e88f98aa74c34d84 100644 --- a/src/lib_protocol_environment/environment_V7.mli +++ b/src/lib_protocol_environment/environment_V7.mli @@ -104,8 +104,8 @@ module type T = sig and type Dal.commitment = Tezos_crypto_dal.Cryptobox.Verifier.commitment and type Bounded.Non_negative_int32.t = Tezos_base.Bounded.Non_negative_int32.t - and type Wasm_2_0_0.input = Tezos_scoru_wasm.Wasm_pvm_sig.input_info - and type Wasm_2_0_0.output = Tezos_scoru_wasm.Wasm_pvm_sig.output_info + and type Wasm_2_0_0.input = Tezos_scoru_wasm.Wasm_pvm_state.input_info + and type Wasm_2_0_0.output = Tezos_scoru_wasm.Wasm_pvm_state.output_info (** An [Ecoproto_error e] is a shell error that carry a protocol error. diff --git a/src/lib_protocol_environment/environment_V8.ml b/src/lib_protocol_environment/environment_V8.ml index baab9642d3d9a136fedd16a573a2e82d95638826..9102916ead49afb8149d08a6ca205641db8cb8ff 100644 --- a/src/lib_protocol_environment/environment_V8.ml +++ b/src/lib_protocol_environment/environment_V8.ml @@ -105,13 +105,14 @@ module type T = sig and type Dal.page_proof = Tezos_crypto_dal.Cryptobox.Verifier.page_proof and type Bounded.Non_negative_int32.t = Tezos_base.Bounded.Non_negative_int32.t - and type Wasm_2_0_0.input = Tezos_scoru_wasm.Wasm_pvm_sig.input_info - and type Wasm_2_0_0.output = Tezos_scoru_wasm.Wasm_pvm_sig.output_info - and type Wasm_2_0_0.input_hash = Tezos_scoru_wasm.Wasm_pvm_sig.input_hash - and type Wasm_2_0_0.reveal = Tezos_scoru_wasm.Wasm_pvm_sig.reveal + and type Wasm_2_0_0.input = Tezos_scoru_wasm.Wasm_pvm_state.input_info + and type Wasm_2_0_0.output = Tezos_scoru_wasm.Wasm_pvm_state.output_info + and type Wasm_2_0_0.input_hash = + Tezos_scoru_wasm.Wasm_pvm_state.input_hash + and type Wasm_2_0_0.reveal = Tezos_scoru_wasm.Wasm_pvm_state.reveal and type Wasm_2_0_0.input_request = - Tezos_scoru_wasm.Wasm_pvm_sig.input_request - and type Wasm_2_0_0.info = Tezos_scoru_wasm.Wasm_pvm_sig.info + Tezos_scoru_wasm.Wasm_pvm_state.input_request + and type Wasm_2_0_0.info = Tezos_scoru_wasm.Wasm_pvm_state.info type error += Ecoproto_error of Error_monad.error @@ -1044,30 +1045,30 @@ struct end module Wasm_2_0_0 = struct - type input = Tezos_scoru_wasm.Wasm_pvm_sig.input_info = { + type input = Tezos_scoru_wasm.Wasm_pvm_state.input_info = { inbox_level : Bounded.Non_negative_int32.t; message_counter : Z.t; } - type output = Tezos_scoru_wasm.Wasm_pvm_sig.output_info = { + type output = Tezos_scoru_wasm.Wasm_pvm_state.output_info = { outbox_level : Bounded.Non_negative_int32.t; message_index : Z.t; } - type input_hash = Tezos_scoru_wasm.Wasm_pvm_sig.input_hash + type input_hash = Tezos_scoru_wasm.Wasm_pvm_state.input_hash let input_hash_to_string = - Tezos_scoru_wasm.Wasm_pvm_sig.input_hash_to_string + Tezos_scoru_wasm.Wasm_pvm_state.input_hash_to_string - type reveal = Tezos_scoru_wasm.Wasm_pvm_sig.reveal = + type reveal = Tezos_scoru_wasm.Wasm_pvm_state.reveal = | Reveal_raw_data of input_hash - type input_request = Tezos_scoru_wasm.Wasm_pvm_sig.input_request = + type input_request = Tezos_scoru_wasm.Wasm_pvm_state.input_request = | No_input_required | Input_required | Reveal_required of reveal - type info = Tezos_scoru_wasm.Wasm_pvm_sig.info = { + type info = Tezos_scoru_wasm.Wasm_pvm_state.info = { current_tick : Z.t; last_input_read : input option; input_request : input_request; diff --git a/src/lib_protocol_environment/environment_V8.mli b/src/lib_protocol_environment/environment_V8.mli index 6421bbf18047a7d446cf0af52c08ec3564a12871..d6eaafc9b6b62f6a81787925f192e0ee3450d00c 100644 --- a/src/lib_protocol_environment/environment_V8.mli +++ b/src/lib_protocol_environment/environment_V8.mli @@ -105,13 +105,14 @@ module type T = sig and type Dal.page_proof = Tezos_crypto_dal.Cryptobox.Verifier.page_proof and type Bounded.Non_negative_int32.t = Tezos_base.Bounded.Non_negative_int32.t - and type Wasm_2_0_0.input = Tezos_scoru_wasm.Wasm_pvm_sig.input_info - and type Wasm_2_0_0.output = Tezos_scoru_wasm.Wasm_pvm_sig.output_info - and type Wasm_2_0_0.input_hash = Tezos_scoru_wasm.Wasm_pvm_sig.input_hash - and type Wasm_2_0_0.reveal = Tezos_scoru_wasm.Wasm_pvm_sig.reveal + and type Wasm_2_0_0.input = Tezos_scoru_wasm.Wasm_pvm_state.input_info + and type Wasm_2_0_0.output = Tezos_scoru_wasm.Wasm_pvm_state.output_info + and type Wasm_2_0_0.input_hash = + Tezos_scoru_wasm.Wasm_pvm_state.input_hash + and type Wasm_2_0_0.reveal = Tezos_scoru_wasm.Wasm_pvm_state.reveal and type Wasm_2_0_0.input_request = - Tezos_scoru_wasm.Wasm_pvm_sig.input_request - and type Wasm_2_0_0.info = Tezos_scoru_wasm.Wasm_pvm_sig.info + Tezos_scoru_wasm.Wasm_pvm_state.input_request + and type Wasm_2_0_0.info = Tezos_scoru_wasm.Wasm_pvm_state.info (** An [Ecoproto_error e] is a shell error that carry a protocol error. diff --git a/src/lib_scoru_wasm/gather_floppies.ml b/src/lib_scoru_wasm/gather_floppies.ml index 399c5a4e3d190365346e1782772ce3ab5d07ec7c..0b9ee82d69f02d6e5feb5bed976a4a77d3cab340 100644 --- a/src/lib_scoru_wasm/gather_floppies.ml +++ b/src/lib_scoru_wasm/gather_floppies.ml @@ -24,6 +24,7 @@ (*****************************************************************************) open Wasm_pvm_sig +open Wasm_pvm_state open Tezos_lazy_containers (** FIXME: https://gitlab.com/tezos/tezos/-/issues/3361 @@ -106,10 +107,7 @@ module type S = sig include Wasm_pvm_sig.S module Internal_for_tests : sig - include - Wasm_pvm_sig.Internal_for_tests - with type tree := tree - and type tick_state := tick_state + include Wasm_pvm_sig.Internal_for_tests with type tree := tree val get_internal_status : tree -> internal_status option Lwt.t @@ -120,11 +118,9 @@ end module Make (T : Tezos_tree_encoding.TREE) (Wasm : Wasm_pvm_sig.S with type tree = T.tree) : - S with type tree = T.tree and type tick_state = Wasm.tick_state = struct + S with type tree = T.tree = struct type tree = Wasm.tree - type tick_state = Wasm.tick_state - module Tree_encoding_runner = Tezos_tree_encoding.Runner.Make (T) (** The tick state of the [Gathering_floppies] instrumentation. *) @@ -430,6 +426,8 @@ module Make let compute_step_many ~max_steps = compute_step_gen (Wasm.compute_step_many ~max_steps) + module Internal_for_benchmark = Wasm.Internal_for_benchmark + module Internal_for_tests = struct include Wasm.Internal_for_tests diff --git a/src/lib_scoru_wasm/gather_floppies.mli b/src/lib_scoru_wasm/gather_floppies.mli index 72c9f801075f49ccc97906e718625f4cfb867b04..ce031d34ded5da81dc9d34c65745445222e4ac9c 100644 --- a/src/lib_scoru_wasm/gather_floppies.mli +++ b/src/lib_scoru_wasm/gather_floppies.mli @@ -64,10 +64,7 @@ module type S = sig include Wasm_pvm_sig.S module Internal_for_tests : sig - include - Wasm_pvm_sig.Internal_for_tests - with type tree := tree - and type tick_state := tick_state + include Wasm_pvm_sig.Internal_for_tests with type tree := tree val get_internal_status : tree -> internal_status option Lwt.t @@ -81,5 +78,4 @@ end messages. *) module Make (T : Tezos_tree_encoding.TREE) - (Wasm : Wasm_pvm_sig.S with type tree = T.tree) : - S with type tree = T.tree and type tick_state = Wasm.tick_state + (Wasm : Wasm_pvm_sig.S with type tree = T.tree) : S with type tree = T.tree diff --git a/src/lib_scoru_wasm/test/test_gather_floppies.ml b/src/lib_scoru_wasm/test/test_gather_floppies.ml index 293b331153b85a9ca6af0a9927dfedd94c240f0d..904d0349ff2d43a5013b35a2a3bf9107ba590e98 100644 --- a/src/lib_scoru_wasm/test/test_gather_floppies.ml +++ b/src/lib_scoru_wasm/test/test_gather_floppies.ml @@ -76,7 +76,7 @@ let set_floppy_input_step chunk sk counter tree = @@ `External input in let input_info = - Wasm_pvm_sig. + Wasm_pvm_state. { inbox_level = Option.value_f ~default:(fun () -> assert false) @@ -176,7 +176,7 @@ let init_tree_with_floppies ?(max_steps = Wasm_utils.default_max_tick) kernel = let*! tree = eval_until_input_requested ~max_steps tree in let set_input (tree, counter) chunk = let*! info = Wasm.get_info tree in - if info.input_request = Wasm_pvm_sig.Input_required then + if info.input_request = Wasm_pvm_state.Input_required then let*! tree = set_floppy_input_step chunk sk counter tree in let+ () = check_gathering_status (Gathering_floppies pk) tree in (tree, succ counter) diff --git a/src/lib_scoru_wasm/test/test_get_set.ml b/src/lib_scoru_wasm/test/test_get_set.ml index 8a3df8641d353a235570c50189647f9066624725..bc248673e16d5b1ae9f91f111c05aef8bf4f3cf6 100644 --- a/src/lib_scoru_wasm/test/test_get_set.ml +++ b/src/lib_scoru_wasm/test/test_get_set.ml @@ -108,7 +108,7 @@ let initialise_tree () = tree let make_inbox_info ~inbox_level ~message_counter = - Wasm_pvm_sig. + Wasm_pvm_state. { inbox_level = WithExceptions.Option.get @@ -118,7 +118,7 @@ let make_inbox_info ~inbox_level ~message_counter = } let make_output_info ~outbox_level ~message_index = - Wasm_pvm_sig. + Wasm_pvm_state. { outbox_level = WithExceptions.Option.get @@ -149,7 +149,7 @@ let test_get_info () = let open Lwt_syntax in let* tree = initialise_tree () in let expected_info ~inbox_level ~message_counter = - let open Wasm_pvm_sig in + let open Wasm_pvm_state in let last_input_read = Some (make_inbox_info ~inbox_level ~message_counter) in @@ -207,7 +207,7 @@ let test_set_input () = let* result_input = Tree_encoding_runner.decode inp_encoding tree in let* current_tick = Tree_encoding_runner.decode current_tick_encoding tree in let expected_info = - let open Wasm_pvm_sig in + let open Wasm_pvm_state in let last_input_read = Some (make_inbox_info ~inbox_level:5 ~message_counter:10) in diff --git a/src/lib_scoru_wasm/test/test_reveal.ml b/src/lib_scoru_wasm/test/test_reveal.ml index da5ff77d27def05c0463c45068fd8911414fa5b2..9e075edd02b0fac4e9c93ce2d2e66a6b6492063d 100644 --- a/src/lib_scoru_wasm/test/test_reveal.ml +++ b/src/lib_scoru_wasm/test/test_reveal.ml @@ -68,8 +68,9 @@ let test_reveal_preimage_gen preimage max_bytes = let*! state = eval_until_input_requested state_with_dummy_input in let*! info = Wasm.get_info state in let* () = - match info.Wasm_pvm_sig.input_request with - | Wasm_pvm_sig.No_input_required | Input_required -> assert false + let open Wasm_pvm_state in + match info.input_request with + | No_input_required | Input_required -> assert false | Reveal_required (Reveal_raw_data hash) -> (* The PVM has reached a point where it’s asking for some preimage. Since the memory is left blank, we are looking @@ -83,8 +84,9 @@ let test_reveal_preimage_gen preimage max_bytes = let*! state = Wasm.reveal_step (Bytes.of_string preimage) state in let*! info = Wasm.get_info state in let* () = - match info.Wasm_pvm_sig.input_request with - | Wasm_pvm_sig.No_input_required -> return_unit + let open Wasm_pvm_state in + match info.input_request with + | No_input_required -> return_unit | Input_required -> failwith "should be running, but expect input from the L1" | Reveal_required _ -> failwith "should be running, but expect reveal tick" diff --git a/src/lib_scoru_wasm/test/test_wasm_pvm.ml b/src/lib_scoru_wasm/test/test_wasm_pvm.ml index 1443666ca700c769344c79f10daecc2265558dc7..b6b433cdc178218b4b90265b55bdb89944ac4198 100644 --- a/src/lib_scoru_wasm/test/test_wasm_pvm.ml +++ b/src/lib_scoru_wasm/test/test_wasm_pvm.ml @@ -212,6 +212,7 @@ let should_run_store_delete_kernel kernel = let build_snapshot_wasm_state_from_set_input ?(max_tick = Z.of_int64 default_max_tick) tree = let open Lwt_syntax in + let open Wasm_pvm_state.Internal_state in (* Only serves to encode the `Snapshot` state. *) let state_encoding = Tezos_tree_encoding.( @@ -221,7 +222,7 @@ let build_snapshot_wasm_state_from_set_input case "snapshot" (value [] Data_encoding.unit) - (function Wasm_pvm.Snapshot -> Some () | _ -> None) + (function Snapshot -> Some () | _ -> None) (fun () -> Snapshot); ]) in @@ -229,7 +230,7 @@ let build_snapshot_wasm_state_from_set_input let* tree = Test_encodings_util.Tree_encoding_runner.encode (Tezos_tree_encoding.scope ["wasm"] state_encoding) - Wasm_pvm.Snapshot + Snapshot tree in (* Since we start directly after at an input_step, we need to offset the tick diff --git a/src/lib_scoru_wasm/test/wasm_utils.ml b/src/lib_scoru_wasm/test/wasm_utils.ml index 52f002e1e082b46e2d51fbbd54352727a66985f8..6ff13bc9188a67aa25a703eda35bf67a78da204f 100644 --- a/src/lib_scoru_wasm/test/wasm_utils.ml +++ b/src/lib_scoru_wasm/test/wasm_utils.ml @@ -97,7 +97,7 @@ let rec eval_until_init tree = let set_input_step message message_counter tree = let input_info = - Wasm_pvm_sig. + Wasm_pvm_state. { inbox_level = Option.value_f ~default:(fun () -> assert false) @@ -110,7 +110,7 @@ let set_input_step message message_counter tree = let pp_state fmt state = let pp_s s = Format.fprintf fmt "%s" s in match state with - | Wasm_pvm.Decode _ -> pp_s "Decode" + | Wasm_pvm_state.Internal_state.Decode _ -> pp_s "Decode" | Eval _ -> pp_s "Eval" | Stuck e -> Format.fprintf fmt "Stuck (%a)" Test_wasm_pvm_encodings.pp_error_state e @@ -153,7 +153,7 @@ let check_error ?expected_kind ?expected_reason error = | None, _ -> true let is_stuck ?step ?reason = function - | Wasm_pvm.Stuck err -> + | Wasm_pvm_state.Internal_state.Stuck err -> check_error ?expected_kind:step ?expected_reason:reason err | _ -> false diff --git a/src/lib_scoru_wasm/wasm_pvm.ml b/src/lib_scoru_wasm/wasm_pvm.ml index b22967a63005bda975db5a2dd07c86ab255a545d..883c1c296d1b5895936151d21b714de70862c73e 100644 --- a/src/lib_scoru_wasm/wasm_pvm.ml +++ b/src/lib_scoru_wasm/wasm_pvm.ml @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2022 TriliTech *) +(* Copyright (c) 2022 Marigold *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -23,6 +24,8 @@ (* *) (*****************************************************************************) +open Wasm_pvm_state.Internal_state + (* The name by which the module is registered. This can be anything as long as we use the same name to lookup from the registry. *) let wasm_main_module_name = "main" @@ -48,48 +51,13 @@ let reboot_flag_key = Durable.key_of_string_exn "/kernel/env/reboot" module Wasm = Tezos_webassembly_interpreter -type tick_state = - | Decode of Tezos_webassembly_interpreter.Decode.decode_kont - | Link of { - ast_module : Wasm.Ast.module_; - externs : Wasm.Instance.extern Wasm.Instance.Vector.t; - imports_offset : int32; - } - | Init of { - self : Wasm.Instance.module_key; - ast_module : Tezos_webassembly_interpreter.Ast.module_; - init_kont : Tezos_webassembly_interpreter.Eval.init_kont; - module_reg : Wasm.Instance.module_reg; - } - | Eval of Wasm.Eval.config - | Stuck of Wasm_pvm_errors.t - | Snapshot - type computation_status = Starting | Restarting | Running | Failing | Reboot -type pvm_state = { - last_input_info : Wasm_pvm_sig.input_info option; - (** Info about last read input. *) - current_tick : Z.t; (** Current tick of the PVM. *) - reboot_counter : Z.t; (** Number of reboots for the current input. *) - durable : Durable.t; (** The durable storage of the PVM. *) - buffers : Wasm.Eval.buffers; - (** Input and outut buffers used by the PVM host functions. *) - tick_state : tick_state; (** The current tick state. *) - last_top_level_call : Z.t; - (** Last tick corresponding to a top-level call. *) - max_nb_ticks : Z.t; (** Number of ticks between top level call. *) - maximum_reboots_per_input : Z.t; (** Number of reboots between two inputs. *) -} - module Make (T : Tezos_tree_encoding.TREE) : - Gather_floppies.S with type tree = T.tree and type tick_state = tick_state = -struct + Gather_floppies.S with type tree = T.tree = struct module Raw = struct type tree = T.tree - type nonrec tick_state = tick_state - module Tree_encoding_runner = Tezos_tree_encoding.Runner.Make (T) module Parsing = Binary_parser_encodings @@ -470,28 +438,50 @@ struct let input_request pvm_state = let open Lwt_syntax in match pvm_state.tick_state with - | Stuck _ -> return Wasm_pvm_sig.Input_required + | Stuck _ -> return Wasm_pvm_state.Input_required | Snapshot -> let+ has_reboot_flag = has_reboot_flag pvm_state.durable in - if has_reboot_flag then Wasm_pvm_sig.No_input_required - else Wasm_pvm_sig.Input_required + if has_reboot_flag then Wasm_pvm_state.No_input_required + else Wasm_pvm_state.Input_required | Eval config -> ( match Tezos_webassembly_interpreter.Eval.is_reveal_tick config with - | Some reveal -> return (Wasm_pvm_sig.Reveal_required reveal) - | None -> return Wasm_pvm_sig.No_input_required) - | _ -> return Wasm_pvm_sig.No_input_required + | Some reveal -> return (Wasm_pvm_state.Reveal_required reveal) + | None -> return Wasm_pvm_state.No_input_required) + | _ -> return Wasm_pvm_state.No_input_required let is_top_level_padding pvm_state = eval_has_finished pvm_state.tick_state && not (is_time_for_snapshot pvm_state) - let compute_step_many ~max_steps tree = + let decode tree = Tree_encoding_runner.decode pvm_state_encoding tree + + let encode pvm_state tree = let open Lwt.Syntax in - assert (max_steps > 0L) ; + (* {{Note tick state clean-up}} + The "wasm" directory in the Irmin tree of the PVM is used to + maintain a lot of information across tick, but as of now, it + was never cleaned up. It meant that the tree would become + crowded with data that were no longer needed. + + It turns out it is very simple to clean up, thanks to subtree + move. Because we keep in the lazy-containers the original + subtree, and we inject it prior to updating read keys, the + tree-encoding library does not rely on the input tree at + encoding time. + + With this, we gain an additional 5% of proof size in the + worst tick of the computation.wasm kernel. *) + let* tree = T.remove tree ["wasm"] in + Tree_encoding_runner.encode pvm_state_encoding pvm_state tree + + let compute_step_many_until_pvm_state ?(max_steps = 1L) should_continue + pvm_state = + let open Lwt.Syntax in + assert (max_steps > 0L) ; let rec go steps_left pvm_state = - let* input_request = input_request pvm_state in - if steps_left > 0L && input_request = No_input_required then + let* continue = should_continue pvm_state in + if steps_left > 0L && continue then if is_top_level_padding pvm_state then (* We're in the top-level padding after the evaluation has finished. That means we can skip up to the tick before the @@ -511,37 +501,40 @@ struct go (Int64.pred steps_left) pvm_state else Lwt.return pvm_state in - - let* pvm_state = Tree_encoding_runner.decode pvm_state_encoding tree in (* Make sure we perform at least 1 step. The assertion above ensures that we were asked to perform at least 1. *) let* pvm_state = compute_step_inner pvm_state in - let* pvm_state = go (Int64.pred max_steps) pvm_state in + go (Int64.pred max_steps) pvm_state - (* {{Note tick state clean-up}} + let compute_step_many_until ?(max_steps = 1L) should_continue tree = + let open Lwt.Syntax in + assert (max_steps > 0L) ; - The "wasm" directory in the Irmin tree of the PVM is used to - maintain a lot of information across tick, but as of now, it - was never cleaned up. It meant that the tree would become - crowded with data that were no longer needed. + let* pvm_state = decode tree in - It turns out it is very simple to clean up, thanks to subtree - move. Because we keep in the lazy-containers the original - subtree, and we inject it prior to updating read keys, the - tree-encoding library does not rely on the input tree at - encoding time. + (* Make sure we perform at least 1 step. The assertion above ensures that + we were asked to perform at least 1. *) + let* pvm_state = + compute_step_many_until_pvm_state ~max_steps should_continue pvm_state + in - With this, we gain an additional 5% of proof size in the - worst tick of the computation.wasm kernel. *) - let* tree = T.remove tree ["wasm"] in + encode pvm_state tree - Tree_encoding_runner.encode pvm_state_encoding pvm_state tree + let compute_step_many ~max_steps tree = + let open Lwt_syntax in + let should_continue pvm_state = + let* input_request_val = input_request pvm_state in + match input_request_val with + | Reveal_required _ | Input_required -> return false + | No_input_required -> return true + in + compute_step_many_until ~max_steps should_continue tree let compute_step tree = compute_step_many ~max_steps:1L tree let get_output output_info tree = let open Lwt_syntax in - let open Wasm_pvm_sig in + let open Wasm_pvm_state in let {outbox_level; message_index} = output_info in let outbox_level = Bounded.Non_negative_int32.to_value outbox_level in let* candidate = @@ -565,12 +558,12 @@ struct Tree_encoding_runner.decode pvm_state_encoding tree in let+ input_request = input_request pvm in - Wasm_pvm_sig. + Wasm_pvm_state. {current_tick; last_input_read = last_input_info; input_request} let set_input_step input_info message tree = let open Lwt_syntax in - let open Wasm_pvm_sig in + let open Wasm_pvm_state in let {inbox_level; message_counter} = input_info in let raw_level = Bounded.Non_negative_int32.to_value inbox_level in let level = Int32.to_string raw_level in @@ -678,6 +671,18 @@ struct in Tree_encoding_runner.encode pvm_state_encoding pvm_state tree + module Internal_for_benchmark = struct + let decode = decode + + let encode = encode + + let compute_step_many_until = compute_step_many_until + + let compute_step_many_until_pvm_state = compute_step_many_until_pvm_state + + let eval_has_finished = eval_has_finished + end + module Internal_for_tests = struct let get_tick_state tree = let open Lwt_syntax in diff --git a/src/lib_scoru_wasm/wasm_pvm.mli b/src/lib_scoru_wasm/wasm_pvm.mli index ff96bfbd5ac0b52e7fb8fd7311774549e845dee1..0fda7172042c637cb9c40df2a5736169a57d557e 100644 --- a/src/lib_scoru_wasm/wasm_pvm.mli +++ b/src/lib_scoru_wasm/wasm_pvm.mli @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2022 TriliTech *) +(* Copyright (c) 2022 Marigold *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -26,26 +27,5 @@ (** Maximum number of reboots per inputs. *) val maximum_reboots_per_input : Z.t -type tick_state = - | Decode of Tezos_webassembly_interpreter.Decode.decode_kont - | Link of { - ast_module : Tezos_webassembly_interpreter.Ast.module_; - externs : - Tezos_webassembly_interpreter.Instance.extern - Tezos_webassembly_interpreter.Instance.Vector.t; - imports_offset : int32; - } - | Init of { - self : Tezos_webassembly_interpreter.Instance.module_key; - ast_module : Tezos_webassembly_interpreter.Ast.module_; - init_kont : Tezos_webassembly_interpreter.Eval.init_kont; - module_reg : Tezos_webassembly_interpreter.Instance.module_reg; - } - | Eval of Tezos_webassembly_interpreter.Eval.config - | Stuck of Wasm_pvm_errors.t - | Snapshot - -(** Builds a WASM VM given a concrete implementation of {!Tree.S}. *) - module Make (T : Tezos_tree_encoding.TREE) : - Gather_floppies.S with type tree = T.tree and type tick_state = tick_state + Gather_floppies.S with type tree = T.tree diff --git a/src/lib_scoru_wasm/wasm_pvm_sig.ml b/src/lib_scoru_wasm/wasm_pvm_sig.ml index 6ce6d328202e5a5b383a21b01feba83586b7519f..e30cc010b419eeee61ec0dfa36b54d1326ef7f68 100644 --- a/src/lib_scoru_wasm/wasm_pvm_sig.ml +++ b/src/lib_scoru_wasm/wasm_pvm_sig.ml @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2022 TriliTech *) +(* Copyright (c) 2022 Marigold *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -22,49 +23,59 @@ (* DEALINGS IN THE SOFTWARE. *) (* *) (*****************************************************************************) +open Wasm_pvm_state + +(** This module type expose internals necessary for benchmarking. + + /!\ Not intended for unit tests: the functions could be used to redefine the + main execution loop, at the risk of departing from what is defined in the + PVM definition. [Internal_for_benchmark.compute_step_many_until] can use + custom stopping condition and therefore should not be used in unit test: + the test could hide regression if the condition change in the code, but not + in the test. *) +module type Internal_for_benchmark = sig + open Internal_state -(** Represents the location of an input message. *) -type input_info = { - inbox_level : Tezos_base.Bounded.Non_negative_int32.t; - (** The inbox level at which the message exists.*) - message_counter : Z.t; (** The index of the message in the inbox. *) -} - -(** Represents the location of an output message. *) -type output_info = { - outbox_level : Tezos_base.Bounded.Non_negative_int32.t; - (** The outbox level at which the message exists.*) - message_index : Z.t; (** The index of the message in the outbox. *) -} - -type input_hash = Tezos_webassembly_interpreter.Reveal.input_hash - -let input_hash_to_string = - Tezos_webassembly_interpreter.Reveal.input_hash_to_string - -type reveal = Tezos_webassembly_interpreter.Reveal.reveal = - | Reveal_raw_data of Tezos_webassembly_interpreter.Reveal.input_hash - -(** Represents the state of input requests. *) -type input_request = - | No_input_required (** The VM does not expect any input. *) - | Input_required (** The VM needs input in order to progress. *) - | Reveal_required of reveal - -(** Represents the state of the VM. *) -type info = { - current_tick : Z.t; - (** The number of ticks processed by the VM, zero for the initial state. - [current_tick] must be incremented for each call to [step] *) - last_input_read : input_info option; - (** The last message to be read by the VM, if any. *) - input_request : input_request; (** The current VM input request. *) -} + type tree + + val decode : tree -> pvm_state Lwt.t + + val encode : pvm_state -> tree -> tree Lwt.t + + (** [compute_step_many_until_pvm_state max_step should_continue pvm_state] + advance forwards the VM in the same manners as [compute_step_many] + as long as [should_continue] returns true. + + IS applied on [pvm_state] rather than a tree. + + /!\ as it allows to redefine the stop condition, this function should + not be used in unit test: the test could hide regression if the + condition change in the code, but not in the test. + *) + val compute_step_many_until_pvm_state : + ?max_steps:int64 -> + (pvm_state -> bool Lwt.t) -> + pvm_state -> + pvm_state Lwt.t + + (** [compute_step_many_until max_step should_continue tree] + advance forwards the VM in the same manners as [compute_step_many] + as long as [should_continue] returns true. + + /!\ as it allows to redefine the stop condition, this function should + not be used in unit test: the test could hide regression if the + condition change in the code, but not in the test. + *) + val compute_step_many_until : + ?max_steps:int64 -> (pvm_state -> bool Lwt.t) -> tree -> tree Lwt.t + + val eval_has_finished : tick_state -> bool +end module type Internal_for_tests = sig - type tree + open Internal_state - type tick_state + type tree val get_tick_state : tree -> tick_state Lwt.t @@ -84,8 +95,6 @@ end module type S = sig type tree - type tick_state - (** [compute_step_many ~max_steps tree] forwards the VM by at most [max_step] compute tick, yielding if it reaches the maximum number of ticks for a toplevel kernel call. If the VM is expecting input, it gets stuck. If the @@ -123,14 +132,16 @@ module type S = sig raise. *) val get_info : tree -> info Lwt.t - module Internal_for_tests : - Internal_for_tests with type tree := tree and type tick_state := tick_state + module Internal_for_benchmark : Internal_for_benchmark with type tree := tree + + module Internal_for_tests : Internal_for_tests with type tree := tree end (* Encodings *) let input_info_encoding = let open Data_encoding in + let open Wasm_pvm_state in conv (fun {inbox_level; message_counter} -> (inbox_level, message_counter)) (fun (inbox_level, message_counter) -> {inbox_level; message_counter}) diff --git a/src/lib_scoru_wasm/wasm_pvm_state.ml b/src/lib_scoru_wasm/wasm_pvm_state.ml new file mode 100644 index 0000000000000000000000000000000000000000..c20e8bf188454d979d737d4f0641d21f15a1cfcf --- /dev/null +++ b/src/lib_scoru_wasm/wasm_pvm_state.ml @@ -0,0 +1,101 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 TriliTech *) +(* Copyright (c) 2022 Marigold *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Represents the location of an input message. *) +type input_info = { + inbox_level : Tezos_base.Bounded.Non_negative_int32.t; + (** The inbox level at which the message exists.*) + message_counter : Z.t; (** The index of the message in the inbox. *) +} + +(** Represents the location of an output message. *) +type output_info = { + outbox_level : Tezos_base.Bounded.Non_negative_int32.t; + (** The outbox level at which the message exists.*) + message_index : Z.t; (** The index of the message in the outbox. *) +} + +type input_hash = Tezos_webassembly_interpreter.Reveal.input_hash + +let input_hash_to_string = + Tezos_webassembly_interpreter.Reveal.input_hash_to_string + +type reveal = Tezos_webassembly_interpreter.Reveal.reveal = + | Reveal_raw_data of Tezos_webassembly_interpreter.Reveal.input_hash + +(** Represents the state of input requests. *) +type input_request = + | No_input_required (** The VM does not expect any input. *) + | Input_required (** The VM needs input in order to progress. *) + | Reveal_required of reveal + +(** Represents the state of the VM. *) +type info = { + current_tick : Z.t; + (** The number of ticks processed by the VM, zero for the initial state. + [current_tick] must be incremented for each call to [step] *) + last_input_read : input_info option; + (** The last message to be read by the VM, if any. *) + input_request : input_request; (** The current VM input request. *) +} + +(** This module type defines the state for the PVM. + For use in lib_scoru_wasm only. *) +module Internal_state = struct + type tick_state = + | Decode of Tezos_webassembly_interpreter.Decode.decode_kont + | Link of { + ast_module : Tezos_webassembly_interpreter.Ast.module_; + externs : + Tezos_webassembly_interpreter.Instance.extern + Tezos_webassembly_interpreter.Instance.Vector.t; + imports_offset : int32; + } + | Init of { + self : Tezos_webassembly_interpreter.Instance.module_key; + ast_module : Tezos_webassembly_interpreter.Ast.module_; + init_kont : Tezos_webassembly_interpreter.Eval.init_kont; + module_reg : Tezos_webassembly_interpreter.Instance.module_reg; + } + | Eval of Tezos_webassembly_interpreter.Eval.config + | Stuck of Wasm_pvm_errors.t + | Snapshot + + type pvm_state = { + last_input_info : input_info option; (** Info about last read input. *) + current_tick : Z.t; (** Current tick of the PVM. *) + reboot_counter : Z.t; (** Number of reboots for the current input. *) + durable : Durable.t; (** The durable storage of the PVM. *) + buffers : Tezos_webassembly_interpreter.Eval.buffers; + (** Input and outut buffers used by the PVM host functions. *) + tick_state : tick_state; (** The current tick state. *) + last_top_level_call : Z.t; + (** Last tick corresponding to a top-level call. *) + max_nb_ticks : Z.t; (** Number of ticks between top level call. *) + maximum_reboots_per_input : Z.t; + (** Number of reboots between two inputs. *) + } +end