From fcd2370d2295412d0e8f0361cdaa143a84488c48 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 10 May 2024 18:38:55 -0600 Subject: [PATCH 1/4] test-cases/user.wat: add call to storage_load_bytes --- arbitrator/prover/test-cases/dynamic.wat | 3 +-- arbitrator/prover/test-cases/link.wat | 2 +- arbitrator/prover/test-cases/user.wat | 13 +++++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/arbitrator/prover/test-cases/dynamic.wat b/arbitrator/prover/test-cases/dynamic.wat index 97c55ba80b..8771bde87c 100644 --- a/arbitrator/prover/test-cases/dynamic.wat +++ b/arbitrator/prover/test-cases/dynamic.wat @@ -12,8 +12,7 @@ ;; WAVM Module hash (data (i32.const 0x000) - "\87\12\6b\19\8a\ce\0c\ba\00\6a\ab\9b\b7\45\bb\0a\ac\48\4d\6b\b8\b5\f9\03\a2\99\8f\64\00\9f\e2\04") ;; user - + "\a1\49\cf\81\13\ff\9c\95\f2\c8\c2\a1\42\35\75\36\7d\e8\6d\d4\22\d8\71\14\bb\9e\a4\7b\af\53\5d\d7") ;; user (func $start (local $user i32) (local $internals i32) ;; link in user.wat i32.const 0 diff --git a/arbitrator/prover/test-cases/link.wat b/arbitrator/prover/test-cases/link.wat index e033bf0e98..ef15326481 100644 --- a/arbitrator/prover/test-cases/link.wat +++ b/arbitrator/prover/test-cases/link.wat @@ -30,7 +30,7 @@ (data (i32.const 0x140) "\47\f7\4f\9c\21\51\4f\52\24\ea\d3\37\5c\bf\a9\1b\1a\5f\ef\22\a5\2a\60\30\c5\52\18\90\6b\b1\51\e5") ;; iops (data (i32.const 0x160) - "\87\12\6b\19\8a\ce\0c\ba\00\6a\ab\9b\b7\45\bb\0a\ac\48\4d\6b\b8\b5\f9\03\a2\99\8f\64\00\9f\e2\04") ;; user + "\a1\49\cf\81\13\ff\9c\95\f2\c8\c2\a1\42\35\75\36\7d\e8\6d\d4\22\d8\71\14\bb\9e\a4\7b\af\53\5d\d7") ;; user (data (i32.const 0x180) "\ee\47\08\f6\47\b2\10\88\1f\89\86\e7\e3\79\6b\b2\77\43\f1\4e\ee\cf\45\4a\9b\7c\d7\c4\5b\63\b6\d7") ;; return diff --git a/arbitrator/prover/test-cases/user.wat b/arbitrator/prover/test-cases/user.wat index d159339f66..9ecb4dcc45 100644 --- a/arbitrator/prover/test-cases/user.wat +++ b/arbitrator/prover/test-cases/user.wat @@ -2,6 +2,14 @@ ;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE (module + (import "vm_hooks" "storage_load_bytes32" (func $storage_load_bytes32 (param i32 i32))) + + (func $storage_load (result i32) + i32.const 0 + i32.const 32 + call $storage_load_bytes32 + i32.const 0 + ) (func $safe (result i32) i32.const 5 ) @@ -35,6 +43,11 @@ (then (call $out_of_bounds) (return)) ) + (i32.eq (local.get $args_len) (i32.const 32)) + (if + (then (call $storage_load) (return)) + ) + unreachable ) (memory (export "memory") 1 1)) From 5dd329a9da4f9716b88234a36fc76a0ef8388b5a Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 10 May 2024 18:40:09 -0600 Subject: [PATCH 2/4] prover backoff by data for SwitchThread there is an important difference between 0 and non-zero (and only two non-zero values used) --- arbitrator/prover/src/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arbitrator/prover/src/main.rs b/arbitrator/prover/src/main.rs index 697d178fc7..9ddd5020c8 100644 --- a/arbitrator/prover/src/main.rs +++ b/arbitrator/prover/src/main.rs @@ -263,7 +263,10 @@ fn main() -> Result<()> { if opts.proving_backoff { let mut extra_data = 0; - if matches!(next_opcode, Opcode::ReadInboxMessage | Opcode::ReadPreImage) { + if matches!( + next_opcode, + Opcode::ReadInboxMessage | Opcode::ReadPreImage | Opcode::SwitchThread + ) { extra_data = next_inst.argument_data; } let count_entry = proving_backoff From df5dcb53905eeeccf1d97ddcb86db3ec2b97185a Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 10 May 2024 18:41:31 -0600 Subject: [PATCH 3/4] arbos: make more types and lgic public, to be used by tests --- arbos/programs/native.go | 16 ++++++++-------- arbos/programs/programs.go | 28 ++++++++++++++-------------- arbos/programs/wasm.go | 25 ++++++++++++++++++------- arbos/programs/wasm_api.go | 8 ++++---- 4 files changed, 44 insertions(+), 33 deletions(-) diff --git a/arbos/programs/native.go b/arbos/programs/native.go index 09989f3380..c44f8f56cb 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -106,13 +106,13 @@ func callProgram( interpreter *vm.EVMInterpreter, tracingInfo *util.TracingInfo, calldata []byte, - evmData *evmData, - stylusParams *goParams, + evmData *EvmData, + stylusParams *ProgParams, memoryModel *MemoryModel, ) ([]byte, error) { db := interpreter.Evm().StateDB asm := db.GetActivatedAsm(moduleHash) - debug := stylusParams.debugMode + debug := stylusParams.DebugMode if len(asm) == 0 { log.Error("missing asm", "program", address, "module", moduleHash) @@ -236,18 +236,18 @@ func goSlice(slice []byte) C.GoSliceData { } } -func (params *goParams) encode() C.StylusConfig { +func (params *ProgParams) encode() C.StylusConfig { pricing := C.PricingParams{ - ink_price: u32(params.inkPrice.ToUint32()), + ink_price: u32(params.InkPrice.ToUint32()), } return C.StylusConfig{ - version: u16(params.version), - max_depth: u32(params.maxDepth), + version: u16(params.Version), + max_depth: u32(params.MaxDepth), pricing: pricing, } } -func (data *evmData) encode() C.EvmData { +func (data *EvmData) encode() C.EvmData { return C.EvmData{ block_basefee: hashToBytes32(data.blockBasefee), chainid: u64(data.chainId), diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 779f2d6c67..3f7bdc39ca 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -181,7 +181,7 @@ func (p Programs) CallProgram( if err != nil { return nil, err } - goParams := p.goParams(program.version, debugMode, params) + goParams := p.progParams(program.version, debugMode, params) l1BlockNumber, err := evm.ProcessingHook.L1BlockNumber(evm.Context) if err != nil { return nil, err @@ -205,7 +205,7 @@ func (p Programs) CallProgram( statedb.AddStylusPages(program.footprint) defer statedb.SetStylusPagesOpen(open) - evmData := &evmData{ + evmData := &EvmData{ blockBasefee: common.BigToHash(evm.Context.BaseFee), chainId: evm.ChainConfig().ChainID.Uint64(), blockCoinbase: evm.Context.Coinbase, @@ -444,23 +444,23 @@ func (p Program) cachedGas(params *StylusParams) uint64 { return am.SaturatingUAdd(base, am.DivCeil(dyno, 100)) } -type goParams struct { - version uint16 - maxDepth uint32 - inkPrice uint24 - debugMode bool +type ProgParams struct { + Version uint16 + MaxDepth uint32 + InkPrice uint24 + DebugMode bool } -func (p Programs) goParams(version uint16, debug bool, params *StylusParams) *goParams { - return &goParams{ - version: version, - maxDepth: params.MaxStackDepth, - inkPrice: params.InkPrice, - debugMode: debug, +func (p Programs) progParams(version uint16, debug bool, params *StylusParams) *ProgParams { + return &ProgParams{ + Version: version, + MaxDepth: params.MaxStackDepth, + InkPrice: params.InkPrice, + DebugMode: debug, } } -type evmData struct { +type EvmData struct { blockBasefee common.Hash chainId uint64 blockCoinbase common.Address diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index 77eb7e0f2f..1e9b5e680b 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -135,14 +135,26 @@ func callProgram( interpreter *vm.EVMInterpreter, tracingInfo *util.TracingInfo, calldata []byte, - evmData *evmData, - params *goParams, + evmData *EvmData, + params *ProgParams, memoryModel *MemoryModel, ) ([]byte, error) { reqHandler := newApiClosures(interpreter, tracingInfo, scope, memoryModel) + gasLeft, retData, err := CallProgramLoop(moduleHash, calldata, scope.Contract.Gas, evmData, params, reqHandler) + scope.Contract.Gas = gasLeft + return retData, err +} + +func CallProgramLoop( + moduleHash common.Hash, + calldata []byte, + gas uint64, + evmData *EvmData, + params *ProgParams, + reqHandler RequestHandler) (uint64, []byte, error) { configHandler := params.createHandler() dataHandler := evmData.createHandler() - debug := params.debugMode + debug := params.DebugMode module := newProgram( unsafe.Pointer(&moduleHash[0]), @@ -150,7 +162,7 @@ func callProgram( uint32(len(calldata)), configHandler, dataHandler, - scope.Contract.Gas, + gas, ) reqId := startProgram(module) for { @@ -162,12 +174,11 @@ func callProgram( popProgram() status := userStatus(reqTypeId) gasLeft := arbmath.BytesToUint(reqData[:8]) - scope.Contract.Gas = gasLeft data, msg, err := status.toResult(reqData[8:], debug) if status == userFailure && debug { - log.Warn("program failure", "err", err, "msg", msg, "program", address) + log.Warn("program failure", "err", err, "msg", msg, "moduleHash", moduleHash) } - return data, err + return gasLeft, data, err } reqType := RequestType(reqTypeId - EvmApiMethodReqOffset) diff --git a/arbos/programs/wasm_api.go b/arbos/programs/wasm_api.go index fb0f731402..d7bac056c0 100644 --- a/arbos/programs/wasm_api.go +++ b/arbos/programs/wasm_api.go @@ -38,12 +38,12 @@ func createEvmData( reentrant uint32, ) evmDataHandler -func (params *goParams) createHandler() stylusConfigHandler { - debug := arbmath.BoolToUint32(params.debugMode) - return createStylusConfig(uint32(params.version), params.maxDepth, params.inkPrice.ToUint32(), debug) +func (params *ProgParams) createHandler() stylusConfigHandler { + debug := arbmath.BoolToUint32(params.DebugMode) + return createStylusConfig(uint32(params.Version), params.MaxDepth, params.InkPrice.ToUint32(), debug) } -func (data *evmData) createHandler() evmDataHandler { +func (data *EvmData) createHandler() evmDataHandler { return createEvmData( arbutil.SliceToUnsafePointer(data.blockBasefee[:]), data.chainId, From f2448fede36f35df82db94930ea35b7ca1ebe426 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 10 May 2024 18:42:57 -0600 Subject: [PATCH 4/4] test-cases/go: add stylus contract this is the only test that checks 5 recursive stylus calls with one-step-proofs for each relevant SwitchThread --- Makefile | 10 ++++-- arbitrator/prover/test-cases/go/main.go | 44 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 36b55bed6e..53b89c8d72 100644 --- a/Makefile +++ b/Makefile @@ -289,7 +289,7 @@ $(arbitrator_jit): $(DEP_PREDICATE) $(jit_files) $(arbitrator_cases)/rust/$(wasm32_wasi)/%.wasm: $(arbitrator_cases)/rust/src/bin/%.rs $(arbitrator_cases)/rust/src/lib.rs cargo build --manifest-path $(arbitrator_cases)/rust/Cargo.toml --release --target wasm32-wasi --bin $(patsubst $(arbitrator_cases)/rust/$(wasm32_wasi)/%.wasm,%, $@) -$(arbitrator_cases)/go/testcase.wasm: $(arbitrator_cases)/go/*.go +$(arbitrator_cases)/go/testcase.wasm: $(arbitrator_cases)/go/*.go .make/solgen cd $(arbitrator_cases)/go && GOOS=wasip1 GOARCH=wasm go build -o testcase.wasm $(arbitrator_generated_header): $(DEP_PREDICATE) $(stylus_files) @@ -439,8 +439,12 @@ target/testdata/preimages.bin: contracts/test/prover/proofs/rust-%.json: $(arbitrator_cases)/rust/$(wasm32_wasi)/%.wasm $(prover_bin) $(arbitrator_wasm_libs) target/testdata/preimages.bin $(prover_bin) $< $(arbitrator_wasm_lib_flags) -o $@ -b --allow-hostapi --require-success --inbox-add-stub-headers --inbox $(arbitrator_cases)/rust/data/msg0.bin --inbox $(arbitrator_cases)/rust/data/msg1.bin --delayed-inbox $(arbitrator_cases)/rust/data/msg0.bin --delayed-inbox $(arbitrator_cases)/rust/data/msg1.bin --preimages target/testdata/preimages.bin -contracts/test/prover/proofs/go.json: $(arbitrator_cases)/go/testcase.wasm $(prover_bin) $(arbitrator_wasm_libs) target/testdata/preimages.bin $(arbitrator_tests_link_deps) - $(prover_bin) $< $(arbitrator_wasm_lib_flags) -o $@ -i 50000000 --require-success --preimages target/testdata/preimages.bin +contracts/test/prover/proofs/go.json: $(arbitrator_cases)/go/testcase.wasm $(prover_bin) $(arbitrator_wasm_libs) target/testdata/preimages.bin $(arbitrator_tests_link_deps) $(arbitrator_cases)/user.wasm + $(prover_bin) $< $(arbitrator_wasm_lib_flags) -o $@ -b --require-success --preimages target/testdata/preimages.bin --stylus-modules $(arbitrator_cases)/user.wasm + +# avoid testing user.wasm in onestepproofs. It can only run as stylus program. +contracts/test/prover/proofs/user.json: + echo "[]" > $@ # avoid testing read-inboxmsg-10 in onestepproofs. It's used for go challenge testing. contracts/test/prover/proofs/read-inboxmsg-10.json: diff --git a/arbitrator/prover/test-cases/go/main.go b/arbitrator/prover/test-cases/go/main.go index 0df8010449..1f81553af2 100644 --- a/arbitrator/prover/test-cases/go/main.go +++ b/arbitrator/prover/test-cases/go/main.go @@ -1,6 +1,9 @@ // Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE +//go:build wasm +// +build wasm + package main import ( @@ -19,6 +22,7 @@ import ( merkletree "github.com/wealdtech/go-merkletree" "github.com/offchainlabs/nitro/arbcompress" + "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/wavmio" ) @@ -69,11 +73,51 @@ const BYTES_PER_FIELD_ELEMENT = 32 var BLS_MODULUS, _ = new(big.Int).SetString("52435875175126190479447740508185965837690552500527637822603658699938581184513", 10) +var stylusModuleHash = common.HexToHash("a149cf8113ff9c95f2c8c2a1423575367de86dd422d87114bb9ea47baf535dd7") // user.wat + +func callStylusProgram(recurse int) { + evmData := programs.EvmData{} + progParams := programs.ProgParams{ + MaxDepth: 10000, + InkPrice: 1, + DebugMode: true, + } + reqHandler := func(req programs.RequestType, input []byte) ([]byte, []byte, uint64) { + fmt.Printf("got request type %d req %v\n", req, input) + if req == programs.GetBytes32 { + if recurse > 0 { + callStylusProgram(recurse - 1) + } + answer := common.Hash{} + return answer[:], nil, 1 + } + + panic("unsupported call") + } + calldata := common.Hash{}.Bytes() + _, _, err := programs.CallProgramLoop( + stylusModuleHash, + calldata, + 160000000, + &evmData, + &progParams, + reqHandler) + if err != nil { + panic(err) + } +} + func main() { fmt.Printf("starting executable with %v arg(s): %v\n", len(os.Args), os.Args) runtime.GC() time.Sleep(time.Second) + fmt.Printf("Stylus test\n") + + callStylusProgram(5) + + fmt.Printf("Stylus test done!\n") + // Data for the tree data := [][]byte{ []byte("Foo"),