diff --git a/.gitlab/ci/opam-ci.yml b/.gitlab/ci/opam-ci.yml index b860b7234d3b95d6c25eec5bf4385878cc6962c7..e452c570cf63bc447bd46658dac3d55ff90192f4 100644 --- a/.gitlab/ci/opam-ci.yml +++ b/.gitlab/ci/opam-ci.yml @@ -33,6 +33,8 @@ # Ignoring package tezos-scoru-wasm-test, it only contains tests or private targets +# Ignoring package tezos-scoru-wasm-test-helpers, it only contains tests or private targets + # Ignoring package tezos-shell-context-test, it only contains tests or private targets # Ignoring package tezos-tps-evaluation, it only contains tests or private targets diff --git a/dune-project b/dune-project index c7ebb8021228e1397bdca0ca8ae27ad0400c196b..70f4f423dd9791ff2527e182014d8037589446a1 100644 --- a/dune-project +++ b/dune-project @@ -186,6 +186,7 @@ (package (name tezos-sc-rollup-alpha)) (package (name tezos-scoru-wasm)) (package (name tezos-scoru-wasm-test)(allow_empty)) +(package (name tezos-scoru-wasm-test-helpers)(allow_empty)) (package (name tezos-shell)) (package (name tezos-shell-benchmarks)) (package (name tezos-shell-context)) diff --git a/manifest/main.ml b/manifest/main.ml index f73d8b1c975a84408edcec724e92fc663fe6f754..0ce9ec6f85b48778a0bb715874b6590d8d7a4f87 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -3142,6 +3142,28 @@ let octez_dal_node_lib = octez_protocol_updater |> open_; ] +let octez_scoru_wasm_tests_helpers = + private_lib + "test_scoru_wasm_test_helpers" + ~path:"src/lib_scoru_wasm/test/helpers" + ~opam:"tezos-scoru-wasm-test-helpers" + ~synopsis:"helpers for testing for the scoru-wasm functionality" + ~deps: + [ + octez_base |> open_ ~m:"TzPervasives"; + tree_encoding; + octez_base_unix; + octez_context_disk; + octez_base_test_helpers |> open_; + octez_test_helpers; + octez_scoru_wasm; + qcheck_alcotest; + alcotest_lwt; + tezt_lib; + octez_webassembly_interpreter_extra |> open_; + ] + ~preprocess:[staged_pps [ppx_import; ppx_deriving_show]] + let _octez_scoru_wasm_tests = test "test_scoru_wasm" @@ -3160,6 +3182,7 @@ let _octez_scoru_wasm_tests = qcheck_alcotest; alcotest_lwt; tezt_lib; + octez_scoru_wasm_tests_helpers |> open_; octez_webassembly_interpreter_extra |> open_; ] ~preprocess:[staged_pps [ppx_import; ppx_deriving_show]] @@ -3660,9 +3683,13 @@ end = struct main |> open_; test_helpers |> if_some |> open_; alcotest_lwt; + octez_scoru_wasm_tests_helpers |> if_ N.(number >= 016) |> open_; octez_stdlib |> if_ N.(number >= 013) |> open_; octez_crypto_dal |> if_ N.(number >= 016) |> open_; octez_scoru_wasm; + octez_webassembly_interpreter_extra + |> if_ N.(number >= 016) + |> open_; ] ~dune: Dune. diff --git a/opam/tezos-protocol-alpha-tests.opam b/opam/tezos-protocol-alpha-tests.opam index f92f66d77d145862aa1a99cd5306578e6fc6c32b..bab091fb319dee74602902acbe4a87d902f8ffac 100644 --- a/opam/tezos-protocol-alpha-tests.opam +++ b/opam/tezos-protocol-alpha-tests.opam @@ -30,8 +30,10 @@ depends: [ "tezos-client-base" {with-test} "tezos-protocol-environment" {with-test} "tezos-stdlib-unix" {with-test} + "tezos-scoru-wasm-test-helpers" {with-test} "tezos-stdlib" {with-test} "tezos-scoru-wasm" {with-test} + "tezos-webassembly-interpreter-extra" {with-test} "tezt-tezos" {with-test} ] build: [ diff --git a/opam/tezos-scoru-wasm-test-helpers.opam b/opam/tezos-scoru-wasm-test-helpers.opam new file mode 100644 index 0000000000000000000000000000000000000000..8452670e9fbb54bcb2b4c2cce3a4a6f03f0459e1 --- /dev/null +++ b/opam/tezos-scoru-wasm-test-helpers.opam @@ -0,0 +1,30 @@ +# This file was automatically generated, do not edit. +# Edit file manifest/main.ml instead. +opam-version: "2.0" +maintainer: "contact@tezos.com" +authors: ["Tezos devteam"] +homepage: "https://www.tezos.com/" +bug-reports: "https://gitlab.com/tezos/tezos/issues" +dev-repo: "git+https://gitlab.com/tezos/tezos.git" +license: "MIT" +depends: [ + "dune" { >= "3.0" } + "ppx_import" + "ppx_deriving" + "tezos-base" + "tezos-tree-encoding" + "tezos-context" + "tezos-base-test-helpers" + "tezos-test-helpers" + "tezos-scoru-wasm" + "qcheck-alcotest" { >= "0.18" } + "alcotest-lwt" { >= "1.5.0" } + "tezt" + "tezos-webassembly-interpreter-extra" +] +build: [ + ["rm" "-r" "vendors"] + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] +synopsis: "helpers for testing for the scoru-wasm functionality" diff --git a/opam/tezos-scoru-wasm-test.opam b/opam/tezos-scoru-wasm-test.opam index cbc156f07753fa291c4578db51799b41b149a4ad..a810c23f7a0e2d2ade5ab907606c79affc3e6e97 100644 --- a/opam/tezos-scoru-wasm-test.opam +++ b/opam/tezos-scoru-wasm-test.opam @@ -20,6 +20,7 @@ depends: [ "qcheck-alcotest" { with-test & >= "0.18" } "alcotest-lwt" { with-test & >= "1.5.0" } "tezt" {with-test} + "tezos-scoru-wasm-test-helpers" {with-test} "tezos-webassembly-interpreter-extra" {with-test} ] build: [ diff --git a/src/lib_scoru_wasm/test/dune b/src/lib_scoru_wasm/test/dune index 84cd43c8581a7448309f86cfd934081fdc6b7e36..31242561c34aea2007bcab078a7c44ffa6e8bc92 100644 --- a/src/lib_scoru_wasm/test/dune +++ b/src/lib_scoru_wasm/test/dune @@ -14,12 +14,14 @@ qcheck-alcotest alcotest-lwt tezt + test_scoru_wasm_test_helpers tezos-webassembly-interpreter-extra) (preprocess (staged_pps ppx_import ppx_deriving.show)) (flags (:standard) -open Tezos_base.TzPervasives -open Tezos_base_test_helpers + -open Test_scoru_wasm_test_helpers -open Tezos_webassembly_interpreter_extra)) (rule diff --git a/src/lib_scoru_wasm/test/ast_generators.ml b/src/lib_scoru_wasm/test/helpers/ast_generators.ml similarity index 100% rename from src/lib_scoru_wasm/test/ast_generators.ml rename to src/lib_scoru_wasm/test/helpers/ast_generators.ml diff --git a/src/lib_scoru_wasm/test/ast_printer.ml b/src/lib_scoru_wasm/test/helpers/ast_printer.ml similarity index 100% rename from src/lib_scoru_wasm/test/ast_printer.ml rename to src/lib_scoru_wasm/test/helpers/ast_printer.ml diff --git a/src/lib_scoru_wasm/test/helpers/dune b/src/lib_scoru_wasm/test/helpers/dune new file mode 100644 index 0000000000000000000000000000000000000000..61d7cef1fd59cfb05c70a742be40fbb90a6cf01f --- /dev/null +++ b/src/lib_scoru_wasm/test/helpers/dune @@ -0,0 +1,25 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(library + (name test_scoru_wasm_test_helpers) + (package tezos-scoru-wasm-test-helpers) + (instrumentation (backend bisect_ppx)) + (libraries + tezos-base + tezos-tree-encoding + tezos-base.unix + tezos-context.disk + tezos-base-test-helpers + tezos-test-helpers + tezos-scoru-wasm + qcheck-alcotest + alcotest-lwt + tezt + tezos-webassembly-interpreter-extra) + (preprocess (staged_pps ppx_import ppx_deriving.show)) + (flags + (:standard) + -open Tezos_base.TzPervasives + -open Tezos_base_test_helpers + -open Tezos_webassembly_interpreter_extra)) diff --git a/src/lib_scoru_wasm/test/test_encodings_util.ml b/src/lib_scoru_wasm/test/helpers/test_encodings_util.ml similarity index 100% rename from src/lib_scoru_wasm/test/test_encodings_util.ml rename to src/lib_scoru_wasm/test/helpers/test_encodings_util.ml diff --git a/src/lib_scoru_wasm/test/test_wasm_pvm_encodings.ml b/src/lib_scoru_wasm/test/helpers/test_wasm_pvm_encodings.ml similarity index 100% rename from src/lib_scoru_wasm/test/test_wasm_pvm_encodings.ml rename to src/lib_scoru_wasm/test/helpers/test_wasm_pvm_encodings.ml diff --git a/src/lib_scoru_wasm/test/wasm_utils.ml b/src/lib_scoru_wasm/test/helpers/wasm_utils.ml similarity index 99% rename from src/lib_scoru_wasm/test/wasm_utils.ml rename to src/lib_scoru_wasm/test/helpers/wasm_utils.ml index 6ff13bc9188a67aa25a703eda35bf67a78da204f..95532b047c68b98e0e5115c060a1ba65ed27a379 100644 --- a/src/lib_scoru_wasm/test/wasm_utils.ml +++ b/src/lib_scoru_wasm/test/helpers/wasm_utils.ml @@ -262,7 +262,7 @@ let test_with_kernel kernel (test : string -> (unit, _) result Lwt.t) () = (* Reading files using `Tezt_lib` can be fragile and not future-proof, see issue https://gitlab.com/tezos/tezos/-/issues/3746. *) let kernel_file = - project_root // Filename.dirname __FILE__ // "wasm_kernels" + project_root // Filename.dirname __FILE__ // "../wasm_kernels" // (kernel ^ ".wasm") in let* () = diff --git a/src/lib_scoru_wasm/wasm_pvm.ml b/src/lib_scoru_wasm/wasm_pvm.ml index 883c1c296d1b5895936151d21b714de70862c73e..b3f6fc2b39f056d087b067272ad5fc9d622cf078 100644 --- a/src/lib_scoru_wasm/wasm_pvm.ml +++ b/src/lib_scoru_wasm/wasm_pvm.ml @@ -723,6 +723,16 @@ module Make (T : Tezos_tree_encoding.TREE) : {pvm_state with reboot_counter = pvm_state.maximum_reboots_per_input} in Tree_encoding_runner.encode pvm_state_encoding pvm_state tree + + let get_output_buffer tree = + let open Lwt.Syntax in + let+ pvm = Tree_encoding_runner.decode pvm_state_encoding tree in + pvm.buffers.output + + let get_input_buffer tree = + let open Lwt.Syntax in + let+ pvm = Tree_encoding_runner.decode pvm_state_encoding tree in + pvm.buffers.input end end diff --git a/src/lib_scoru_wasm/wasm_pvm_sig.ml b/src/lib_scoru_wasm/wasm_pvm_sig.ml index a58de1e57401745cc125d5f139369652cdf38615..a29cc3bfafade1c2ceb33fa22528c59ecffd91a2 100644 --- a/src/lib_scoru_wasm/wasm_pvm_sig.ml +++ b/src/lib_scoru_wasm/wasm_pvm_sig.ml @@ -89,6 +89,12 @@ module type Internal_for_tests = sig val set_maximum_reboots_per_input : Z.t -> tree -> tree Lwt.t val reset_reboot_counter : tree -> tree Lwt.t + + val get_input_buffer : + tree -> Tezos_webassembly_interpreter.Input_buffer.t Lwt.t + + val get_output_buffer : + tree -> Tezos_webassembly_interpreter.Output_buffer.t Lwt.t end (** This module type defines a WASM VM API used for smart-contract rollups. *) diff --git a/src/proto_alpha/lib_protocol/test/unit/dune b/src/proto_alpha/lib_protocol/test/unit/dune index cc9f541a60600a32f4f52b49131223e52e28e6f1..25103a12fff8c513deaaa8f6b77393ad0b68514b 100644 --- a/src/proto_alpha/lib_protocol/test/unit/dune +++ b/src/proto_alpha/lib_protocol/test/unit/dune @@ -15,9 +15,11 @@ tezos-protocol-alpha tezos-alpha-test-helpers alcotest-lwt + test_scoru_wasm_test_helpers tezos-stdlib tezos-crypto-dal - tezos-scoru-wasm) + tezos-scoru-wasm + tezos-webassembly-interpreter-extra) (flags (:standard) -open Tezos_base.TzPervasives @@ -27,8 +29,10 @@ -open Tezos_client_alpha -open Tezos_protocol_alpha -open Tezos_alpha_test_helpers + -open Test_scoru_wasm_test_helpers -open Tezos_stdlib - -open Tezos_crypto_dal)) + -open Tezos_crypto_dal + -open Tezos_webassembly_interpreter_extra)) (rule (alias runtest) diff --git a/src/proto_alpha/lib_protocol/test/unit/test_sc_rollup_wasm.ml b/src/proto_alpha/lib_protocol/test/unit/test_sc_rollup_wasm.ml index 7fc39d919daa7c6dc86ba53d2da4f2104023ae63..b47364aa4e9d3534dfec49466c8064fe02a39eb2 100644 --- a/src/proto_alpha/lib_protocol/test/unit/test_sc_rollup_wasm.ml +++ b/src/proto_alpha/lib_protocol/test/unit/test_sc_rollup_wasm.ml @@ -34,6 +34,60 @@ *) open Protocol +open Tezos_micheline.Micheline +open Michelson_v1_primitives +open Tezos_webassembly_interpreter +module Context = Tezos_context_memory.Context_binary +open Tezos_scoru_wasm +open Wasm_utils + +module Proof_encoding = + Tezos_context_merkle_proof_encoding.Merkle_proof_encoding + +module Wasm_context = struct + module Tree = struct + include Context.Tree + + type tree = Context.tree + + type t = Context.t + + type key = string list + + type value = bytes + end + + type tree = Context.tree + + type proof = Context.Proof.tree Context.Proof.t + + let verify_proof p f = + Lwt.map Result.to_option (Context.verify_tree_proof p f) + + let produce_proof context tree step = + let open Lwt_syntax in + let* context = Context.add_tree context [] tree in + let* _hash = Context.commit ~time:Time.Protocol.epoch context in + let index = Context.index context in + match Context.Tree.kinded_key tree with + | Some k -> + let* p = Context.produce_tree_proof index k step in + return (Some p) + | None -> return None + + let kinded_hash_to_state_hash = function + | `Value hash | `Node hash -> + Sc_rollup_repr.State_hash.context_hash_to_state_hash hash + + let proof_before proof = kinded_hash_to_state_hash proof.Context.Proof.before + + let proof_after proof = kinded_hash_to_state_hash proof.Context.Proof.after + + let proof_encoding = Proof_encoding.V2.Tree32.tree_proof_encoding +end + +module Full_Wasm = + Sc_rollup_wasm.V2_0_0.Make (Environment.Wasm_2_0_0.Make) (Wasm_context) let test_initial_state_hash_wasm_pvm () = let open Alpha_context in @@ -85,6 +139,131 @@ let test_metadata_size () = = Tezos_scoru_wasm.Host_funcs.Internal_for_tests.metadata_size) ; Lwt_result_syntax.return_unit +let make_transaction value text contract = + let entrypoint = Entrypoint_repr.default in + let destination : Contract_hash.t = + Contract_hash.of_bytes_exn @@ Bytes.of_string contract + in + let unparsed_parameters = + strip_locations + @@ Prim + ( 0, + I_TICKET, + [Prim (0, I_PAIR, [Int (0, Z.of_int32 value); String (1, text)], [])], + [] ) + in + Sc_rollup_outbox_message_repr.{unparsed_parameters; entrypoint; destination} + +let make_transactions () = + let l = + QCheck2.Gen.( + generate1 + @@ list_size + (return 3) + (triple (string_size @@ return 20) int32 (small_string ~gen:char))) + in + List.map (fun (contract, i, s) -> make_transaction i s contract) l + +(* This is simple "echo kernel" it spits out whatever it receive. It uses the + [write_output] host function and so it is used to test this function. *) +let test_output () = + let open Lwt_result_syntax in + let rtype_offset = 0 in + let level_offset = 20 in + let id_offset = 40 in + let dst = 60 in + let max_bytes = 3600 in + let modul = + Format.sprintf + {| + (module + (type (;0;) (func (param i32 i32 i32 i32 i32) (result i32))) + (type $t0 (func (param i32 i32) (result i32))) + (type $t3 (func (param i32 i32 i32 i32 i32) (result i32))) + (import "rollup_safe_core" "read_input" (func $read_input (type $t3))) + (import "rollup_safe_core" "write_output" (func $write_output (type $t0))) + (func (export "kernel_next") + (local $size i32) + (local.set $size (call $read_input (i32.const %d) + (i32.const %d) + (i32.const %d) + (i32.const %d) + (i32.const %d))) + (call $write_output (i32.const %d) + (local.get $size)) + drop) + (memory (;0;) 17) + (export "memory" (memory 0)) + ) + + |} + rtype_offset + level_offset + id_offset + dst + max_bytes + dst + in + let*! dummy = Context.init "/tmp" in + let dummy_context = Context.empty dummy in + let*! (empty_tree : Wasm.tree) = Test_encodings_util.empty_tree () in + let parsed = Parse.string_to_module modul in + let parsed = + match parsed.it with Script.Textual m -> m | _ -> assert false + in + let*! code = Encode.encode parsed in + let boot_sector = + Data_encoding.Binary.to_string_exn + Gather_floppies.origination_message_encoding + (Gather_floppies.Complete_kernel (String.to_bytes code)) + in + let*! tree = + Wasm.Internal_for_tests.initial_tree_from_boot_sector + ~empty_tree + boot_sector + in + let*! tree = + Wasm.Internal_for_tests.set_max_nb_ticks (Z.of_int64 50_000_000L) tree + in + let*! tree = eval_until_input_requested tree in + let transactions = make_transactions () in + let out = + Sc_rollup_outbox_message_repr.(Atomic_transaction_batch {transactions}) + in + let string_input_message = + Data_encoding.Binary.to_string_exn + Sc_rollup_outbox_message_repr.encoding + out + in + let*! tree = set_input_step string_input_message 0 tree in + let*! final_tree = eval_until_input_requested tree in + let*! output = Wasm.Internal_for_tests.get_output_buffer final_tree in + let*! level, message_index = + Tezos_webassembly_interpreter.Output_buffer.get_id output + in + let*! bytes_output_message = + Tezos_webassembly_interpreter.Output_buffer.get output level message_index + in + assert (string_input_message = Bytes.to_string bytes_output_message) ; + let message = + Data_encoding.Binary.of_bytes_exn + Sc_rollup_outbox_message_repr.encoding + bytes_output_message + in + assert (message = out) ; + let*? outbox_level = + Environment.wrap_tzresult @@ Raw_level_repr.of_int32 level + in + let output = Sc_rollup_PVM_sig.{outbox_level; message_index; message} in + + let*! pf = Full_Wasm.produce_output_proof dummy_context final_tree output in + + match pf with + | Ok proof -> + let*! valid = Full_Wasm.verify_output_proof proof in + fail_unless valid (Exn (Failure "An output proof is not valid.")) + | Error _ -> failwith "Error during proof generation" + let tests = [ Tztest.tztest @@ -96,4 +275,5 @@ let tests = `Quick test_incomplete_kernel_chunk_limit; Tztest.tztest "size of a rollup metadata" `Quick test_metadata_size; + Tztest.tztest "test output proofs" `Quick test_output; ]