diff --git a/manifest/main.ml b/manifest/main.ml index 4f938bc961b654a9749550108f96148bbb65b779..5174e88519255f5b1a823f6a6ec6767e1435e637 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2021-2022 Nomadic Labs *) +(* Copyright (c) 2022 Trili Tech *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -3282,6 +3283,7 @@ end = struct ~opam:(sf "tezos-protocol-%s-tests" name_dash) ~deps: [ + (if N.(number >= 015) then Some tezt_lib else None) |> if_some; octez_context; alcotest_lwt; octez_base |> open_ ~m:"TzPervasives" @@ -3293,6 +3295,7 @@ end = struct test_helpers |> if_some |> open_; octez_base_test_helpers |> open_; ] + ~dep_globs:(if N.(number >= 015) then ["wasm_kernel/*.wasm"] else []) in let _pbt = tests diff --git a/opam/tezos-protocol-alpha-tests.opam b/opam/tezos-protocol-alpha-tests.opam index 495c0121aa9ada1a8212972639d5db2979b2037f..23bb4812e2faf6370ed75a45e42a4ceb8468daa4 100644 --- a/opam/tezos-protocol-alpha-tests.opam +++ b/opam/tezos-protocol-alpha-tests.opam @@ -21,6 +21,7 @@ depends: [ "tezos-benchmark-alpha" {with-test} "tezos-benchmark-type-inference-alpha" {with-test} "qcheck-alcotest" { with-test & >= "0.18" } + "tezt" {with-test} "tezos-context" {with-test} "tezos-test-helpers" {with-test} "alcotest" { with-test & >= "1.5.0" } @@ -28,7 +29,6 @@ depends: [ "tezos-protocol-environment" {with-test} "tezos-stdlib-unix" {with-test} "tezos-stdlib" {with-test} - "tezt" {with-test} ] build: [ ["rm" "-r" "vendors"] diff --git a/src/proto_alpha/lib_protocol/test/integration/dune b/src/proto_alpha/lib_protocol/test/integration/dune index 3136d7705e9bf52dcbb28157607cc9bf6e6e2e55..fafe56f8b6ee081fce1a40fa91b86bcb27ed9bfc 100644 --- a/src/proto_alpha/lib_protocol/test/integration/dune +++ b/src/proto_alpha/lib_protocol/test/integration/dune @@ -4,6 +4,7 @@ (executable (name main) (libraries + tezt tezos-context alcotest-lwt tezos-base @@ -25,4 +26,5 @@ (rule (alias runtest) (package tezos-protocol-alpha-tests) + (deps (glob_files wasm_kernel/*.wasm)) (action (run %{dep:./main.exe}))) diff --git a/src/proto_alpha/lib_protocol/test/integration/test_sc_rollup_wasm.ml b/src/proto_alpha/lib_protocol/test/integration/test_sc_rollup_wasm.ml index fb2f2f21ee66d9614697b140bb6a7fefcecc3bb5..b0619b5aa351e09d561361fee2c9d59edc788023 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_sc_rollup_wasm.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_sc_rollup_wasm.ml @@ -110,6 +110,37 @@ module Verifier = Alpha_context.Sc_rollup.Wasm_2_0_0PVM.ProtocolImplementation module Prover = Alpha_context.Sc_rollup.Wasm_2_0_0PVM.Make (WASM_P) (* Helpers *) +(* FIXME: https://gitlab.com/tezos/tezos/-/issues/2198 + SCORU system should expose a constant upper bound for proof size. + One suggestion for this constant is 16KB - so is used here. +*) +let proof_size_limit = 1024 * 1024 * 16 + +let check_proof_size ~loc context input_opt s = + let open Lwt_result_syntax in + let*! proof = Prover.produce_proof context input_opt s in + match proof with + | Error _ -> Stdlib.failwith "missing proof" + | Ok proof -> + let bytes = + Data_encoding.Binary.to_bytes_exn Prover.proof_encoding proof + in + Assert.leq_int ~loc (Bytes.length bytes) proof_size_limit + +(* Like [eval] but also checks the proof size. *) +let checked_eval ~loc context s = + let open Lwt_result_syntax in + let*! s = Prover.eval s in + let+ () = check_proof_size ~loc context None s in + s + +(* Like [set_input] but also checks the proof size. *) +let checked_set_input ~loc context input s = + let open Lwt_result_syntax in + let*! s = Prover.set_input input s in + let+ () = check_proof_size ~loc context (Some input) s in + s + let complete_boot_sector sector : Tezos_scoru_wasm.Gather_floppies.origination_message = Complete_kernel (Bytes.of_string sector) @@ -189,7 +220,7 @@ let should_boot_complete_boot_sector boot_sector () = let* () = check_chunks_count s 0l in (* At this step, the [eval] function of the PVM will interpret the origination message encoded in [boot_sector]. *) - let*! s = Prover.eval s in + let* s = checked_eval ~loc:__LOC__ context s in (* We expect that the WASM does not expect more floppies, and that the kernel as been correctly splitted into several chunks. *) let*! () = check_status s (Some Not_gathering_floppies) in @@ -237,11 +268,11 @@ let should_interpret_empty_chunk () = let*! s = Prover.initial_state context in let*! s = Prover.install_boot_sector s origination_message in (* Intererptation of the origination message *) - let*! s = Prover.eval s in + let* s = checked_eval ~loc:__LOC__ context s in let*! () = check_status s (Some Gathering_floppies) in let* () = check_chunks_count s 1l in (* Try to interpret the empty input (correctly signed) *) - let*! s = Prover.set_input correct_input s in + let* s = checked_set_input ~loc:__LOC__ context correct_input s in let*! () = check_status s (Some Not_gathering_floppies) in (* We still have 1 chunk. *) let* () = check_chunks_count s 1l in @@ -267,55 +298,81 @@ let should_refuse_chunks_with_incorrect_signature () = let*! s = Prover.initial_state context in let*! s = Prover.install_boot_sector s origination_message in (* Intererptation of the origination message *) - let*! s = Prover.eval s in + let* s = checked_eval ~loc:__LOC__ context s in let*! () = check_status s (Some Gathering_floppies) in let* () = check_chunks_count s 1l in (* Try to interpret the incorrect input (badly signed) *) - let*! s = Prover.set_input incorrect_input s in + let* s = checked_set_input ~loc:__LOC__ context incorrect_input s in let*! () = check_status s (Some Gathering_floppies) in (* We still have 1 chunk. *) let* () = check_chunks_count s 1l in (* Try to interpret the correct input (correctly signed) *) - let*! s = Prover.set_input correct_input s in + let* s = checked_set_input ~loc:__LOC__ context correct_input s in let*! () = check_status s (Some Gathering_floppies) in (* We now have 2 chunks. *) let* () = check_chunks_count s 2l in return_unit -let should_boot_incomplete_boot_sector () = +let should_boot_incomplete_boot_sector kernel () = let open Lwt_result_syntax in let operator = operator () in let chunk_size = Tezos_scoru_wasm.Gather_floppies.chunk_size in + let initial_chunk, rem_chunks = + let split_chunk s = + let len = String.length s in + let size = min len chunk_size in + let chunk = String.sub s 0 size in + let rest = + if len > chunk_size then Some (String.sub s size @@ (len - size)) + else None + in + (chunk, rest) + in + let rec do_chunks chunks left = + match left with + | None -> chunks + | Some left -> + let chunk, rest = split_chunk left in + (do_chunks [@tailcall]) (chunk :: chunks) rest + in + let initial, rest = split_chunk kernel in + (initial, List.rev @@ do_chunks [] rest) + in let initial_chunk = Data_encoding.Binary.to_string_exn Tezos_scoru_wasm__Gather_floppies.origination_message_encoding - @@ incomplete_boot_sector (String.make chunk_size 'a') operator + @@ incomplete_boot_sector initial_chunk operator + in + let chunks = + rem_chunks + |> List.take_n (List.length rem_chunks - 1) + |> List.map Bytes.of_string in - let chunks = [Bytes.make chunk_size 'b'; Bytes.make chunk_size 'c'] in - let final_chunk = Bytes.make 2 'd' in + let final_chunk = Bytes.of_string @@ List.last "" rem_chunks in let*! index = Context_binary.init "/tmp" in let context = Context_binary.empty index in let*! s = Prover.initial_state context in let*! s = Prover.install_boot_sector s initial_chunk in + let* () = check_proof_size ~loc:__LOC__ context None s in let*! () = check_status s None in let* () = check_chunks_count s 0l in (* First tick, to interpret the boot sector. One chunk have been provided, and the PVM expects more chunk to come. *) - let*! s = Prover.eval s in + (* First tick, to interpret the boot sector*) + let* s = checked_eval ~loc:__LOC__ context s in let*! () = check_status s (Some Gathering_floppies) in let* () = check_chunks_count s 1l in - (* Then, installing the additional chunks. *) + (* Then, installing the additional chunks *) let* s = List.fold_left_i_es (fun i s chunk -> (* We are installing the [i+2]th chunk ([i] starts at 0, and the first chunk is not part of the list). *) let* input = floppy_input i operator chunk in - let*! s = Prover.set_input input s in - (* We are still gathering floppies. *) - let*! () = check_status s (Some Gathering_floppies) in + let* s = checked_set_input ~loc:__LOC__ context input s in (* We have [i+2] chunks. *) + let*! () = check_status s (Some Gathering_floppies) in let* () = check_chunks_count s Int32.(of_int @@ (i + 2)) in return s) s @@ -324,16 +381,29 @@ let should_boot_incomplete_boot_sector () = (* Up until the very last one, where the status of the PVM change. *) let len = List.length chunks in let* input = floppy_input len operator final_chunk in - let*! s = Prover.set_input input s in + let* s = checked_set_input ~loc:__LOC__ context input s in let*! () = check_status s (Some Not_gathering_floppies) in let* () = check_chunks_count s Int32.(of_int @@ (len + 2)) in return_unit +(* Read the chosen `wasm_kernel` into memory. *) +let read_kernel name = + let open Tezt.Base in + let kernel_file = + project_root // Filename.dirname __FILE__ // "wasm_kernel" + // (name ^ ".wasm") + in + read_file kernel_file + +(* Kernel with allocation & simple computation only. + 9863 bytes long - will be split into 3 chunks. *) +let computation_kernel () = read_kernel "computation" + let tests = [ Tztest.tztest "should boot a complete boot sector" `Quick @@ should_boot_complete_boot_sector - (complete_boot_sector @@ String.make 10_000 'a'); + (complete_boot_sector @@ computation_kernel ()); ( Tztest.tztest "should boot an incomplete but too small boot sector" `Quick @@ fun () -> let operator = operator () in @@ -343,7 +413,7 @@ let tests = Tztest.tztest "should boot an incomplete boot sector with floppies" `Quick - should_boot_incomplete_boot_sector; + (should_boot_incomplete_boot_sector @@ computation_kernel ()); Tztest.tztest "should interpret an empty chunk as EOF" `Quick diff --git a/src/proto_alpha/lib_protocol/test/integration/wasm_kernel/README.md b/src/proto_alpha/lib_protocol/test/integration/wasm_kernel/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d0ab49dbdac4a78f0d395a02e03c322d3e741cfb --- /dev/null +++ b/src/proto_alpha/lib_protocol/test/integration/wasm_kernel/README.md @@ -0,0 +1,43 @@ +# About +This folder contains example test kernels, used for running `SCORU WASM` integration tests. + +The test kernels have been built from [trili/kernel](https://gitlab.com/trili/kernel.git). + +# Available kernels +It is possible to build the test kernels manually, and verify that they are bit-for-bit identical. + +## Prerequisites +You will need `docker`, `git` and `wasm-strip` installed, alongside either `bash` or `zsh`. +- `wasm-strip` is part of the [WebAssembly Binary Toolkit](https://github.com/WebAssembly/wabt). + +Next, clone the *trili/kernel* repository: +``` shell +git clone https://gitlab.com/trili/kernel.git wasm_kernel +cd wasm_kernel +``` +and then follow the instructions below for the required kernel. + +## [computation.wasm](./computation.wasm) +The computation kernel performs a simple computation (addition) on each call to its `kernel_next` entrypoint. +It keeps the result on the heap, and therefore uses the allocator. It makes no use of any *PVM host-capabilities*. + +It is designed to be small enough to be able to originate directly within a boot sector, but also large enough to be +used with the *gather-floppies* mechanism. + +To build the `computation.wasm` kernel, run the following from the checked-out `trili/kernel` repo: +``` shell +git checkout 60e2dedc2b5debb9a6add98038e52e4cd0a358a6 + +# Load the required rust toolchain dockerfile +source scripts/cargo-docker.sh + +cargo build -p test_kernel --target wasm32-unknown-unknown --release \ + --no-default-features --features none,wee_alloc + +# computation_kernel.wasm is a 1.6M wasm binary. +cp target/wasm32-unknown-unknown/release/test_kernel.wasm computation_kernel.wasm + +# Strips binary down to 9.7K +wasm-strip computation_kernel.wasm +``` + diff --git a/src/proto_alpha/lib_protocol/test/integration/wasm_kernel/computation.wasm b/src/proto_alpha/lib_protocol/test/integration/wasm_kernel/computation.wasm new file mode 100644 index 0000000000000000000000000000000000000000..da2df75b0e18b30e75d76eb42438c28f1d2c6148 Binary files /dev/null and b/src/proto_alpha/lib_protocol/test/integration/wasm_kernel/computation.wasm differ