From 4513500e5f151287c36cadb252db121fa758d357 Mon Sep 17 00:00:00 2001 From: Emma Turner Date: Mon, 11 Jul 2022 17:04:05 +0100 Subject: [PATCH] Scoru,wasm: test gather floppies proof generation --- manifest/main.ml | 3 + opam/tezos-protocol-alpha-tests.opam | 2 +- .../lib_protocol/test/integration/dune | 2 + .../test/integration/test_sc_rollup_wasm.ml | 106 +++++++++++++++--- .../test/integration/wasm_kernel/README.md | 43 +++++++ .../integration/wasm_kernel/computation.wasm | Bin 0 -> 9863 bytes 6 files changed, 137 insertions(+), 19 deletions(-) create mode 100644 src/proto_alpha/lib_protocol/test/integration/wasm_kernel/README.md create mode 100644 src/proto_alpha/lib_protocol/test/integration/wasm_kernel/computation.wasm diff --git a/manifest/main.ml b/manifest/main.ml index 4f938bc961b6..5174e8851925 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 495c0121aa9a..23bb4812e2fa 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 3136d7705e9b..fafe56f8b6ee 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 fb2f2f21ee66..b0619b5aa351 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 000000000000..d0ab49dbdac4 --- /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 GIT binary patch literal 9863 zcmZQbEY4+QU|?YM;z?ktuV<_WkqimU^~RN%ErjR%ErRZz`@DH4x*SjSXfwC*%`UG7+6>tI2ajO7#SHExfz)l z7#JBDnHd=wSeRHDnYb7jI5;>MxEYw3SXdbg7-eNynHd=b*(90k866uM4lr=nGdND^ zfzS&;G`leiTW)G@eo-Y819x_6QC@0Jd|qlr2?GOHe0)k`Nn(6zUJ5fKcYJ(CYGOfr zQetr`GZUv0BZDI&w+y2VCl@!5ppdYDvbcM%H@e zhK7a)21kKxB?c8RQ(y{6fXRW$!I43O=|S^>1_oXRM+V1wGYAJJ^8_TL#OTV)puniX z#Nf!7rN{)*@dhNW$P8k>05e&57`Yu;99aaKKn8@-KlEU3E_7#sz%AYS0- zcHGi%0OTtL21kx8$0K0Q5)dhH2;>L`h=Vpl*h~!E!iLSkRS`v7X5Rs+L)S38Ges1r(kv3JeO&3XBSj3e4Wp0!KmWrJyPqKw$$lLV<;w ziGjNuY!H)!0+SIz@ zkt2fwrvj%g1A`-Dp|m0+HPatO2N#9V6z-+3PJt_u~`(kxD{9xxEz@a!FoYr3`(3} zE(4Q;0;eNWA;&?B}T{pjNZJUOa@Kspx9z6 z$#P`L0wq?*1}0BwB?b*91~VoePznQOBuAbs1!hob@&;Uv^n!Aw638}Yu&@HF0+YZ* zkf0-jBBKJc1``9w0#IrL^LQYciqVXT2c%Si8Jl^EjNn|R!Ng<6!~lvvW(7=FI9>rc z0744Dox$Xwzyitxj0#Nf0)Rz23gF6v$rY6H6__E>!{o@E<;YY9iX>)eB!L|T zilB-tB_;(XL~>(M0L2feSaO8K9XQGqnZfCR#gQjVU@|B^z~+Nex?@e25-Ui1O`#GS zD0Q=e$}4to83fV;N+ci=4sbYva+xA0C=%EdIYFsHfdiZ`I6yi%6gZ&yf(?`}z=>Fi z5u}gPm6s9X$eKb$MsWUAWan1kRA5wKcVvc_EDWFkV^v^;6eS7}e<{JZjNo#GQGrbX z7#fkS~&0aWaQ z%Qlc|Q1G#n8hl`vfr82rRD#0Y!>GWF6mrankYh%K9D@R=00Mc35zJCx05?&%6`#|8lf5aBJY#OTPP07|=#EsdZS z448yuF(pt=QD6nt4Y1;Y1)@rU(UC=<3zXKu2D4}|F@QoDRVk<>VuEVsP+$Q$p9iE7 zhi!^X2&)vBcp14J86l-A6Q~+t0i{?4MsU6Z*Lt8r4^(zBf$B+c%Z>p#Xjm8&7(s0X z22dceFff2=EpW?4fx(d_3zY4^r2(k8Qv$mll;ar%nnAwe=7uQ+c>PBq$h} z9R)xYup_8~0yT=vm_$HzG`MyU0Hp|KQ05Wf1{IQyjG*>ZmcT5KvzZ(~$sFuLP{jfA zBLgUTGi!iaQ3BA`CrAy8A`>_mAPomd(o$e{RDhPD{M-VYKn5~!Zv}Y{ixIhQD#F)UQD>3u3DlqagD=;ZC^RR%TiP4OS1*D!E6mL9i+zO!l3X)|6 zH@+cFbx=#3*_D@(hY3_KgIbe}0xckSC^7SbD=LsFu*w$PR%Zb<>_O(UfC?T^+Z*g7 z?%Rxr`dT4di3w2yID*@$*y04#0A|J&Crrq3!lb|giW4SqQDw#ijuR$moG>XcI)bW1 z0YsdDN*7SPG9i)>xWr~rV1UO7lLixuA~Q;yFoV3rge6XR7@WR6`myoT!}ZTuxp^6! zF0Pw#WM=cG?=AJ*-+U?MM5ALEVfyyi;=4=HUV&8M*(t?d32B>(B zL`k@ekbqTS5ZDh=;P?b=>_QM5RB{P`{U)#o%m?LLCQxIS5flQT#w0lBFbd2ENi%S7 zWMX2eXRL$9fdM>Mfa;`iG$U7&P>uz~I;dauO)zKx{@kOX)LL4#=mhz%OkVgrp{F*q_>L8Q4sO-D0`G)SKUyCZ`K zsH@<}1nN+-DzI2FNGPy^8rUMBex4bV04SsV0B1CI$9f;|2nEP;C3Z6=urOqxtpQ{q zxQPc+{|BUAkzIimG>ilq3xH%y1y%)CO=bpA24DyGk3l^R5ooRj=>r?6$PRKPyJLM8 z)CL7MM{swSg_n_=0aQvTFbb>&C3^;LLqYIUW$Q% zJA#o3G}Z%ds4+tdNpRMH6yXfn3QV9P95jf*B!EQ2Fi1 zz%2wVvl#{Ef$ef+;4VS94$*8-fK~L+vOyqA0VOGeQ$MH~2T$D!jNqgU@(Bwf?{j29 z^1dP{%fOmi3XHJvybT~nFu-`ND7?)Oo*#YN|1VXmG)trN|1a?3CC*T}VZC1x7^aQzQ0s!N1jGcldU#5TKx}45`@$jx4#)qDg^og?@yrhlg+-1H z4M!N{&Vt42}*Sps3&g2RJjx?Mw>H(x3rX&^Rx2EE81k;f@D>ZUqKM z9&kg273>fuM*)y`7!{ZV4ufI}<|ZbvgTTFSCP(H%MRus_`YcCge~6p{BV_oO3DnVM zb7TNHRFN4Jx!@Re++Xhn8t-BRI~~elLgp~TIf@(#pn^b=S%D2SJOGIWc2`~oRMWu9 z7_&g30*WklP-KAz*EoE^PGVMIRp7{0Vpn7XxfWC^Dlq6WC@66#a5yr%NjowZ@-Z_p zFfxHsBAWs`#QE%wjAdXG8M8nvHqg)$*bY!G1PvargE9n*BDe`Gp~%4G!0e#F46+~8 zvr%AB;80)(m9LPoR##pIZbwFbP+yl>fdiV{7(r#Q0ux9bEJJ`Z4l}a@lY;`A0yC)b z&8EQS$&auc(^#;j5Jh++n3+2SrBs2nP?Mf1)G%WbP+$g?DxkI_qZyM7 zqzNej8o76r0F|i5p)oT)v;$8$)u7V6@a@+$kpGScSW)-OD zHeOI~iCY2G>40|MK`jo}5>R?_tj_|u#l8^KOkjb!)R6(rO_1)t1{2gx3?TO*yNMap zV_;Qa%TfZRSX~ANaKM3ko|;SyP70l%z+-Tf0Oxv6kSzj=T#gKipj-gX)}R(YB*gfb z85x)uxj_SEC5k+b42tXuTnao{N<5&U${C=jbLVAL-~kuy42qnfRL%~vofFiIW>#Pa zMLuW}4V-Nt@d7JvU`De$GM6ZFDsU-qW+`!^7z>&~0c!*q%MCJ?9g-%wLDe4@C>Js~ zGM8k5x=WyL5l0qChy@n>pk63A+&L8392K&Z*xW(IgCm0?XyOml8}|fF9D)Zn6hMIh z8eL*80gu*8D}km{KvB+u7PzjEVJlG61KyZcVBqFqaBAGKYVz05Pv3}egJN8RNdl74 z9r+X(9c4g4jYu`1Mkb`E2X?T)c2Gcavx6He98j7EOzScTz&oB`9V{SIKv{r+fqOC| zA_pE|fHzA)(=ni?7AvHwh0<_TV0F~Vas&<0DzG}*WI2LcQ=ry1s}qwslZ*nZ;{`}Y zl~7=H1dlkGF`0mxh>iwXW=tAjmOz#nlLeRs>Ma>4usTX)DX_p=9E&*^IqN|&t-t^p zcdsc_Vp3qpR$^8FHy4>5SwL-CMg`_ND4Dj9izbD zSYHgUW*8M1AVbcep;yp2up(H5*#Xqh09A_MnuW;`shR-|iz3M&RWqP=8cUWUlL7~* zn&AK?5Cu?{M>P%HoMQwj1GRvaz|{CCqap)rZ7v132Kibn*uAO!NjJ( zq{F}nuKd7)OrTa2Sda-S2paKa0#!L|3XECcNlj4ykJ*(MG;?|YH2imf(NkIp5>N^( zpb1dOgeQyRAI3r@F3=<`sQV5L7d8ckEYJ+8B9j7}BB;WIR`5!Uj`hWk>}3j!j%;N` zj*U$%9EIQ}Bd8d{E(aY&#ikFJCZ?>cEKo>+a=j9R0y{Xw22H(zifZOUB~bi= z=QbcsRnQC-q!q#h8c1Pu{PX|+fA&H}M$r5-vjeQmQDTRBg&j)}V3&gg0aiIs5MYx) z4gyeF07_bnpri#$+$@fuzE@3_A|uFYxQzh~ggCN*0su7m4VoF`22~vR0*{{?G%W>c z{P1&w=Q0!_O=^$<@P-;Bh(NO;phgq+q!P0Nt0&lCRs|*nmTV<<&}|fI*2(fu#gIT?A?!C^9OrIIHzu+d<8%EJapOds2ZBTycXM4~(G31FBCMxcR_KL=>0=j({rRt&&sMaue>O+KtnS@AwN$cQ6a%EKQA>wp)4_{G&LtP zsVEWT@RAg;;|mh=GLy42^U^^cIL;uzAj81G;KszjAkVP`82j{9xaMLIC0yK?Vi}Av86ha1nuuf%Jf|C<6n77y|=?I0FL%%yEf1Ir+(8 z=M^QE$Csrh>lGCzCl(|oXO>hdxurRC(8GcYhTFbXh$>`7sSxSNfMfdLeTPT=tN jEiG{^D#|ZnV30-%H<16V6c`xRFbaT0&oBxwfZ_rG<@9y$ literal 0 HcmV?d00001 -- GitLab