From 7e18748dbddd59b3d77b83be8c4d4fa4ff917acd Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 26 Jul 2023 12:23:02 -0600 Subject: [PATCH 01/55] prover: use module directly for user compilation --- arbitrator/prover/src/machine.rs | 73 ++++++++++++------- .../wasm-libraries/user-host/src/link.rs | 31 +++----- 2 files changed, 58 insertions(+), 46 deletions(-) diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index d1f515f20..64fec6757 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -24,6 +24,7 @@ use digest::Digest; use eyre::{bail, ensure, eyre, Result, WrapErr}; use fnv::FnvHashMap as HashMap; use itertools::izip; +use lazy_static::lazy_static; use num::{traits::PrimInt, Zero}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; @@ -263,7 +264,7 @@ impl AvailableImport { } #[derive(Clone, Debug, Default, Serialize, Deserialize)] -struct Module { +pub struct Module { globals: Vec, memory: Memory, tables: Vec, @@ -286,9 +287,33 @@ struct Module { all_exports: Arc, } +lazy_static! { + static ref USER_IMPORTS: Result> = Module::calc_user_imports(); +} + impl Module { const FORWARDING_PREFIX: &str = "arbitrator_forward__"; + fn calc_user_imports() -> Result> { + let forward_bytes = include_bytes!("../../../target/machines/latest/forward_stub.wasm"); + let forward_bin = binary::parse(forward_bytes, Path::new("forward")).unwrap(); + + let mut available_imports = HashMap::default(); + + for (name, &(export, kind)) in &forward_bin.exports { + if kind == ExportKind::Func { + let ty = match forward_bin.get_function(FunctionIndex::from_u32(export)) { + Ok(ty) => ty, + Err(error) => bail!("failed to read export {}: {}", name, error), + }; + let import = AvailableImport::new(ty, 1, export); + available_imports.insert(name.to_owned(), import); + } + } + + Ok(available_imports) + } + fn from_binary( bin: &WasmBinary, available_imports: &HashMap, @@ -542,6 +567,26 @@ impl Module { }) } + pub fn from_user_binary( + bin: &WasmBinary, + debug_funcs: bool, + stylus_data: Option, + ) -> Result { + Self::from_binary( + bin, + USER_IMPORTS.as_ref().unwrap(), + &HashMap::default(), + false, + debug_funcs, + stylus_data, + ) + } + + pub fn program_info(&self) -> (u32, u32) { + let main = self.find_func(STYLUS_ENTRY_POINT).unwrap(); + (main, self.internals_offset) + } + fn name(&self) -> &str { &self.names.module } @@ -553,7 +598,7 @@ impl Module { Ok(*func.1) } - fn hash(&self) -> Bytes32 { + pub fn hash(&self) -> Bytes32 { let mut h = Keccak256::new(); h.update("Module:"); h.update( @@ -1091,24 +1136,8 @@ impl Machine { let config = CompileConfig::version(version, debug_funcs); let stylus_data = bin.instrument(&config)?; - let forward = include_bytes!("../../../target/machines/latest/forward_stub.wasm"); - let forward = binary::parse(forward, Path::new("forward")).unwrap(); + let module = Module::from_user_binary(&bin, debug_funcs, Some(stylus_data))?; - let mut machine = Self::from_binaries( - &[forward], - bin, - false, - false, - false, - debug_funcs, - self.debug_info, - GlobalState::default(), - HashMap::default(), - Arc::new(|_, _| panic!("tried to read preimage")), - Some(stylus_data), - )?; - - let module = machine.modules.pop().unwrap(); let hash = hash.unwrap_or_else(|| module.hash()); self.stylus_modules.insert(hash, module); Ok(hash) @@ -1555,12 +1584,6 @@ impl Machine { } } - pub fn program_info(&self) -> (u32, u32) { - let module = self.modules.last().unwrap(); - let main = module.find_func(STYLUS_ENTRY_POINT).unwrap(); - (main, module.internals_offset) - } - pub fn main_module_name(&self) -> String { self.modules.last().expect("no module").name().to_owned() } diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index abdc68f8c..7ceb3950b 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -11,7 +11,7 @@ use go_abi::GoStack; use prover::{ binary::WasmBinary, programs::config::{CompileConfig, PricingParams, StylusConfig}, - Machine, + machine::Module, }; use std::{mem, path::Path, sync::Arc}; @@ -69,37 +69,26 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compil let forward = include_bytes!("../../../../target/machines/latest/forward_stub.wasm"); let forward = prover::binary::parse(forward, Path::new("forward")).unwrap(); - let machine = Machine::from_binaries( - &[forward], - bin, - false, - false, - false, - compile.debug.debug_funcs, - debug, - prover::machine::GlobalState::default(), - HashMap::default(), - Arc::new(|_, _| panic!("user program tried to read preimage")), - Some(stylus_data), - ); - let machine = match machine { - Ok(machine) => machine, + let module = prover::machine::Module::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)); + + let module = match module { + Ok(module) => module, Err(err) => error!("failed to instrument program", err), }; - sp.write_ptr(heapify(machine)); + sp.write_ptr(heapify(module)); sp.write_u16(footprint).skip_space(); sp.write_nullptr(); } /// Links and executes a user wasm. -/// λ(mach *Machine, calldata []byte, params *Config, evmApi []byte, evmData *EvmData, gas *u64, root *[32]byte) +/// λ(module *Module, calldata []byte, params *Config, evmApi []byte, evmData *EvmData, gas *u64, root *[32]byte) /// -> (status byte, out *Vec) #[no_mangle] pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_callUserWasmRustImpl( sp: usize, ) { let mut sp = GoStack::new(sp); - let machine: Machine = sp.unbox(); + let module: Module = sp.unbox(); let calldata = sp.read_go_slice_owned(); let config: StylusConfig = sp.unbox(); let evm_api = JsEvmApi::new(sp.read_go_slice_owned(), ApiCaller::new()); @@ -113,8 +102,8 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_callUs // compute the module root, or accept one from the caller let root = sp.read_go_ptr(); let root = (root != 0).then(|| wavm::read_bytes32(root)); - let module = root.unwrap_or_else(|| machine.main_module_hash().0); - let (main, internals) = machine.program_info(); + let (main, internals) = module.program_info(); + let module = root.unwrap_or_else(|| module.hash().0); // link the program and ready its instrumentation let module = wavm_link_module(&MemoryLeaf(module)); From db151695b82ca2212b514e7b02cab243c6ff6c96 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Thu, 27 Jul 2023 14:11:41 -0600 Subject: [PATCH 02/55] store compiled machine hash onchain: attempt1 --- arbitrator/jit/src/user/mod.rs | 19 ++++++- arbitrator/stylus/src/lib.rs | 35 +++++++++--- .../wasm-libraries/user-host/src/link.rs | 18 +++--- arbos/programs/native.go | 19 ++++--- arbos/programs/programs.go | 56 +++++++++++-------- arbos/programs/wasm.go | 24 ++++---- 6 files changed, 111 insertions(+), 60 deletions(-) diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 0bfb6d0e7..cede1fce0 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -20,13 +20,15 @@ use stylus::native; mod evm_api; /// Compiles and instruments user wasm. -/// go side: λ(wasm []byte, version, debug u32, pageLimit u16) (machine *Machine, footprint u32, err *Vec) +/// go side: λ(wasm []byte, version, debug u32, pageLimit u16, machineHash []byte) (module *Module, footprint u16, err *Vec) + pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { let mut sp = GoStack::simple(sp, &env); let wasm = sp.read_go_slice_owned(); let compile = CompileConfig::version(sp.read_u32(), sp.read_u32() != 0); let page_limit = sp.read_u16(); sp.skip_space(); + let (out_hash_ptr, mut out_hash_len) = sp.read_go_slice(); macro_rules! error { ($error:expr) => {{ @@ -40,14 +42,25 @@ pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { } // ensure the wasm compiles during proving - let footprint = match WasmBinary::parse_user(&wasm, page_limit, &compile) { - Ok((.., pages)) => pages, + let (bin, stylus_data, footprint) = match WasmBinary::parse_user(&wasm, page_limit, &compile) { + Ok(result) => result, Err(error) => error!(error), }; let module = match native::module(&wasm, compile) { Ok(module) => module, Err(error) => error!(error), }; + let prover_module = + match prover::machine::Module::from_user_binary(&bin, false, Some(stylus_data)) { + Ok(prover_module) => prover_module, + Err(error) => error!(error), + }; + if out_hash_len != 32 { + error!(eyre::eyre!( + "Go attempting to read compiled machine hash into bad buffer length: {out_len}" + )); + } + sp.write_slice(out_hash_ptr, prover_module.hash().as_slice()); sp.write_ptr(heapify(module)); sp.write_u16(footprint).skip_space(); sp.write_nullptr(); diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index fe9e92297..f2ce5a60f 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -84,7 +84,7 @@ impl RustVec { /// /// # Safety /// -/// Output must not be null +/// Output, footprint, output_canonical_hash must not be null #[no_mangle] pub unsafe extern "C" fn stylus_compile( wasm: GoSliceData, @@ -92,17 +92,38 @@ pub unsafe extern "C" fn stylus_compile( page_limit: u16, footprint: *mut u16, output: *mut RustVec, + canonical_hash: *mut RustVec, debug_mode: usize, ) -> UserOutcomeKind { let wasm = wasm.slice(); - let output = &mut *output; let compile = CompileConfig::version(version, debug_mode != 0); - // ensure the wasm compiles during proving - *footprint = match WasmBinary::parse_user(wasm, page_limit, &compile) { - Ok((.., pages)) => pages, - Err(err) => return output.write_err(err.wrap_err("failed to parse program")), - }; + if output.is_null() { + return UserOutcomeKind::Failure; + } + let output = &mut *output; + + if canonical_hash.is_null() { + return output.write_err(eyre::eyre!("canonical_hash is null")); + } + if footprint.is_null() { + return output.write_err(eyre::eyre!("footprint is null")); + } + + let parse_user_result = WasmBinary::parse_user(wasm, page_limit, &compile); + if let Err(err) = parse_user_result { + return output.write_err(err.wrap_err("failed to parse program")); + } + let (bin, _, pages) = parse_user_result.unwrap(); + + let module = prover::machine::Module::from_user_binary(&bin, compile.debug.debug_funcs, None); + if let Err(err) = module { + return output.write_err(err.wrap_err("failed to build module from program")); + } + let canonical_hash = &mut *canonical_hash; + canonical_hash.write(module.unwrap().hash().to_vec()); + + *footprint = pages; // TODO: compilation pricing, including memory charges let module = match native::module(wasm, compile) { diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index 7ceb3950b..b3dbc9b80 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -37,7 +37,7 @@ extern "C" { struct MemoryLeaf([u8; 32]); /// Compiles and instruments user wasm. -/// Safety: λ(wasm []byte, version, debug u32, pageLimit u16) (machine *Machine, footprint u16, err *Vec) +/// Safety: λ(wasm []byte, version, debug u32, pageLimit u16, machineHash []byte) (module *Module, footprint u16, err *Vec) #[no_mangle] pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compileUserWasmRustImpl( sp: usize, @@ -48,6 +48,7 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compil let debug = sp.read_u32() != 0; let page_limit = sp.read_u16(); sp.skip_space(); + let (out_hash_ptr, mut out_hash_len) = sp.read_go_slice(); macro_rules! error { ($msg:expr, $error:expr) => {{ @@ -66,15 +67,16 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compil Err(error) => error!("failed to parse program", error), }; - let forward = include_bytes!("../../../../target/machines/latest/forward_stub.wasm"); - let forward = prover::binary::parse(forward, Path::new("forward")).unwrap(); - - let module = prover::machine::Module::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)); - - let module = match module { + let module = match prover::machine::Module::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) { Ok(module) => module, - Err(err) => error!("failed to instrument program", err), + Err(error) => error!("failed to instrument program", error), }; + + if out_hash_len != 32 { + error!("Go attempting to read compiled machine hash into bad buffer",eyre::eyre!("buffer length: {out_hash_ptr}")); + } + wavm::write_slice(module.hash().as_slice(), out_hash_ptr); + sp.write_ptr(heapify(module)); sp.write_u16(footprint).skip_space(); sp.write_nullptr(); diff --git a/arbos/programs/native.go b/arbos/programs/native.go index 7feffd648..4ebd3db63 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -41,29 +41,32 @@ type rustVec = C.RustVec func compileUserWasm( db vm.StateDB, program common.Address, wasm []byte, pageLimit uint16, version uint32, debug bool, -) (uint16, error) { +) (uint16, common.Hash, error) { footprint := uint16(0) output := &rustVec{} + canonicalHashRust := &rustVec{} status := userStatus(C.stylus_compile( goSlice(wasm), u32(version), u16(pageLimit), (*u16)(&footprint), output, + canonicalHashRust, usize(arbmath.BoolToUint32(debug)), )) data := output.intoBytes() result, err := status.output(data) - if err == nil { - db.SetCompiledWasmCode(program, result, version) - } else { + if err != nil { data := arbutil.ToStringOrHex(data) log.Debug("compile failure", "err", err.Error(), "data", data, "program", program) + return 0, common.Hash{}, err } - return footprint, err + db.SetCompiledWasmCode(program, result, version) + return footprint, common.BytesToHash(canonicalHashRust.intoBytes()), err } func callUserWasm( + address common.Address, program Program, scope *vm.ScopeContext, db vm.StateDB, @@ -75,9 +78,9 @@ func callUserWasm( memoryModel *MemoryModel, ) ([]byte, error) { if db, ok := db.(*state.StateDB); ok { - db.RecordProgram(program.address, scope.Contract.CodeHash, stylusParams.version) + db.RecordProgram(address, scope.Contract.CodeHash, stylusParams.version) } - module := db.GetCompiledWasmCode(program.address, stylusParams.version) + module := db.GetCompiledWasmCode(address, stylusParams.version) evmApi, id := newApi(interpreter, tracingInfo, scope, memoryModel) defer dropApi(id) @@ -98,7 +101,7 @@ func callUserWasm( if status == userFailure { str := arbutil.ToStringOrHex(returnData) - log.Debug("program failure", "err", string(data), "program", program.address, "returnData", str) + log.Debug("program failure", "err", string(data), "program", address, "returnData", str) } return data, err } diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 4bcbd4d00..0757c8956 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -21,6 +21,7 @@ import ( type Programs struct { backingStorage *storage.Storage programs *storage.Storage + compiledHashes *storage.Storage inkPrice storage.StorageBackedUBips wasmMaxDepth storage.StorageBackedUint32 wasmHostioInk storage.StorageBackedUint64 @@ -32,12 +33,13 @@ type Programs struct { } type Program struct { - footprint uint16 - version uint32 - address common.Address // not saved in state + footprint uint16 + version uint32 + compiledHash common.Hash } var machineVersionsKey = []byte{0} +var machineHashesKey = []byte{1} const ( versionOffset uint64 = iota @@ -83,6 +85,7 @@ func Open(sto *storage.Storage) *Programs { return &Programs{ backingStorage: sto, programs: sto.OpenSubStorage(machineVersionsKey), + compiledHashes: sto.OpenSubStorage(machineHashesKey), inkPrice: sto.OpenStorageBackedUBips(inkPriceOffset), wasmMaxDepth: sto.OpenStorageBackedUint32(wasmMaxDepthOffset), wasmHostioInk: sto.OpenStorageBackedUint64(wasmHostioInkOffset), @@ -189,7 +192,7 @@ func (p Programs) CompileProgram(evm *vm.EVM, program common.Address, debugMode } pageLimit = arbmath.SaturatingUSub(pageLimit, statedb.GetStylusPagesOpen()) - footprint, err := compileUserWasm(statedb, program, wasm, pageLimit, version, debugMode) + footprint, compiledHash, err := compileUserWasm(statedb, program, wasm, pageLimit, version, debugMode) if err != nil { return 0, true, err } @@ -199,11 +202,11 @@ func (p Programs) CompileProgram(evm *vm.EVM, program common.Address, debugMode statedb.AddStylusPagesEver(footprint) programData := Program{ - footprint: footprint, - version: version, - address: program, + footprint: footprint, + version: version, + compiledHash: compiledHash, } - return version, false, p.programs.Set(codeHash, programData.serialize()) + return version, false, p.setProgram(codeHash, programData) } func (p Programs) CallProgram( @@ -220,7 +223,7 @@ func (p Programs) CallProgram( return nil, err } contract := scope.Contract - program, err := p.getProgram(contract) + program, err := p.getProgram(contract.CodeHash) if err != nil { return nil, err } @@ -270,7 +273,7 @@ func (p Programs) CallProgram( txOrigin: evm.TxContext.Origin, } - return callUserWasm(program, scope, statedb, interpreter, tracingInfo, calldata, evmData, params, model) + return callUserWasm(contract.Address(), program, scope, statedb, interpreter, tracingInfo, calldata, evmData, params, model) } func getWasm(statedb vm.StateDB, program common.Address) ([]byte, error) { @@ -285,24 +288,31 @@ func getWasm(statedb vm.StateDB, program common.Address) ([]byte, error) { return arbcompress.Decompress(wasm, MaxWasmSize) } -func (p Program) serialize() common.Hash { +func (p Programs) setProgram(codehash common.Hash, program Program) error { data := common.Hash{} - copy(data[26:], arbmath.Uint16ToBytes(p.footprint)) - copy(data[28:], arbmath.Uint32ToBytes(p.version)) - return data + copy(data[26:], arbmath.Uint16ToBytes(program.footprint)) + copy(data[28:], arbmath.Uint32ToBytes(program.version)) + err := p.programs.Set(codehash, data) + if err != nil { + return err + } + return p.compiledHashes.Set(codehash, program.compiledHash) } -func (p Programs) getProgram(contract *vm.Contract) (Program, error) { - address := contract.Address() - if contract.CodeAddr != nil { - address = *contract.CodeAddr +func (p Programs) getProgram(codehash common.Hash) (Program, error) { + data, err := p.programs.Get(codehash) + if err != nil { + return Program{}, err + } + compiledHash, err := p.compiledHashes.Get(codehash) + if err != nil { + return Program{}, err } - data, err := p.programs.Get(contract.CodeHash) return Program{ - footprint: arbmath.BytesToUint16(data[26:28]), - version: arbmath.BytesToUint32(data[28:]), - address: address, - }, err + footprint: arbmath.BytesToUint16(data[26:28]), + version: arbmath.BytesToUint32(data[28:]), + compiledHash: compiledHash, + }, nil } type goParams struct { diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index e9889b614..f42e13553 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -34,7 +34,7 @@ type rustMachine byte type rustEvmData byte func compileUserWasmRustImpl( - wasm []byte, version, debugMode u32, pageLimit u16, + wasm []byte, version, debugMode u32, pageLimit u16, outMachineHash []byte, ) (machine *rustMachine, footprint u16, err *rustVec) func callUserWasmRustImpl( @@ -59,13 +59,14 @@ func rustEvmDataImpl( txOrigin *addr, ) *rustEvmData -func compileUserWasm(db vm.StateDB, program addr, wasm []byte, pageLimit u16, version u32, debug bool) (u16, error) { +func compileUserWasm(db vm.StateDB, program addr, wasm []byte, pageLimit u16, version u32, debug bool) (u16, common.Hash, error) { debugMode := arbmath.BoolToUint32(debug) - _, footprint, err := compileMachine(db, program, wasm, pageLimit, version, debugMode) - return footprint, err + _, footprint, hash, err := compileUserWasmRustWrapper(db, program, wasm, pageLimit, version, debugMode) + return footprint, hash, err } func callUserWasm( + address common.Address, program Program, scope *vm.ScopeContext, db vm.StateDB, @@ -79,11 +80,11 @@ func callUserWasm( // since the program has previously passed compilation, don't limit memory pageLimit := uint16(math.MaxUint16) - wasm, err := getWasm(db, program.address) + wasm, err := getWasm(db, address) if err != nil { log.Crit("failed to get wasm", "program", program, "err", err) } - machine, _, err := compileMachine(db, program.address, wasm, pageLimit, params.version, params.debugMode) + machine, _, _, err := compileUserWasmRustWrapper(db, address, wasm, pageLimit, params.version, params.debugMode) if err != nil { log.Crit("failed to create machine", "program", program, "err", err) } @@ -105,15 +106,16 @@ func callUserWasm( return status.output(result) } -func compileMachine( +func compileUserWasmRustWrapper( db vm.StateDB, program addr, wasm []byte, pageLimit u16, version, debugMode u32, -) (*rustMachine, u16, error) { - machine, footprint, err := compileUserWasmRustImpl(wasm, version, debugMode, pageLimit) +) (*rustMachine, u16, common.Hash, error) { + outHash := common.Hash{} + machine, footprint, err := compileUserWasmRustImpl(wasm, version, debugMode, pageLimit, outHash[:]) if err != nil { _, err := userFailure.output(err.intoSlice()) - return nil, footprint, err + return nil, footprint, common.Hash{}, err } - return machine, footprint, nil + return machine, footprint, outHash, nil } func (vec *rustVec) intoSlice() []byte { From 983a1e6fd9c49121f5e3bd944a4531b98be3bf97 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 28 Jul 2023 11:27:14 -0600 Subject: [PATCH 03/55] fix calculating compiled hash - arbitrator --- arbitrator/stylus/src/lib.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index f2ce5a60f..780662785 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -92,7 +92,7 @@ pub unsafe extern "C" fn stylus_compile( page_limit: u16, footprint: *mut u16, output: *mut RustVec, - canonical_hash: *mut RustVec, + out_canonical_hash: *mut RustVec, debug_mode: usize, ) -> UserOutcomeKind { let wasm = wasm.slice(); @@ -103,9 +103,6 @@ pub unsafe extern "C" fn stylus_compile( } let output = &mut *output; - if canonical_hash.is_null() { - return output.write_err(eyre::eyre!("canonical_hash is null")); - } if footprint.is_null() { return output.write_err(eyre::eyre!("footprint is null")); } @@ -114,15 +111,19 @@ pub unsafe extern "C" fn stylus_compile( if let Err(err) = parse_user_result { return output.write_err(err.wrap_err("failed to parse program")); } - let (bin, _, pages) = parse_user_result.unwrap(); + let (bin, stylus_data, pages) = parse_user_result.unwrap(); - let module = prover::machine::Module::from_user_binary(&bin, compile.debug.debug_funcs, None); - if let Err(err) = module { + let prover_module = prover::machine::Module::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)); + if let Err(err) = prover_module { return output.write_err(err.wrap_err("failed to build module from program")); } - let canonical_hash = &mut *canonical_hash; - canonical_hash.write(module.unwrap().hash().to_vec()); + let canonical_hash = prover_module.as_ref().unwrap().hash(); + if out_canonical_hash.is_null() { + return output.write_err(eyre::eyre!("canonical_hash is null")); + } + let out_canonical_hash = &mut *out_canonical_hash; + out_canonical_hash.write(canonical_hash.to_vec()); *footprint = pages; // TODO: compilation pricing, including memory charges From 7d4548d459d51cbb14f2a7587c977e679b6930a5 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 28 Jul 2023 11:27:46 -0600 Subject: [PATCH 04/55] fix calculating compiled hash - jit --- arbitrator/jit/src/user/mod.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index cede1fce0..dfeb1ddcf 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -12,6 +12,7 @@ use arbutil::{ }; use prover::{ binary::WasmBinary, + machine as prover_machine, programs::{config::PricingParams, prelude::*}, }; use std::mem; @@ -28,7 +29,7 @@ pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { let compile = CompileConfig::version(sp.read_u32(), sp.read_u32() != 0); let page_limit = sp.read_u16(); sp.skip_space(); - let (out_hash_ptr, mut out_hash_len) = sp.read_go_slice(); + let (out_hash_ptr, out_hash_len) = sp.read_go_slice(); macro_rules! error { ($error:expr) => {{ @@ -46,21 +47,26 @@ pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { Ok(result) => result, Err(error) => error!(error), }; - let module = match native::module(&wasm, compile) { - Ok(module) => module, - Err(error) => error!(error), - }; + let prover_module = - match prover::machine::Module::from_user_binary(&bin, false, Some(stylus_data)) { + match prover_machine::Module::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) { Ok(prover_module) => prover_module, Err(error) => error!(error), }; + + let module = match native::module(&wasm, compile) { + Ok(module) => module, + Err(error) => error!(error), + }; + if out_hash_len != 32 { error!(eyre::eyre!( - "Go attempting to read compiled machine hash into bad buffer length: {out_len}" + "Go attempting to read compiled machine hash into bad buffer length: {out_hash_len}" )); } - sp.write_slice(out_hash_ptr, prover_module.hash().as_slice()); + + let canonical_hash = prover_module.hash(); + sp.write_slice(out_hash_ptr, canonical_hash.as_slice()); sp.write_ptr(heapify(module)); sp.write_u16(footprint).skip_space(); sp.write_nullptr(); From 850afbfa0fe767ade59d811d1db1dd73770a0c79 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 28 Jul 2023 17:38:42 -0600 Subject: [PATCH 05/55] arbitrator: split rayon feature to separate config from native allows usig native without rayon inside jit. --- arbitrator/prover/Cargo.toml | 5 +++-- arbitrator/prover/src/machine.rs | 10 +++++----- arbitrator/prover/src/memory.rs | 6 +++--- arbitrator/prover/src/merkle.rs | 6 +++--- arbitrator/stylus/Cargo.toml | 3 ++- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/arbitrator/prover/Cargo.toml b/arbitrator/prover/Cargo.toml index 43181b0f0..091fff13c 100644 --- a/arbitrator/prover/Cargo.toml +++ b/arbitrator/prover/Cargo.toml @@ -40,6 +40,7 @@ name = "prover" crate-type = ["staticlib", "lib"] [features] -default = ["native", "singlepass_rayon"] -native = ["dep:wasmer", "dep:wasmer-compiler-singlepass", "dep:rayon", "dep:brotli2"] +default = ["native", "rayon", "singlepass_rayon"] +native = ["dep:wasmer", "dep:wasmer-compiler-singlepass", "dep:brotli2"] singlepass_rayon = ["wasmer-compiler-singlepass?/rayon"] +rayon = ["dep:rayon"] diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 64fec6757..ca7fd84d5 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -44,7 +44,7 @@ use std::{ use wasmer_types::FunctionIndex; use wasmparser::{DataKind, ElementItem, ElementKind, Operator, TableType}; -#[cfg(feature = "native")] +#[cfg(feature = "rayon")] use rayon::prelude::*; fn hash_call_indirect_data(table: u32, ty: &FunctionType) -> Bytes32 { @@ -134,10 +134,10 @@ impl Function { "Function instruction count doesn't fit in a u32", ); - #[cfg(feature = "native")] + #[cfg(feature = "rayon")] let code_hashes = code.par_iter().map(|i| i.hash()).collect(); - #[cfg(not(feature = "native"))] + #[cfg(not(feature = "rayon"))] let code_hashes = code.iter().map(|i| i.hash()).collect(); Function { @@ -1456,10 +1456,10 @@ impl Machine { let funcs = Arc::get_mut(&mut module.funcs).expect("Multiple copies of module functions"); for func in funcs.iter_mut() { - #[cfg(feature = "native")] + #[cfg(feature = "rayon")] let code_hashes = func.code.par_iter().map(|i| i.hash()).collect(); - #[cfg(not(feature = "native"))] + #[cfg(not(feature = "rayon"))] let code_hashes = func.code.iter().map(|i| i.hash()).collect(); func.code_merkle = Merkle::new(MerkleType::Instruction, code_hashes); diff --git a/arbitrator/prover/src/memory.rs b/arbitrator/prover/src/memory.rs index 60fe15bad..34f9a49f0 100644 --- a/arbitrator/prover/src/memory.rs +++ b/arbitrator/prover/src/memory.rs @@ -13,7 +13,7 @@ use sha3::Keccak256; use std::{borrow::Cow, convert::TryFrom}; use wasmer_types::Pages; -#[cfg(feature = "native")] +#[cfg(feature = "rayon")] use rayon::prelude::*; pub struct MemoryType { @@ -105,10 +105,10 @@ impl Memory { // Round the size up to 8 byte long leaves, then round up to the next power of two number of leaves let leaves = round_up_to_power_of_two(div_round_up(self.buffer.len(), Self::LEAF_SIZE)); - #[cfg(feature = "native")] + #[cfg(feature = "rayon")] let leaf_hashes = self.buffer.par_chunks(Self::LEAF_SIZE); - #[cfg(not(feature = "native"))] + #[cfg(not(feature = "rayon"))] let leaf_hashes = self.buffer.chunks(Self::LEAF_SIZE); let mut leaf_hashes: Vec = leaf_hashes diff --git a/arbitrator/prover/src/merkle.rs b/arbitrator/prover/src/merkle.rs index c00712821..1e2691788 100644 --- a/arbitrator/prover/src/merkle.rs +++ b/arbitrator/prover/src/merkle.rs @@ -6,7 +6,7 @@ use digest::Digest; use sha3::Keccak256; use std::convert::TryFrom; -#[cfg(feature = "native")] +#[cfg(feature = "rayon")] use rayon::prelude::*; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -77,10 +77,10 @@ impl Merkle { while layers.last().unwrap().len() > 1 || layers.len() < min_depth { let empty_layer = *empty_layers.last().unwrap(); - #[cfg(feature = "native")] + #[cfg(feature = "rayon")] let new_layer = layers.last().unwrap().par_chunks(2); - #[cfg(not(feature = "native"))] + #[cfg(not(feature = "rayon"))] let new_layer = layers.last().unwrap().chunks(2); let new_layer = new_layer diff --git a/arbitrator/stylus/Cargo.toml b/arbitrator/stylus/Cargo.toml index 832188e46..fcedef7e1 100644 --- a/arbitrator/stylus/Cargo.toml +++ b/arbitrator/stylus/Cargo.toml @@ -22,10 +22,11 @@ sha3 = "0.10.5" hex = "0.4.3" [features] -default = ["singlepass_rayon"] +default = ["singlepass_rayon", "rayon"] llvm = ["dep:wasmer-compiler-llvm"] benchmark = [] singlepass_rayon = ["prover/singlepass_rayon", "wasmer-compiler-singlepass/rayon"] +rayon = ["prover/rayon"] [lib] crate-type = ["lib", "staticlib"] From 838a4d99c07cb63010e35dee283a865ac8ce93a5 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Fri, 28 Jul 2023 17:41:12 -0600 Subject: [PATCH 06/55] makefile: add missing dependencies --- Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 4ca4f711a..69cb38323 100644 --- a/Makefile +++ b/Makefile @@ -143,6 +143,7 @@ build-node-deps: $(go_source) build-prover-header build-prover-lib build-jit .ma test-go-deps: \ build-replay-env \ $(stylus_test_wasms) \ + $(arbitrator_stylus_lib) \ $(patsubst %,$(arbitrator_cases)/%.wasm, global-state read-inboxmsg-10 global-state-wrapper const) build-prover-header: $(arbitrator_generated_header) @@ -248,17 +249,17 @@ $(replay_wasm): $(DEP_PREDICATE) $(go_source) .make/solgen mkdir -p `dirname $(replay_wasm)` GOOS=js GOARCH=wasm go build -o $@ ./cmd/replay/... -$(prover_bin): $(DEP_PREDICATE) $(rust_prover_files) +$(prover_bin): $(DEP_PREDICATE) $(rust_prover_files) $(output_latest)/forward_stub.wasm mkdir -p `dirname $(prover_bin)` cargo build --manifest-path arbitrator/Cargo.toml --release --bin prover ${CARGOFLAGS} install arbitrator/target/release/prover $@ -$(arbitrator_stylus_lib): $(DEP_PREDICATE) $(stylus_files) +$(arbitrator_stylus_lib): $(DEP_PREDICATE) $(stylus_files) $(output_latest)/forward_stub.wasm mkdir -p `dirname $(arbitrator_stylus_lib)` cargo build --manifest-path arbitrator/Cargo.toml --release --lib -p stylus ${CARGOFLAGS} install arbitrator/target/release/libstylus.a $@ -$(arbitrator_jit): $(DEP_PREDICATE) .make/cbrotli-lib $(jit_files) +$(arbitrator_jit): $(DEP_PREDICATE) .make/cbrotli-lib $(jit_files) $(output_latest)/forward_stub.wasm mkdir -p `dirname $(arbitrator_jit)` cargo build --manifest-path arbitrator/Cargo.toml --release -p jit ${CARGOFLAGS} install arbitrator/target/release/jit $@ @@ -341,6 +342,7 @@ $(output_latest)/forward.wasm: $(DEP_PREDICATE) $(wasm_lib)/user-host/forward.wa wat2wasm $(wasm_lib)/user-host/forward.wat -o $@ $(output_latest)/forward_stub.wasm: $(DEP_PREDICATE) $(wasm_lib)/user-host/forward_stub.wat .make/machines + mkdir -p $(output_latest) wat2wasm $(wasm_lib)/user-host/forward_stub.wat -o $@ $(output_latest)/machine.wavm.br: $(DEP_PREDICATE) $(prover_bin) $(arbitrator_wasm_libs) $(replay_wasm) From 261f3782ccdae06be68cdeb10e706a1c6ef82bb3 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Mon, 31 Jul 2023 11:49:26 -0600 Subject: [PATCH 07/55] restructure stylus compile_user_wasm --- arbitrator/jit/src/user/mod.rs | 28 +++++++++------------------- arbitrator/stylus/src/lib.rs | 33 +++++++++++---------------------- arbitrator/stylus/src/native.rs | 32 +++++++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index dfeb1ddcf..865150f6f 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -26,7 +26,8 @@ mod evm_api; pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { let mut sp = GoStack::simple(sp, &env); let wasm = sp.read_go_slice_owned(); - let compile = CompileConfig::version(sp.read_u32(), sp.read_u32() != 0); + let version = sp.read_u32(); + let debug = sp.read_u32() != 0; let page_limit = sp.read_u16(); sp.skip_space(); let (out_hash_ptr, out_hash_len) = sp.read_go_slice(); @@ -42,30 +43,19 @@ pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { }}; } - // ensure the wasm compiles during proving - let (bin, stylus_data, footprint) = match WasmBinary::parse_user(&wasm, page_limit, &compile) { - Ok(result) => result, - Err(error) => error!(error), - }; - - let prover_module = - match prover_machine::Module::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) { - Ok(prover_module) => prover_module, - Err(error) => error!(error), - }; - - let module = match native::module(&wasm, compile) { - Ok(module) => module, - Err(error) => error!(error), - }; - if out_hash_len != 32 { error!(eyre::eyre!( "Go attempting to read compiled machine hash into bad buffer length: {out_hash_len}" )); } - let canonical_hash = prover_module.hash(); + // ensure the wasm compiles during proving + let (module, canonical_hash, footprint) = + match native::compile_user_wasm(&wasm, version, page_limit, debug) { + Ok(result) => result, + Err(error) => error!(error), + }; + sp.write_slice(out_hash_ptr, canonical_hash.as_slice()); sp.write_ptr(heapify(module)); sp.write_u16(footprint).skip_space(); diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index 780662785..15addf6a1 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -90,48 +90,37 @@ pub unsafe extern "C" fn stylus_compile( wasm: GoSliceData, version: u32, page_limit: u16, - footprint: *mut u16, + out_footprint: *mut u16, output: *mut RustVec, out_canonical_hash: *mut RustVec, debug_mode: usize, ) -> UserOutcomeKind { let wasm = wasm.slice(); - let compile = CompileConfig::version(version, debug_mode != 0); if output.is_null() { return UserOutcomeKind::Failure; } let output = &mut *output; - if footprint.is_null() { + if out_footprint.is_null() { return output.write_err(eyre::eyre!("footprint is null")); } - - let parse_user_result = WasmBinary::parse_user(wasm, page_limit, &compile); - if let Err(err) = parse_user_result { - return output.write_err(err.wrap_err("failed to parse program")); - } - let (bin, stylus_data, pages) = parse_user_result.unwrap(); - - let prover_module = prover::machine::Module::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)); - if let Err(err) = prover_module { - return output.write_err(err.wrap_err("failed to build module from program")); - } - let canonical_hash = prover_module.as_ref().unwrap().hash(); - if out_canonical_hash.is_null() { return output.write_err(eyre::eyre!("canonical_hash is null")); } let out_canonical_hash = &mut *out_canonical_hash; + + let (module, canonical_hash, footprint) = + match native::compile_user_wasm(wasm, version, page_limit, debug_mode != 0) { + Err(err) => return output.write_err(err), + Ok(val) => val, + }; + out_canonical_hash.write(canonical_hash.to_vec()); - *footprint = pages; + *out_footprint = footprint; + output.write(module); // TODO: compilation pricing, including memory charges - let module = match native::module(wasm, compile) { - Ok(module) => module, - Err(err) => return output.write_err(err), - }; - output.write(module); UserOutcomeKind::Success } diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index 224631e1b..efc6515e4 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -8,9 +8,10 @@ use crate::{ use arbutil::{ evm::{api::EvmApi, EvmData}, operator::OperatorCode, - Color, + Bytes32, Color, }; use eyre::{bail, eyre, ErrReport, Result}; +use prover::binary::WasmBinary; use prover::programs::{ config::PricingParams, counter::{Counter, CountingMachine, OP_OFFSETS}, @@ -356,3 +357,32 @@ pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { let module = module.serialize()?; Ok(module.to_vec()) } + +pub fn compile_user_wasm( + wasm: &[u8], + version: u32, + page_limit: u16, + debug_mode: bool, +) -> Result<(Vec, Bytes32, u16)> { + let compile = CompileConfig::version(version, debug_mode); + let (bin, stylus_data, footprint) = match WasmBinary::parse_user(wasm, page_limit, &compile) { + Err(err) => return Err(err.wrap_err("failed to parse program")), + Ok(res) => res, + }; + + let canonical_hash = match prover::machine::Module::from_user_binary( + &bin, + compile.debug.debug_funcs, + Some(stylus_data), + ) { + Err(err) => return Err(err.wrap_err("failed to build module from program")), + Ok(res) => res.hash(), + }; + + let module = match module(wasm, compile) { + Err(err) => return Err(err.wrap_err("failed generating stylus module")), + Ok(module) => module, + }; + + Ok((module, canonical_hash, footprint)) +} From 7e0cf898321d79749b8a1ee851856320adc65257 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Mon, 31 Jul 2023 16:49:56 -0600 Subject: [PATCH 08/55] jit: compile wasm when adding it to machine and not on execution --- arbitrator/jit/src/machine.rs | 6 +----- arbitrator/jit/src/user/mod.rs | 16 ++++++++++++---- arbitrator/jit/src/wavmio.rs | 15 +++++++++++++-- arbos/programs/wasm.go | 23 +++-------------------- 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/arbitrator/jit/src/machine.rs b/arbitrator/jit/src/machine.rs index c12537a1d..681c41edf 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -193,10 +193,6 @@ pub type WasmEnvMut<'a> = FunctionEnvMut<'a, WasmEnv>; pub type Inbox = BTreeMap>; pub type Oracle = BTreeMap>; -/// Represents a mapping of a WASM program codehash and version to the compiled wasm -/// code itself and its noncanonical program hash. -pub type UserWasms = HashMap<(Bytes32, u32), (Vec, Bytes32)>; - #[derive(Default)] pub struct WasmEnv { /// Mechanism for reading and writing the module's memory @@ -212,7 +208,7 @@ pub struct WasmEnv { /// An oracle allowing the prover to reverse keccak256 pub preimages: Oracle, /// A collection of user wasms called during the course of execution - pub user_wasms: UserWasms, + pub compiled_modules: HashMap>, /// The sequencer inbox's messages pub sequencer_messages: Inbox, /// The delayed inbox's messages diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 865150f6f..511760157 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -70,19 +70,27 @@ pub fn call_user_wasm(env: WasmEnvMut, sp: u32) -> MaybeEscape { use UserOutcome::*; // move inputs - let module: Vec = sp.unbox(); + let compiled_hash = sp.read_bytes32(); let calldata = sp.read_go_slice_owned(); let (compile, config): (CompileConfig, StylusConfig) = sp.unbox(); let evm_api = sp.read_go_slice_owned(); let evm_data: EvmData = sp.unbox(); + let gas = sp.read_go_ptr(); // buy ink let pricing = config.pricing; - let gas = sp.read_go_ptr(); let ink = pricing.gas_to_ink(sp.read_u64_raw(gas)); - // skip the root since we don't use these - sp.skip_u64(); + let module = match &env.data().compiled_modules.get(&compiled_hash) { + None => { + return Err(Escape::Failure(format!( + "compiled hash requested {:?} not found in {:?}", + compiled_hash, + env.data().compiled_modules.keys() + ))) + } + Some(module) => (*module).clone(), + }; let result = exec_wasm( sp, env, module, calldata, compile, config, evm_api, evm_data, ink, diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index 0457c358d..51464da4b 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -14,6 +14,7 @@ use std::{ net::TcpStream, time::Instant, }; +use stylus::native; pub type Bytes20 = [u8; 20]; pub type Bytes32 = [u8; 32]; @@ -316,9 +317,19 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { for _ in 0..programs_count { let codehash = socket::read_bytes32(stream)?; let wasm = socket::read_bytes(stream)?; - let hash = socket::read_bytes32(stream)?; + let compiled_hash = socket::read_bytes32(stream)?; let version = socket::read_u32(stream)?; - env.user_wasms.insert((codehash, version), (wasm, hash)); + // todo: test wasm against codehash? + // no need to test page_limit, we're just retracing previous compilation + let (module, computed_hash, _) = + match native::compile_user_wasm(wasm.as_slice(), version, u16::MAX, false) { + Err(err) => return Escape::hostio(err.to_string()), + Ok(res) => res, + }; + if compiled_hash != *computed_hash { + return Escape::hostio(format!("error! compiled wasm different from expected codehash {:?}, version {}, expected {:?} computed {}", codehash, version, compiled_hash, computed_hash)); + } + env.compiled_modules.insert(compiled_hash, module); } if socket::read_u8(stream)? != socket::READY { diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index f42e13553..90e20c057 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -7,11 +7,8 @@ package programs import ( - "math" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/arbmath" @@ -38,8 +35,8 @@ func compileUserWasmRustImpl( ) (machine *rustMachine, footprint u16, err *rustVec) func callUserWasmRustImpl( - machine *rustMachine, calldata []byte, params *rustConfig, evmApi []byte, - evmData *rustEvmData, gas *u64, root *hash, + compiledHash *hash, calldata []byte, params *rustConfig, evmApi []byte, + evmData *rustEvmData, gas *u64, ) (status userStatus, out *rustVec) func readRustVecLenImpl(vec *rustVec) (len u32) @@ -77,30 +74,16 @@ func callUserWasm( params *goParams, memoryModel *MemoryModel, ) ([]byte, error) { - // since the program has previously passed compilation, don't limit memory - pageLimit := uint16(math.MaxUint16) - - wasm, err := getWasm(db, address) - if err != nil { - log.Crit("failed to get wasm", "program", program, "err", err) - } - machine, _, _, err := compileUserWasmRustWrapper(db, address, wasm, pageLimit, params.version, params.debugMode) - if err != nil { - log.Crit("failed to create machine", "program", program, "err", err) - } - - root := db.NoncanonicalProgramHash(scope.Contract.CodeHash, params.version) evmApi := newApi(interpreter, tracingInfo, scope, memoryModel) defer evmApi.drop() status, output := callUserWasmRustImpl( - machine, + &program.compiledHash, calldata, params.encode(), evmApi.funcs, evmData.encode(), &scope.Contract.Gas, - &root, ) result := output.intoSlice() return status.output(result) From c8c120dd0ce0ffe00f7ec8d91b5837349bcec021 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Mon, 31 Jul 2023 16:51:37 -0600 Subject: [PATCH 09/55] stylus recording stores compiledHash and compressedWasm --- arbnode/execution/block_recorder.go | 14 +------------- arbos/programs/native.go | 2 +- validator/server_api/json.go | 19 ++++++------------- validator/server_jit/jit_machine.go | 10 ++++++++-- 4 files changed, 16 insertions(+), 29 deletions(-) diff --git a/arbnode/execution/block_recorder.go b/arbnode/execution/block_recorder.go index deef5e815..468fb3bf9 100644 --- a/arbnode/execution/block_recorder.go +++ b/arbnode/execution/block_recorder.go @@ -12,11 +12,9 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbcompress" "github.com/offchainlabs/nitro/arbos" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/arbostypes" - "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/validator" ) @@ -166,16 +164,6 @@ func (r *BlockRecorder) RecordBlockCreation( return nil, err } - userWasms := recordingdb.UserWasms() - for _, wasm := range userWasms { - inflated, err := arbcompress.Decompress(wasm.CompressedWasm, programs.MaxWasmSize) - if err != nil { - return nil, fmt.Errorf("error decompressing program: %w", err) - } - wasm.CompressedWasm = nil // release the memory - wasm.Wasm = inflated - } - // check we got the canonical hash canonicalHash := r.execEngine.bc.GetCanonicalHash(uint64(blockNum)) if canonicalHash != blockHash { @@ -186,7 +174,7 @@ func (r *BlockRecorder) RecordBlockCreation( r.updateLastHdr(prevHeader) r.updateValidCandidateHdr(prevHeader) - return &RecordResult{pos, blockHash, preimages, userWasms, readBatchInfo}, err + return &RecordResult{pos, blockHash, preimages, recordingdb.UserWasms(), readBatchInfo}, err } func (r *BlockRecorder) updateLastHdr(hdr *types.Header) { diff --git a/arbos/programs/native.go b/arbos/programs/native.go index 4ebd3db63..7d3518757 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -78,7 +78,7 @@ func callUserWasm( memoryModel *MemoryModel, ) ([]byte, error) { if db, ok := db.(*state.StateDB); ok { - db.RecordProgram(address, scope.Contract.CodeHash, stylusParams.version) + db.RecordProgram(address, scope.Contract.CodeHash, stylusParams.version, program.compiledHash) } module := db.GetCompiledWasmCode(address, stylusParams.version) diff --git a/validator/server_api/json.go b/validator/server_api/json.go index a5fc59c43..f999888b4 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -20,9 +20,8 @@ type BatchInfoJson struct { } type UserWasmJson struct { - NoncanonicalHash common.Hash - CompressedWasm string - Wasm string + CompiledHash common.Hash + CompressedWasm string } type ValidationInputJson struct { @@ -57,9 +56,8 @@ func ValidationInputToJson(entry *validator.ValidationInput) *ValidationInputJso callBytes = append(callBytes, call.CodeHash.Bytes()...) encCall := base64.StdEncoding.EncodeToString(callBytes) encWasm := UserWasmJson{ - NoncanonicalHash: wasm.NoncanonicalHash, - CompressedWasm: base64.StdEncoding.EncodeToString(wasm.CompressedWasm), - Wasm: base64.StdEncoding.EncodeToString(wasm.Wasm), + CompressedWasm: base64.StdEncoding.EncodeToString(wasm.CompressedWasm), + CompiledHash: wasm.CompiledHash, } res.UserWasms[encCall] = encWasm } @@ -105,14 +103,9 @@ func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationI if err != nil { return nil, err } - uncompressed, err := base64.StdEncoding.DecodeString(wasm.Wasm) - if err != nil { - return nil, err - } decWasm := state.UserWasm{ - NoncanonicalHash: wasm.NoncanonicalHash, - CompressedWasm: compressed, - Wasm: uncompressed, + CompiledHash: wasm.CompiledHash, + CompressedWasm: compressed, } valInput.UserWasms[decCall] = &decWasm } diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index 0df914884..c5d08a83a 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -16,6 +16,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/arbcompress" + "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/validator" ) @@ -210,10 +212,14 @@ func (machine *JitMachine) prove( if err := writeExact(call.CodeHash[:]); err != nil { return state, err } - if err := writeBytes(wasm.Wasm); err != nil { + inflated, err := arbcompress.Decompress(wasm.CompressedWasm, programs.MaxWasmSize) + if err != nil { + return state, fmt.Errorf("error decompressing program: %w", err) + } + if err := writeBytes(inflated); err != nil { return state, err } - if err := writeExact(wasm.NoncanonicalHash[:]); err != nil { + if err := writeExact(wasm.CompiledHash[:]); err != nil { return state, err } if err := writeUint32(call.Version); err != nil { From 2b7e0d2ccf8e29499ecae187df674dd8d1d9be96 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Mon, 31 Jul 2023 16:51:59 -0600 Subject: [PATCH 10/55] update geth for updated stylus recording --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 3bef3417f..cde326b79 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 3bef3417ff89511f2074890f41b526162855dbcc +Subproject commit cde326b79f91f65107d815d104d9e8d9f3dd2566 From ca0b41b6b11e621891c053226d31c7eab858755a Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Mon, 31 Jul 2023 16:54:59 -0600 Subject: [PATCH 11/55] program_test: standard arbitrator-kekkack program test --- system_tests/program_test.go | 2 +- system_tests/stylus_test.go | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/system_tests/program_test.go b/system_tests/program_test.go index 1c5041342..1f95e464f 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -43,7 +43,7 @@ func TestProgramKeccak(t *testing.T) { keccakTest(t, true) } -func TestProgramKeccakArb(t *testing.T) { +func TestProgramArbitratorKeccak(t *testing.T) { keccakTest(t, false) } diff --git a/system_tests/stylus_test.go b/system_tests/stylus_test.go index 2b5456016..df9fb7983 100644 --- a/system_tests/stylus_test.go +++ b/system_tests/stylus_test.go @@ -10,10 +10,6 @@ import ( "testing" ) -func TestProgramArbitratorKeccak(t *testing.T) { - keccakTest(t, false) -} - func TestProgramArbitratorErrors(t *testing.T) { errorTest(t, false) } From 3e8b4c1f5bbd3f59acba255eff29d5c2073482b3 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 1 Aug 2023 09:48:45 -0600 Subject: [PATCH 12/55] prover/host: add CallMain internal func --- arbitrator/prover/src/host.rs | 13 +++++++------ arbitrator/prover/src/machine.rs | 23 +++++++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/arbitrator/prover/src/host.rs b/arbitrator/prover/src/host.rs index 1802ea609..10d3f58b7 100644 --- a/arbitrator/prover/src/host.rs +++ b/arbitrator/prover/src/host.rs @@ -31,6 +31,7 @@ pub enum InternalFunc { UserSetInk, UserStackLeft, UserSetStack, + CallMain, } impl InternalFunc { @@ -52,6 +53,7 @@ impl InternalFunc { UserSetInk => func!([I64, I32], []), // λ(ink_left, ink_status) UserStackLeft => func!([], [I32]), // λ() → stack_left UserSetStack => func!([I32], []), // λ(stack_left) + CallMain => func!([I32], [I32]), }; ty } @@ -312,7 +314,7 @@ impl Hostio { dynamic!(UserSetStack); } ProgramCallMain => { - // λ(module, main, args_len) → status + // λ(module, internals, args_len) → status opcode!(PushErrorGuard); opcode!(ArbitraryJumpIf, prior + body.len() + 3); opcode!(I32Const, UserOutcomeKind::Failure as u32); @@ -320,9 +322,7 @@ impl Hostio { // jumps here in the happy case opcode!(LocalGet, 2); // args_len - opcode!(LocalGet, 0); // module - opcode!(LocalGet, 1); // main - opcode!(CrossModuleDynamicCall); // consumes module and main, passing args_len + dynamic!(CallMain); opcode!(PopErrorGuard); } UserInkLeft => { @@ -363,7 +363,7 @@ pub fn get_impl(module: &str, name: &str) -> Result<(Function, bool)> { /// Adds internal functions to a module. /// Note: the order of the functions must match that of the `InternalFunc` enum -pub fn new_internal_funcs(globals: Option) -> Vec { +pub fn new_internal_funcs(stylus_data: Option<(StylusData, u32)>) -> Vec { use ArbValueType::*; use InternalFunc::*; use Opcode::*; @@ -412,7 +412,7 @@ pub fn new_internal_funcs(globals: Option) -> Vec { let mut add_func = |code: &[_], internal| add_func(code_func(code, internal), internal); - if let Some(globals) = globals { + if let Some((globals, main_idx)) = stylus_data { let (gas, status, depth) = globals.global_offsets(); add_func(&[Instruction::with_data(GlobalGet, gas)], UserInkLeft); add_func(&[Instruction::with_data(GlobalGet, status)], UserInkStatus); @@ -425,6 +425,7 @@ pub fn new_internal_funcs(globals: Option) -> Vec { ); add_func(&[Instruction::with_data(GlobalGet, depth)], UserStackLeft); add_func(&[Instruction::with_data(GlobalSet, depth)], UserSetStack); + add_func(&[Instruction::with_data(Call, main_idx as u64)], CallMain); } funcs } diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index ca7fd84d5..4f0fe6061 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -382,7 +382,21 @@ impl Module { } func_type_idxs.extend(bin.functions.iter()); - let internals = host::new_internal_funcs(stylus_data); + let func_exports: HashMap = bin + .exports + .iter() + .filter_map(|(name, (offset, kind))| { + (kind == &ExportKind::Func).then(|| (name.to_owned(), *offset)) + }) + .collect(); + + let stylus_main = func_exports + .iter() + .find(|x| x.0 == STYLUS_ENTRY_POINT) + .and_then(|x| Some(x.1)); + let internals = host::new_internal_funcs( + stylus_data.and_then(|x| stylus_main.and_then(|y| Some((x, y.clone())))), + ); let internals_offset = (code.len() + bin.codes.len()) as u32; let internals_types = internals.iter().map(|f| f.ty.clone()); @@ -538,13 +552,6 @@ impl Module { ensure!(!code.is_empty(), "Module has no code"); let tables_hashes: Result<_, _> = tables.iter().map(Table::hash).collect(); - let func_exports = bin - .exports - .iter() - .filter_map(|(name, (offset, kind))| { - (kind == &ExportKind::Func).then(|| (name.to_owned(), *offset)) - }) - .collect(); Ok(Module { memory, From 84d5583030a496b33678d0264042ce537d2360a5 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 1 Aug 2023 11:59:16 -0600 Subject: [PATCH 13/55] prover: replace dynamic with internal cross_call --- arbitrator/prover/src/host.rs | 75 +++++++++---------- arbitrator/prover/src/machine.rs | 38 +++++++--- arbitrator/prover/src/wavm.rs | 4 +- .../wasm-libraries/user-host/src/link.rs | 36 ++++----- 4 files changed, 79 insertions(+), 74 deletions(-) diff --git a/arbitrator/prover/src/host.rs b/arbitrator/prover/src/host.rs index 10d3f58b7..49d4598f6 100644 --- a/arbitrator/prover/src/host.rs +++ b/arbitrator/prover/src/host.rs @@ -8,8 +8,8 @@ use crate::{ machine::{Function, InboxIdentifier}, programs::StylusData, utils, - value::{ArbValueType, FunctionType, IntegerValType}, - wavm::{wasm_to_wavm, IBinOpType, Instruction, Opcode}, + value::{ArbValueType, FunctionType}, + wavm::{wasm_to_wavm, Instruction, Opcode}, }; use arbutil::{evm::user::UserOutcomeKind, Color}; use eyre::{bail, ErrReport, Result}; @@ -171,23 +171,23 @@ impl Hostio { WavmReadInboxMessage => func!([I64, I32, I32], [I32]), WavmReadDelayedInboxMessage => func!([I64, I32, I32], [I32]), WavmHaltAndSetFinished => func!(), - WavmLinkModule => func!([I32], [I32]), // λ(module_hash) → module - WavmUnlinkModule => func!(), // λ() - ProgramInkLeft => func!([I32, I32], [I64]), // λ(module, internals) → ink_left - ProgramInkStatus => func!([I32, I32], [I32]), // λ(module, internals) → ink_status - ProgramSetInk => func!([I32, I32, I64]), // λ(module, internals, ink_left) - ProgramStackLeft => func!([I32, I32], [I32]), // λ(module, internals) → stack_left - ProgramSetStack => func!([I32, I32, I32]), // λ(module, internals, stack_left) - ProgramCallMain => func!([I32, I32, I32], [I32]), // λ(module, main, args_len) → status - ConsoleLogTxt => func!([I32, I32]), // λ(text, len) - ConsoleLogI32 => func!([I32]), // λ(value) - ConsoleLogI64 => func!([I64]), // λ(value) - ConsoleLogF32 => func!([F32]), // λ(value) - ConsoleLogF64 => func!([F64]), // λ(value) - ConsoleTeeI32 => func!([I32], [I32]), // λ(value) → value - ConsoleTeeI64 => func!([I64], [I64]), // λ(value) → value - ConsoleTeeF32 => func!([F32], [F32]), // λ(value) → value - ConsoleTeeF64 => func!([F64], [F64]), // λ(value) → value + WavmLinkModule => func!([I32], [I32]), // λ(module_hash) → module + WavmUnlinkModule => func!(), // λ() + ProgramInkLeft => func!([I32], [I64]), // λ(module) → ink_left + ProgramInkStatus => func!([I32], [I32]), // λ(module) → ink_status + ProgramSetInk => func!([I32, I64]), // λ(module, ink_left) + ProgramStackLeft => func!([I32], [I32]), // λ(module) → stack_left + ProgramSetStack => func!([I32, I32]), // λ(module, stack_left) + ProgramCallMain => func!([I32, I32], [I32]), // λ(module, args_len) → status + ConsoleLogTxt => func!([I32, I32]), // λ(text, len) + ConsoleLogI32 => func!([I32]), // λ(value) + ConsoleLogI64 => func!([I64]), // λ(value) + ConsoleLogF32 => func!([F32]), // λ(value) + ConsoleLogF64 => func!([F64]), // λ(value) + ConsoleTeeI32 => func!([I32], [I32]), // λ(value) → value + ConsoleTeeI64 => func!([I64], [I64]), // λ(value) → value + ConsoleTeeF32 => func!([F32], [F32]), // λ(value) → value + ConsoleTeeF64 => func!([F64], [F64]), // λ(value) → value UserInkLeft => InternalFunc::UserInkLeft.ty(), UserInkStatus => InternalFunc::UserInkStatus.ty(), UserSetInk => InternalFunc::UserSetInk.ty(), @@ -206,13 +206,10 @@ impl Hostio { body.push(Instruction::with_data($opcode, $value as u64)) }; } - macro_rules! dynamic { + macro_rules! cross_internal { ($func:ident) => { opcode!(LocalGet, 0); // module - opcode!(LocalGet, 1); // internals offset - opcode!(I32Const, InternalFunc::$func); // relative position of the func - opcode!(IBinOp(IntegerValType::I32, IBinOpType::Add)); // absolute position of the func - opcode!(CrossModuleDynamicCall); // consumes module and func + opcode!(CrossModuleInternalCall, InternalFunc::$func); // consumes module and func }; } macro_rules! intern { @@ -291,38 +288,38 @@ impl Hostio { opcode!(UnlinkModule); } ProgramInkLeft => { - // λ(module, internals) → ink_left - dynamic!(UserInkLeft); + // λ(module) → ink_left + cross_internal!(UserInkLeft); } ProgramInkStatus => { - // λ(module, internals) → ink_status - dynamic!(UserInkStatus); + // λ(module) → ink_status + cross_internal!(UserInkStatus); } ProgramSetInk => { - // λ(module, internals, ink_left) - opcode!(LocalGet, 2); // ink_left + // λ(module, ink_left) + opcode!(LocalGet, 1); // ink_left opcode!(I32Const, 0); // ink_status - dynamic!(UserSetInk); + cross_internal!(UserSetInk); } ProgramStackLeft => { - // λ(module, internals) → stack_left - dynamic!(UserStackLeft); + // λ(module) → stack_left + cross_internal!(UserStackLeft); } ProgramSetStack => { - // λ(module, internals, stack_left) - opcode!(LocalGet, 2); // stack_left - dynamic!(UserSetStack); + // λ(module, stack_left) + opcode!(LocalGet, 1); // stack_left + cross_internal!(UserSetStack); } ProgramCallMain => { - // λ(module, internals, args_len) → status + // λ(module, args_len) → status opcode!(PushErrorGuard); opcode!(ArbitraryJumpIf, prior + body.len() + 3); opcode!(I32Const, UserOutcomeKind::Failure as u32); opcode!(Return); // jumps here in the happy case - opcode!(LocalGet, 2); // args_len - dynamic!(CallMain); + opcode!(LocalGet, 1); // args_len + cross_internal!(CallMain); opcode!(PopErrorGuard); } UserInkLeft => { diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 4f0fe6061..7373faccd 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -589,11 +589,6 @@ impl Module { ) } - pub fn program_info(&self) -> (u32, u32) { - let main = self.find_func(STYLUS_ENTRY_POINT).unwrap(); - (main, self.internals_offset) - } - fn name(&self) -> &str { &self.names.module } @@ -1144,10 +1139,20 @@ impl Machine { let stylus_data = bin.instrument(&config)?; let module = Module::from_user_binary(&bin, debug_funcs, Some(stylus_data))?; - - let hash = hash.unwrap_or_else(|| module.hash()); - self.stylus_modules.insert(hash, module); - Ok(hash) + let computed_hash = module.hash(); + + if let Some(expected_hash) = hash { + if computed_hash != expected_hash { + return Err(eyre::eyre!( + "compulted hash {} doesn't match expected {}", + computed_hash, + expected_hash + )); + } + } + eprintln!("adding module {}", computed_hash); + self.stylus_modules.insert(computed_hash, module); + Ok(computed_hash) } pub fn from_binaries( @@ -1930,15 +1935,16 @@ impl Machine { self.pc.inst = 0; reset_refs!(); } - Opcode::CrossModuleDynamicCall => { + Opcode::CrossModuleInternalCall => { flush_module!(); - let call_func = self.value_stack.pop().unwrap().assume_u32(); + let call_internal = inst.argument_data as u32; let call_module = self.value_stack.pop().unwrap().assume_u32(); self.value_stack.push(Value::InternalRef(self.pc)); self.value_stack.push(self.pc.module.into()); self.value_stack.push(module.internals_offset.into()); + module = &mut self.modules[call_module as usize]; self.pc.module = call_module; - self.pc.func = call_func; + self.pc.func = module.internals_offset + call_internal; self.pc.inst = 0; reset_refs!(); } @@ -2781,6 +2787,14 @@ impl Machine { .expect("Failed to prove elements merkle")); } } + CrossModuleInternalCall => { + let module_idx = self.value_stack.last().unwrap().assume_u32() as usize; + let called_module = &self.modules[module_idx]; + out!(called_module.serialize_for_proof(&called_module.memory.merkelize())); + out!(mod_merkle + .prove(module_idx) + .expect("Failed to prove module")); + } GetGlobalStateBytes32 | SetGlobalStateBytes32 => { out!(self.global_state.serialize()); let ptr = self.value_stack.last().unwrap().assume_u32(); diff --git a/arbitrator/prover/src/wavm.rs b/arbitrator/prover/src/wavm.rs index 54aa3d0a8..f325277cb 100644 --- a/arbitrator/prover/src/wavm.rs +++ b/arbitrator/prover/src/wavm.rs @@ -146,7 +146,7 @@ pub enum Opcode { /// Call a function in a different module, acting as the caller's module CrossModuleForward, /// Call a function in a different module provided via the stack - CrossModuleDynamicCall, + CrossModuleInternalCall, /// Call a caller module's internal method with a given function offset CallerModuleInternalCall, /// Gets bytes32 from global state @@ -274,7 +274,7 @@ impl Opcode { Opcode::Dup => 0x8008, Opcode::CrossModuleCall => 0x8009, Opcode::CrossModuleForward => 0x800B, - Opcode::CrossModuleDynamicCall => 0x800C, + Opcode::CrossModuleInternalCall => 0x800C, Opcode::CallerModuleInternalCall => 0x800A, Opcode::GetGlobalStateBytes32 => 0x8010, Opcode::SetGlobalStateBytes32 => 0x8011, diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index b3dbc9b80..7327943be 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -25,12 +25,12 @@ extern "C" { // these dynamic hostio methods allow introspection into user modules #[link(wasm_import_module = "hostio")] extern "C" { - fn program_set_ink(module: u32, internals: u32, ink: u64); - fn program_set_stack(module: u32, internals: u32, stack: u32); - fn program_ink_left(module: u32, internals: u32) -> u64; - fn program_ink_status(module: u32, internals: u32) -> u32; - fn program_stack_left(module: u32, internals: u32) -> u32; - fn program_call_main(module: u32, main: u32, args_len: usize) -> u32; + fn program_set_ink(module: u32, ink: u64); + fn program_set_stack(module: u32, stack: u32); + fn program_ink_left(module: u32) -> u64; + fn program_ink_status(module: u32) -> u32; + fn program_stack_left(module: u32) -> u32; + fn program_call_main(module: u32, args_len: usize) -> u32; } #[repr(C, align(256))] @@ -90,27 +90,21 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_callUs sp: usize, ) { let mut sp = GoStack::new(sp); - let module: Module = sp.unbox(); + let compiled_hash = wavm::read_bytes32(sp.read_go_ptr()); let calldata = sp.read_go_slice_owned(); let config: StylusConfig = sp.unbox(); let evm_api = JsEvmApi::new(sp.read_go_slice_owned(), ApiCaller::new()); let evm_data: EvmData = sp.unbox(); + let gas = sp.read_go_ptr(); // buy ink let pricing = config.pricing; - let gas = sp.read_go_ptr(); let ink = pricing.gas_to_ink(wavm::caller_load64(gas)); - // compute the module root, or accept one from the caller - let root = sp.read_go_ptr(); - let root = (root != 0).then(|| wavm::read_bytes32(root)); - let (main, internals) = module.program_info(); - let module = root.unwrap_or_else(|| module.hash().0); - // link the program and ready its instrumentation - let module = wavm_link_module(&MemoryLeaf(module)); - program_set_ink(module, internals, ink); - program_set_stack(module, internals, config.max_depth); + let module = wavm_link_module(&MemoryLeaf(compiled_hash)); + program_set_ink(module, ink); + program_set_stack(module, config.max_depth); // provide arguments let args_len = calldata.len(); @@ -118,7 +112,7 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_callUs // call the program let go_stack = sp.save_stack(); - let status = program_call_main(module, main, args_len); + let status = program_call_main(module, args_len); let outs = PROGRAMS.pop().unwrap().into_outs(); sp.restore_stack(go_stack); @@ -138,15 +132,15 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_callUs // check if instrumentation stopped the program use UserOutcomeKind::*; - if program_ink_status(module, internals) != 0 { + if program_ink_status(module) != 0 { finish!(OutOfInk, 0); } - if program_stack_left(module, internals) == 0 { + if program_stack_left(module) == 0 { finish!(OutOfStack, 0); } // the program computed a final result - let ink_left = program_ink_left(module, internals); + let ink_left = program_ink_left(module); finish!(status, heapify(outs), ink_left) } From 9b35f7c2e1889eb94d70729e098f8b6450bb3251 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 1 Aug 2023 12:00:10 -0600 Subject: [PATCH 14/55] prover lib: add_user_wasm --- arbitrator/prover/src/lib.rs | 8 ++++++-- validator/server_arb/machine.go | 20 +++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/arbitrator/prover/src/lib.rs b/arbitrator/prover/src/lib.rs index c77129deb..6f9c31f4d 100644 --- a/arbitrator/prover/src/lib.rs +++ b/arbitrator/prover/src/lib.rs @@ -206,11 +206,15 @@ pub unsafe extern "C" fn arbitrator_add_user_wasm( ) -> *mut libc::c_char { let wasm = std::slice::from_raw_parts(wasm, wasm_len as usize); + if root.is_null() { + return err_to_c_string(eyre::eyre!( + "arbitrator_add_user_wasm got null ptr for module hash" + )); + } // provide the opportunity to skip calculating the module root - let root = (!root.is_null()).then(|| *root); let debug = debug != 0; - match (*mach).add_program(wasm, version, debug, root) { + match (*mach).add_program(wasm, version, debug, Some(*root)) { Ok(_) => std::ptr::null_mut(), Err(err) => err_to_c_string(err), } diff --git a/validator/server_arb/machine.go b/validator/server_arb/machine.go index ab71d3a6b..4f41008d3 100644 --- a/validator/server_arb/machine.go +++ b/validator/server_arb/machine.go @@ -22,6 +22,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/arbcompress" + "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/validator" @@ -402,24 +404,28 @@ func (m *ArbitratorMachine) AddUserWasm(call state.WasmCall, wasm *state.UserWas return errors.New("machine frozen") } hashBytes := [32]u8{} - for index, byte := range wasm.NoncanonicalHash.Bytes() { + for index, byte := range wasm.CompiledHash.Bytes() { hashBytes[index] = u8(byte) } debugInt := 0 if debug { debugInt = 1 } - err := C.arbitrator_add_user_wasm( + decompressed, err := arbcompress.Decompress(wasm.CompressedWasm, programs.MaxWasmSize) + if err != nil { + return err + } + cErr := C.arbitrator_add_user_wasm( m.ptr, - (*u8)(arbutil.SliceToPointer(wasm.Wasm)), - u32(len(wasm.Wasm)), + (*u8)(arbutil.SliceToPointer(decompressed)), + u32(len(decompressed)), u32(call.Version), u32(debugInt), &C.struct_Bytes32{hashBytes}, ) - defer C.free(unsafe.Pointer(err)) - if err != nil { - return errors.New(C.GoString(err)) + defer C.free(unsafe.Pointer(cErr)) + if cErr != nil { + return errors.New(C.GoString(cErr)) } return nil } From 7bc43a10af004690f2226806e1998894cbb46207 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 1 Aug 2023 15:26:06 -0600 Subject: [PATCH 15/55] arbitrator: minor fixes --- arbitrator/jit/src/user/mod.rs | 6 +----- arbitrator/prover/src/machine.rs | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 511760157..e1392c9ef 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -10,11 +10,7 @@ use arbutil::{ evm::{user::UserOutcome, EvmData}, heapify, }; -use prover::{ - binary::WasmBinary, - machine as prover_machine, - programs::{config::PricingParams, prelude::*}, -}; +use prover::programs::{config::PricingParams, prelude::*}; use std::mem; use stylus::native; diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 7373faccd..dcf895a74 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -2793,7 +2793,7 @@ impl Machine { out!(called_module.serialize_for_proof(&called_module.memory.merkelize())); out!(mod_merkle .prove(module_idx) - .expect("Failed to prove module")); + .expect("Failed to prove module for CrossModuleInternalCall")); } GetGlobalStateBytes32 | SetGlobalStateBytes32 => { out!(self.global_state.serialize()); From 40e6998a63ca7c3ddc7c77eac08892ea3344d832 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 1 Aug 2023 15:27:22 -0600 Subject: [PATCH 16/55] prover test_cases: update for callModuleInterna --- arbitrator/prover/test-cases/dynamic.wat | 34 +++++++++--------------- arbitrator/prover/test-cases/link.wat | 26 +++++++++--------- arbitrator/prover/test-cases/user.wat | 32 ++++++++++++++++++---- 3 files changed, 52 insertions(+), 40 deletions(-) diff --git a/arbitrator/prover/test-cases/dynamic.wat b/arbitrator/prover/test-cases/dynamic.wat index 0505e074b..63ac15b42 100644 --- a/arbitrator/prover/test-cases/dynamic.wat +++ b/arbitrator/prover/test-cases/dynamic.wat @@ -1,32 +1,26 @@ (module - (import "hostio" "wavm_link_module" (func $link (param i32) (result i32))) - (import "hostio" "wavm_unlink_module" (func $unlink )) - (import "hostio" "program_set_ink" (func $set_ink (param i32 i32 i64) )) - (import "hostio" "program_ink_left" (func $ink_left (param i32 i32) (result i64))) - (import "hostio" "program_ink_status" (func $ink_status (param i32 i32) (result i32))) - (import "hostio" "program_call_main" (func $user_func (param i32 i32 i32) (result i32))) + (import "hostio" "wavm_link_module" (func $link (param i32) (result i32))) + (import "hostio" "wavm_unlink_module" (func $unlink )) + (import "hostio" "program_set_ink" (func $set_ink (param i32 i64) )) + (import "hostio" "program_ink_left" (func $ink_left (param i32) (result i64))) + (import "hostio" "program_ink_status" (func $ink_status (param i32) (result i32))) + (import "hostio" "program_call_main" (func $user_func (param i32 i32) (result i32))) (data (i32.const 0x0) - "\dd\13\30\ba\64\c6\e1\e9\e9\c3\b0\f9\63\c8\d9\69\c9\f0\13\70\42\17\f3\9e\60\c3\0d\13\5b\48\a5\88") ;; user - (func $start (local $user i32) (local $internals i32) + "\c5\27\90\19\f2\b6\5e\7b\5e\c7\2d\11\8f\71\37\a5\a2\47\61\4e\c3\bb\8d\49\88\0a\d9\52\c9\f5\aa\4c") ;; user + (func $start (local $user i32) ;; link in user.wat i32.const 0 call $link local.set $user - ;; set internals offset - i32.const 3 - local.set $internals - ;; set gas globals local.get $user - local.get $internals i64.const 1024 call $set_ink ;; get gas local.get $user - local.get $internals call $ink_left i64.const 1024 i64.ne @@ -35,7 +29,6 @@ ;; get gas status local.get $user - local.get $internals call $ink_status i32.const 0 i32.ne @@ -44,18 +37,16 @@ ;; call a successful func in user.wat ($safe) local.get $user - i32.const 0 ;; $safe - i32.const 64 + i32.const 1 ;; $safe call $user_func - i32.const 64 + i32.const 5 i32.ne (if (then (unreachable))) ;; recover from an unreachable local.get $user - i32.const 1 ;; $unreachable - i32.const 0 + i32.const 2 ;; $unreachable call $user_func i32.const 1 ;; indicates failure i32.ne @@ -69,8 +60,7 @@ ;; recover from an out-of-bounds memory access local.get $user - i32.const 2 ;; $out_of_bounds - i32.const 0 + i32.const 3 ;; $out_of_bounds call $user_func i32.const 1 ;; indicates failure i32.ne diff --git a/arbitrator/prover/test-cases/link.wat b/arbitrator/prover/test-cases/link.wat index 69597f737..171e4baea 100644 --- a/arbitrator/prover/test-cases/link.wat +++ b/arbitrator/prover/test-cases/link.wat @@ -5,31 +5,31 @@ (import "hostio" "wavm_link_module" (func $link (param i32) (result i32))) (import "hostio" "wavm_unlink_module" (func $unlink (param) (result))) (data (i32.const 0x000) - "\b8\ef\6c\9d\e3\a2\aa\9a\94\56\09\7b\13\ef\19\ab\82\ab\c6\d2\eb\76\ca\72\9a\dd\b8\20\84\7b\7a\f7") ;; block + "\6b\56\5a\a3\ec\ca\cf\6f\83\96\c2\bb\c9\c3\c8\07\71\69\2b\0c\2a\a3\c6\d1\6d\24\d6\10\7f\4a\74\92") (data (i32.const 0x020) - "\e0\7a\1c\2e\5f\26\ab\ff\5b\ef\d4\ae\b4\c1\7a\18\52\06\cd\be\9c\ea\fb\c3\b6\4a\03\4b\c0\c4\51\14") ;; call + "\62\42\d1\93\7c\ee\c9\78\15\c1\bf\75\9a\2e\8e\c3\b5\16\23\26\3f\00\3f\bf\88\a0\c8\83\43\7f\9d\6a") (data (i32.const 0x040) - "\4c\85\59\6e\e9\89\38\42\7e\52\63\f5\98\c9\fe\23\4a\17\11\d8\37\e3\ed\d4\2a\93\e7\32\15\c2\d7\1c") ;; indirect + "\cb\17\0a\20\c9\c8\12\3f\fd\e0\e1\77\c4\a7\c6\ac\aa\ad\c0\51\18\04\b3\66\4e\80\b0\1c\e6\30\39\0f") (data (i32.const 0x060) - "\f9\0f\a0\15\f7\e0\52\c2\c8\ec\ac\1c\b7\a3\58\8c\00\52\52\95\a1\ec\a0\55\90\74\aa\11\3f\4e\75\55") ;; const + "\8f\7c\08\6b\39\9f\cf\a6\ed\e7\2a\0e\fc\7d\bc\90\58\17\28\6a\d1\7e\0a\02\e3\d8\ba\fc\59\d5\78\87") (data (i32.const 0x080) - "\2d\e2\b0\dd\15\63\31\27\e5\31\2d\2d\23\e4\13\67\a0\d5\4d\0e\66\d9\2d\79\37\5d\44\7c\d0\80\f8\b0") ;; div + "\d8\71\08\f8\cb\ab\11\3b\6a\99\1e\0c\82\da\1c\ba\2a\f7\71\47\ac\b9\a0\ab\3c\c6\8b\8c\b7\95\b8\73") (data (i32.const 0x0a0) - "\91\7a\9d\9d\03\77\07\f0\f7\84\b4\f1\01\e5\50\ff\5f\40\13\34\c1\af\c1\f2\f6\8f\f7\03\a3\ba\32\47") ;; globals + "\c5\27\90\19\f2\b6\5e\7b\5e\c7\2d\11\8f\71\37\a5\a2\47\61\4e\c3\bb\8d\49\88\0a\d9\52\c9\f5\aa\4c") (data (i32.const 0x0c0) - "\31\81\c9\76\80\55\57\40\6d\93\0d\46\3b\60\31\de\4b\0f\93\14\8e\78\58\63\8c\66\88\55\c3\d3\47\b2") ;; if-else + "\c7\2c\02\ce\c5\f3\17\02\85\70\31\30\8d\53\d3\2c\82\1c\2d\4f\e1\1e\68\32\08\61\73\af\90\3f\c1\f8") (data (i32.const 0x0e0) - "\8d\e8\a2\29\d8\22\25\97\3e\57\7f\17\2f\b0\24\94\3f\fe\73\6b\ef\34\18\10\4b\18\73\87\4c\3d\97\88") ;; locals + "\20\db\c1\9a\5a\aa\63\da\47\26\f8\9c\a0\64\7b\f2\aa\93\39\d3\3c\f9\8a\0c\3e\38\8d\f2\6c\f2\d4\a9") (data (i32.const 0x100) - "\6a\0b\f4\9a\9f\c4\68\18\a5\23\79\11\bf\3a\8d\02\72\ea\e1\21\db\b8\f3\a5\72\d3\66\9a\27\f1\01\74") ;; loop + "\d2\41\eb\7b\a0\df\33\59\aa\80\7e\0d\7f\9e\6d\91\74\34\71\4d\74\f1\5d\70\f9\10\f9\ce\c0\81\a9\a0") (data (i32.const 0x120) - "\35\fb\41\8d\94\da\56\dc\3b\2e\8f\6c\7f\43\bf\dd\9a\30\7c\27\b9\b2\e0\dd\7d\15\29\36\53\ca\b7\77") ;; math + "\34\24\4c\84\9b\ad\81\38\9e\ae\1b\95\c0\f1\07\1f\4e\53\28\4c\13\2f\c0\34\7b\33\0f\3d\f3\f4\53\a3") (data (i32.const 0x140) - "\26\d0\56\ce\4a\fa\f1\ef\cd\d2\7a\4d\64\48\a3\86\5a\00\5f\6f\89\7e\a4\95\5c\b9\2d\b8\f1\04\eb\3e") ;; iops + "\95\d3\c7\9d\44\69\91\f1\a4\f2\fe\c9\99\b8\52\28\70\7b\f4\88\a4\9a\df\36\91\c0\17\7d\7b\76\ec\09") (data (i32.const 0x160) - "\dd\13\30\ba\64\c6\e1\e9\e9\c3\b0\f9\63\c8\d9\69\c9\f0\13\70\42\17\f3\9e\60\c3\0d\13\5b\48\a5\88") ;; user + "\a8\14\b0\a9\a0\23\cc\ed\26\2a\cd\f0\98\b8\bb\d8\67\31\83\39\73\f7\31\ad\14\a4\3a\f9\7a\c6\b9\af") (data (i32.const 0x180) - "\07\33\43\e0\1d\b9\16\3e\99\1a\99\bd\cc\93\bf\26\15\f4\4c\11\c3\32\c0\2c\65\ba\76\11\76\eb\c1\7b") ;; return + "\a7\ff\9f\75\57\98\78\14\f4\1f\de\4e\a9\48\ac\64\de\51\66\ce\28\6d\bc\22\17\80\83\00\0c\e2\99\5a") (func $start (local $counter i32) ;; add modules diff --git a/arbitrator/prover/test-cases/user.wat b/arbitrator/prover/test-cases/user.wat index 582c22ccd..62199f96c 100644 --- a/arbitrator/prover/test-cases/user.wat +++ b/arbitrator/prover/test-cases/user.wat @@ -2,13 +2,35 @@ ;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE (module - (func $safe (param $args_len i32) (result i32) - local.get $args_len) - (func $unreachable (param $args_len i32) (result i32) + (func $safe + i32.const 5 + drop) + (func $unreachable i32.const 0 i64.const 4 unreachable) - (func $out_of_bounds (param $args_len i32) (result i32) + (func $out_of_bounds i32.const 0xFFFFFF - i32.load) + i32.load + drop) + (func (export "arbitrum_main") (param $args_len i32) (result i32) + local.get $args_len + i32.const 1 + i32.eq + (if + (then (call $safe)) + ) + local.get $args_len + i32.const 2 + i32.eq + (if + (then (call $unreachable)) + ) + local.get $args_len + i32.const 3 + i32.eq + (if + (then (call $out_of_bounds)) + ) + i32.const 100) (memory 1 1)) From 116529c30a17e59d6980cb4f3376820603ff1496 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 1 Aug 2023 15:32:52 -0600 Subject: [PATCH 17/55] contract: update one-step-proof for cross module internal call --- contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts b/contracts index 623a8f017..0f0a59f8b 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 623a8f017d0e1cb9e55fe8ee8c46e9ac326d07f6 +Subproject commit 0f0a59f8b33294abbcdf90375641593b0eef3f3d From d1db4458ec2e5de10cc2d7626b47014d14470499 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 2 Aug 2023 09:50:56 -0600 Subject: [PATCH 18/55] jit: use debug mode when feeding new machine --- arbitrator/jit/src/wavmio.rs | 5 +++-- validator/server_jit/jit_machine.go | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index 51464da4b..3fd273764 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -4,7 +4,7 @@ use crate::{ gostack::GoStack, machine::{Escape, Inbox, MaybeEscape, WasmEnv, WasmEnvMut}, - socket, + socket::{self, read_u8}, }; use arbutil::Color; @@ -313,6 +313,7 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { env.preimages.insert(hash, preimage); } + let stylus_debug = socket::read_u8(stream)? != 0; let programs_count = socket::read_u32(stream)?; for _ in 0..programs_count { let codehash = socket::read_bytes32(stream)?; @@ -322,7 +323,7 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { // todo: test wasm against codehash? // no need to test page_limit, we're just retracing previous compilation let (module, computed_hash, _) = - match native::compile_user_wasm(wasm.as_slice(), version, u16::MAX, false) { + match native::compile_user_wasm(wasm.as_slice(), version, u16::MAX, stylus_debug) { Err(err) => return Escape::hostio(err.to_string()), Ok(res) => res, }; diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index c5d08a83a..1df86f269 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -29,7 +29,7 @@ type JitMachine struct { } func createJitMachine(jitBinary string, binaryPath string, cranelift bool, moduleRoot common.Hash, fatalErrChan chan error) (*JitMachine, error) { - invocation := []string{"--binary", binaryPath, "--forks"} + invocation := []string{"--binary", binaryPath, "--forks", "--debug"} if cranelift { invocation = append(invocation, "--cranelift") } @@ -204,6 +204,13 @@ func (machine *JitMachine) prove( } // send user wasms + debugFlag := uint8(0) + if entry.DebugChain { + debugFlag = 1 + } + if err := writeUint8(debugFlag); err != nil { + return state, err + } userWasms := entry.UserWasms if err := writeUint32(uint32(len(userWasms))); err != nil { return state, err From 4423aa5a4f95a7a1020f17d8524105831e9f8878 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 2 Aug 2023 09:52:25 -0600 Subject: [PATCH 19/55] programs: use codeAddr if not nil --- arbos/programs/programs.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 0757c8956..f87b7515c 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -273,7 +273,11 @@ func (p Programs) CallProgram( txOrigin: evm.TxContext.Origin, } - return callUserWasm(contract.Address(), program, scope, statedb, interpreter, tracingInfo, calldata, evmData, params, model) + address := contract.Address() + if contract.CodeAddr != nil { + address = *contract.CodeAddr + } + return callUserWasm(address, program, scope, statedb, interpreter, tracingInfo, calldata, evmData, params, model) } func getWasm(statedb vm.StateDB, program common.Address) ([]byte, error) { From c48380e8712c0f1eb047953ff61611e4b7711533 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 2 Aug 2023 10:06:24 -0600 Subject: [PATCH 20/55] style and format fixes --- arbitrator/stylus/src/lib.rs | 2 +- arbitrator/stylus/src/native.rs | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index 15addf6a1..1a6a89876 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -8,7 +8,7 @@ use arbutil::evm::{ }; use eyre::ErrReport; use native::NativeInstance; -use prover::{binary::WasmBinary, programs::prelude::*}; +use prover::programs::prelude::*; use run::RunProgram; use std::mem; diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index efc6515e4..9d3d788df 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -10,7 +10,7 @@ use arbutil::{ operator::OperatorCode, Bytes32, Color, }; -use eyre::{bail, eyre, ErrReport, Result}; +use eyre::{bail, eyre, Context, ErrReport, Result}; use prover::binary::WasmBinary; use prover::programs::{ config::PricingParams, @@ -365,24 +365,18 @@ pub fn compile_user_wasm( debug_mode: bool, ) -> Result<(Vec, Bytes32, u16)> { let compile = CompileConfig::version(version, debug_mode); - let (bin, stylus_data, footprint) = match WasmBinary::parse_user(wasm, page_limit, &compile) { - Err(err) => return Err(err.wrap_err("failed to parse program")), - Ok(res) => res, - }; + let (bin, stylus_data, footprint) = + WasmBinary::parse_user(wasm, page_limit, &compile).wrap_err("failed to parse program")?; - let canonical_hash = match prover::machine::Module::from_user_binary( + let canonical_hash = prover::machine::Module::from_user_binary( &bin, compile.debug.debug_funcs, Some(stylus_data), - ) { - Err(err) => return Err(err.wrap_err("failed to build module from program")), - Ok(res) => res.hash(), - }; + ) + .wrap_err("failed to build module from program")? + .hash(); - let module = match module(wasm, compile) { - Err(err) => return Err(err.wrap_err("failed generating stylus module")), - Ok(module) => module, - }; + let module = module(wasm, compile).wrap_err("failed generating stylus module")?; Ok((module, canonical_hash, footprint)) } From cd0314f88c73ee89eac372ca1e3c9d7640f1d5f1 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 2 Aug 2023 10:08:14 -0600 Subject: [PATCH 21/55] update geth --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index cde326b79..2deeb2107 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit cde326b79f91f65107d815d104d9e8d9f3dd2566 +Subproject commit 2deeb21072ea8aa20e4ae117090855746ca9a29d From d27a2ebebd36602db2eb3a74efa1e142e0223c7b Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 2 Aug 2023 10:12:26 -0600 Subject: [PATCH 22/55] arbitrator/wavmio: improve escape sequence --- arbitrator/jit/src/wavmio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index 3fd273764..81d22b088 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -324,7 +324,7 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { // no need to test page_limit, we're just retracing previous compilation let (module, computed_hash, _) = match native::compile_user_wasm(wasm.as_slice(), version, u16::MAX, stylus_debug) { - Err(err) => return Escape::hostio(err.to_string()), + Err(err) => return Escape::hostio(format!("{:?}", err)), Ok(res) => res, }; if compiled_hash != *computed_hash { From ae2f297d4df36c2ad38c4223b0b33826ce5cf8fd Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 2 Aug 2023 12:29:23 -0600 Subject: [PATCH 23/55] prover/user-host: fix warnings --- arbitrator/wasm-libraries/user-host/src/link.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index 7327943be..0c65e79ba 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -6,14 +6,12 @@ use arbutil::{ evm::{js::JsEvmApi, user::UserOutcomeKind, EvmData}, heapify, wavm, }; -use fnv::FnvHashMap as HashMap; use go_abi::GoStack; use prover::{ binary::WasmBinary, programs::config::{CompileConfig, PricingParams, StylusConfig}, - machine::Module, }; -use std::{mem, path::Path, sync::Arc}; +use std::mem; // these hostio methods allow the replay machine to modify itself #[link(wasm_import_module = "hostio")] @@ -48,7 +46,7 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compil let debug = sp.read_u32() != 0; let page_limit = sp.read_u16(); sp.skip_space(); - let (out_hash_ptr, mut out_hash_len) = sp.read_go_slice(); + let (out_hash_ptr, out_hash_len) = sp.read_go_slice(); macro_rules! error { ($msg:expr, $error:expr) => {{ From 28a95bb69d3910df99866b3051adbb64832c9cc5 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 2 Aug 2023 15:02:37 -0600 Subject: [PATCH 24/55] testCompilationReuse: use fallible contract to revert --- system_tests/program_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/system_tests/program_test.go b/system_tests/program_test.go index 1f95e464f..7dd6b4f67 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -161,6 +161,8 @@ func testCompilationReuse(t *testing.T, jit bool) { colors.PrintMint("Deploying multiaddr and compiling it") multiAddr := deployWasm(t, ctx, auth, l2client, rustFile("norevert-multicall")) + fallibleAddr := deployWasm(t, ctx, auth, l2client, rustFile("fallible")) + colors.PrintBlue("fallible deployed to ", fallibleAddr.Hex()) preimage := []byte("°º¤ø,¸,ø¤°º¤ø,¸,ø¤°º¤ø,¸ nyan nyan ~=[,,_,,]:3 nyan nyan") correct := crypto.Keccak256Hash(preimage) @@ -200,8 +202,7 @@ func testCompilationReuse(t *testing.T, jit bool) { compileProgramData, )..., ) - legacyErrorMethod := "0x1e48fe82" - innerCallArgs = multicallNorevertAppend(innerCallArgs, vm.CALL, types.ArbDebugAddress, hexutil.MustDecode(legacyErrorMethod)) + innerCallArgs = multicallNorevertAppend(innerCallArgs, vm.CALL, fallibleAddr, hexutil.MustDecode("0x00")) // Our second inner call will attempt to call a program that has not yet been compiled, and revert on error. secondInnerCallArgs := []byte{0x01} From cd61d26020e6e032d4300381ebe7a39ece55c06e Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 2 Aug 2023 15:42:37 -0600 Subject: [PATCH 25/55] makefile: fix soft-float dependency --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 69cb38323..f79849a42 100644 --- a/Makefile +++ b/Makefile @@ -471,10 +471,9 @@ contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(prover_bin) test -f target/lib-wasm/libbrotlidec-static.a || ./scripts/build-brotli.sh -w -d @touch $@ -.make/wasm-lib: $(DEP_PREDICATE) $(ORDER_ONLY_PREDICATE) .make +.make/wasm-lib: $(DEP_PREDICATE) arbitrator/wasm-libraries/soft-float/SoftFloat/build/Wasm-Clang/softfloat.a $(ORDER_ONLY_PREDICATE) .make test -f arbitrator/wasm-libraries/soft-float/bindings32.o || ./scripts/build-brotli.sh -f -d -t . test -f arbitrator/wasm-libraries/soft-float/bindings64.o || ./scripts/build-brotli.sh -f -d -t . - test -f arbitrator/wasm-libraries/soft-float/SoftFloat/build/Wasm-Clang/softfloat.a || ./scripts/build-brotli.sh -f -d -t . @touch $@ .make/machines: $(DEP_PREDICATE) $(ORDER_ONLY_PREDICATE) .make From d73a12ced8716f42995c0842dd8fb8ec8c4735a7 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Mon, 21 Aug 2023 12:30:15 -0600 Subject: [PATCH 26/55] merge fix also, drop module instead of machine --- arbitrator/jit/src/machine.rs | 2 +- arbitrator/jit/src/user/mod.rs | 26 +++++------ arbitrator/jit/src/wavmio.rs | 2 +- arbitrator/prover/src/machine.rs | 39 +--------------- arbitrator/stylus/src/lib.rs | 46 ++++--------------- arbitrator/stylus/src/native.rs | 12 +++-- .../wasm-libraries/user-host/src/link.rs | 31 +++++++++---- arbos/programs/native.go | 33 +++++++------ arbos/programs/programs.go | 18 ++++++-- arbos/programs/raw.s | 2 +- arbos/programs/wasm.go | 28 +++++------ 11 files changed, 102 insertions(+), 137 deletions(-) diff --git a/arbitrator/jit/src/machine.rs b/arbitrator/jit/src/machine.rs index 7fd32f508..e233f03a4 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -118,7 +118,7 @@ pub fn create(opts: &Opts, env: WasmEnv) -> (Instance, FunctionEnv, Sto github!("arbos/programs.callUserWasmRustImpl") => func!(user::call_user_wasm), github!("arbos/programs.readRustVecLenImpl") => func!(user::read_rust_vec_len), github!("arbos/programs.rustVecIntoSliceImpl") => func!(user::rust_vec_into_slice), - github!("arbos/programs.rustMachineDropImpl") => func!(user::drop_machine), + github!("arbos/programs.rustModuleDropImpl") => func!(user::drop_module), github!("arbos/programs.rustConfigImpl") => func!(user::rust_config_impl), github!("arbos/programs.rustEvmDataImpl") => func!(user::evm_data_impl), diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index d5e916734..4b58ed19c 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -13,9 +13,7 @@ use arbutil::{ }; use prover::{ programs::{config::PricingParams, prelude::*}, - Machine, }; -use prover::programs::{config::PricingParams, prelude::*}; use std::mem; use stylus::native; @@ -35,9 +33,10 @@ mod evm_api; pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { let mut sp = GoStack::simple(sp, &env); let wasm = sp.read_go_slice_owned(); - let compile = CompileConfig::version(sp.read_u32(), sp.read_u32() != 0); let page_limit = sp.read_u16(); - sp.skip_space(); + let version = sp.read_u16(); + let debug = sp.read_bool32(); + let (out_hash_ptr, out_hash_len) = sp.read_go_slice(); macro_rules! error { ($error:expr) => {{ @@ -56,16 +55,15 @@ pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { } // ensure the wasm compiles during proving - let footprint = match WasmBinary::parse_user(&wasm, page_limit, &compile) { - Ok((.., pages)) => pages, - Err(error) => error!(error), - }; - let module = match native::module(&wasm, compile) { - Ok(module) => module, - Err(error) => error!(error), - }; + let (module, canonical_hash, info) = + match native::compile_user_wasm(&wasm, version, page_limit, debug) { + Ok(result) => result, + Err(error) => error!(error), + }; + + sp.write_slice(out_hash_ptr, canonical_hash.as_slice()); sp.write_ptr(heapify(module)); - sp.write_u16(footprint).skip_u16().write_u32(size); // wasm info + sp.write_u16(info.footprint).skip_u16().write_u32(info.size); // wasm info sp.write_nullptr(); } @@ -166,7 +164,7 @@ pub fn rust_vec_into_slice(env: WasmEnvMut, sp: u32) { /// The Go compiler expects the call to take the form /// λ(module *Vec) /// -pub fn drop_machine(env: WasmEnvMut, sp: u32) { +pub fn drop_module(env: WasmEnvMut, sp: u32) { let mut sp = GoStack::simple(sp, &env); if let Some(module) = sp.unbox_option::>() { mem::drop(module); diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index caca116a1..7fb10c5ea 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -4,7 +4,7 @@ use crate::{ gostack::GoStack, machine::{Escape, Inbox, MaybeEscape, WasmEnv, WasmEnvMut}, - socket::{self, read_u8}, + socket, }; use arbutil::Color; diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 4b0dc23c9..5972b970f 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -9,7 +9,7 @@ use crate::{ memory::Memory, merkle::{Merkle, MerkleType}, programs::{ - config::{CompileConfig, WasmPricingInfo}, + config::CompileConfig, meter::MeteredMachine, ModuleMod, StylusData, STYLUS_ENTRY_POINT, }, @@ -1127,43 +1127,6 @@ impl Machine { Ok(machine) } - /// Produces a compile-only `Machine` from a user program. - /// Note: the machine's imports are stubbed, so execution isn't possible. - pub fn new_user_stub( - wasm: &[u8], - page_limit: u16, - version: u16, - debug: bool, - ) -> Result<(Machine, WasmPricingInfo)> { - let compile = CompileConfig::version(version, debug); - let forward = include_bytes!("../../../target/machines/latest/forward_stub.wasm"); - let forward = binary::parse(forward, Path::new("forward")).unwrap(); - - let binary = WasmBinary::parse_user(wasm, page_limit, &compile); - let (bin, stylus_data, footprint) = match binary { - Ok(data) => data, - Err(err) => return Err(err.wrap_err("failed to parse program")), - }; - let info = WasmPricingInfo { - footprint, - size: wasm.len() as u32, - }; - let mach = Self::from_binaries( - &[forward], - bin, - false, - false, - false, - compile.debug.debug_funcs, - debug, - GlobalState::default(), - HashMap::default(), - Arc::new(|_, _| panic!("user program tried to read preimage")), - Some(stylus_data), - )?; - Ok((mach, info)) - } - /// Adds a user program to the machine's known set of wasms, compiling it into a link-able module. /// Note that the module produced will need to be configured before execution via hostio calls. pub fn add_program( diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index 257098f15..d1b231ab8 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -82,48 +82,21 @@ impl RustVec { } } -/// Ensures a user program can be proven. -/// On success, `wasm_info` is populated with pricing information. -/// On error, a message is written to `output`. -/// -/// # Safety -/// -/// `output` and `wasm_info` must not be null. -#[no_mangle] -pub unsafe extern "C" fn stylus_parse_wasm( - wasm: GoSliceData, - page_limit: u16, - version: u16, - debug: bool, - wasm_info: *mut WasmPricingInfo, - output: *mut RustVec, -) -> UserOutcomeKind { - let wasm = wasm.slice(); - let info = &mut *wasm_info; - let output = &mut *output; - - match Machine::new_user_stub(wasm, page_limit, version, debug) { - Ok((_, data)) => *info = data, - Err(error) => return output.write_err(error), - } - UserOutcomeKind::Success -} - /// Compiles a user program to its native representation. /// The `output` is either the serialized module or an error string. /// /// # Safety /// -/// Output, footprint, output_canonical_hash must not be null. +/// Output, pricing_info, output_canonical_hash must not be null. #[no_mangle] pub unsafe extern "C" fn stylus_compile( wasm: GoSliceData, - version: u16, page_limit: u16, - out_footprint: *mut u16, + version: u16, + debug_mode: bool, + out_pricing_info: *mut WasmPricingInfo, output: *mut RustVec, out_canonical_hash: *mut RustVec, - debug_mode: usize, ) -> UserOutcomeKind { let wasm = wasm.slice(); @@ -132,25 +105,24 @@ pub unsafe extern "C" fn stylus_compile( } let output = &mut *output; - if out_footprint.is_null() { - return output.write_err(eyre::eyre!("footprint is null")); + if out_pricing_info.is_null() { + return output.write_err(eyre::eyre!("pricing_info is null")); } if out_canonical_hash.is_null() { return output.write_err(eyre::eyre!("canonical_hash is null")); } let out_canonical_hash = &mut *out_canonical_hash; - let (module, canonical_hash, footprint) = - match native::compile_user_wasm(wasm, version, page_limit, debug_mode != 0) { + let (module, canonical_hash, pricing_info) = + match native::compile_user_wasm(wasm, version, page_limit, debug_mode) { Err(err) => return output.write_err(err), Ok(val) => val, }; out_canonical_hash.write(canonical_hash.to_vec()); - *out_footprint = footprint; + *out_pricing_info = pricing_info; output.write(module); - // TODO: compilation pricing, including memory charges UserOutcomeKind::Success } diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index c58d37a54..c794a7952 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -13,7 +13,7 @@ use arbutil::{ use eyre::{bail, eyre, Context, ErrReport, Result}; use prover::binary::WasmBinary; use prover::programs::{ - config::PricingParams, + config::{PricingParams, WasmPricingInfo}, counter::{Counter, CountingMachine, OP_OFFSETS}, depth::STYLUS_STACK_LEFT, meter::{STYLUS_INK_LEFT, STYLUS_INK_STATUS}, @@ -373,10 +373,10 @@ pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { pub fn compile_user_wasm( wasm: &[u8], - version: u32, + version: u16, page_limit: u16, debug_mode: bool, -) -> Result<(Vec, Bytes32, u16)> { +) -> Result<(Vec, Bytes32, WasmPricingInfo)> { let compile = CompileConfig::version(version, debug_mode); let (bin, stylus_data, footprint) = WasmBinary::parse_user(wasm, page_limit, &compile).wrap_err("failed to parse program")?; @@ -389,7 +389,11 @@ pub fn compile_user_wasm( .wrap_err("failed to build module from program")? .hash(); + let info = WasmPricingInfo { + size: wasm.len().try_into()?, + footprint: footprint, + }; let module = module(wasm, compile).wrap_err("failed generating stylus module")?; - Ok((module, canonical_hash, footprint)) + Ok((module, canonical_hash, info)) } diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index ed90f1d0d..3a2c21bf6 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -10,6 +10,7 @@ use arbutil::{ use go_abi::GoStack; use prover::{ binary::WasmBinary, + machine::Module, programs::config::{CompileConfig, PricingParams, StylusConfig}, }; use std::mem; @@ -57,13 +58,24 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compil let debug = sp.read_bool32(); let (out_hash_ptr, out_hash_len) = sp.read_go_slice(); + macro_rules! error { + ($msg:expr, $error:expr) => {{ + let error = $error.wrap_err($msg); + let error = format!("{error:?}").as_bytes().to_vec(); + sp.write_nullptr(); + sp.skip_space(); // skip footprint + sp.write_ptr(heapify(error)); + return; + }}; + } + let compile = CompileConfig::version(version, debug); let (bin, stylus_data, footprint) = match WasmBinary::parse_user(&wasm, page_limit, &compile) { Ok(parse) => parse, Err(error) => error!("failed to parse program", error), }; - let module = match prover::machine::Module::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) { + let module = match Module::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) { Ok(module) => module, Err(error) => error!("failed to instrument program", error), }; @@ -73,8 +85,11 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compil } wavm::write_slice(module.hash().as_slice(), out_hash_ptr); + let Ok(wasm_len) = TryInto::::try_into(wasm.len()) else { + error!("wasm len not u32",eyre::eyre!("wasm length: {}", wasm.len())); + }; sp.write_ptr(heapify(module)); - sp.write_u16(footprint).skip_space(); + sp.write_u16(footprint).skip_u16().write_u32(wasm_len); sp.write_nullptr(); } @@ -108,7 +123,7 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_callUs let ink = pricing.gas_to_ink(wavm::caller_load64(gas)); // link the program and ready its instrumentation - let module = wavm_link_module(&MemoryLeaf(compiled_hash)); + let module = wavm_link_module(&MemoryLeaf(*compiled_hash)); program_set_ink(module, ink); program_set_stack(module, config.max_depth); @@ -190,20 +205,20 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_rustVe mem::drop(vec) } -/// Drops a `Machine`. +/// Drops a `Module`. /// /// # Safety /// /// The Go compiler expects the call to take the form -/// λ(mach *Machine) +/// λ(mach *Module) /// #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_rustMachineDropImpl( +pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_rustModuleDropImpl( sp: usize, ) { let mut sp = GoStack::new(sp); - if let Some(mach) = sp.unbox_option::() { - mem::drop(mach); + if let Some(module) = sp.unbox_option::() { + mem::drop(module); } } diff --git a/arbos/programs/native.go b/arbos/programs/native.go index deee1411a..12089da68 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -28,7 +28,6 @@ import ( "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/arbutil" - "github.com/offchainlabs/nitro/util/arbmath" ) type u8 = C.uint8_t @@ -50,27 +49,32 @@ func compileUserWasm( debug bool, burner burn.Burner, ) (*wasmPricingInfo, common.Hash, error) { - - // check that compilation would succeed during proving rustInfo := &C.WasmPricingInfo{} output := &rustVec{} canonicalHashRust := &rustVec{} status := userStatus(C.stylus_compile( goSlice(wasm), + u16(page_limit), u16(version), cbool(debug), + rustInfo, output, canonicalHashRust, - usize(arbmath.BoolToUint32(debug)), )) - data := output.intoBytes() - result, err := status.output(data) + data, msg, err := status.toResult(output.intoBytes(), debug) + if err != nil { - data := arbutil.ToStringOrHex(data) - log.Debug("compile failure", "err", err.Error(), "data", data, "program", program) - return 0, common.Hash{}, err + if debug { + log.Warn("stylus parse failed", "err", err, "msg", msg, "program", program) + } + return nil, common.Hash{}, err } - db.SetCompiledWasmCode(program, result, version) + + info := rustInfo.decode() + if err := payForCompilation(burner, &info); err != nil { + return nil, common.Hash{}, err + } + db.SetCompiledWasmCode(program, data, version) return &info, common.BytesToHash(canonicalHashRust.intoBytes()), err } @@ -88,7 +92,7 @@ func callUserWasm( memoryModel *MemoryModel, ) ([]byte, error) { if db, ok := db.(*state.StateDB); ok { - db.RecordProgram(program.address, scope.Contract.CodeHash, stylusParams.version) + db.RecordProgram(address, scope.Contract.CodeHash, stylusParams.version, program.compiledHash) } module := db.GetCompiledWasmCode(address, stylusParams.version) @@ -107,9 +111,10 @@ func callUserWasm( (*u64)(&scope.Contract.Gas), )) - if status == userFailure { - str := arbutil.ToStringOrHex(returnData) - log.Debug("program failure", "err", string(data), "program", program.address, "returnData", str) + debug := stylusParams.debugMode != 0 + data, msg, err := status.toResult(output.intoBytes(), debug) + if status == userFailure && debug { + log.Warn("program failure", "err", err, "msg", msg, "program", address) } return data, err } diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 688bbb5bd..4ac5114ab 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -168,9 +168,17 @@ func (p Programs) SetCallScalar(scalar uint16) error { return p.callScalar.Set(scalar) } -func (p Programs) CompileProgram(evm *vm.EVM, program common.Address, debugMode bool) (uint16, bool, error) { +func (p Programs) ProgramVersion(codeHash common.Hash) (uint16, error) { + program, err := p.deserializeProgram(codeHash) + if err != nil { + return 0, err + } + return program.version, nil +} + +func (p Programs) CompileProgram(evm *vm.EVM, address common.Address, debugMode bool) (uint16, bool, error) { statedb := evm.StateDB - codeHash := statedb.GetCodeHash(program) + codeHash := statedb.GetCodeHash(address) version, err := p.StylusVersion() if err != nil { @@ -184,7 +192,7 @@ func (p Programs) CompileProgram(evm *vm.EVM, program common.Address, debugMode if latest >= version { return 0, false, ProgramUpToDateError() } - wasm, err := getWasm(statedb, program) + wasm, err := getWasm(statedb, address) if err != nil { return 0, false, err } @@ -201,7 +209,7 @@ func (p Programs) CompileProgram(evm *vm.EVM, program common.Address, debugMode if err := burner.Burn(3000000); err != nil { return 0, false, err } - info, compiledHash, err := compileUserWasm(statedb, program, wasm, pageLimit, version, debugMode, burner) + info, compiledHash, err := compileUserWasm(statedb, address, wasm, pageLimit, version, debugMode, burner) if err != nil { return 0, true, err } @@ -318,7 +326,7 @@ func (p Programs) deserializeProgram(codeHash common.Hash) (Program, error) { if err != nil { return Program{}, err } - compiledHash, err := p.compiledHashes.Get(codehash) + compiledHash, err := p.compiledHashes.Get(codeHash) if err != nil { return Program{}, err } diff --git a/arbos/programs/raw.s b/arbos/programs/raw.s index d17ce906d..f855d53c3 100644 --- a/arbos/programs/raw.s +++ b/arbos/programs/raw.s @@ -22,7 +22,7 @@ TEXT ·rustVecIntoSliceImpl(SB), NOSPLIT, $0 CallImport RET -TEXT ·rustMachineDropImpl(SB), NOSPLIT, $0 +TEXT ·rustModuleDropImpl(SB), NOSPLIT, $0 CallImport RET diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index 196cf97c4..d09cc00d2 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -42,7 +42,7 @@ func callUserWasmRustImpl( func readRustVecLenImpl(vec *rustVec) (len u32) func rustVecIntoSliceImpl(vec *rustVec, ptr *byte) -func rustMachineDropImpl(mach *rustMachine) +func rustModuleDropImpl(mach *rustMachine) func rustConfigImpl(version u16, maxDepth, inkPrice, debugMode u32) *rustConfig func rustEvmDataImpl( blockBasefee *hash, @@ -68,17 +68,15 @@ func compileUserWasm( debug bool, burner burn.Burner, ) (*wasmPricingInfo, common.Hash, error) { - debugMode := arbmath.BoolToUint32(debug) - module, info, hash, err := compileUserWasmRustWrapper(db, program, wasm, pageLimit, version, debugMode) + module, info, hash, err := compileUserWasmRustWrapper(db, program, wasm, pageLimit, version, debug) defer rustModuleDropImpl(module) if err != nil { - _, _, err := userFailure.toResult(err.intoSlice(), debug) - return nil, err + return nil, common.Hash{}, err } if err := payForCompilation(burner, &info); err != nil { - return nil, err + return nil, common.Hash{}, err } - return footprint, hash, err + return &info, hash, err } func callUserWasm( @@ -109,15 +107,17 @@ func callUserWasm( return data, err } -func compileMachine( - db vm.StateDB, program addr, wasm []byte, pageLimit u16, version, debugMode u32, -) (*rustMachine, u16, error) { - machine, footprint, err := compileUserWasmRustImpl(wasm, version, debugMode, pageLimit) +func compileUserWasmRustWrapper( + db vm.StateDB, program addr, wasm []byte, pageLimit, version u16, debug bool, +) (*rustMachine, wasmPricingInfo, common.Hash, error) { + debugMode := arbmath.BoolToUint32(debug) + outHash := common.Hash{} + machine, info, err := compileUserWasmRustImpl(wasm, pageLimit, version, debugMode, outHash[:]) if err != nil { - _, err := userFailure.output(err.intoSlice()) - return nil, footprint, err + _, _, err := userFailure.toResult(err.intoSlice(), debug) + return nil, info, outHash, err } - return machine, footprint, nil + return machine, info, outHash, nil } func (vec *rustVec) intoSlice() []byte { From 91ab285c8efcf9c19a7beb3ef79332323458c177 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 23 Aug 2023 08:59:37 -0600 Subject: [PATCH 27/55] add TestProgramArbitratorCompilationReuse --- system_tests/stylus_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system_tests/stylus_test.go b/system_tests/stylus_test.go index 5fd6f2334..46b824f54 100644 --- a/system_tests/stylus_test.go +++ b/system_tests/stylus_test.go @@ -41,3 +41,8 @@ func TestProgramArbitratorEvmData(t *testing.T) { func TestProgramArbitratorMemory(t *testing.T) { testMemory(t, false) } + +func TestProgramArbitratorCompilationReuse(t *testing.T) { + t.Parallel() + testCompilationReuse(t, false) +} From c64550a5d53e0cda3a766ec529e7387ffdf59336 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 23 Aug 2023 09:38:36 -0600 Subject: [PATCH 28/55] fix docs and unnecessary diffs --- arbitrator/jit/src/user/mod.rs | 4 ++-- arbitrator/wasm-libraries/user-host/src/link.rs | 4 ++-- arbos/programs/programs.go | 16 ++++++++-------- validator/server_jit/jit_machine.go | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 4b58ed19c..3f041bc6b 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -73,12 +73,12 @@ pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { /// /// The Go compiler expects the call to take the form /// λ( -/// mach *Machine, calldata []byte, params *Configs, evmApi []byte, evmData: *EvmData, +/// hash *common.Hash, calldata []byte, params *Configs, evmApi []byte, evmData: *EvmData, /// gas *u64, root *[32]byte /// ) -> (status byte, out *Vec) /// /// These values are placed on the stack as follows -/// || mach || calldata... || params || evmApi... || evmData || gas || root || status | 3 pad | out ptr || +/// || hash || calldata... || params || evmApi... || evmData || gas || root || status | 3 pad | out ptr || /// pub fn call_user_wasm(env: WasmEnvMut, sp: u32) -> MaybeEscape { let sp = &mut GoStack::simple(sp, &env); diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index 3a2c21bf6..ea40c902d 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -99,12 +99,12 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compil /// /// The Go compiler expects the call to take the form /// λ( -/// module *Module, calldata []byte, params *Configs, evmApi []byte, evmData: *EvmData, +/// hash *common.Hash, calldata []byte, params *Configs, evmApi []byte, evmData: *EvmData, /// gas *u64, root *[32]byte /// ) -> (status byte, out *Vec) /// /// These values are placed on the stack as follows -/// || module || calldata... || params || evmApi... || evmData || gas || root || status | 3 pad | out ptr || +/// || hash || calldata... || params || evmApi... || evmData || gas || root || status | 3 pad | out ptr || /// #[no_mangle] pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_callUserWasmRustImpl( diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 4ac5114ab..e0311fce9 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -168,14 +168,6 @@ func (p Programs) SetCallScalar(scalar uint16) error { return p.callScalar.Set(scalar) } -func (p Programs) ProgramVersion(codeHash common.Hash) (uint16, error) { - program, err := p.deserializeProgram(codeHash) - if err != nil { - return 0, err - } - return program.version, nil -} - func (p Programs) CompileProgram(evm *vm.EVM, address common.Address, debugMode bool) (uint16, bool, error) { statedb := evm.StateDB codeHash := statedb.GetCodeHash(address) @@ -350,6 +342,14 @@ func (p Programs) setProgram(codehash common.Hash, program Program) error { return p.compiledHashes.Set(codehash, program.compiledHash) } +func (p Programs) ProgramVersion(codeHash common.Hash) (uint16, error) { + program, err := p.deserializeProgram(codeHash) + if err != nil { + return 0, err + } + return program.version, nil +} + type goParams struct { version uint16 maxDepth uint32 diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index 69c3717fa..cb2530d7b 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -29,7 +29,7 @@ type JitMachine struct { } func createJitMachine(jitBinary string, binaryPath string, cranelift bool, moduleRoot common.Hash, fatalErrChan chan error) (*JitMachine, error) { - invocation := []string{"--binary", binaryPath, "--forks", "--debug"} + invocation := []string{"--binary", binaryPath, "--forks"} if cranelift { invocation = append(invocation, "--cranelift") } From 596c897caf2eff8231b15504803765ca8ea9b1e1 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 23 Aug 2023 12:41:38 -0600 Subject: [PATCH 29/55] prover: don't allow stylus modules without entry point --- arbitrator/prover/src/machine.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 5972b970f..97ebd39a4 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -392,13 +392,18 @@ impl Module { }) .collect(); - let stylus_main = func_exports - .iter() - .find(|x| x.0 == STYLUS_ENTRY_POINT) - .and_then(|x| Some(x.1)); - let internals = host::new_internal_funcs( - stylus_data.and_then(|x| stylus_main.and_then(|y| Some((x, y.clone())))), - ); + let internals_data = match stylus_data { + None => None, + Some(data) => { + let stylus_main = func_exports + .iter() + .find(|x| x.0 == STYLUS_ENTRY_POINT) + .and_then(|x| Some(x.1)) + .ok_or(eyre::eyre!("stylus program without entry point"))?; + Some((data, *stylus_main)) + } + }; + let internals = host::new_internal_funcs(internals_data); let internals_offset = (code.len() + bin.codes.len()) as u32; let internals_types = internals.iter().map(|f| f.ty.clone()); From ee7c0b160dad8a583de04a1605ab1c8187546ef1 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 23 Aug 2023 13:34:41 -0600 Subject: [PATCH 30/55] cargo fmt --- arbitrator/jit/src/user/mod.rs | 4 +--- arbitrator/prover/src/machine.rs | 12 +++++------- arbitrator/wasm-libraries/user-host/src/link.rs | 1 - 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 3f041bc6b..3f86667a9 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -11,9 +11,7 @@ use arbutil::{ format::DebugBytes, heapify, }; -use prover::{ - programs::{config::PricingParams, prelude::*}, -}; +use prover::programs::{config::PricingParams, prelude::*}; use std::mem; use stylus::native; diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 97ebd39a4..15a40df5c 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -9,9 +9,7 @@ use crate::{ memory::Memory, merkle::{Merkle, MerkleType}, programs::{ - config::CompileConfig, - meter::MeteredMachine, - ModuleMod, StylusData, STYLUS_ENTRY_POINT, + config::CompileConfig, meter::MeteredMachine, ModuleMod, StylusData, STYLUS_ENTRY_POINT, }, reinterpret::{ReinterpretAsSigned, ReinterpretAsUnsigned}, utils::{file_bytes, CBytes, RemoteTableType}, @@ -396,10 +394,10 @@ impl Module { None => None, Some(data) => { let stylus_main = func_exports - .iter() - .find(|x| x.0 == STYLUS_ENTRY_POINT) - .and_then(|x| Some(x.1)) - .ok_or(eyre::eyre!("stylus program without entry point"))?; + .iter() + .find(|x| x.0 == STYLUS_ENTRY_POINT) + .and_then(|x| Some(x.1)) + .ok_or(eyre::eyre!("stylus program without entry point"))?; Some((data, *stylus_main)) } }; diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index ea40c902d..9d958c75c 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -4,7 +4,6 @@ use crate::{evm_api::ApiCaller, Program, PROGRAMS}; use arbutil::{ evm::{js::JsEvmApi, user::UserOutcomeKind, EvmData}, - format::DebugBytes, heapify, wavm, }; use go_abi::GoStack; From 64666fec45e528f07d51df113ea59376433583f4 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Wed, 23 Aug 2023 13:36:25 -0600 Subject: [PATCH 31/55] arbitrator: fix test-cases --- arbitrator/prover/test-cases/block.wat | 4 ++ .../prover/test-cases/call-indirect.wat | 4 ++ arbitrator/prover/test-cases/call.wat | 4 ++ arbitrator/prover/test-cases/const.wat | 4 ++ arbitrator/prover/test-cases/div-overflow.wat | 4 ++ arbitrator/prover/test-cases/dynamic.wat | 2 +- arbitrator/prover/test-cases/globals.wat | 4 ++ arbitrator/prover/test-cases/if-else.wat | 4 ++ arbitrator/prover/test-cases/iops.wat | 4 ++ arbitrator/prover/test-cases/link.wat | 42 +++++++++---------- arbitrator/prover/test-cases/locals.wat | 4 ++ arbitrator/prover/test-cases/loop.wat | 4 ++ arbitrator/prover/test-cases/math.wat | 4 ++ arbitrator/prover/test-cases/return.wat | 4 ++ arbitrator/prover/test-cases/user.wat | 2 +- 15 files changed, 71 insertions(+), 23 deletions(-) diff --git a/arbitrator/prover/test-cases/block.wat b/arbitrator/prover/test-cases/block.wat index 32ac7a5a1..484f4e4cb 100644 --- a/arbitrator/prover/test-cases/block.wat +++ b/arbitrator/prover/test-cases/block.wat @@ -66,4 +66,8 @@ (br_if 0) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/call-indirect.wat b/arbitrator/prover/test-cases/call-indirect.wat index 6f1b8ab9f..fefac2776 100644 --- a/arbitrator/prover/test-cases/call-indirect.wat +++ b/arbitrator/prover/test-cases/call-indirect.wat @@ -26,4 +26,8 @@ (i32.mul) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/call.wat b/arbitrator/prover/test-cases/call.wat index e4bcf8d12..e1db21973 100644 --- a/arbitrator/prover/test-cases/call.wat +++ b/arbitrator/prover/test-cases/call.wat @@ -16,4 +16,8 @@ (call 1) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/const.wat b/arbitrator/prover/test-cases/const.wat index e9e46ff97..665a5adaa 100644 --- a/arbitrator/prover/test-cases/const.wat +++ b/arbitrator/prover/test-cases/const.wat @@ -8,4 +8,8 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/div-overflow.wat b/arbitrator/prover/test-cases/div-overflow.wat index 993185b82..cbb4462dc 100644 --- a/arbitrator/prover/test-cases/div-overflow.wat +++ b/arbitrator/prover/test-cases/div-overflow.wat @@ -9,4 +9,8 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/dynamic.wat b/arbitrator/prover/test-cases/dynamic.wat index cb6f26306..441102ca1 100644 --- a/arbitrator/prover/test-cases/dynamic.wat +++ b/arbitrator/prover/test-cases/dynamic.wat @@ -7,7 +7,7 @@ (import "hostio" "program_ink_status" (func $ink_status (param i32) (result i32))) (import "hostio" "program_call_main" (func $user_func (param i32 i32) (result i32))) (data (i32.const 0x0) - "\a6\d9\ac\fb\b4\01\cd\f8\4d\eb\6c\4c\07\cd\89\97\f7\c6\76\07\a7\6a\e9\a6\6f\60\04\c4\34\e7\2b\eb") ;; user + "\80\d3\d5\e6\1a\9a\9d\58\9a\e8\42\d5\69\2f\c2\38\16\47\44\b1\5b\66\c5\d6\dc\8f\f5\b3\66\91\4a\ee") (func $start (local $user i32) (local $internals i32) ;; link in user.wat i32.const 0 diff --git a/arbitrator/prover/test-cases/globals.wat b/arbitrator/prover/test-cases/globals.wat index a4b6bfd69..e8829294d 100644 --- a/arbitrator/prover/test-cases/globals.wat +++ b/arbitrator/prover/test-cases/globals.wat @@ -18,6 +18,10 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/if-else.wat b/arbitrator/prover/test-cases/if-else.wat index 252a3be75..ced2af6ec 100644 --- a/arbitrator/prover/test-cases/if-else.wat +++ b/arbitrator/prover/test-cases/if-else.wat @@ -18,4 +18,8 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/iops.wat b/arbitrator/prover/test-cases/iops.wat index 7ec8ab945..f435ac099 100644 --- a/arbitrator/prover/test-cases/iops.wat +++ b/arbitrator/prover/test-cases/iops.wat @@ -80,4 +80,8 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) \ No newline at end of file diff --git a/arbitrator/prover/test-cases/link.wat b/arbitrator/prover/test-cases/link.wat index 1f64655f2..edd55a5d1 100644 --- a/arbitrator/prover/test-cases/link.wat +++ b/arbitrator/prover/test-cases/link.wat @@ -4,32 +4,32 @@ (module (import "hostio" "wavm_link_module" (func $link (param i32) (result i32))) (import "hostio" "wavm_unlink_module" (func $unlink (param) (result))) - (data (i32.const 0x000) - "\ae\dc\38\6b\36\2c\42\23\73\d0\10\e1\e6\72\a8\6b\58\9f\10\79\8f\3a\84\89\f1\c1\55\d8\3a\7c\74\08") ;; block - (data (i32.const 0x020) - "\a5\3a\c1\b1\a1\2e\87\f1\a9\68\67\13\25\1e\f9\75\85\30\5f\51\47\3c\87\3f\f4\4b\02\74\00\53\b7\44") ;; call - (data (i32.const 0x040) - "\ad\24\b8\29\6b\15\4e\50\48\0b\69\89\f1\cc\ed\68\22\ae\2f\b2\e8\3d\ed\50\06\d4\fb\5b\c1\bd\dd\e1") ;; indirect - (data (i32.const 0x060) - "\3d\92\82\57\c7\5f\03\cd\98\d1\49\7a\7b\6b\e1\13\b0\d3\92\38\94\f4\27\3b\5a\94\e4\2f\8c\ac\fb\06") ;; const - (data (i32.const 0x080) - "\27\6e\5d\0d\79\e8\b8\c5\e4\77\45\e4\8e\fb\93\eb\b9\83\1e\38\e1\a5\34\e5\15\a3\87\af\75\fc\b0\75") ;; div - (data (i32.const 0x0a0) - "\3f\b4\8c\32\cd\4e\12\1b\a6\af\18\d4\36\b2\2c\87\ba\f3\08\e9\d6\6d\91\61\69\dd\cc\91\6b\ae\77\6d") ;; globals - "\31\81\c9\76\80\55\57\40\6d\93\0d\46\3b\60\31\de\4b\0f\93\14\8e\78\58\63\8c\66\88\55\c3\d3\47\b2") ;; if-else - "\8f\b0\a8\9e\16\fa\76\ac\3e\16\86\94\4b\ce\17\e1\87\c6\ed\de\da\4d\49\9b\b4\70\47\7d\0b\0f\cf\c5") ;; if-else - (data (i32.const 0x0e0) - "\ec\2c\89\ff\20\c7\a8\af\4b\76\e0\0d\18\d7\24\27\aa\86\81\50\2a\f6\41\31\01\9f\24\fc\cf\06\92\b8") ;; locals + (data (i32.const 0x0) + "\88\ff\33\e2\65\70\e0\88\b2\1f\03\64\34\36\05\7e\39\71\a5\6c\ba\96\76\7b\6a\e9\70\13\13\46\95\2f") + (data (i32.const 0x20) + "\d2\49\05\37\66\54\9e\fc\eb\af\cb\3d\50\71\2b\34\45\34\02\45\ed\16\83\34\bb\63\8b\c7\e6\a1\ff\10") + (data (i32.const 0x40) + "\e8\14\3b\94\37\e9\51\b6\58\41\40\77\8b\82\bf\c9\df\23\35\a1\74\9d\8c\0e\03\eb\5d\51\b0\13\5f\91") + (data (i32.const 0x60) + "\4f\bb\49\69\d5\5e\d7\bc\c8\15\4b\4d\44\47\2a\0d\99\c6\d0\6f\c4\45\12\b7\23\4d\08\7d\e5\d8\f3\90") + (data (i32.const 0x80) + "\e6\b9\67\33\7a\c5\b0\b5\76\00\3a\6e\f2\9b\11\2f\42\64\b1\ae\98\b1\77\92\b0\b1\51\58\23\94\d6\ee") + (data (i32.const 0xa0) + "\7f\96\bd\e6\06\55\44\38\ec\a9\82\e5\3c\0d\b2\76\b2\62\9d\20\91\65\c8\ff\ed\20\0e\59\7e\ef\38\a0") + (data (i32.const 0xc0) + "\36\7c\f6\0c\3c\bc\29\2f\ab\7d\4e\59\2c\6b\61\1d\c5\9c\49\a5\65\d3\a7\ef\2d\2a\f7\f1\d0\b1\5e\e9") + (data (i32.const 0xe0) + "\be\9e\03\4f\9e\57\a7\c4\ae\af\8f\43\65\55\8e\68\d7\81\1a\e9\07\4e\5e\a8\d1\3d\21\34\e4\18\dd\68") (data (i32.const 0x100) - "\f5\70\c9\95\e1\71\4b\55\fe\70\1f\90\ce\31\c4\ed\11\35\25\b0\4a\4d\01\f9\3c\77\39\8b\f4\cd\0c\10") ;; loop + "\b0\9d\f3\19\d9\ac\bc\dd\cf\55\b0\7b\06\6d\98\2c\59\7d\07\88\47\b3\b2\22\ca\40\64\22\30\ae\a0\67") (data (i32.const 0x120) - "\54\07\a2\84\19\02\c5\5c\3c\d9\52\3c\fd\03\7a\b3\d5\1b\00\b7\9a\89\cf\de\ed\5a\c0\69\90\31\49\0d") ;; math + "\80\d3\d5\e6\1a\9a\9d\58\9a\e8\42\d5\69\2f\c2\38\16\47\44\b1\5b\66\c5\d6\dc\8f\f5\b3\66\91\4a\ee") (data (i32.const 0x140) - "\ea\15\0f\0e\ae\6d\e9\21\05\f4\45\bd\a8\b6\0f\4f\ea\e6\57\f4\b4\d5\64\e5\7e\bb\1b\6c\12\82\8a\77") ;; iops + "\db\ec\76\43\14\38\9b\f4\a6\6e\25\f7\f5\94\6c\da\b6\e4\f9\cc\e6\2f\00\36\bc\e9\8c\66\fd\dd\68\f9") (data (i32.const 0x160) - "\a6\d9\ac\fb\b4\01\cd\f8\4d\eb\6c\4c\07\cd\89\97\f7\c6\76\07\a7\6a\e9\a6\6f\60\04\c4\34\e7\2b\eb") ;; user + "\2c\ab\68\c3\22\a0\7e\25\00\a5\64\5b\29\72\27\bb\c0\54\d1\60\69\9b\d9\c9\c8\7c\30\1e\eb\ea\3e\f9") (data (i32.const 0x180) - "\1f\e6\67\ce\e9\86\70\06\b5\11\5d\fd\08\c1\6b\76\c3\8d\6c\a2\de\42\e5\ab\45\89\cc\6d\c0\88\d7\c4") ;; return + "\ef\4e\09\6e\04\c1\9f\eb\58\84\a5\40\8a\79\27\23\ac\e8\bf\bf\db\be\e2\f3\cb\4c\c3\d2\5f\c7\c7\4c") (func $start (local $counter i32) ;; add modules diff --git a/arbitrator/prover/test-cases/locals.wat b/arbitrator/prover/test-cases/locals.wat index 7a2da6634..878467ab6 100644 --- a/arbitrator/prover/test-cases/locals.wat +++ b/arbitrator/prover/test-cases/locals.wat @@ -16,4 +16,8 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/loop.wat b/arbitrator/prover/test-cases/loop.wat index 34cdb77da..3cdf8e416 100644 --- a/arbitrator/prover/test-cases/loop.wat +++ b/arbitrator/prover/test-cases/loop.wat @@ -29,6 +29,10 @@ (unreachable) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/math.wat b/arbitrator/prover/test-cases/math.wat index 7315e2d71..40b77d0ce 100644 --- a/arbitrator/prover/test-cases/math.wat +++ b/arbitrator/prover/test-cases/math.wat @@ -81,4 +81,8 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/return.wat b/arbitrator/prover/test-cases/return.wat index f2d36f8e8..36e1c0640 100644 --- a/arbitrator/prover/test-cases/return.wat +++ b/arbitrator/prover/test-cases/return.wat @@ -20,5 +20,9 @@ ) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) diff --git a/arbitrator/prover/test-cases/user.wat b/arbitrator/prover/test-cases/user.wat index 62199f96c..a61cf10e5 100644 --- a/arbitrator/prover/test-cases/user.wat +++ b/arbitrator/prover/test-cases/user.wat @@ -13,7 +13,7 @@ i32.const 0xFFFFFF i32.load drop) - (func (export "arbitrum_main") (param $args_len i32) (result i32) + (func (export "user_entrypoint") (param $args_len i32) (result i32) local.get $args_len i32.const 1 i32.eq From 8ebafd66e0258ff07cbdface030a9eb32135849a Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Thu, 24 Aug 2023 11:56:36 -0600 Subject: [PATCH 32/55] add user_entrypoint to wat tests --- arbitrator/prover/test-cases/bulk-memory.wat | 5 +++++ arbitrator/stylus/tests/add.wat | 5 ++++- arbitrator/stylus/tests/bulk-memory-oob.wat | 3 +++ arbitrator/stylus/tests/console.wat | 3 +++ arbitrator/stylus/tests/depth.wat | 5 ++++- arbitrator/stylus/tests/start.wat | 3 +++ 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/arbitrator/prover/test-cases/bulk-memory.wat b/arbitrator/prover/test-cases/bulk-memory.wat index 21648c739..5076e26c8 100644 --- a/arbitrator/prover/test-cases/bulk-memory.wat +++ b/arbitrator/prover/test-cases/bulk-memory.wat @@ -79,5 +79,10 @@ local.get 1 i32.ne (if (then (unreachable)))) + + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + ) + (start $start) (memory (export "mem") 1 1)) diff --git a/arbitrator/stylus/tests/add.wat b/arbitrator/stylus/tests/add.wat index c102963fd..f14eb338b 100644 --- a/arbitrator/stylus/tests/add.wat +++ b/arbitrator/stylus/tests/add.wat @@ -6,4 +6,7 @@ (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) get_local $p0 i32.const 1 - i32.add)) + i32.add) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + )) diff --git a/arbitrator/stylus/tests/bulk-memory-oob.wat b/arbitrator/stylus/tests/bulk-memory-oob.wat index 80ef17e59..dceba8ec2 100644 --- a/arbitrator/stylus/tests/bulk-memory-oob.wat +++ b/arbitrator/stylus/tests/bulk-memory-oob.wat @@ -10,5 +10,8 @@ (memory.copy (i32.const 0xfffe) (i32.const 0xffff) (i32.const 2))) (func (export "copy_same") (memory.copy (i32.const 0xffff) (i32.const 0xffff) (i32.const 2))) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + ) (data (i32.const 0xfffe) "\01\02") ;; last two bytes shouldn't change (memory (export "memory") 1 1)) diff --git a/arbitrator/stylus/tests/console.wat b/arbitrator/stylus/tests/console.wat index e3372e712..28162cf2c 100644 --- a/arbitrator/stylus/tests/console.wat +++ b/arbitrator/stylus/tests/console.wat @@ -31,4 +31,7 @@ f64.const -64.32 call $tee_f64 call $log_f64) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + ) (start $start)) diff --git a/arbitrator/stylus/tests/depth.wat b/arbitrator/stylus/tests/depth.wat index 88e2453b4..4ad10b672 100644 --- a/arbitrator/stylus/tests/depth.wat +++ b/arbitrator/stylus/tests/depth.wat @@ -10,4 +10,7 @@ i32.const 1 ;; push 1 -- 3 on stack <- 3 words max i32.add ;; pop 2, push 1 -- 2 on stack global.set $depth ;; pop 1 -- 1 on stack - call $recurse)) + call $recurse) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + )) diff --git a/arbitrator/stylus/tests/start.wat b/arbitrator/stylus/tests/start.wat index 1c405820b..50bc5b455 100644 --- a/arbitrator/stylus/tests/start.wat +++ b/arbitrator/stylus/tests/start.wat @@ -10,4 +10,7 @@ i32.add set_global $status ;; increment the global ) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + ) (start $start)) From 125e834c7750847d59059cac4cf41f208cfba984 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 5 Sep 2023 11:23:23 -0600 Subject: [PATCH 33/55] cargo.lock updates --- arbitrator/stylus/tests/create/Cargo.lock | 4 +--- arbitrator/stylus/tests/erc20/Cargo.lock | 4 +--- arbitrator/stylus/tests/evm-data/Cargo.lock | 4 +--- arbitrator/stylus/tests/fallible/Cargo.lock | 4 +--- arbitrator/stylus/tests/keccak-100/Cargo.lock | 4 +--- arbitrator/stylus/tests/keccak/Cargo.lock | 4 +--- arbitrator/stylus/tests/log/Cargo.lock | 4 +--- arbitrator/stylus/tests/multicall/Cargo.lock | 4 +--- arbitrator/stylus/tests/read-return-data/Cargo.lock | 4 +--- arbitrator/stylus/tests/sdk-storage/Cargo.lock | 4 +--- arbitrator/stylus/tests/storage/Cargo.lock | 4 +--- 11 files changed, 11 insertions(+), 33 deletions(-) diff --git a/arbitrator/stylus/tests/create/Cargo.lock b/arbitrator/stylus/tests/create/Cargo.lock index 924fd6a28..69a54ed59 100644 --- a/arbitrator/stylus/tests/create/Cargo.lock +++ b/arbitrator/stylus/tests/create/Cargo.lock @@ -436,9 +436,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", diff --git a/arbitrator/stylus/tests/erc20/Cargo.lock b/arbitrator/stylus/tests/erc20/Cargo.lock index f3a316a45..b06910e01 100644 --- a/arbitrator/stylus/tests/erc20/Cargo.lock +++ b/arbitrator/stylus/tests/erc20/Cargo.lock @@ -646,9 +646,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", diff --git a/arbitrator/stylus/tests/evm-data/Cargo.lock b/arbitrator/stylus/tests/evm-data/Cargo.lock index 8191751e5..2108a6a7e 100644 --- a/arbitrator/stylus/tests/evm-data/Cargo.lock +++ b/arbitrator/stylus/tests/evm-data/Cargo.lock @@ -436,9 +436,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", diff --git a/arbitrator/stylus/tests/fallible/Cargo.lock b/arbitrator/stylus/tests/fallible/Cargo.lock index 94e121d48..6015667eb 100644 --- a/arbitrator/stylus/tests/fallible/Cargo.lock +++ b/arbitrator/stylus/tests/fallible/Cargo.lock @@ -435,9 +435,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", diff --git a/arbitrator/stylus/tests/keccak-100/Cargo.lock b/arbitrator/stylus/tests/keccak-100/Cargo.lock index 0b6090627..4dd3dbc58 100644 --- a/arbitrator/stylus/tests/keccak-100/Cargo.lock +++ b/arbitrator/stylus/tests/keccak-100/Cargo.lock @@ -436,9 +436,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", diff --git a/arbitrator/stylus/tests/keccak/Cargo.lock b/arbitrator/stylus/tests/keccak/Cargo.lock index c4e01bb8d..b45423744 100644 --- a/arbitrator/stylus/tests/keccak/Cargo.lock +++ b/arbitrator/stylus/tests/keccak/Cargo.lock @@ -436,9 +436,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", diff --git a/arbitrator/stylus/tests/log/Cargo.lock b/arbitrator/stylus/tests/log/Cargo.lock index ec0f38791..96d277207 100644 --- a/arbitrator/stylus/tests/log/Cargo.lock +++ b/arbitrator/stylus/tests/log/Cargo.lock @@ -436,9 +436,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", diff --git a/arbitrator/stylus/tests/multicall/Cargo.lock b/arbitrator/stylus/tests/multicall/Cargo.lock index f9c9e9a79..b5f6448b9 100644 --- a/arbitrator/stylus/tests/multicall/Cargo.lock +++ b/arbitrator/stylus/tests/multicall/Cargo.lock @@ -436,9 +436,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", diff --git a/arbitrator/stylus/tests/read-return-data/Cargo.lock b/arbitrator/stylus/tests/read-return-data/Cargo.lock index 6714df776..93aa43d18 100644 --- a/arbitrator/stylus/tests/read-return-data/Cargo.lock +++ b/arbitrator/stylus/tests/read-return-data/Cargo.lock @@ -436,9 +436,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", diff --git a/arbitrator/stylus/tests/sdk-storage/Cargo.lock b/arbitrator/stylus/tests/sdk-storage/Cargo.lock index 596e64381..37fdf787c 100644 --- a/arbitrator/stylus/tests/sdk-storage/Cargo.lock +++ b/arbitrator/stylus/tests/sdk-storage/Cargo.lock @@ -449,9 +449,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", diff --git a/arbitrator/stylus/tests/storage/Cargo.lock b/arbitrator/stylus/tests/storage/Cargo.lock index 7a967b751..81dbed9c5 100644 --- a/arbitrator/stylus/tests/storage/Cargo.lock +++ b/arbitrator/stylus/tests/storage/Cargo.lock @@ -435,9 +435,7 @@ dependencies = [ [[package]] name = "stylus-proc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ed6e3a897eea8ef2e19836b622fde7a9b114009f7eeca00d26a232e70603d0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", From aa5d5c9d0df8d58dbf141f475272562dc5276fbc Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 5 Sep 2023 11:23:49 -0600 Subject: [PATCH 34/55] update module-hashes in wat tests --- arbitrator/prover/test-cases/dynamic.wat | 2 +- arbitrator/prover/test-cases/link.wat | 42 ++++++++++++------------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/arbitrator/prover/test-cases/dynamic.wat b/arbitrator/prover/test-cases/dynamic.wat index 00455ca9c..d284e0bd3 100644 --- a/arbitrator/prover/test-cases/dynamic.wat +++ b/arbitrator/prover/test-cases/dynamic.wat @@ -7,7 +7,7 @@ (import "hostio" "program_ink_status" (func $ink_status (param i32) (result i32))) (import "hostio" "program_call_main" (func $user_func (param i32 i32) (result i32))) (data (i32.const 0x0) - "\10\a4\b0\c7\91\26\6b\fb\f7\92\f5\e5\67\e0\03\d7\ee\7f\cf\7e\0a\52\6e\b3\92\46\c3\94\6f\21\b8\f8") ;; user + "\97\0c\df\6a\a9\bf\d4\3c\03\80\7f\8a\7e\67\9a\5c\12\05\94\4f\c6\5e\39\9e\00\df\5c\b3\7d\de\55\ad") ;; 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 dda3c8a71..72ce0783a 100644 --- a/arbitrator/prover/test-cases/link.wat +++ b/arbitrator/prover/test-cases/link.wat @@ -4,32 +4,32 @@ (module (import "hostio" "wavm_link_module" (func $link (param i32) (result i32))) (import "hostio" "wavm_unlink_module" (func $unlink (param) (result))) - (data (i32.const 0x000) - "\a6\44\c7\fc\d3\a2\7b\00\60\f2\7c\32\47\2c\3b\0f\c0\88\94\8c\5b\9f\b1\9c\17\11\9d\70\04\6e\9e\25") ;; block - (data (i32.const 0x020) - "\4f\0f\fa\e9\f1\a2\5b\72\85\9d\c8\23\aa\ed\42\18\54\ed\b1\14\9f\08\97\26\fc\e2\ff\ad\ca\2b\96\bc") ;; call - (data (i32.const 0x040) - "\71\4b\0c\ab\49\45\e7\e1\e5\34\83\c7\33\0f\36\6a\29\42\45\a5\91\e0\91\7a\f7\0a\ae\f2\fe\2a\72\b4") ;; indirect - (data (i32.const 0x060) - "\fc\ef\2f\e4\98\5c\63\b5\4d\f2\39\86\98\91\c6\70\93\18\d6\22\45\7a\f4\be\fb\ac\34\19\8f\9a\69\3b") ;; const - (data (i32.const 0x080) - "\ce\85\04\55\06\33\44\e6\30\3b\14\33\b3\8e\c5\41\ac\bf\96\60\cb\45\47\97\8c\b6\99\6e\ef\76\d1\36") ;; div - (data (i32.const 0x0a0) - "\01\05\9b\42\54\f2\80\00\0e\2c\41\ed\79\e3\f5\69\d1\28\e6\d3\4e\f5\20\b9\4d\ee\31\5e\78\a4\6b\3e") ;; globals - (data (i32.const 0x0c0) - "\e7\ac\97\8c\df\27\ca\1d\50\30\4d\b4\0c\1f\23\1a\76\bb\eb\5e\2a\2e\5b\e5\4d\24\a4\cc\9d\91\eb\93") ;; if-else - (data (i32.const 0x0e0) - "\f3\3e\62\9a\ee\08\b3\4e\cd\15\a0\38\dc\cc\80\71\b0\31\35\16\fb\4e\77\34\c6\4d\77\54\85\38\7f\35") ;; locals + (data (i32.const 0x0) + "\6d\c0\9f\17\5f\5b\e8\73\64\bc\79\62\e8\13\fd\cb\09\2a\12\24\87\4a\af\15\f2\e1\2e\93\b0\95\30\9a") + (data (i32.const 0x20) + "\f5\6b\4c\c7\19\da\61\01\e4\e4\9a\f1\04\ca\29\97\fd\07\05\d6\c2\3b\e6\55\70\c5\54\65\a0\3f\3d\ee") + (data (i32.const 0x40) + "\57\27\40\77\40\da\77\f8\1f\fd\81\cb\00\e0\02\17\40\f0\be\e4\11\89\0a\56\ba\80\e4\b9\31\74\13\a2") + (data (i32.const 0x60) + "\53\36\71\e6\bf\90\0f\50\fd\18\5f\44\d6\18\77\2f\70\17\19\2a\1a\8d\b6\92\5a\3c\14\1a\af\86\81\d4") + (data (i32.const 0x80) + "\97\0c\df\6a\a9\bf\d4\3c\03\80\7f\8a\7e\67\9a\5c\12\05\94\4f\c6\5e\39\9e\00\df\5c\b3\7d\de\55\ad") + (data (i32.const 0xa0) + "\c7\db\9f\8e\ed\13\ac\66\72\62\76\65\93\1b\9a\64\03\c3\c8\21\44\92\5c\8d\bc\1a\d6\bd\65\f8\2b\20") + (data (i32.const 0xc0) + "\83\46\03\41\b4\5f\a6\e6\a3\0d\e9\fc\79\fc\3c\d6\c9\c3\c7\ac\97\42\bc\48\54\92\e6\84\08\37\07\a6") + (data (i32.const 0xe0) + "\42\1d\62\e9\9a\51\d4\71\ce\50\6e\b4\83\72\18\ea\f8\ab\ab\b9\29\b8\bd\6d\66\ea\52\b3\3d\50\26\34") (data (i32.const 0x100) - "\1d\c4\11\d8\36\83\4a\04\c0\7b\e0\46\a7\8d\4e\91\0b\13\f2\d5\1a\9e\fe\ed\9d\e6\2f\ee\54\6f\94\95") ;; loop + "\74\22\43\ad\22\2e\e5\6d\f4\bb\3f\0b\09\76\0a\bf\51\b7\17\a4\c5\50\c9\5b\45\be\ea\ed\4c\57\4d\17") (data (i32.const 0x120) - "\8a\f6\10\f0\c6\a1\91\55\0a\72\1e\4d\36\91\88\6b\18\f5\42\73\9d\c5\9a\ea\1d\4d\b5\fb\bf\cf\06\f0") ;; math + "\16\90\98\f2\7f\8d\bf\73\90\b9\eb\94\9f\b9\41\cd\c3\93\2e\30\b8\12\1b\d5\87\98\18\26\f2\62\7d\2c") (data (i32.const 0x140) - "\fc\27\e9\2e\12\23\f2\d6\ef\2a\83\3b\c8\1a\22\99\77\76\23\d8\f5\cf\51\f8\28\ba\a4\27\98\af\aa\24") ;; iops + "\3f\c3\a1\eb\a6\62\70\2b\3b\fa\dc\5b\29\22\11\6f\58\4a\6e\e5\70\60\6f\cf\6c\66\d8\c9\77\c5\c9\23") (data (i32.const 0x160) - "\10\a4\b0\c7\91\26\6b\fb\f7\92\f5\e5\67\e0\03\d7\ee\7f\cf\7e\0a\52\6e\b3\92\46\c3\94\6f\21\b8\f8") ;; user + "\a7\66\cb\0e\c4\31\ea\16\fd\c6\2f\d3\11\ca\4a\78\f8\48\6a\69\0a\4c\b9\1c\fc\47\f8\b6\63\6d\80\fa") (data (i32.const 0x180) - "\f6\ad\69\79\fc\db\8a\af\27\48\ac\7c\54\5f\b2\a8\f2\80\f8\69\a6\75\59\a7\80\58\ba\26\39\5e\aa\c9") ;; return + "\ea\02\78\f7\a3\b3\e0\0e\55\f6\8f\13\87\d6\6f\04\38\b3\6b\4c\d5\33\e2\3d\0b\36\71\9f\57\f5\f0\59") (func $start (local $counter i32) ;; add modules From d6c5be095e049616e04d4933d879936e3d01b954 Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 5 Sep 2023 11:54:35 -0600 Subject: [PATCH 35/55] re-introduce lowering ink rice in tetMemory --- system_tests/program_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_tests/program_test.go b/system_tests/program_test.go index 801184974..c1f31ee15 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -778,7 +778,7 @@ func testMemory(t *testing.T, jit bool) { arbOwner, err := precompilesgen.NewArbOwner(types.ArbOwnerAddress, l2client) Require(t, err) - ensure(arbOwner.SetInkPrice(&auth, 1e4)) + ensure(arbOwner.SetInkPrice(&auth, 1e2)) ensure(arbOwner.SetMaxTxGasLimit(&auth, 34000000)) memoryAddr := deployWasm(t, ctx, auth, l2client, watFile("memory")) From f18ee9e9c60bf28e19c62a8d2c3849e2ff64ceef Mon Sep 17 00:00:00 2001 From: Tsahi Zidenberg Date: Tue, 5 Sep 2023 12:46:16 -0600 Subject: [PATCH 36/55] reduce testProgramMemory ink price, add gas flexibility on deploy --- system_tests/common_test.go | 2 +- system_tests/program_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system_tests/common_test.go b/system_tests/common_test.go index a4a2c4f2c..9d665fc35 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -938,7 +938,7 @@ func deployContract( t *testing.T, ctx context.Context, auth bind.TransactOpts, client *ethclient.Client, code []byte, ) common.Address { deploy := deployContractInitCode(code, false) - basefee := GetBaseFee(t, client, ctx) + basefee := arbmath.BigMulByFrac(GetBaseFee(t, client, ctx), 6, 5) // current*1.2 nonce, err := client.NonceAt(ctx, auth.From, nil) Require(t, err) gas, err := client.EstimateGas(ctx, ethereum.CallMsg{ diff --git a/system_tests/program_test.go b/system_tests/program_test.go index c1f31ee15..801184974 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -778,7 +778,7 @@ func testMemory(t *testing.T, jit bool) { arbOwner, err := precompilesgen.NewArbOwner(types.ArbOwnerAddress, l2client) Require(t, err) - ensure(arbOwner.SetInkPrice(&auth, 1e2)) + ensure(arbOwner.SetInkPrice(&auth, 1e4)) ensure(arbOwner.SetMaxTxGasLimit(&auth, 34000000)) memoryAddr := deployWasm(t, ctx, auth, l2client, watFile("memory")) From 96bf2d49f5fa7d007d293a76a6928f38be4c542a Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Wed, 11 Oct 2023 18:36:02 -0600 Subject: [PATCH 37/55] simplifications and fixes --- Makefile | 7 ++-- arbitrator/Cargo.lock | 5 ++- arbitrator/arbutil/Cargo.toml | 1 + arbitrator/arbutil/src/math.rs | 20 +++++++++ arbitrator/jit/src/user/mod.rs | 6 +-- arbitrator/jit/src/wavmio.rs | 24 +++++------ arbitrator/prover/src/binary.rs | 59 +++++++++++++-------------- arbitrator/prover/src/host.rs | 33 +++++++-------- arbitrator/prover/src/lib.rs | 59 ++++++++++++++------------- arbitrator/prover/src/machine.rs | 39 ++++-------------- arbitrator/prover/src/programs/mod.rs | 1 + arbitrator/prover/src/value.rs | 11 ++++- arbitrator/stylus/src/lib.rs | 2 +- arbitrator/stylus/src/native.rs | 10 ++--- arbitrator/wasm-libraries/Cargo.lock | 5 ++- go-ethereum | 2 +- system_tests/program_test.go | 4 -- system_tests/stylus_test.go | 4 ++ validator/server_api/json.go | 6 +-- validator/server_arb/machine.go | 8 ++-- validator/server_jit/jit_machine.go | 2 +- 21 files changed, 155 insertions(+), 153 deletions(-) diff --git a/Makefile b/Makefile index c9bf2316b..1082e7a12 100644 --- a/Makefile +++ b/Makefile @@ -259,17 +259,17 @@ $(replay_wasm): $(DEP_PREDICATE) $(go_source) .make/solgen mkdir -p `dirname $(replay_wasm)` GOOS=js GOARCH=wasm go build -o $@ ./cmd/replay/... -$(prover_bin): $(DEP_PREDICATE) $(rust_prover_files) $(output_latest)/forward_stub.wasm +$(prover_bin): $(DEP_PREDICATE) $(rust_prover_files) mkdir -p `dirname $(prover_bin)` cargo build --manifest-path arbitrator/Cargo.toml --release --bin prover ${CARGOFLAGS} install arbitrator/target/release/prover $@ -$(arbitrator_stylus_lib): $(DEP_PREDICATE) $(stylus_files) $(output_latest)/forward_stub.wasm +$(arbitrator_stylus_lib): $(DEP_PREDICATE) $(stylus_files) mkdir -p `dirname $(arbitrator_stylus_lib)` cargo build --manifest-path arbitrator/Cargo.toml --release --lib -p stylus ${CARGOFLAGS} install arbitrator/target/release/libstylus.a $@ -$(arbitrator_jit): $(DEP_PREDICATE) .make/cbrotli-lib $(jit_files) $(output_latest)/forward_stub.wasm +$(arbitrator_jit): $(DEP_PREDICATE) .make/cbrotli-lib $(jit_files) mkdir -p `dirname $(arbitrator_jit)` cargo build --manifest-path arbitrator/Cargo.toml --release -p jit ${CARGOFLAGS} install arbitrator/target/release/jit $@ @@ -352,7 +352,6 @@ $(output_latest)/forward.wasm: $(DEP_PREDICATE) $(wasm_lib)/user-host/forward.wa wat2wasm $(wasm_lib)/user-host/forward.wat -o $@ $(output_latest)/forward_stub.wasm: $(DEP_PREDICATE) $(wasm_lib)/user-host/forward_stub.wat .make/machines - mkdir -p $(output_latest) wat2wasm $(wasm_lib)/user-host/forward_stub.wat -o $@ $(output_latest)/machine.wavm.br: $(DEP_PREDICATE) $(prover_bin) $(arbitrator_wasm_libs) $(replay_wasm) diff --git a/arbitrator/Cargo.lock b/arbitrator/Cargo.lock index f0de36f23..ef9116953 100644 --- a/arbitrator/Cargo.lock +++ b/arbitrator/Cargo.lock @@ -65,6 +65,7 @@ dependencies = [ "digest 0.9.0", "eyre", "hex", + "num-traits", "serde", "sha3 0.10.8", "siphasher", @@ -1097,9 +1098,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] diff --git a/arbitrator/arbutil/Cargo.toml b/arbitrator/arbutil/Cargo.toml index f48556548..e2a3a750e 100644 --- a/arbitrator/arbutil/Cargo.toml +++ b/arbitrator/arbutil/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" digest = "0.9.0" eyre = "0.6.5" hex = "0.4.3" +num-traits = "0.2.17" sha3 = "0.10.5" siphasher = "0.3.10" wasmparser = "0.83" diff --git a/arbitrator/arbutil/src/math.rs b/arbitrator/arbutil/src/math.rs index 2e8631214..a7556974d 100644 --- a/arbitrator/arbutil/src/math.rs +++ b/arbitrator/arbutil/src/math.rs @@ -1,6 +1,7 @@ // Copyright 2023, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE +use num_traits::{ops::saturating::SaturatingAdd, Zero}; use std::ops::{BitAnd, Sub}; /// Checks if a number is a power of 2. @@ -13,3 +14,22 @@ where } value & (value - 1.into()) == 0.into() } + +/// Calculates a sum, saturating in cases of overflow. +pub trait SaturatingSum { + type Number; + + fn saturating_sum(self) -> Self::Number; +} + +impl SaturatingSum for I +where + I: Iterator, + T: SaturatingAdd + Zero, +{ + type Number = T; + + fn saturating_sum(self) -> Self::Number { + self.fold(T::zero(), |acc, x| acc.saturating_add(&x)) + } +} diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 62240609a..9d3b3e9f6 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -48,18 +48,18 @@ pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { if out_hash_len != 32 { error!(eyre::eyre!( - "Go attempting to read compiled machine hash into bad buffer length: {out_hash_len}" + "Go attempting to read module hash into bad buffer length: {out_hash_len}" )); } // ensure the wasm compiles during proving - let (module, canonical_hash, info) = + let (module, module_hash, info) = match native::compile_user_wasm(&wasm, version, page_limit, debug) { Ok(result) => result, Err(error) => error!(error), }; - sp.write_slice(out_hash_ptr, canonical_hash.as_slice()); + sp.write_slice(out_hash_ptr, module_hash.as_slice()); sp.write_ptr(heapify(module)); sp.write_u16(info.footprint).skip_u16().write_u32(info.size); // wasm info sp.write_nullptr(); diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index 7fb10c5ea..850d61673 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -313,24 +313,24 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { env.preimages.insert(hash, preimage); } - let stylus_debug = socket::read_u8(stream)? != 0; + let debug = socket::read_u8(stream)? != 0; let programs_count = socket::read_u32(stream)?; for _ in 0..programs_count { let codehash = socket::read_bytes32(stream)?; - let wasm = socket::read_bytes(stream)?; - let compiled_hash = socket::read_bytes32(stream)?; + let wasm = &socket::read_bytes(stream)?; + let module_hash = socket::read_bytes32(stream)?; let version = socket::read_u16(stream)?; - // todo: test wasm against codehash? + // no need to test page_limit, we're just retracing previous compilation - let (module, computed_hash, _) = - match native::compile_user_wasm(wasm.as_slice(), version, u16::MAX, stylus_debug) { - Err(err) => return Escape::hostio(format!("{:?}", err)), - Ok(res) => res, - }; - if compiled_hash != *computed_hash { - return Escape::hostio(format!("error! compiled wasm different from expected codehash {:?}, version {}, expected {:?} computed {}", codehash, version, compiled_hash, computed_hash)); + let (module, hash, _) = match native::compile_user_wasm(wasm, version, u16::MAX, debug) { + Ok(res) => res, + Err(err) => return Escape::hostio(format!("{err:?}")), + }; + if module_hash != *hash { + let msg = format!("module hash divergence {codehash:?}, version {version}, expected {module_hash:?} computed {hash}"); + return Escape::hostio(msg); } - env.compiled_modules.insert(compiled_hash, module); + env.compiled_modules.insert(module_hash, module); } if socket::read_u8(stream)? != socket::READY { diff --git a/arbitrator/prover/src/binary.rs b/arbitrator/prover/src/binary.rs index 8880f8048..c9a3d6040 100644 --- a/arbitrator/prover/src/binary.rs +++ b/arbitrator/prover/src/binary.rs @@ -9,7 +9,7 @@ use crate::{ }, value::{ArbValueType, FunctionType, IntegerValType, Value}, }; -use arbutil::{Color, DebugColor}; +use arbutil::{math::SaturatingSum, Color, DebugColor}; use eyre::{bail, ensure, eyre, Result, WrapErr}; use fnv::{FnvHashMap as HashMap, FnvHashSet as HashSet}; use nom::{ @@ -80,22 +80,16 @@ pub enum FloatInstruction { impl FloatInstruction { pub fn signature(&self) -> FunctionType { match *self { - FloatInstruction::UnOp(t, _) => FunctionType::new(vec![t.into()], vec![t.into()]), - FloatInstruction::BinOp(t, _) => FunctionType::new(vec![t.into(); 2], vec![t.into()]), - FloatInstruction::RelOp(t, _) => { - FunctionType::new(vec![t.into(); 2], vec![ArbValueType::I32]) - } - FloatInstruction::TruncIntOp(i, f, ..) => { - FunctionType::new(vec![f.into()], vec![i.into()]) - } - FloatInstruction::ConvertIntOp(f, i, _) => { - FunctionType::new(vec![i.into()], vec![f.into()]) - } + FloatInstruction::UnOp(t, _) => FunctionType::new([t.into()], [t.into()]), + FloatInstruction::BinOp(t, _) => FunctionType::new([t.into(); 2], [t.into()]), + FloatInstruction::RelOp(t, _) => FunctionType::new([t.into(); 2], [ArbValueType::I32]), + FloatInstruction::TruncIntOp(i, f, ..) => FunctionType::new([f.into()], [i.into()]), + FloatInstruction::ConvertIntOp(f, i, _) => FunctionType::new([i.into()], [f.into()]), FloatInstruction::F32DemoteF64 => { - FunctionType::new(vec![ArbValueType::F64], vec![ArbValueType::F32]) + FunctionType::new([ArbValueType::F64], [ArbValueType::F32]) } FloatInstruction::F64PromoteF32 => { - FunctionType::new(vec![ArbValueType::F32], vec![ArbValueType::F64]) + FunctionType::new([ArbValueType::F32], [ArbValueType::F64]) } } } @@ -585,6 +579,10 @@ impl<'a> WasmBinary<'a> { // 4GB maximum implies `footprint` fits in a u16 let footprint = self.memory_info()?.min.0 as u16; + // check the entrypoint + let ty = FunctionType::new([ArbValueType::I32], [ArbValueType::I32]); + let user_main = self.check_func(STYLUS_ENTRY_POINT, ty)?; + let [ink_left, ink_status] = meter.globals(); let depth_left = depth.globals(); Ok(StylusData { @@ -592,6 +590,7 @@ impl<'a> WasmBinary<'a> { ink_status, depth_left, footprint, + user_main, }) } @@ -634,6 +633,9 @@ impl<'a> WasmBinary<'a> { limit!(4096, function.locals.len(), "locals") } + let table_entries = bin.tables.iter().map(|x| x.initial).saturating_sum(); + limit!(10_000, table_entries, "table entries"); + let max_len = 500; macro_rules! too_long { ($name:expr, $len:expr) => { @@ -654,27 +656,22 @@ impl<'a> WasmBinary<'a> { if bin.start.is_some() { bail!("wasm start functions not allowed"); } + Ok((bin, stylus_data, pages as u16)) + } - // check the entrypoint - let Some(&(entrypoint, kind)) = bin.exports.get(STYLUS_ENTRY_POINT) else { - bail!("missing export with name {}", STYLUS_ENTRY_POINT.red()); + /// Ensures a func exists and has the right type. + fn check_func(&self, name: &str, ty: FunctionType) -> Result { + let Some(&(func, kind)) = self.exports.get(name) else { + bail!("missing export with name {}", name.red()); }; if kind != ExportKind::Func { - bail!( - "export {} must be a function but is a {}", - STYLUS_ENTRY_POINT.red(), - kind.debug_red(), - ); + let kind = kind.debug_red(); + bail!("export {} must be a function but is a {kind}", name.red()); } - let entrypoint_ty = bin.get_function(FunctionIndex::new(entrypoint.try_into()?))?; - if entrypoint_ty != FunctionType::new(vec![ArbValueType::I32], vec![ArbValueType::I32]) { - bail!( - "wrong type for {}: {}", - STYLUS_ENTRY_POINT.red(), - entrypoint_ty.red(), - ); + let func_ty = self.get_function(FunctionIndex::new(func.try_into()?))?; + if func_ty != ty { + bail!("wrong type for {}: {}", name.red(), func_ty.red()); } - - Ok((bin, stylus_data, pages as u16)) + Ok(func) } } diff --git a/arbitrator/prover/src/host.rs b/arbitrator/prover/src/host.rs index 184005722..c98ebdf65 100644 --- a/arbitrator/prover/src/host.rs +++ b/arbitrator/prover/src/host.rs @@ -1,7 +1,7 @@ // Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -#![allow(clippy::vec_init_then_push)] +#![allow(clippy::vec_init_then_push, clippy::redundant_closure)] use crate::{ binary, host, @@ -53,7 +53,7 @@ impl InternalFunc { UserSetInk => func!([I64, I32], []), // λ(ink_left, ink_status) UserStackLeft => func!([], [I32]), // λ() → stack_left UserSetStack => func!([I32], []), // λ(stack_left) - CallMain => func!([I32], [I32]), + CallMain => func!([I32], [I32]), // λ(args_len) → status }; ty } @@ -209,7 +209,7 @@ impl Hostio { macro_rules! cross_internal { ($func:ident) => { opcode!(LocalGet, 0); // module - opcode!(CrossModuleInternalCall, InternalFunc::$func); // consumes module and func + opcode!(CrossModuleInternalCall, InternalFunc::$func); // consumes module }; } macro_rules! intern { @@ -360,7 +360,7 @@ pub fn get_impl(module: &str, name: &str) -> Result<(Function, bool)> { /// Adds internal functions to a module. /// Note: the order of the functions must match that of the `InternalFunc` enum -pub fn new_internal_funcs(stylus_data: Option<(StylusData, u32)>) -> Vec { +pub fn new_internal_funcs(stylus_data: Option) -> Vec { use ArbValueType::*; use InternalFunc::*; use Opcode::*; @@ -409,20 +409,17 @@ pub fn new_internal_funcs(stylus_data: Option<(StylusData, u32)>) -> Vec mach, Err(err) => { eprintln!("Error loading binary: {:?}", err); - std::ptr::null_mut() + ptr::null_mut() } } } @@ -111,7 +112,7 @@ pub unsafe extern "C" fn arbitrator_load_wavm_binary(binary_path: *const c_char) Ok(mach) => Box::into_raw(Box::new(mach)), Err(err) => { eprintln!("Error loading binary: {}", err); - std::ptr::null_mut() + ptr::null_mut() } } } @@ -120,6 +121,23 @@ unsafe fn cstr_to_string(c_str: *const c_char) -> String { CStr::from_ptr(c_str).to_string_lossy().into_owned() } +pub fn err_to_c_string(err: Report) -> *mut libc::c_char { + str_to_c_string(&format!("{err:?}")) +} + +/// Copies the str-data into a libc free-able C string +pub fn str_to_c_string(text: &str) -> *mut libc::c_char { + unsafe { + let buf = libc::malloc(text.len() + 1); // includes null-terminating byte + if buf.is_null() { + panic!("Failed to allocate memory for error string"); + } + ptr::copy_nonoverlapping(text.as_ptr(), buf as *mut u8, text.len()); + *(buf.add(text.len()) as *mut u8) = 0; + buf as *mut libc::c_char + } +} + #[no_mangle] pub unsafe extern "C" fn arbitrator_free_machine(mach: *mut Machine) { drop(Box::from_raw(mach)); @@ -137,19 +155,6 @@ pub unsafe extern "C" fn atomic_u8_store(ptr: *mut u8, contents: u8) { (*(ptr as *mut AtomicU8)).store(contents, atomic::Ordering::Relaxed); } -pub fn err_to_c_string(err: eyre::Report) -> *mut libc::c_char { - let err = format!("{:?}", err); - unsafe { - let buf = libc::malloc(err.len() + 1); - if buf.is_null() { - panic!("Failed to allocate memory for error string"); - } - std::ptr::copy_nonoverlapping(err.as_ptr(), buf as *mut u8, err.len()); - *(buf.add(err.len()) as *mut u8) = 0; - buf as *mut libc::c_char - } -} - /// Runs the machine while the condition variable is zero. May return early if num_steps is hit. /// Returns a c string error (freeable with libc's free) on error, or nullptr on success. #[no_mangle] @@ -172,7 +177,7 @@ pub unsafe extern "C" fn arbitrator_step( } remaining_steps -= stepping; } - std::ptr::null_mut() + ptr::null_mut() } #[no_mangle] @@ -205,17 +210,13 @@ pub unsafe extern "C" fn arbitrator_add_user_wasm( root: *const Bytes32, ) -> *mut libc::c_char { let wasm = std::slice::from_raw_parts(wasm, wasm_len as usize); + let debug = debug != 0; if root.is_null() { - return err_to_c_string(eyre::eyre!( - "arbitrator_add_user_wasm got null ptr for module hash" - )); + return str_to_c_string("arbitrator_add_user_wasm got null ptr for module hash"); } - // provide the opportunity to skip calculating the module root - let debug = debug != 0; - match (*mach).add_program(wasm, version, debug, Some(*root)) { - Ok(_) => std::ptr::null_mut(), + Ok(_) => ptr::null_mut(), Err(err) => err_to_c_string(err), } } @@ -232,10 +233,10 @@ pub unsafe extern "C" fn arbitrator_step_until_host_io( while condition.load(atomic::Ordering::Relaxed) == 0 { for _ in 0..1_000_000 { if mach.is_halted() { - return std::ptr::null_mut(); + return ptr::null_mut(); } if mach.next_instruction_is_host_io() { - return std::ptr::null_mut(); + return ptr::null_mut(); } match mach.step_n(1) { Ok(()) => {} @@ -243,7 +244,7 @@ pub unsafe extern "C" fn arbitrator_step_until_host_io( } } } - std::ptr::null_mut() + ptr::null_mut() } #[no_mangle] @@ -254,7 +255,7 @@ pub unsafe extern "C" fn arbitrator_serialize_state( let mach = &*mach; let res = CStr::from_ptr(path) .to_str() - .map_err(eyre::Report::from) + .map_err(Report::from) .and_then(|path| mach.serialize_state(path)); if let Err(err) = res { eprintln!("Failed to serialize machine state: {}", err); @@ -272,7 +273,7 @@ pub unsafe extern "C" fn arbitrator_deserialize_and_replace_state( let mach = &mut *mach; let res = CStr::from_ptr(path) .to_str() - .map_err(eyre::Report::from) + .map_err(Report::from) .and_then(|path| mach.deserialize_and_replace_state(path)); if let Err(err) = res { eprintln!("Failed to deserialize machine state: {}", err); diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index c25a71a2e..aa43e2976 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -8,9 +8,7 @@ use crate::{ host, memory::Memory, merkle::{Merkle, MerkleType}, - programs::{ - config::CompileConfig, meter::MeteredMachine, ModuleMod, StylusData, STYLUS_ENTRY_POINT, - }, + programs::{config::CompileConfig, meter::MeteredMachine, ModuleMod, StylusData}, reinterpret::{ReinterpretAsSigned, ReinterpretAsUnsigned}, utils::{file_bytes, CBytes, RemoteTableType}, value::{ArbValueType, FunctionType, IntegerValType, ProgramCounter, Value}, @@ -390,18 +388,7 @@ impl Module { }) .collect(); - let internals_data = match stylus_data { - None => None, - Some(data) => { - let stylus_main = func_exports - .iter() - .find(|x| x.0 == STYLUS_ENTRY_POINT) - .and_then(|x| Some(x.1)) - .ok_or(eyre::eyre!("stylus program without entry point"))?; - Some((data, *stylus_main)) - } - }; - let internals = host::new_internal_funcs(internals_data); + let internals = host::new_internal_funcs(stylus_data); let internals_offset = (code.len() + bin.codes.len()) as u32; let internals_types = internals.iter().map(|f| f.ty.clone()); @@ -1137,27 +1124,17 @@ impl Machine { wasm: &[u8], version: u16, debug_funcs: bool, - hash: Option, + hash: Option, // computed if not already known ) -> Result { let mut bin = binary::parse(wasm, Path::new("user"))?; let config = CompileConfig::version(version, debug_funcs); let stylus_data = bin.instrument(&config)?; let module = Module::from_user_binary(&bin, debug_funcs, Some(stylus_data))?; - let computed_hash = module.hash(); - - if let Some(expected_hash) = hash { - if computed_hash != expected_hash { - return Err(eyre::eyre!( - "compulted hash {} doesn't match expected {}", - computed_hash, - expected_hash - )); - } - } - eprintln!("adding module {}", computed_hash); - self.stylus_modules.insert(computed_hash, module); - Ok(computed_hash) + let hash = hash.unwrap_or_else(|| module.hash()); + + self.stylus_modules.insert(hash, module); + Ok(hash) } pub fn from_binaries( @@ -1291,7 +1268,7 @@ impl Machine { // Rust support let rust_fn = "__main_void"; if let Some(&f) = main_exports.get(rust_fn).filter(|_| runtime_support) { - let expected_type = FunctionType::new(vec![], vec![I32]); + let expected_type = FunctionType::new([], [I32]); ensure!( main_module.func_types[f as usize] == expected_type, "Main function doesn't match expected signature of [] -> [ret]", diff --git a/arbitrator/prover/src/programs/mod.rs b/arbitrator/prover/src/programs/mod.rs index a519714d9..ccc3c7664 100644 --- a/arbitrator/prover/src/programs/mod.rs +++ b/arbitrator/prover/src/programs/mod.rs @@ -367,6 +367,7 @@ pub struct StylusData { pub ink_status: GlobalIndex, pub depth_left: GlobalIndex, pub footprint: u16, + pub user_main: u32, } impl StylusData { diff --git a/arbitrator/prover/src/value.rs b/arbitrator/prover/src/value.rs index e7bd77fc3..6bd688604 100644 --- a/arbitrator/prover/src/value.rs +++ b/arbitrator/prover/src/value.rs @@ -409,8 +409,15 @@ pub struct FunctionType { } impl FunctionType { - pub fn new(inputs: Vec, outputs: Vec) -> FunctionType { - FunctionType { inputs, outputs } + pub fn new(inputs: T, outputs: U) -> FunctionType + where + T: Into>, + U: Into>, + { + FunctionType { + inputs: inputs.into(), + outputs: outputs.into(), + } } pub fn hash(&self) -> Bytes32 { diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index 6c46a16be..9152151a7 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -132,8 +132,8 @@ pub unsafe extern "C" fn stylus_compile( let (module, canonical_hash, pricing_info) = match native::compile_user_wasm(wasm, version, page_limit, debug_mode) { - Err(err) => return output.write_err(err), Ok(val) => val, + Err(err) => return output.write_err(err), }; out_canonical_hash.write(canonical_hash.to_vec()); diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index c794a7952..558ba116d 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -379,9 +379,9 @@ pub fn compile_user_wasm( ) -> Result<(Vec, Bytes32, WasmPricingInfo)> { let compile = CompileConfig::version(version, debug_mode); let (bin, stylus_data, footprint) = - WasmBinary::parse_user(wasm, page_limit, &compile).wrap_err("failed to parse program")?; + WasmBinary::parse_user(wasm, page_limit, &compile).wrap_err("failed to parse wasm")?; - let canonical_hash = prover::machine::Module::from_user_binary( + let module_hash = prover::machine::Module::from_user_binary( &bin, compile.debug.debug_funcs, Some(stylus_data), @@ -391,9 +391,9 @@ pub fn compile_user_wasm( let info = WasmPricingInfo { size: wasm.len().try_into()?, - footprint: footprint, + footprint, }; - let module = module(wasm, compile).wrap_err("failed generating stylus module")?; + let module = module(wasm, compile).wrap_err("failed to generate stylus module")?; - Ok((module, canonical_hash, info)) + Ok((module, module_hash, info)) } diff --git a/arbitrator/wasm-libraries/Cargo.lock b/arbitrator/wasm-libraries/Cargo.lock index daaa28fee..cdb4bea75 100644 --- a/arbitrator/wasm-libraries/Cargo.lock +++ b/arbitrator/wasm-libraries/Cargo.lock @@ -29,6 +29,7 @@ dependencies = [ "digest 0.9.0", "eyre", "hex", + "num-traits", "serde", "sha3 0.10.6", "siphasher", @@ -593,9 +594,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] diff --git a/go-ethereum b/go-ethereum index 35fead93e..97f1a4d8f 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 35fead93eece0bccf4998360657f02d10155eccc +Subproject commit 97f1a4d8f5150e61a3dea4378cdf39bbb3820787 diff --git a/system_tests/program_test.go b/system_tests/program_test.go index a07f37510..36bcb2f45 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -48,10 +48,6 @@ func TestProgramKeccak(t *testing.T) { keccakTest(t, true) } -func TestProgramArbitratorKeccak(t *testing.T) { - keccakTest(t, false) -} - func keccakTest(t *testing.T, jit bool) { ctx, node, _, l2client, auth, cleanup := setupProgramTest(t, jit) defer cleanup() diff --git a/system_tests/stylus_test.go b/system_tests/stylus_test.go index 128c107b8..f4615da56 100644 --- a/system_tests/stylus_test.go +++ b/system_tests/stylus_test.go @@ -10,6 +10,10 @@ import ( "testing" ) +func TestProgramArbitratorKeccak(t *testing.T) { + keccakTest(t, false) +} + func TestProgramArbitratorErrors(t *testing.T) { errorTest(t, false) } diff --git a/validator/server_api/json.go b/validator/server_api/json.go index 303dd1a05..bee8309dd 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -20,7 +20,7 @@ type BatchInfoJson struct { } type UserWasmJson struct { - CompiledHash common.Hash + ModuleHash common.Hash CompressedWasm string } @@ -57,7 +57,7 @@ func ValidationInputToJson(entry *validator.ValidationInput) *ValidationInputJso encCall := base64.StdEncoding.EncodeToString(callBytes) encWasm := UserWasmJson{ CompressedWasm: base64.StdEncoding.EncodeToString(wasm.CompressedWasm), - CompiledHash: wasm.CompiledHash, + ModuleHash: wasm.ModuleHash, } res.UserWasms[encCall] = encWasm } @@ -104,7 +104,7 @@ func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationI return nil, err } decWasm := state.UserWasm{ - CompiledHash: wasm.CompiledHash, + ModuleHash: wasm.ModuleHash, CompressedWasm: compressed, } valInput.UserWasms[decCall] = &decWasm diff --git a/validator/server_arb/machine.go b/validator/server_arb/machine.go index b3ceea8c0..0993985ed 100644 --- a/validator/server_arb/machine.go +++ b/validator/server_arb/machine.go @@ -405,21 +405,21 @@ func (m *ArbitratorMachine) AddUserWasm(call state.WasmCall, wasm *state.UserWas return errors.New("machine frozen") } hashBytes := [32]u8{} - for index, byte := range wasm.CompiledHash.Bytes() { + for index, byte := range wasm.ModuleHash.Bytes() { hashBytes[index] = u8(byte) } debugInt := 0 if debug { debugInt = 1 } - decompressed, err := arbcompress.Decompress(wasm.CompressedWasm, programs.MaxWasmSize) + inflated, err := arbcompress.Decompress(wasm.CompressedWasm, programs.MaxWasmSize) if err != nil { return err } cErr := C.arbitrator_add_user_wasm( m.ptr, - (*u8)(arbutil.SliceToPointer(decompressed)), - u32(len(decompressed)), + (*u8)(arbutil.SliceToPointer(inflated)), + u32(len(inflated)), u16(call.Version), u32(debugInt), &C.struct_Bytes32{hashBytes}, diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index cb2530d7b..b02b9af24 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -229,7 +229,7 @@ func (machine *JitMachine) prove( if err := writeBytes(inflated); err != nil { return state, err } - if err := writeExact(wasm.CompiledHash[:]); err != nil { + if err := writeExact(wasm.ModuleHash[:]); err != nil { return state, err } if err := writeUint16(call.Version); err != nil { From afe6187f7ec49bd7e81d71ff8cadc9024fe4733c Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 12 Oct 2023 11:02:50 -0600 Subject: [PATCH 38/55] jit asm --- arbitrator/jit/src/machine.rs | 6 ++++-- arbitrator/jit/src/socket.rs | 9 ++++----- arbitrator/jit/src/user/evm_api.rs | 4 ++-- arbitrator/jit/src/user/mod.rs | 16 ++++++---------- arbitrator/jit/src/wavmio.rs | 18 ++---------------- 5 files changed, 18 insertions(+), 35 deletions(-) diff --git a/arbitrator/jit/src/machine.rs b/arbitrator/jit/src/machine.rs index e233f03a4..a634290f2 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -21,6 +21,7 @@ use std::{ io::{self, Write}, io::{BufReader, BufWriter, ErrorKind, Read}, net::TcpStream, + sync::Arc, time::{Duration, Instant}, }; @@ -193,6 +194,7 @@ impl From for Escape { pub type WasmEnvMut<'a> = FunctionEnvMut<'a, WasmEnv>; pub type Inbox = BTreeMap>; pub type Oracle = BTreeMap>; +pub type ModuleAsm = Arc<[u8]>; #[derive(Default)] pub struct WasmEnv { @@ -208,8 +210,8 @@ pub struct WasmEnv { pub large_globals: [Bytes32; 2], /// An oracle allowing the prover to reverse keccak256 pub preimages: Oracle, - /// A collection of user wasms called during the course of execution - pub compiled_modules: HashMap>, + /// A collection of programs called during the course of execution + pub module_asms: HashMap, /// The sequencer inbox's messages pub sequencer_messages: Inbox, /// The delayed inbox's messages diff --git a/arbitrator/jit/src/socket.rs b/arbitrator/jit/src/socket.rs index 6b4370196..634af3635 100644 --- a/arbitrator/jit/src/socket.rs +++ b/arbitrator/jit/src/socket.rs @@ -20,11 +20,6 @@ pub fn read_u8(reader: &mut BufReader) -> Result { reader.read_exact(&mut buf).map(|_| u8::from_be_bytes(buf)) } -pub fn read_u16(reader: &mut BufReader) -> Result { - let mut buf = [0; 2]; - reader.read_exact(&mut buf).map(|_| u16::from_be_bytes(buf)) -} - pub fn read_u32(reader: &mut BufReader) -> Result { let mut buf = [0; 4]; reader.read_exact(&mut buf).map(|_| u32::from_be_bytes(buf)) @@ -47,6 +42,10 @@ pub fn read_bytes(reader: &mut BufReader) -> Result, io::Err Ok(buf) } +pub fn read_box(reader: &mut BufReader) -> Result, io::Error> { + Ok(Vec::into_boxed_slice(read_bytes(reader)?)) +} + pub fn write_u8(writer: &mut BufWriter, data: u8) -> Result<(), io::Error> { let buf = [data; 1]; writer.write_all(&buf) diff --git a/arbitrator/jit/src/user/evm_api.rs b/arbitrator/jit/src/user/evm_api.rs index 183ebfb0e..d17290226 100644 --- a/arbitrator/jit/src/user/evm_api.rs +++ b/arbitrator/jit/src/user/evm_api.rs @@ -5,7 +5,7 @@ use crate::{ gostack::GoStack, - machine::WasmEnvMut, + machine::{ModuleAsm, WasmEnvMut}, syscall::{DynamicObject, GoValue, JsValue, STYLUS_ID}, }; use arbutil::{ @@ -53,7 +53,7 @@ impl JsCallIntoGo for ApiCaller { pub(super) fn exec_wasm( sp: &mut GoStack, mut env: WasmEnvMut, - module: Vec, + module: ModuleAsm, calldata: Vec, compile: CompileConfig, config: StylusConfig, diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 9d3b3e9f6..b188a9bf6 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -83,7 +83,7 @@ pub fn call_user_wasm(env: WasmEnvMut, sp: u32) -> MaybeEscape { use UserOutcome::*; // move inputs - let compiled_hash = sp.read_bytes32(); + let module_hash = sp.read_bytes32(); let calldata = sp.read_go_slice_owned(); let (compile, config): (CompileConfig, StylusConfig) = sp.unbox(); let evm_api = sp.read_go_slice_owned(); @@ -94,15 +94,11 @@ pub fn call_user_wasm(env: WasmEnvMut, sp: u32) -> MaybeEscape { let pricing = config.pricing; let ink = pricing.gas_to_ink(sp.read_u64_raw(gas)); - let module = match &env.data().compiled_modules.get(&compiled_hash) { - None => { - return Err(Escape::Failure(format!( - "compiled hash requested {:?} not found in {:?}", - compiled_hash, - env.data().compiled_modules.keys() - ))) - } - Some(module) => (*module).clone(), + let Some(module) = env.data().module_asms.get(&module_hash).cloned() else { + return Escape::failure(format!( + "module hash {module_hash:?} not found in {:?}", + env.data().module_asms.keys() + )); }; let result = exec_wasm( diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index 850d61673..1aa39fcc7 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -14,7 +14,6 @@ use std::{ net::TcpStream, time::Instant, }; -use stylus::native; pub type Bytes20 = [u8; 20]; pub type Bytes32 = [u8; 32]; @@ -313,24 +312,11 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { env.preimages.insert(hash, preimage); } - let debug = socket::read_u8(stream)? != 0; let programs_count = socket::read_u32(stream)?; for _ in 0..programs_count { - let codehash = socket::read_bytes32(stream)?; - let wasm = &socket::read_bytes(stream)?; + let module_asm = socket::read_box(stream)?; let module_hash = socket::read_bytes32(stream)?; - let version = socket::read_u16(stream)?; - - // no need to test page_limit, we're just retracing previous compilation - let (module, hash, _) = match native::compile_user_wasm(wasm, version, u16::MAX, debug) { - Ok(res) => res, - Err(err) => return Escape::hostio(format!("{err:?}")), - }; - if module_hash != *hash { - let msg = format!("module hash divergence {codehash:?}, version {version}, expected {module_hash:?} computed {hash}"); - return Escape::hostio(msg); - } - env.compiled_modules.insert(module_hash, module); + env.module_asms.insert(module_hash, module_asm.into()); } if socket::read_u8(stream)? != socket::READY { From 5c2fbf871e3f3701ba3ef8dd28dbc34b277b40fd Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 12 Oct 2023 11:20:15 -0600 Subject: [PATCH 39/55] skip JIT recompilation --- arbitrator/jit/src/wavmio.rs | 2 +- go-ethereum | 2 +- validator/server_api/json.go | 7 +++++++ validator/server_jit/jit_machine.go | 26 ++------------------------ 4 files changed, 11 insertions(+), 26 deletions(-) diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index 1aa39fcc7..17ba6fa81 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -314,8 +314,8 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { let programs_count = socket::read_u32(stream)?; for _ in 0..programs_count { - let module_asm = socket::read_box(stream)?; let module_hash = socket::read_bytes32(stream)?; + let module_asm = socket::read_box(stream)?; env.module_asms.insert(module_hash, module_asm.into()); } diff --git a/go-ethereum b/go-ethereum index 97f1a4d8f..017546438 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 97f1a4d8f5150e61a3dea4378cdf39bbb3820787 +Subproject commit 017546438f3f887c3061cbe1b1f7ffd86461f794 diff --git a/validator/server_api/json.go b/validator/server_api/json.go index bee8309dd..bb97683b0 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -21,6 +21,7 @@ type BatchInfoJson struct { type UserWasmJson struct { ModuleHash common.Hash + ModuleAsm string CompressedWasm string } @@ -58,6 +59,7 @@ func ValidationInputToJson(entry *validator.ValidationInput) *ValidationInputJso encWasm := UserWasmJson{ CompressedWasm: base64.StdEncoding.EncodeToString(wasm.CompressedWasm), ModuleHash: wasm.ModuleHash, + ModuleAsm: base64.StdEncoding.EncodeToString(wasm.ModuleAsm), } res.UserWasms[encCall] = encWasm } @@ -103,8 +105,13 @@ func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationI if err != nil { return nil, err } + moduleAsm, err := base64.StdEncoding.DecodeString(wasm.ModuleAsm) + if err != nil { + return nil, err + } decWasm := state.UserWasm{ ModuleHash: wasm.ModuleHash, + ModuleAsm: moduleAsm, CompressedWasm: compressed, } valInput.UserWasms[decCall] = &decWasm diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index b02b9af24..e356ae719 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -16,8 +16,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbcompress" - "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/validator" ) @@ -120,9 +118,6 @@ func (machine *JitMachine) prove( writeUint8 := func(data uint8) error { return writeExact([]byte{data}) } - writeUint16 := func(data uint16) error { - return writeExact(arbmath.Uint16ToBytes(data)) - } writeUint32 := func(data uint32) error { return writeExact(arbmath.Uint32ToBytes(data)) } @@ -207,32 +202,15 @@ func (machine *JitMachine) prove( } // send user wasms - debugFlag := uint8(0) - if entry.DebugChain { - debugFlag = 1 - } - if err := writeUint8(debugFlag); err != nil { - return state, err - } userWasms := entry.UserWasms if err := writeUint32(uint32(len(userWasms))); err != nil { return state, err } - for call, wasm := range userWasms { - if err := writeExact(call.CodeHash[:]); err != nil { - return state, err - } - inflated, err := arbcompress.Decompress(wasm.CompressedWasm, programs.MaxWasmSize) - if err != nil { - return state, fmt.Errorf("error decompressing program: %w", err) - } - if err := writeBytes(inflated); err != nil { - return state, err - } + for _, wasm := range userWasms { if err := writeExact(wasm.ModuleHash[:]); err != nil { return state, err } - if err := writeUint16(call.Version); err != nil { + if err := writeBytes(wasm.ModuleAsm); err != nil { return state, err } } From 6b4124ee529b584b13bedc05fba97d9a092c6a9c Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 12 Oct 2023 11:30:03 -0600 Subject: [PATCH 40/55] rename to Get/Set ActivateAsm --- arbos/programs/native.go | 4 ++-- go-ethereum | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arbos/programs/native.go b/arbos/programs/native.go index 4f613c7d2..1da44b3e0 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -81,7 +81,7 @@ func compileUserWasm( return nil, common.Hash{}, err } - db.SetCompiledWasmCode(program, data, version) + db.SetActivatedAsm(program, data, version) return &info, common.BytesToHash(canonicalHashRust.intoBytes()), err } @@ -100,7 +100,7 @@ func callUserWasm( if db, ok := db.(*state.StateDB); ok { db.RecordProgram(address, scope.Contract.CodeHash, stylusParams.version, program.compiledHash) } - module := db.GetCompiledWasmCode(address, stylusParams.version) + module := db.GetActivatedAsm(address, stylusParams.version) evmApi, id := newApi(interpreter, tracingInfo, scope, memoryModel) defer dropApi(id) diff --git a/go-ethereum b/go-ethereum index 017546438..83e9aec6c 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 017546438f3f887c3061cbe1b1f7ffd86461f794 +Subproject commit 83e9aec6c5019a6518c616ded00c8d3e20203221 From 0d9521ef4f527a7da29eb4e10483a29f335bb013 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 12 Oct 2023 16:13:13 -0600 Subject: [PATCH 41/55] switch to asm-module pairs --- arbos/programs/native.go | 5 ++++- go-ethereum | 2 +- validator/server_api/json.go | 22 +++++++++++----------- validator/server_arb/machine.go | 16 +++------------- validator/server_jit/jit_machine.go | 2 +- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/arbos/programs/native.go b/arbos/programs/native.go index 1da44b3e0..30a75a264 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -81,7 +81,10 @@ func compileUserWasm( return nil, common.Hash{}, err } - db.SetActivatedAsm(program, data, version) + asm := data + module := []byte{} + + db.NewActivation(program, version, asm, module) return &info, common.BytesToHash(canonicalHashRust.intoBytes()), err } diff --git a/go-ethereum b/go-ethereum index 83e9aec6c..d65925551 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 83e9aec6c5019a6518c616ded00c8d3e20203221 +Subproject commit d65925551350281002cd2a7ca8093ba38e19214b diff --git a/validator/server_api/json.go b/validator/server_api/json.go index bb97683b0..b093e91cb 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -20,9 +20,9 @@ type BatchInfoJson struct { } type UserWasmJson struct { - ModuleHash common.Hash - ModuleAsm string - CompressedWasm string + ModuleHash common.Hash + Module string + Asm string } type ValidationInputJson struct { @@ -57,9 +57,9 @@ func ValidationInputToJson(entry *validator.ValidationInput) *ValidationInputJso callBytes = append(callBytes, call.CodeHash.Bytes()...) encCall := base64.StdEncoding.EncodeToString(callBytes) encWasm := UserWasmJson{ - CompressedWasm: base64.StdEncoding.EncodeToString(wasm.CompressedWasm), - ModuleHash: wasm.ModuleHash, - ModuleAsm: base64.StdEncoding.EncodeToString(wasm.ModuleAsm), + ModuleHash: wasm.ModuleHash, + Module: base64.StdEncoding.EncodeToString(wasm.Module), + Asm: base64.StdEncoding.EncodeToString(wasm.Asm), } res.UserWasms[encCall] = encWasm } @@ -101,18 +101,18 @@ func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationI Version: arbmath.BytesToUint16(callBytes[:2]), CodeHash: common.BytesToHash(callBytes[2:]), } - compressed, err := base64.StdEncoding.DecodeString(wasm.CompressedWasm) + asm, err := base64.StdEncoding.DecodeString(wasm.Asm) if err != nil { return nil, err } - moduleAsm, err := base64.StdEncoding.DecodeString(wasm.ModuleAsm) + module, err := base64.StdEncoding.DecodeString(wasm.Module) if err != nil { return nil, err } decWasm := state.UserWasm{ - ModuleHash: wasm.ModuleHash, - ModuleAsm: moduleAsm, - CompressedWasm: compressed, + ModuleHash: wasm.ModuleHash, + Module: module, + Asm: asm, } valInput.UserWasms[decCall] = &decWasm } diff --git a/validator/server_arb/machine.go b/validator/server_arb/machine.go index 0993985ed..eba79b95f 100644 --- a/validator/server_arb/machine.go +++ b/validator/server_arb/machine.go @@ -22,8 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbcompress" - "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/validator" @@ -408,20 +406,12 @@ func (m *ArbitratorMachine) AddUserWasm(call state.WasmCall, wasm *state.UserWas for index, byte := range wasm.ModuleHash.Bytes() { hashBytes[index] = u8(byte) } - debugInt := 0 - if debug { - debugInt = 1 - } - inflated, err := arbcompress.Decompress(wasm.CompressedWasm, programs.MaxWasmSize) - if err != nil { - return err - } cErr := C.arbitrator_add_user_wasm( m.ptr, - (*u8)(arbutil.SliceToPointer(inflated)), - u32(len(inflated)), + (*u8)(arbutil.SliceToPointer(wasm.Module)), + u32(len(wasm.Module)), u16(call.Version), - u32(debugInt), + u32(arbmath.BoolToUint32(debug)), &C.struct_Bytes32{hashBytes}, ) defer C.free(unsafe.Pointer(cErr)) diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index e356ae719..6de60e912 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -210,7 +210,7 @@ func (machine *JitMachine) prove( if err := writeExact(wasm.ModuleHash[:]); err != nil { return state, err } - if err := writeBytes(wasm.ModuleAsm); err != nil { + if err := writeBytes(wasm.Asm); err != nil { return state, err } } From e6a5b6cd82267272ce88e190012b852b4dbaf9e0 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 12 Oct 2023 21:37:40 -0600 Subject: [PATCH 42/55] produce module-asm pairs --- arbitrator/Cargo.lock | 1 + arbitrator/jit/src/user/mod.rs | 16 ++++----- arbitrator/prover/src/machine.rs | 8 +++++ arbitrator/stylus/Cargo.toml | 1 + arbitrator/stylus/src/lib.rs | 28 +++++++--------- arbitrator/stylus/src/native.rs | 37 ++++++++++----------- arbos/programs/native.go | 26 +++++++++------ arbos/programs/programs.go | 26 +++++++-------- arbos/programs/wasm.go | 2 +- go-ethereum | 2 +- validator/server_api/json.go | 40 +++++++++-------------- validator/server_arb/machine.go | 11 +++---- validator/server_arb/validator_spawner.go | 6 ++-- validator/server_jit/jit_machine.go | 6 ++-- 14 files changed, 103 insertions(+), 107 deletions(-) diff --git a/arbitrator/Cargo.lock b/arbitrator/Cargo.lock index ef9116953..20a7e8335 100644 --- a/arbitrator/Cargo.lock +++ b/arbitrator/Cargo.lock @@ -1803,6 +1803,7 @@ name = "stylus" version = "0.1.0" dependencies = [ "arbutil", + "bincode", "derivative", "eyre", "fnv", diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index b188a9bf6..92f2618fd 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -52,15 +52,13 @@ pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { )); } - // ensure the wasm compiles during proving - let (module, module_hash, info) = - match native::compile_user_wasm(&wasm, version, page_limit, debug) { - Ok(result) => result, - Err(error) => error!(error), - }; - - sp.write_slice(out_hash_ptr, module_hash.as_slice()); - sp.write_ptr(heapify(module)); + let (asm, module, info) = match native::compile_user_wasm(&wasm, version, page_limit, debug) { + Ok(result) => result, + Err(error) => error!(error), + }; + + sp.write_slice(out_hash_ptr, module.hash().as_slice()); + sp.write_ptr(heapify(asm)); sp.write_u16(info.footprint).skip_u16().write_u32(info.size); // wasm info sp.write_nullptr(); } diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index aa43e2976..9b8baab47 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -631,6 +631,14 @@ impl Module { data } + + pub fn into_bytes(&self) -> Box<[u8]> { + bincode::serialize(self).unwrap().into_boxed_slice() + } + + pub unsafe fn from_bytes(bytes: &[u8]) -> Self { + bincode::deserialize(bytes).unwrap() + } } // Globalstate holds: diff --git a/arbitrator/stylus/Cargo.toml b/arbitrator/stylus/Cargo.toml index c91fc4106..b469288dc 100644 --- a/arbitrator/stylus/Cargo.toml +++ b/arbitrator/stylus/Cargo.toml @@ -15,6 +15,7 @@ wasmer-compiler-llvm = { path = "../tools/wasmer/lib/compiler-llvm", optional = derivative = "2.2.0" parking_lot = "0.12.1" thiserror = "1.0.33" +bincode = "1.3.3" libc = "0.2.108" eyre = "0.6.5" rand = "0.8.5" diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index 9152151a7..83f02656d 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -8,6 +8,7 @@ use arbutil::{ EvmData, }, format::DebugBytes, + Bytes32, }; use eyre::ErrReport; use native::NativeInstance; @@ -104,7 +105,7 @@ impl RustVec { /// /// # Safety /// -/// Output, pricing_info, output_canonical_hash must not be null. +/// output, pricing_info, module_hash must not be null. #[no_mangle] pub unsafe extern "C" fn stylus_compile( wasm: GoSliceData, @@ -113,33 +114,26 @@ pub unsafe extern "C" fn stylus_compile( debug_mode: bool, out_pricing_info: *mut WasmPricingInfo, output: *mut RustVec, - out_canonical_hash: *mut RustVec, + asm_len: *mut usize, + module_hash: *mut Bytes32, ) -> UserOutcomeKind { let wasm = wasm.slice(); - - if output.is_null() { - return UserOutcomeKind::Failure; - } let output = &mut *output; + let module_hash = &mut *module_hash; - if out_pricing_info.is_null() { - return output.write_err(eyre::eyre!("pricing_info is null")); - } - if out_canonical_hash.is_null() { - return output.write_err(eyre::eyre!("canonical_hash is null")); - } - let out_canonical_hash = &mut *out_canonical_hash; - - let (module, canonical_hash, pricing_info) = + let (asm, module, pricing_info) = match native::compile_user_wasm(wasm, version, page_limit, debug_mode) { Ok(val) => val, Err(err) => return output.write_err(err), }; - out_canonical_hash.write(canonical_hash.to_vec()); + *asm_len = asm.len(); + *module_hash = module.hash(); *out_pricing_info = pricing_info; - output.write(module); + let mut data = asm; + data.extend(&*module.into_bytes()); + output.write(data); UserOutcomeKind::Success } diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index 558ba116d..99631499b 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -8,17 +8,20 @@ use crate::{ use arbutil::{ evm::{api::EvmApi, EvmData}, operator::OperatorCode, - Bytes32, Color, + Color, }; use eyre::{bail, eyre, Context, ErrReport, Result}; -use prover::binary::WasmBinary; -use prover::programs::{ - config::{PricingParams, WasmPricingInfo}, - counter::{Counter, CountingMachine, OP_OFFSETS}, - depth::STYLUS_STACK_LEFT, - meter::{STYLUS_INK_LEFT, STYLUS_INK_STATUS}, - prelude::*, - start::STYLUS_START, +use prover::{ + binary::WasmBinary, + machine::Module as ProverModule, + programs::{ + config::{PricingParams, WasmPricingInfo}, + counter::{Counter, CountingMachine, OP_OFFSETS}, + depth::STYLUS_STACK_LEFT, + meter::{STYLUS_INK_LEFT, STYLUS_INK_STATUS}, + prelude::*, + start::STYLUS_START, + }, }; use std::{ collections::BTreeMap, @@ -376,24 +379,20 @@ pub fn compile_user_wasm( version: u16, page_limit: u16, debug_mode: bool, -) -> Result<(Vec, Bytes32, WasmPricingInfo)> { +) -> Result<(Vec, ProverModule, WasmPricingInfo)> { let compile = CompileConfig::version(version, debug_mode); let (bin, stylus_data, footprint) = WasmBinary::parse_user(wasm, page_limit, &compile).wrap_err("failed to parse wasm")?; - let module_hash = prover::machine::Module::from_user_binary( - &bin, - compile.debug.debug_funcs, - Some(stylus_data), - ) - .wrap_err("failed to build module from program")? - .hash(); + let prover_module = + ProverModule::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) + .wrap_err("failed to build module from program")?; let info = WasmPricingInfo { size: wasm.len().try_into()?, footprint, }; - let module = module(wasm, compile).wrap_err("failed to generate stylus module")?; + let asm = module(wasm, compile).wrap_err("failed to generate stylus module")?; - Ok((module, module_hash, info)) + Ok((asm, prover_module, info)) } diff --git a/arbos/programs/native.go b/arbos/programs/native.go index 30a75a264..e1b116674 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -52,9 +52,12 @@ func compileUserWasm( debug bool, burner burn.Burner, ) (*wasmPricingInfo, common.Hash, error) { - rustInfo := &C.WasmPricingInfo{} output := &rustVec{} - canonicalHashRust := &rustVec{} + asmLen := usize(0) + + moduleHash := &bytes32{} + rustInfo := &C.WasmPricingInfo{} + status := userStatus(C.stylus_compile( goSlice(wasm), u16(page_limit), @@ -62,7 +65,8 @@ func compileUserWasm( cbool(debug), rustInfo, output, - canonicalHashRust, + &asmLen, + moduleHash, )) data, msg, err := status.toResult(output.intoBytes(), debug) @@ -81,11 +85,13 @@ func compileUserWasm( return nil, common.Hash{}, err } - asm := data - module := []byte{} + hash := moduleHash.toHash() + split := int(asmLen) + asm := data[:split] + module := data[split:] - db.NewActivation(program, version, asm, module) - return &info, common.BytesToHash(canonicalHashRust.intoBytes()), err + db.ActivateWasm(hash, asm, module) + return &info, hash, err } func callUserWasm( @@ -101,16 +107,16 @@ func callUserWasm( memoryModel *MemoryModel, ) ([]byte, error) { if db, ok := db.(*state.StateDB); ok { - db.RecordProgram(address, scope.Contract.CodeHash, stylusParams.version, program.compiledHash) + db.RecordProgram(program.moduleHash) } - module := db.GetActivatedAsm(address, stylusParams.version) + asm := db.GetActivatedAsm(program.moduleHash) evmApi, id := newApi(interpreter, tracingInfo, scope, memoryModel) defer dropApi(id) output := &rustVec{} status := userStatus(C.stylus_call( - goSlice(module), + goSlice(asm), goSlice(calldata), stylusParams.encode(), evmApi, diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index cdba2f23e..218c92f4f 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -35,10 +35,10 @@ type Programs struct { } type Program struct { - wasmSize uint16 // Unit is half of a kb - footprint uint16 - version uint16 - compiledHash common.Hash + wasmSize uint16 // Unit is half of a kb + footprint uint16 + version uint16 + moduleHash common.Hash } type uint24 = arbmath.Uint24 @@ -212,10 +212,10 @@ func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, debugMode wasmSize := arbmath.SaturatingUCast[uint16]((len(wasm) + 511) / 512) programData := Program{ - wasmSize: wasmSize, - footprint: info.footprint, - version: version, - compiledHash: compiledHash, + wasmSize: wasmSize, + footprint: info.footprint, + version: version, + moduleHash: compiledHash, } return version, false, p.setProgram(codeHash, programData) } @@ -326,10 +326,10 @@ func (p Programs) deserializeProgram(codeHash common.Hash) (Program, error) { return Program{}, err } return Program{ - wasmSize: arbmath.BytesToUint16(data[26:28]), - footprint: arbmath.BytesToUint16(data[28:30]), - version: arbmath.BytesToUint16(data[30:]), - compiledHash: compiledHash, + wasmSize: arbmath.BytesToUint16(data[26:28]), + footprint: arbmath.BytesToUint16(data[28:30]), + version: arbmath.BytesToUint16(data[30:]), + moduleHash: compiledHash, }, nil } @@ -342,7 +342,7 @@ func (p Programs) setProgram(codehash common.Hash, program Program) error { if err != nil { return err } - return p.compiledHashes.Set(codehash, program.compiledHash) + return p.compiledHashes.Set(codehash, program.moduleHash) } func (p Programs) CodehashVersion(codeHash common.Hash) (uint16, error) { diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index d09cc00d2..cb4853e48 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -96,7 +96,7 @@ func callUserWasm( debug := arbmath.UintToBool(params.debugMode) status, output := callUserWasmRustImpl( - &program.compiledHash, + &program.moduleHash, calldata, params.encode(), evmApi.funcs, diff --git a/go-ethereum b/go-ethereum index d65925551..d16318ab8 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit d65925551350281002cd2a7ca8093ba38e19214b +Subproject commit d16318ab8b159730142fa5a2431cac70d172d9d5 diff --git a/validator/server_api/json.go b/validator/server_api/json.go index b093e91cb..07f39b345 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" - "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/jsonapi" "github.com/offchainlabs/nitro/validator" ) @@ -20,9 +19,8 @@ type BatchInfoJson struct { } type UserWasmJson struct { - ModuleHash common.Hash - Module string - Asm string + Module string + Asm string } type ValidationInputJson struct { @@ -52,16 +50,13 @@ func ValidationInputToJson(entry *validator.ValidationInput) *ValidationInputJso encData := base64.StdEncoding.EncodeToString(binfo.Data) res.BatchInfo = append(res.BatchInfo, BatchInfoJson{binfo.Number, encData}) } - for call, wasm := range entry.UserWasms { - callBytes := arbmath.Uint16ToBytes(call.Version) - callBytes = append(callBytes, call.CodeHash.Bytes()...) - encCall := base64.StdEncoding.EncodeToString(callBytes) + for moduleHash, info := range entry.UserWasms { + encModuleHash := base64.StdEncoding.EncodeToString(moduleHash[:]) encWasm := UserWasmJson{ - ModuleHash: wasm.ModuleHash, - Module: base64.StdEncoding.EncodeToString(wasm.Module), - Asm: base64.StdEncoding.EncodeToString(wasm.Asm), + Asm: base64.StdEncoding.EncodeToString(info.Asm), + Module: base64.StdEncoding.EncodeToString(info.Module), } - res.UserWasms[encCall] = encWasm + res.UserWasms[encModuleHash] = encWasm } return res } @@ -92,29 +87,24 @@ func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationI } valInput.BatchInfo = append(valInput.BatchInfo, decInfo) } - for call, wasm := range entry.UserWasms { - callBytes, err := base64.StdEncoding.DecodeString(call) + for moduleHash, info := range entry.UserWasms { + decModuleHash, err := base64.StdEncoding.DecodeString(moduleHash) if err != nil { return nil, err } - decCall := state.WasmCall{ - Version: arbmath.BytesToUint16(callBytes[:2]), - CodeHash: common.BytesToHash(callBytes[2:]), - } - asm, err := base64.StdEncoding.DecodeString(wasm.Asm) + asm, err := base64.StdEncoding.DecodeString(info.Asm) if err != nil { return nil, err } - module, err := base64.StdEncoding.DecodeString(wasm.Module) + module, err := base64.StdEncoding.DecodeString(info.Module) if err != nil { return nil, err } - decWasm := state.UserWasm{ - ModuleHash: wasm.ModuleHash, - Module: module, - Asm: asm, + decInfo := state.ActivatedWasm{ + Asm: asm, + Module: module, } - valInput.UserWasms[decCall] = &decWasm + valInput.UserWasms[common.Hash(decModuleHash)] = decInfo } return valInput, nil } diff --git a/validator/server_arb/machine.go b/validator/server_arb/machine.go index eba79b95f..020530141 100644 --- a/validator/server_arb/machine.go +++ b/validator/server_arb/machine.go @@ -20,7 +20,6 @@ import ( "unsafe" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/arbmath" @@ -397,20 +396,20 @@ func (m *ArbitratorMachine) SetPreimageResolver(resolver GoPreimageResolver) err return nil } -func (m *ArbitratorMachine) AddUserWasm(call state.WasmCall, wasm *state.UserWasm, debug bool) error { +func (m *ArbitratorMachine) AddUserWasm(moduleHash common.Hash, module []byte, debug bool) error { defer runtime.KeepAlive(m) if m.frozen { return errors.New("machine frozen") } hashBytes := [32]u8{} - for index, byte := range wasm.ModuleHash.Bytes() { + for index, byte := range moduleHash.Bytes() { hashBytes[index] = u8(byte) } cErr := C.arbitrator_add_user_wasm( m.ptr, - (*u8)(arbutil.SliceToPointer(wasm.Module)), - u32(len(wasm.Module)), - u16(call.Version), + (*u8)(arbutil.SliceToPointer(module)), + u32(len(module)), + u16(0), // TODO: remove u32(arbmath.BoolToUint32(debug)), &C.struct_Bytes32{hashBytes}, ) diff --git a/validator/server_arb/validator_spawner.go b/validator/server_arb/validator_spawner.go index e89cd20f6..174029f1b 100644 --- a/validator/server_arb/validator_spawner.go +++ b/validator/server_arb/validator_spawner.go @@ -106,12 +106,12 @@ func (v *ArbitratorSpawner) loadEntryToMachine(ctx context.Context, entry *valid return fmt.Errorf("error while trying to add sequencer msg for proving: %w", err) } } - for call, wasm := range entry.UserWasms { - err = mach.AddUserWasm(call, wasm, entry.DebugChain) + for moduleHash, info := range entry.UserWasms { + err = mach.AddUserWasm(moduleHash, info.Module, entry.DebugChain) if err != nil { log.Error( "error adding user wasm for proving", - "err", err, "codehash", call.CodeHash, "blockNr", entry.Id, + "err", err, "moduleHash", moduleHash, "blockNr", entry.Id, ) return fmt.Errorf("error adding user wasm for proving: %w", err) } diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index 6de60e912..c1eb3fe45 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -206,11 +206,11 @@ func (machine *JitMachine) prove( if err := writeUint32(uint32(len(userWasms))); err != nil { return state, err } - for _, wasm := range userWasms { - if err := writeExact(wasm.ModuleHash[:]); err != nil { + for moduleHash, info := range userWasms { + if err := writeExact(moduleHash[:]); err != nil { return state, err } - if err := writeBytes(wasm.Asm); err != nil { + if err := writeBytes(info.Asm); err != nil { return state, err } } From f52b7e20a69cd8cf47d3191eaca0e882a91810c3 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 12 Oct 2023 22:01:29 -0600 Subject: [PATCH 43/55] re-use prover modules --- arbitrator/prover/src/lib.rs | 25 ++++++++--------------- arbitrator/prover/src/machine.rs | 16 +++++++-------- arbitrator/prover/src/main.rs | 16 +++++---------- validator/server_arb/machine.go | 12 +++-------- validator/server_arb/validator_spawner.go | 2 +- 5 files changed, 24 insertions(+), 47 deletions(-) diff --git a/arbitrator/prover/src/lib.rs b/arbitrator/prover/src/lib.rs index 23d7cef58..dbb3ba5d0 100644 --- a/arbitrator/prover/src/lib.rs +++ b/arbitrator/prover/src/lib.rs @@ -23,7 +23,7 @@ pub use machine::Machine; use arbutil::Bytes32; use eyre::{Report, Result}; use machine::{ - argument_data_to_inbox, get_empty_preimage_resolver, GlobalState, MachineStatus, + argument_data_to_inbox, get_empty_preimage_resolver, GlobalState, MachineStatus, Module, PreimageResolver, }; use sha3::{Digest, Keccak256}; @@ -203,22 +203,13 @@ pub unsafe extern "C" fn arbitrator_add_inbox_message( #[no_mangle] pub unsafe extern "C" fn arbitrator_add_user_wasm( mach: *mut Machine, - wasm: *const u8, - wasm_len: u32, - version: u16, - debug: u32, - root: *const Bytes32, -) -> *mut libc::c_char { - let wasm = std::slice::from_raw_parts(wasm, wasm_len as usize); - let debug = debug != 0; - - if root.is_null() { - return str_to_c_string("arbitrator_add_user_wasm got null ptr for module hash"); - } - match (*mach).add_program(wasm, version, debug, Some(*root)) { - Ok(_) => ptr::null_mut(), - Err(err) => err_to_c_string(err), - } + module: *const u8, + module_len: usize, + module_hash: *const Bytes32, +) { + let module = std::slice::from_raw_parts(module, module_len); + let module = Module::from_bytes(module); + (*mach).add_stylus_module(module, *module_hash); } /// Like arbitrator_step, but stops early if it hits a host io operation. diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 9b8baab47..82a067d90 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -1127,22 +1127,20 @@ impl Machine { /// Adds a user program to the machine's known set of wasms, compiling it into a link-able module. /// Note that the module produced will need to be configured before execution via hostio calls. - pub fn add_program( - &mut self, - wasm: &[u8], - version: u16, - debug_funcs: bool, - hash: Option, // computed if not already known - ) -> Result { + pub fn add_program(&mut self, wasm: &[u8], version: u16, debug_funcs: bool) -> Result { let mut bin = binary::parse(wasm, Path::new("user"))?; let config = CompileConfig::version(version, debug_funcs); let stylus_data = bin.instrument(&config)?; let module = Module::from_user_binary(&bin, debug_funcs, Some(stylus_data))?; - let hash = hash.unwrap_or_else(|| module.hash()); + let hash = module.hash(); + self.add_stylus_module(module, hash); + Ok(hash) + } + /// Adds a pre-built program to the machine's known set of wasms. + pub fn add_stylus_module(&mut self, module: Module, hash: Bytes32) { self.stylus_modules.insert(hash, module); - Ok(hash) } pub fn from_binaries( diff --git a/arbitrator/prover/src/main.rs b/arbitrator/prover/src/main.rs index 50831cd02..22476f16d 100644 --- a/arbitrator/prover/src/main.rs +++ b/arbitrator/prover/src/main.rs @@ -4,7 +4,7 @@ #![cfg(feature = "native")] use arbutil::{format, Bytes32, Color, DebugColor}; -use eyre::{Context, Result}; +use eyre::{eyre, Context, Result}; use fnv::{FnvHashMap as HashMap, FnvHashSet as HashSet}; use prover::{ machine::{GlobalState, InboxIdentifier, Machine, MachineStatus, PreimageResolver, ProofInfo}, @@ -201,16 +201,10 @@ fn main() -> Result<()> { preimage_resolver, )?; - for module in &opts.stylus_modules { - let error = || { - format!( - "failed to read module at {}", - module.to_string_lossy().red() - ) - }; - let wasm = file_bytes(module).wrap_err_with(error)?; - mach.add_program(&wasm, 1, true, None) - .wrap_err_with(error)?; + for path in &opts.stylus_modules { + let err = || eyre!("failed to read module at {}", path.to_string_lossy().red()); + let wasm = file_bytes(path).wrap_err_with(err)?; + mach.add_program(&wasm, 1, true).wrap_err_with(err)?; } if let Some(output_path) = opts.generate_binaries { diff --git a/validator/server_arb/machine.go b/validator/server_arb/machine.go index 020530141..23642b90f 100644 --- a/validator/server_arb/machine.go +++ b/validator/server_arb/machine.go @@ -396,7 +396,7 @@ func (m *ArbitratorMachine) SetPreimageResolver(resolver GoPreimageResolver) err return nil } -func (m *ArbitratorMachine) AddUserWasm(moduleHash common.Hash, module []byte, debug bool) error { +func (m *ArbitratorMachine) AddUserWasm(moduleHash common.Hash, module []byte) error { defer runtime.KeepAlive(m) if m.frozen { return errors.New("machine frozen") @@ -405,17 +405,11 @@ func (m *ArbitratorMachine) AddUserWasm(moduleHash common.Hash, module []byte, d for index, byte := range moduleHash.Bytes() { hashBytes[index] = u8(byte) } - cErr := C.arbitrator_add_user_wasm( + C.arbitrator_add_user_wasm( m.ptr, (*u8)(arbutil.SliceToPointer(module)), - u32(len(module)), - u16(0), // TODO: remove - u32(arbmath.BoolToUint32(debug)), + usize(len(module)), &C.struct_Bytes32{hashBytes}, ) - defer C.free(unsafe.Pointer(cErr)) - if cErr != nil { - return errors.New(C.GoString(cErr)) - } return nil } diff --git a/validator/server_arb/validator_spawner.go b/validator/server_arb/validator_spawner.go index 174029f1b..85f55ef6c 100644 --- a/validator/server_arb/validator_spawner.go +++ b/validator/server_arb/validator_spawner.go @@ -107,7 +107,7 @@ func (v *ArbitratorSpawner) loadEntryToMachine(ctx context.Context, entry *valid } } for moduleHash, info := range entry.UserWasms { - err = mach.AddUserWasm(moduleHash, info.Module, entry.DebugChain) + err = mach.AddUserWasm(moduleHash, info.Module) if err != nil { log.Error( "error adding user wasm for proving", From f88557d061d3184c68f99f9614bace3afed34d9c Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Fri, 13 Oct 2023 16:35:57 -0600 Subject: [PATCH 44/55] charge for gas during activation: native + JIT --- arbitrator/jit/src/gostack.rs | 15 ++++-- arbitrator/jit/src/machine.rs | 3 +- arbitrator/jit/src/user/mod.rs | 59 ++++++++--------------- arbitrator/prover/src/programs/config.rs | 7 --- arbitrator/prover/src/programs/prelude.rs | 2 +- arbitrator/stylus/src/lib.rs | 28 +++++------ arbitrator/stylus/src/native.rs | 32 ++++++------ arbos/burn/burn.go | 5 ++ arbos/programs/native.go | 40 +++++++-------- arbos/programs/programs.go | 25 ++-------- arbos/programs/raw.s | 6 +-- arbos/programs/wasm.go | 48 ++++++++---------- precompiles/ArbWasm.go | 5 ++ precompiles/context.go | 4 ++ system_tests/program_test.go | 3 +- 15 files changed, 121 insertions(+), 161 deletions(-) diff --git a/arbitrator/jit/src/gostack.rs b/arbitrator/jit/src/gostack.rs index 14b1d4084..3f355eb18 100644 --- a/arbitrator/jit/src/gostack.rs +++ b/arbitrator/jit/src/gostack.rs @@ -11,7 +11,10 @@ use crate::{ use arbutil::Color; use ouroboros::self_referencing; use rand_pcg::Pcg32; -use std::collections::{BTreeSet, BinaryHeap}; +use std::{ + collections::{BTreeSet, BinaryHeap}, + fmt::Debug, +}; use wasmer::{AsStoreRef, Memory, MemoryView, StoreMut, StoreRef, WasmPtr}; #[self_referencing] @@ -138,6 +141,10 @@ impl GoStack { self.read_u64() as *mut T } + pub unsafe fn read_ref<'a, 'b, T>(&'a mut self) -> &'b T { + &*self.read_ptr() + } + /// TODO: replace `unbox` with a safe id-based API pub fn unbox(&mut self) -> T { let ptr: *mut T = self.read_ptr_mut(); @@ -236,9 +243,9 @@ impl GoStack { data } - pub fn write_slice(&self, ptr: u64, src: &[u8]) { - u32::try_from(ptr).expect("Go pointer not a u32"); - self.view().write(ptr, src).unwrap(); + pub fn write_slice>(&self, ptr: T, src: &[u8]) { + let ptr: u32 = ptr.try_into().map_err(|_| "Go pointer not a u32").unwrap(); + self.view().write(ptr.into(), src).unwrap(); } pub fn read_value_slice(&self, mut ptr: u64, len: u64) -> Vec { diff --git a/arbitrator/jit/src/machine.rs b/arbitrator/jit/src/machine.rs index a634290f2..c3069ac78 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -115,11 +115,10 @@ pub fn create(opts: &Opts, env: WasmEnv) -> (Instance, FunctionEnv, Sto github!("wavmio.readDelayedInboxMessage") => func!(wavmio::read_delayed_inbox_message), github!("wavmio.resolvePreImage") => func!(wavmio::resolve_preimage), - github!("arbos/programs.compileUserWasmRustImpl") => func!(user::compile_user_wasm), + github!("arbos/programs.activateWasmRustImpl") => func!(user::activate_wasm), github!("arbos/programs.callUserWasmRustImpl") => func!(user::call_user_wasm), github!("arbos/programs.readRustVecLenImpl") => func!(user::read_rust_vec_len), github!("arbos/programs.rustVecIntoSliceImpl") => func!(user::rust_vec_into_slice), - github!("arbos/programs.rustModuleDropImpl") => func!(user::drop_module), github!("arbos/programs.rustConfigImpl") => func!(user::rust_config_impl), github!("arbos/programs.rustEvmDataImpl") => func!(user::evm_data_impl), diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 92f2618fd..01d29dab2 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -5,6 +5,7 @@ use crate::{ gostack::GoStack, machine::{Escape, MaybeEscape, WasmEnvMut}, user::evm_api::exec_wasm, + wavmio::Bytes32, }; use arbutil::{ evm::{user::UserOutcome, EvmData}, @@ -17,49 +18,44 @@ use stylus::native; mod evm_api; -/// Compiles and instruments a user wasm. +/// Instruments a user wasm. /// /// # Go side /// /// The Go compiler expects the call to take the form -/// λ(wasm []byte, pageLimit, version u16, debug u32) (module *Vec, info WasmInfo, err *Vec) +/// λ(wasm []byte, pageLimit, version u16, debug u32, modHash *hash, gas *u64) (footprint u16, err *Vec) /// /// These values are placed on the stack as follows -/// stack: || wasm... || pageLimit | version | debug || mod ptr || info... || err ptr || -/// info: || footprint | 2 pad | size || +/// || wasm... || pageLimit | version | debug || modhash ptr || gas ptr || footprint | 6 pad || err ptr || /// -pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { +pub fn activate_wasm(env: WasmEnvMut, sp: u32) { let mut sp = GoStack::simple(sp, &env); let wasm = sp.read_go_slice_owned(); let page_limit = sp.read_u16(); let version = sp.read_u16(); let debug = sp.read_bool32(); - let (out_hash_ptr, out_hash_len) = sp.read_go_slice(); + let module_hash = sp.read_go_ptr(); + let gas = sp.read_go_ptr(); macro_rules! error { ($error:expr) => {{ let error = $error.wrap_err("failed to compile").debug_bytes(); - sp.write_nullptr(); - sp.skip_space(); // skip info + sp.write_u64_raw(gas, 0); + sp.write_slice(module_hash, &Bytes32::default()); + sp.skip_space(); sp.write_ptr(heapify(error)); return; }}; } - if out_hash_len != 32 { - error!(eyre::eyre!( - "Go attempting to read module hash into bad buffer length: {out_hash_len}" - )); - } - - let (asm, module, info) = match native::compile_user_wasm(&wasm, version, page_limit, debug) { + let gas_left = &mut sp.read_u64_raw(gas); + let (_, module, pages) = match native::activate(&wasm, version, page_limit, debug, gas_left) { Ok(result) => result, Err(error) => error!(error), }; - - sp.write_slice(out_hash_ptr, module.hash().as_slice()); - sp.write_ptr(heapify(asm)); - sp.write_u16(info.footprint).skip_u16().write_u32(info.size); // wasm info + sp.write_u64_raw(gas, *gas_left); + sp.write_slice(module_hash, &module.hash().0); + sp.write_u16(pages).skip_space(); sp.write_nullptr(); } @@ -68,13 +64,12 @@ pub fn compile_user_wasm(env: WasmEnvMut, sp: u32) { /// # Go side /// /// The Go compiler expects the call to take the form -/// λ( -/// hash *common.Hash, calldata []byte, params *Configs, evmApi []byte, evmData: *EvmData, -/// gas *u64, root *[32]byte -/// ) -> (status byte, out *Vec) +/// λ(moduleHash *[32]byte, calldata []byte, params *Configs, evmApi []byte, evmData: *EvmData, gas *u64) ( +/// status byte, out *Vec, +/// ) /// /// These values are placed on the stack as follows -/// || hash || calldata... || params || evmApi... || evmData || gas || root || status | 3 pad | out ptr || +/// || modHash || calldata... || params || evmApi... || evmData || gas || status | 7 pad | out ptr || /// pub fn call_user_wasm(env: WasmEnvMut, sp: u32) -> MaybeEscape { let sp = &mut GoStack::simple(sp, &env); @@ -127,7 +122,7 @@ pub fn call_user_wasm(env: WasmEnvMut, sp: u32) -> MaybeEscape { /// pub fn read_rust_vec_len(env: WasmEnvMut, sp: u32) { let mut sp = GoStack::simple(sp, &env); - let vec: &Vec = unsafe { &*sp.read_ptr() }; + let vec: &Vec = unsafe { sp.read_ref() }; sp.write_u32(vec.len() as u32); } @@ -149,20 +144,6 @@ pub fn rust_vec_into_slice(env: WasmEnvMut, sp: u32) { mem::drop(vec) } -/// Drops module bytes. Note that in user-host this would be a `Machine`. -/// -/// # Go side -/// -/// The Go compiler expects the call to take the form -/// λ(module *Vec) -/// -pub fn drop_module(env: WasmEnvMut, sp: u32) { - let mut sp = GoStack::simple(sp, &env); - if let Some(module) = sp.unbox_option::>() { - mem::drop(module); - } -} - /// Creates a `StylusConfig` from its component parts. /// /// # Go side diff --git a/arbitrator/prover/src/programs/config.rs b/arbitrator/prover/src/programs/config.rs index bdb41291f..4d4f331ef 100644 --- a/arbitrator/prover/src/programs/config.rs +++ b/arbitrator/prover/src/programs/config.rs @@ -208,10 +208,3 @@ impl CompileConfig { Store::new(compiler) } } - -/// Information about a wasm for pricing purposes. -#[repr(C)] -pub struct WasmPricingInfo { - pub footprint: u16, - pub size: u32, -} diff --git a/arbitrator/prover/src/programs/prelude.rs b/arbitrator/prover/src/programs/prelude.rs index edf782beb..4db8e0341 100644 --- a/arbitrator/prover/src/programs/prelude.rs +++ b/arbitrator/prover/src/programs/prelude.rs @@ -2,7 +2,7 @@ // For license information, see https://github.com/nitro/blob/master/LICENSE pub use super::{ - config::{CompileConfig, StylusConfig, WasmPricingInfo}, + config::{CompileConfig, StylusConfig}, counter::CountingMachine, depth::DepthCheckedMachine, meter::{GasMeteredMachine, MachineMeter, MeteredMachine}, diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index 83f02656d..b2f29ba06 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -100,36 +100,36 @@ impl RustVec { } } -/// Compiles a user program to its native representation. -/// The `output` is either the serialized module or an error string. +/// Activates a user program. +/// The `output` is either the serialized asm & module or an error string. /// /// # Safety /// -/// output, pricing_info, module_hash must not be null. +/// `output`, `asm_len`, `module_hash`, `footprint`, and `gas` must not be null. #[no_mangle] -pub unsafe extern "C" fn stylus_compile( +pub unsafe extern "C" fn stylus_activate( wasm: GoSliceData, page_limit: u16, version: u16, - debug_mode: bool, - out_pricing_info: *mut WasmPricingInfo, + debug: bool, output: *mut RustVec, asm_len: *mut usize, module_hash: *mut Bytes32, + footprint: *mut u16, + gas: *mut u64, ) -> UserOutcomeKind { let wasm = wasm.slice(); let output = &mut *output; let module_hash = &mut *module_hash; + let gas = &mut *gas; - let (asm, module, pricing_info) = - match native::compile_user_wasm(wasm, version, page_limit, debug_mode) { - Ok(val) => val, - Err(err) => return output.write_err(err), - }; - + let (asm, module, pages) = match native::activate(wasm, version, page_limit, debug, gas) { + Ok(val) => val, + Err(err) => return output.write_err(err), + }; *asm_len = asm.len(); *module_hash = module.hash(); - *out_pricing_info = pricing_info; + *footprint = pages; let mut data = asm; data.extend(&*module.into_bytes()); @@ -141,7 +141,7 @@ pub unsafe extern "C" fn stylus_compile( /// /// # Safety /// -/// `module` must represent a valid module produced from `stylus_compile`. +/// `module` must represent a valid module produced from `stylus_activate`. /// `output` and `gas` must not be null. #[no_mangle] pub unsafe extern "C" fn stylus_call( diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index 99631499b..736611194 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -15,7 +15,7 @@ use prover::{ binary::WasmBinary, machine::Module as ProverModule, programs::{ - config::{PricingParams, WasmPricingInfo}, + config::PricingParams, counter::{Counter, CountingMachine, OP_OFFSETS}, depth::STYLUS_STACK_LEFT, meter::{STYLUS_INK_LEFT, STYLUS_INK_STATUS}, @@ -374,25 +374,29 @@ pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { Ok(module.to_vec()) } -pub fn compile_user_wasm( +pub fn activate( wasm: &[u8], version: u16, page_limit: u16, - debug_mode: bool, -) -> Result<(Vec, ProverModule, WasmPricingInfo)> { - let compile = CompileConfig::version(version, debug_mode); + debug: bool, + gas: &mut u64, +) -> Result<(Vec, ProverModule, u16)> { + // paid for by the 3 million gas charge in program.go + let compile = CompileConfig::version(version, debug); let (bin, stylus_data, footprint) = WasmBinary::parse_user(wasm, page_limit, &compile).wrap_err("failed to parse wasm")?; - let prover_module = - ProverModule::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) - .wrap_err("failed to build module from program")?; + // naively charge 11 million gas to do the rest. + // in the future we'll implement a proper compilation pricing mechanism. + if *gas < 11_000_000 { + *gas = 0; + bail!("out of gas"); + } + *gas -= 11_000_000; - let info = WasmPricingInfo { - size: wasm.len().try_into()?, - footprint, - }; - let asm = module(wasm, compile).wrap_err("failed to generate stylus module")?; + let module = ProverModule::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) + .wrap_err("failed to build user module")?; - Ok((asm, prover_module, info)) + let asm = self::module(wasm, compile).wrap_err("failed to generate stylus module")?; + Ok((asm, module, footprint)) } diff --git a/arbos/burn/burn.go b/arbos/burn/burn.go index 973452cab..c3d778636 100644 --- a/arbos/burn/burn.go +++ b/arbos/burn/burn.go @@ -13,6 +13,7 @@ import ( type Burner interface { Burn(amount uint64) error Burned() uint64 + GasLeft() uint64 BurnOut() error Restrict(err error) HandleError(err error) error @@ -46,6 +47,10 @@ func (burner *SystemBurner) BurnOut() error { panic("called BurnOut on a system burner") } +func (burner *SystemBurner) GasLeft() uint64 { + panic("called GasLeft on a system burner") +} + func (burner *SystemBurner) Restrict(err error) { if err != nil { glog.Error("Restrict() received an error", "err", err) diff --git a/arbos/programs/native.go b/arbos/programs/native.go index e1b116674..797d029f5 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -21,6 +21,7 @@ import "C" import ( "errors" "fmt" + "math" "math/big" "github.com/ethereum/go-ethereum/common" @@ -30,6 +31,7 @@ import ( "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/util/arbmath" ) type u8 = C.uint8_t @@ -43,7 +45,7 @@ type bytes32 = C.Bytes32 type rustVec = C.RustVec type rustSlice = C.RustSlice -func compileUserWasm( +func activateProgram( db vm.StateDB, program common.Address, wasm []byte, @@ -51,38 +53,37 @@ func compileUserWasm( version uint16, debug bool, burner burn.Burner, -) (*wasmPricingInfo, common.Hash, error) { +) (common.Hash, uint16, error) { output := &rustVec{} asmLen := usize(0) - moduleHash := &bytes32{} - rustInfo := &C.WasmPricingInfo{} + footprint := uint16(math.MaxUint16) + gas := burner.GasLeft() - status := userStatus(C.stylus_compile( + status := userStatus(C.stylus_activate( goSlice(wasm), u16(page_limit), u16(version), cbool(debug), - rustInfo, output, &asmLen, moduleHash, + (*u16)(&footprint), + (*u64)(&gas), )) - data, msg, err := status.toResult(output.intoBytes(), debug) + if err := burner.Burn(arbmath.SaturatingUSub(burner.GasLeft(), gas)); err != nil { + return common.Hash{}, footprint, err + } + data, msg, err := status.toResult(output.intoBytes(), debug) if err != nil { if debug { - log.Warn("stylus parse failed", "err", err, "msg", msg, "program", program) + log.Warn("activation failed", "err", err, "msg", msg, "program", program) } if errors.Is(err, vm.ErrExecutionReverted) { - return nil, common.Hash{}, fmt.Errorf("%w: %s", ErrProgramActivation, msg) + return common.Hash{}, footprint, fmt.Errorf("%w: %s", ErrProgramActivation, msg) } - return nil, common.Hash{}, err - } - - info := rustInfo.decode() - if err := payForCompilation(burner, &info); err != nil { - return nil, common.Hash{}, err + return common.Hash{}, footprint, err } hash := moduleHash.toHash() @@ -91,7 +92,7 @@ func compileUserWasm( module := data[split:] db.ActivateWasm(hash, asm, module) - return &info, hash, err + return hash, footprint, err } func callUserWasm( @@ -369,10 +370,3 @@ func (data *evmData) encode() C.EvmData { tracing: cbool(data.tracing), } } - -func (info *C.WasmPricingInfo) decode() wasmPricingInfo { - return wasmPricingInfo{ - footprint: uint16(info.footprint), - size: uint32(info.size), - } -} diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 218c92f4f..36d1f4663 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -13,7 +13,6 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbcompress" - "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/storage" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/arbutil" @@ -173,6 +172,7 @@ func (p Programs) SetCallScalar(scalar uint16) error { func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, debugMode bool) (uint16, bool, error) { statedb := evm.StateDB codeHash := statedb.GetCodeHash(address) + burner := p.programs.Burner() version, err := p.StylusVersion() if err != nil { @@ -198,12 +198,7 @@ func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, debugMode } pageLimit = arbmath.SaturatingUSub(pageLimit, statedb.GetStylusPagesOpen()) - // charge 3 million up front to begin compilation - burner := p.programs.Burner() - if err := burner.Burn(3000000); err != nil { - return 0, false, err - } - info, compiledHash, err := compileUserWasm(statedb, address, wasm, pageLimit, version, debugMode, burner) + moduleHash, footprint, err := activateProgram(statedb, address, wasm, pageLimit, version, debugMode, burner) if err != nil { return 0, true, err } @@ -213,9 +208,9 @@ func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, debugMode programData := Program{ wasmSize: wasmSize, - footprint: info.footprint, + footprint: footprint, version: version, - moduleHash: compiledHash, + moduleHash: moduleHash, } return version, false, p.setProgram(codeHash, programData) } @@ -436,15 +431,3 @@ func (status userStatus) toResult(data []byte, debug bool) ([]byte, string, erro return nil, msg, vm.ErrExecutionReverted } } - -type wasmPricingInfo struct { - footprint uint16 - size uint32 -} - -// Pay for compilation. Right now this is a fixed amount of gas. -// In the future, costs will be variable and based on the wasm. -// Note: memory expansion costs are baked into compilation charging. -func payForCompilation(burner burn.Burner, _info *wasmPricingInfo) error { - return burner.Burn(11000000) -} diff --git a/arbos/programs/raw.s b/arbos/programs/raw.s index f855d53c3..c222bfbe2 100644 --- a/arbos/programs/raw.s +++ b/arbos/programs/raw.s @@ -6,7 +6,7 @@ #include "textflag.h" -TEXT ·compileUserWasmRustImpl(SB), NOSPLIT, $0 +TEXT ·activateWasmRustImpl(SB), NOSPLIT, $0 CallImport RET @@ -22,10 +22,6 @@ TEXT ·rustVecIntoSliceImpl(SB), NOSPLIT, $0 CallImport RET -TEXT ·rustModuleDropImpl(SB), NOSPLIT, $0 - CallImport - RET - TEXT ·rustConfigImpl(SB), NOSPLIT, $0 CallImport RET diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index cb4853e48..aaf1006a9 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -28,21 +28,20 @@ type usize = uintptr // opaque types type rustVec byte type rustConfig byte -type rustMachine byte +type rustModule byte type rustEvmData byte -func compileUserWasmRustImpl( - wasm []byte, pageLimit, version u16, debugMode u32, outMachineHash []byte, -) (machine *rustMachine, info wasmPricingInfo, err *rustVec) +func activateWasmRustImpl( + wasm []byte, pageLimit, version u16, debugMode u32, moduleHash *hash, gas *u64, +) (footprint u16, err *rustVec) func callUserWasmRustImpl( - compiledHash *hash, calldata []byte, params *rustConfig, evmApi []byte, - evmData *rustEvmData, gas *u64, + moduleHash *hash, calldata []byte, params *rustConfig, evmApi []byte, evmData *rustEvmData, gas *u64, ) (status userStatus, out *rustVec) func readRustVecLenImpl(vec *rustVec) (len u32) func rustVecIntoSliceImpl(vec *rustVec, ptr *byte) -func rustModuleDropImpl(mach *rustMachine) +func rustModuleDropImpl(mach *rustModule) func rustConfigImpl(version u16, maxDepth, inkPrice, debugMode u32) *rustConfig func rustEvmDataImpl( blockBasefee *hash, @@ -59,7 +58,7 @@ func rustEvmDataImpl( reentrant u32, ) *rustEvmData -func compileUserWasm( +func activateProgram( db vm.StateDB, program addr, wasm []byte, @@ -67,16 +66,20 @@ func compileUserWasm( version u16, debug bool, burner burn.Burner, -) (*wasmPricingInfo, common.Hash, error) { - module, info, hash, err := compileUserWasmRustWrapper(db, program, wasm, pageLimit, version, debug) - defer rustModuleDropImpl(module) - if err != nil { - return nil, common.Hash{}, err +) (common.Hash, u16, error) { + debugMode := arbmath.BoolToUint32(debug) + moduleHash := common.Hash{} + gas := burner.GasLeft() + + footprint, err := activateWasmRustImpl(wasm, pageLimit, version, debugMode, &moduleHash, &gas) + if err := burner.Burn(arbmath.SaturatingUSub(burner.GasLeft(), gas)); err != nil { + return moduleHash, footprint, err } - if err := payForCompilation(burner, &info); err != nil { - return nil, common.Hash{}, err + if err != nil { + _, _, err := userFailure.toResult(err.intoSlice(), debug) + return moduleHash, footprint, err } - return &info, hash, err + return moduleHash, footprint, nil } func callUserWasm( @@ -107,19 +110,6 @@ func callUserWasm( return data, err } -func compileUserWasmRustWrapper( - db vm.StateDB, program addr, wasm []byte, pageLimit, version u16, debug bool, -) (*rustMachine, wasmPricingInfo, common.Hash, error) { - debugMode := arbmath.BoolToUint32(debug) - outHash := common.Hash{} - machine, info, err := compileUserWasmRustImpl(wasm, pageLimit, version, debugMode, outHash[:]) - if err != nil { - _, _, err := userFailure.toResult(err.intoSlice(), debug) - return nil, info, outHash, err - } - return machine, info, outHash, nil -} - func (vec *rustVec) intoSlice() []byte { len := readRustVecLenImpl(vec) slice := make([]byte, len) diff --git a/precompiles/ArbWasm.go b/precompiles/ArbWasm.go index aeb61d082..be4ad44fc 100644 --- a/precompiles/ArbWasm.go +++ b/precompiles/ArbWasm.go @@ -13,6 +13,11 @@ type ArbWasm struct { // Compile a wasm program with the latest instrumentation func (con ArbWasm) ActivateProgram(c ctx, evm mech, program addr) (uint16, error) { + + // charge 3 million up front to begin activation + if err := c.Burn(3000000); err != nil { + return 0, err + } version, takeAllGas, err := c.State.Programs().ActivateProgram(evm, program, evm.ChainConfig().DebugMode()) if takeAllGas { _ = c.BurnOut() diff --git a/precompiles/context.go b/precompiles/context.go index bea90fbc6..b52c3ce9a 100644 --- a/precompiles/context.go +++ b/precompiles/context.go @@ -53,6 +53,10 @@ func (c *Context) BurnOut() error { return vm.ErrOutOfGas } +func (c *Context) GasLeft() uint64 { + return c.gasLeft +} + func (c *Context) Restrict(err error) { log.Crit("A metered burner was used for access-controlled work", "error", err) } diff --git a/system_tests/program_test.go b/system_tests/program_test.go index 36bcb2f45..d1d216538 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -899,7 +899,7 @@ func TestProgramActivateFails(t *testing.T) { } func testActivateFails(t *testing.T, jit bool) { - ctx, node, _, l2client, auth, cleanup := setupProgramTest(t, false) + ctx, node, _, l2client, auth, cleanup := setupProgramTest(t, jit) defer cleanup() arbWasm, err := precompilesgen.NewArbWasm(types.ArbWasmAddress, l2client) @@ -1189,7 +1189,6 @@ func validateBlockRange( t *testing.T, blocks []uint64, jit bool, ctx context.Context, node *arbnode.Node, l2client *ethclient.Client, ) { - t.Helper() waitForSequencer(t, node, arbmath.MaxInt(blocks...)) blockHeight, err := l2client.BlockNumber(ctx) Require(t, err) From 411d64492c741b713447d87a8f48551dcbe3bf50 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Fri, 13 Oct 2023 16:57:41 -0600 Subject: [PATCH 45/55] charge for gas in arbitrator --- arbitrator/jit/src/gostack.rs | 2 +- arbitrator/jit/src/user/mod.rs | 2 +- arbitrator/prover/src/programs/mod.rs | 33 +++++++- arbitrator/stylus/src/native.rs | 16 +--- .../wasm-libraries/user-host/src/link.rs | 79 ++++++------------- 5 files changed, 59 insertions(+), 73 deletions(-) diff --git a/arbitrator/jit/src/gostack.rs b/arbitrator/jit/src/gostack.rs index 3f355eb18..63cf2547b 100644 --- a/arbitrator/jit/src/gostack.rs +++ b/arbitrator/jit/src/gostack.rs @@ -141,7 +141,7 @@ impl GoStack { self.read_u64() as *mut T } - pub unsafe fn read_ref<'a, 'b, T>(&'a mut self) -> &'b T { + pub unsafe fn read_ref<'a, T>(&mut self) -> &'a T { &*self.read_ptr() } diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 01d29dab2..e1697d47f 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -39,7 +39,7 @@ pub fn activate_wasm(env: WasmEnvMut, sp: u32) { macro_rules! error { ($error:expr) => {{ - let error = $error.wrap_err("failed to compile").debug_bytes(); + let error = $error.wrap_err("failed to activate").debug_bytes(); sp.write_u64_raw(gas, 0); sp.write_slice(module_hash, &Bytes32::default()); sp.skip_space(); diff --git a/arbitrator/prover/src/programs/mod.rs b/arbitrator/prover/src/programs/mod.rs index ccc3c7664..5b2562822 100644 --- a/arbitrator/prover/src/programs/mod.rs +++ b/arbitrator/prover/src/programs/mod.rs @@ -3,11 +3,12 @@ use crate::{ binary::{ExportKind, WasmBinary}, + machine::Module, memory::MemoryType, value::{FunctionType as ArbFunctionType, Value}, }; use arbutil::Color; -use eyre::{bail, eyre, Report, Result}; +use eyre::{bail, eyre, Report, Result, WrapErr}; use fnv::FnvHashMap as HashMap; use std::fmt::Debug; use wasmer_types::{ @@ -16,6 +17,8 @@ use wasmer_types::{ }; use wasmparser::{Operator, Type as WpType}; +use self::config::CompileConfig; + #[cfg(feature = "native")] use { super::value, @@ -379,3 +382,31 @@ impl StylusData { ) } } + +impl Module { + pub fn activate( + wasm: &[u8], + version: u16, + page_limit: u16, + debug: bool, + gas: &mut u64, + ) -> Result<(Self, u16)> { + // paid for by the 3 million gas charge in program.go + let compile = CompileConfig::version(version, debug); + let (bin, stylus_data, footprint) = + WasmBinary::parse_user(wasm, page_limit, &compile).wrap_err("failed to parse wasm")?; + + // naively charge 11 million gas to do the rest. + // in the future we'll implement a proper compilation pricing mechanism. + if *gas < 11_000_000 { + *gas = 0; + bail!("out of gas"); + } + *gas -= 11_000_000; + + let module = Self::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) + .wrap_err("failed to build user module")?; + + Ok((module, footprint)) + } +} diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index 736611194..b34d3f195 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -12,7 +12,6 @@ use arbutil::{ }; use eyre::{bail, eyre, Context, ErrReport, Result}; use prover::{ - binary::WasmBinary, machine::Module as ProverModule, programs::{ config::PricingParams, @@ -381,21 +380,8 @@ pub fn activate( debug: bool, gas: &mut u64, ) -> Result<(Vec, ProverModule, u16)> { - // paid for by the 3 million gas charge in program.go let compile = CompileConfig::version(version, debug); - let (bin, stylus_data, footprint) = - WasmBinary::parse_user(wasm, page_limit, &compile).wrap_err("failed to parse wasm")?; - - // naively charge 11 million gas to do the rest. - // in the future we'll implement a proper compilation pricing mechanism. - if *gas < 11_000_000 { - *gas = 0; - bail!("out of gas"); - } - *gas -= 11_000_000; - - let module = ProverModule::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) - .wrap_err("failed to build user module")?; + let (module, footprint) = ProverModule::activate(wasm, version, page_limit, debug, gas)?; let asm = self::module(wasm, compile).wrap_err("failed to generate stylus module")?; Ok((asm, module, footprint)) diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index 70c2b5cdd..51a530362 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -4,13 +4,13 @@ use crate::{evm_api::ApiCaller, Program, PROGRAMS}; use arbutil::{ evm::{js::JsEvmApi, user::UserOutcomeKind, EvmData}, - heapify, wavm, + format::DebugBytes, + heapify, wavm, Bytes32, }; use go_abi::GoStack; use prover::{ - binary::WasmBinary, machine::Module, - programs::config::{CompileConfig, PricingParams, StylusConfig}, + programs::config::{PricingParams, StylusConfig}, }; use std::mem; @@ -35,19 +35,18 @@ extern "C" { #[repr(C, align(256))] struct MemoryLeaf([u8; 32]); -/// Compiles and instruments a user wasm. +/// Instruments a user wasm. /// /// # Safety /// /// The Go compiler expects the call to take the form -/// λ(wasm []byte, pageLimit, version u16, debug u32, machineHash []byte) (module *Module, info WasmInfo, err *Vec) +/// λ(wasm []byte, pageLimit, version u16, debug u32, modHash *hash, gas *u64) (footprint u16, err *Vec) /// /// These values are placed on the stack as follows -/// stack: || wasm... || pageLimit | version | debug || mach ptr || info... || err ptr || -/// info: || footprint | 2 pad | size || +/// || wasm... || pageLimit | version | debug || modhash ptr || gas ptr || footprint | 6 pad || err ptr || /// #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compileUserWasmRustImpl( +pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_activateWasmRustImpl( sp: usize, ) { let mut sp = GoStack::new(sp); @@ -55,40 +54,28 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compil let page_limit = sp.read_u16(); let version = sp.read_u16(); let debug = sp.read_bool32(); - let (out_hash_ptr, out_hash_len) = sp.read_go_slice(); + let module_hash = sp.read_go_ptr(); + let gas = sp.read_go_ptr(); macro_rules! error { ($msg:expr, $error:expr) => {{ - let error = $error.wrap_err($msg); - let error = format!("{error:?}").as_bytes().to_vec(); - sp.write_nullptr(); - sp.skip_space(); // skip footprint + let error = $error.wrap_err($msg).debug_bytes(); + wavm::caller_store64(gas, 0); + wavm::write_bytes32(module_hash, Bytes32::default()); + sp.skip_space(); sp.write_ptr(heapify(error)); return; }}; } - let compile = CompileConfig::version(version, debug); - let (bin, stylus_data, footprint) = match WasmBinary::parse_user(&wasm, page_limit, &compile) { - Ok(parse) => parse, - Err(error) => error!("failed to parse program", error), - }; - - let module = match Module::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) { - Ok(module) => module, - Err(error) => error!("failed to instrument program", error), - }; - - if out_hash_len != 32 { - error!("Go attempting to read compiled machine hash into bad buffer",eyre::eyre!("buffer length: {out_hash_ptr}")); - } - wavm::write_slice(module.hash().as_slice(), out_hash_ptr); - - let Ok(wasm_len) = TryInto::::try_into(wasm.len()) else { - error!("wasm len not u32",eyre::eyre!("wasm length: {}", wasm.len())); + let gas_left = &mut wavm::caller_load64(gas); + let (module, pages) = match Module::activate(&wasm, version, page_limit, debug, gas_left) { + Ok(data) => data, + Err(error) => error!("failed to activate", error), }; - sp.write_ptr(heapify(module)); - sp.write_u16(footprint).skip_u16().write_u32(wasm_len); + wavm::caller_store64(gas, *gas_left); + wavm::write_bytes32(module_hash, module.hash()); + sp.write_u16(pages).skip_space(); sp.write_nullptr(); } @@ -97,13 +84,12 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_compil /// # Safety /// /// The Go compiler expects the call to take the form -/// λ( -/// hash *common.Hash, calldata []byte, params *Configs, evmApi []byte, evmData: *EvmData, -/// gas *u64, root *[32]byte -/// ) -> (status byte, out *Vec) +/// λ(moduleHash *[32]byte, calldata []byte, params *Configs, evmApi []byte, evmData: *EvmData, gas *u64) ( +/// status byte, out *Vec, +/// ) /// /// These values are placed on the stack as follows -/// || hash || calldata... || params || evmApi... || evmData || gas || root || status | 3 pad | out ptr || +/// || modHash || calldata... || params || evmApi... || evmData || gas || status | 7 pad | out ptr || /// #[no_mangle] pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_callUserWasmRustImpl( @@ -204,23 +190,6 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_rustVe mem::drop(vec) } -/// Drops a `Module`. -/// -/// # Safety -/// -/// The Go compiler expects the call to take the form -/// λ(mach *Module) -/// -#[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_rustModuleDropImpl( - sp: usize, -) { - let mut sp = GoStack::new(sp); - if let Some(module) = sp.unbox_option::() { - mem::drop(module); - } -} - /// Creates a `StylusConfig` from its component parts. /// /// # Safety From b8598e0ac12bc563da33d32795d5bd72ab68668f Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sat, 14 Oct 2023 11:28:19 -0600 Subject: [PATCH 46/55] address review comments --- arbitrator/jit/src/user/mod.rs | 7 ++++++- arbitrator/stylus/src/lib.rs | 9 +++++++-- arbitrator/wasm-libraries/user-host/src/link.rs | 7 ++++++- arbos/burn/burn.go | 4 ++-- arbos/programs/native.go | 7 +------ arbos/programs/programs.go | 10 +++++----- arbos/programs/wasm.go | 7 ++----- precompiles/context.go | 4 ++-- 8 files changed, 31 insertions(+), 24 deletions(-) diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index e1697d47f..425afb43b 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -18,10 +18,15 @@ use stylus::native; mod evm_api; -/// Instruments a user wasm. +/// Instruments and "activates" a user wasm, producing a unique module hash. +/// +/// Note that this operation costs gas and is limited by the amount supplied via the `gas` pointer. +/// The amount left is written back at the end of the call. /// /// # Go side /// +/// The `modHash` and `gas` pointers must not be null. +/// /// The Go compiler expects the call to take the form /// λ(wasm []byte, pageLimit, version u16, debug u32, modHash *hash, gas *u64) (footprint u16, err *Vec) /// diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index b2f29ba06..fd406ee1b 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -100,8 +100,13 @@ impl RustVec { } } -/// Activates a user program. -/// The `output` is either the serialized asm & module or an error string. +/// Instruments and "activates" a user wasm. +/// +/// The `output` is either the serialized asm & module pair or an error string. +/// Returns consensus info such as the module hash and footprint on success. +/// +/// Note that this operation costs gas and is limited by the amount supplied via the `gas` pointer. +/// The amount left is written back at the end of the call. /// /// # Safety /// diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index 51a530362..a1d829a45 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -35,10 +35,15 @@ extern "C" { #[repr(C, align(256))] struct MemoryLeaf([u8; 32]); -/// Instruments a user wasm. +/// Instruments and "activates" a user wasm, producing a unique module hash. +/// +/// Note that this operation costs gas and is limited by the amount supplied via the `gas` pointer. +/// The amount left is written back at the end of the call. /// /// # Safety /// +/// The `modHash` and `gas` pointers must not be null. +/// /// The Go compiler expects the call to take the form /// λ(wasm []byte, pageLimit, version u16, debug u32, modHash *hash, gas *u64) (footprint u16, err *Vec) /// diff --git a/arbos/burn/burn.go b/arbos/burn/burn.go index c3d778636..665ff4e58 100644 --- a/arbos/burn/burn.go +++ b/arbos/burn/burn.go @@ -13,7 +13,7 @@ import ( type Burner interface { Burn(amount uint64) error Burned() uint64 - GasLeft() uint64 + GasLeft() *uint64 BurnOut() error Restrict(err error) HandleError(err error) error @@ -47,7 +47,7 @@ func (burner *SystemBurner) BurnOut() error { panic("called BurnOut on a system burner") } -func (burner *SystemBurner) GasLeft() uint64 { +func (burner *SystemBurner) GasLeft() *uint64 { panic("called GasLeft on a system burner") } diff --git a/arbos/programs/native.go b/arbos/programs/native.go index 797d029f5..9aa541c9c 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -31,7 +31,6 @@ import ( "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/arbutil" - "github.com/offchainlabs/nitro/util/arbmath" ) type u8 = C.uint8_t @@ -58,7 +57,6 @@ func activateProgram( asmLen := usize(0) moduleHash := &bytes32{} footprint := uint16(math.MaxUint16) - gas := burner.GasLeft() status := userStatus(C.stylus_activate( goSlice(wasm), @@ -69,11 +67,8 @@ func activateProgram( &asmLen, moduleHash, (*u16)(&footprint), - (*u64)(&gas), + (*u64)(burner.GasLeft()), )) - if err := burner.Burn(arbmath.SaturatingUSub(burner.GasLeft(), gas)); err != nil { - return common.Hash{}, footprint, err - } data, msg, err := status.toResult(output.intoBytes(), debug) if err != nil { diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 36d1f4663..7dd96f3a1 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -22,7 +22,7 @@ import ( type Programs struct { backingStorage *storage.Storage programs *storage.Storage - compiledHashes *storage.Storage + moduleHashes *storage.Storage inkPrice storage.StorageBackedUint24 maxStackDepth storage.StorageBackedUint32 freePages storage.StorageBackedUint16 @@ -43,7 +43,7 @@ type Program struct { type uint24 = arbmath.Uint24 var programDataKey = []byte{0} -var machineHashesKey = []byte{1} +var moduleHashesKey = []byte{1} const ( versionOffset uint64 = iota @@ -93,7 +93,7 @@ func Open(sto *storage.Storage) *Programs { return &Programs{ backingStorage: sto, programs: sto.OpenSubStorage(programDataKey), - compiledHashes: sto.OpenSubStorage(machineHashesKey), + moduleHashes: sto.OpenSubStorage(moduleHashesKey), inkPrice: sto.OpenStorageBackedUint24(inkPriceOffset), maxStackDepth: sto.OpenStorageBackedUint32(maxStackDepthOffset), freePages: sto.OpenStorageBackedUint16(freePagesOffset), @@ -316,7 +316,7 @@ func (p Programs) deserializeProgram(codeHash common.Hash) (Program, error) { if err != nil { return Program{}, err } - compiledHash, err := p.compiledHashes.Get(codeHash) + compiledHash, err := p.moduleHashes.Get(codeHash) if err != nil { return Program{}, err } @@ -337,7 +337,7 @@ func (p Programs) setProgram(codehash common.Hash, program Program) error { if err != nil { return err } - return p.compiledHashes.Set(codehash, program.moduleHash) + return p.moduleHashes.Set(codehash, program.moduleHash) } func (p Programs) CodehashVersion(codeHash common.Hash) (uint16, error) { diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index aaf1006a9..5f72c1335 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -69,12 +69,9 @@ func activateProgram( ) (common.Hash, u16, error) { debugMode := arbmath.BoolToUint32(debug) moduleHash := common.Hash{} - gas := burner.GasLeft() + gasPtr := burner.GasLeft() - footprint, err := activateWasmRustImpl(wasm, pageLimit, version, debugMode, &moduleHash, &gas) - if err := burner.Burn(arbmath.SaturatingUSub(burner.GasLeft(), gas)); err != nil { - return moduleHash, footprint, err - } + footprint, err := activateWasmRustImpl(wasm, pageLimit, version, debugMode, &moduleHash, gasPtr) if err != nil { _, _, err := userFailure.toResult(err.intoSlice(), debug) return moduleHash, footprint, err diff --git a/precompiles/context.go b/precompiles/context.go index b52c3ce9a..670ffa744 100644 --- a/precompiles/context.go +++ b/precompiles/context.go @@ -53,8 +53,8 @@ func (c *Context) BurnOut() error { return vm.ErrOutOfGas } -func (c *Context) GasLeft() uint64 { - return c.gasLeft +func (c *Context) GasLeft() *uint64 { + return &c.gasLeft } func (c *Context) Restrict(err error) { From 10a8b79237eed30730e7c1d6da58e4598f366994 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sun, 15 Oct 2023 11:13:47 -0600 Subject: [PATCH 47/55] panic on divergence --- arbitrator/stylus/src/native.rs | 4 ++-- go-ethereum | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index b34d3f195..e0ac105b2 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -10,7 +10,7 @@ use arbutil::{ operator::OperatorCode, Color, }; -use eyre::{bail, eyre, Context, ErrReport, Result}; +use eyre::{bail, eyre, ErrReport, Result}; use prover::{ machine::Module as ProverModule, programs::{ @@ -383,6 +383,6 @@ pub fn activate( let compile = CompileConfig::version(version, debug); let (module, footprint) = ProverModule::activate(wasm, version, page_limit, debug, gas)?; - let asm = self::module(wasm, compile).wrap_err("failed to generate stylus module")?; + let asm = self::module(wasm, compile).expect("failed to generate stylus module"); Ok((asm, module, footprint)) } diff --git a/go-ethereum b/go-ethereum index d16318ab8..548c77470 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit d16318ab8b159730142fa5a2431cac70d172d9d5 +Subproject commit 548c7747099b4feb21369b02941482e7cd79e1c5 From 67f52b9954f8fdb69cec7b529af7848eb3cf77c9 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sun, 15 Oct 2023 12:18:45 -0600 Subject: [PATCH 48/55] make names consistent --- arbitrator/jit/src/machine.rs | 2 +- arbitrator/jit/src/user/mod.rs | 2 +- arbitrator/wasm-libraries/user-host/src/link.rs | 2 +- arbos/programs/raw.s | 2 +- arbos/programs/wasm.go | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arbitrator/jit/src/machine.rs b/arbitrator/jit/src/machine.rs index c3069ac78..44597706b 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -115,7 +115,7 @@ pub fn create(opts: &Opts, env: WasmEnv) -> (Instance, FunctionEnv, Sto github!("wavmio.readDelayedInboxMessage") => func!(wavmio::read_delayed_inbox_message), github!("wavmio.resolvePreImage") => func!(wavmio::resolve_preimage), - github!("arbos/programs.activateWasmRustImpl") => func!(user::activate_wasm), + github!("arbos/programs.activateProgramRustImpl") => func!(user::stylus_activate), github!("arbos/programs.callUserWasmRustImpl") => func!(user::call_user_wasm), github!("arbos/programs.readRustVecLenImpl") => func!(user::read_rust_vec_len), github!("arbos/programs.rustVecIntoSliceImpl") => func!(user::rust_vec_into_slice), diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 425afb43b..370d9fada 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -33,7 +33,7 @@ mod evm_api; /// These values are placed on the stack as follows /// || wasm... || pageLimit | version | debug || modhash ptr || gas ptr || footprint | 6 pad || err ptr || /// -pub fn activate_wasm(env: WasmEnvMut, sp: u32) { +pub fn stylus_activate(env: WasmEnvMut, sp: u32) { let mut sp = GoStack::simple(sp, &env); let wasm = sp.read_go_slice_owned(); let page_limit = sp.read_u16(); diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index a1d829a45..299a02b64 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -51,7 +51,7 @@ struct MemoryLeaf([u8; 32]); /// || wasm... || pageLimit | version | debug || modhash ptr || gas ptr || footprint | 6 pad || err ptr || /// #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_activateWasmRustImpl( +pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_activateProgramRustImpl( sp: usize, ) { let mut sp = GoStack::new(sp); diff --git a/arbos/programs/raw.s b/arbos/programs/raw.s index c222bfbe2..8e2dbf625 100644 --- a/arbos/programs/raw.s +++ b/arbos/programs/raw.s @@ -6,7 +6,7 @@ #include "textflag.h" -TEXT ·activateWasmRustImpl(SB), NOSPLIT, $0 +TEXT ·activateProgramRustImpl(SB), NOSPLIT, $0 CallImport RET diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index 5f72c1335..b099a3f02 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -31,7 +31,7 @@ type rustConfig byte type rustModule byte type rustEvmData byte -func activateWasmRustImpl( +func activateProgramRustImpl( wasm []byte, pageLimit, version u16, debugMode u32, moduleHash *hash, gas *u64, ) (footprint u16, err *rustVec) @@ -71,7 +71,7 @@ func activateProgram( moduleHash := common.Hash{} gasPtr := burner.GasLeft() - footprint, err := activateWasmRustImpl(wasm, pageLimit, version, debugMode, &moduleHash, gasPtr) + footprint, err := activateProgramRustImpl(wasm, pageLimit, version, debugMode, &moduleHash, gasPtr) if err != nil { _, _, err := userFailure.toResult(err.intoSlice(), debug) return moduleHash, footprint, err From 238b5f32e6ee00ec9cc0e2d79152d2fc2ce4c160 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sun, 15 Oct 2023 14:10:27 -0600 Subject: [PATCH 49/55] more renaming --- arbitrator/jit/src/machine.rs | 2 +- arbitrator/jit/src/user/mod.rs | 2 +- arbitrator/wasm-libraries/user-host/src/link.rs | 2 +- arbos/programs/native.go | 2 +- arbos/programs/programs.go | 2 +- arbos/programs/raw.s | 2 +- arbos/programs/wasm.go | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arbitrator/jit/src/machine.rs b/arbitrator/jit/src/machine.rs index 44597706b..40feb9d7e 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -116,7 +116,7 @@ pub fn create(opts: &Opts, env: WasmEnv) -> (Instance, FunctionEnv, Sto github!("wavmio.resolvePreImage") => func!(wavmio::resolve_preimage), github!("arbos/programs.activateProgramRustImpl") => func!(user::stylus_activate), - github!("arbos/programs.callUserWasmRustImpl") => func!(user::call_user_wasm), + github!("arbos/programs.callProgramRustImpl") => func!(user::stylus_call), github!("arbos/programs.readRustVecLenImpl") => func!(user::read_rust_vec_len), github!("arbos/programs.rustVecIntoSliceImpl") => func!(user::rust_vec_into_slice), github!("arbos/programs.rustConfigImpl") => func!(user::rust_config_impl), diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 370d9fada..313dd9620 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -76,7 +76,7 @@ pub fn stylus_activate(env: WasmEnvMut, sp: u32) { /// These values are placed on the stack as follows /// || modHash || calldata... || params || evmApi... || evmData || gas || status | 7 pad | out ptr || /// -pub fn call_user_wasm(env: WasmEnvMut, sp: u32) -> MaybeEscape { +pub fn stylus_call(env: WasmEnvMut, sp: u32) -> MaybeEscape { let sp = &mut GoStack::simple(sp, &env); use UserOutcome::*; diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index 299a02b64..fb3b65444 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -97,7 +97,7 @@ pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_activa /// || modHash || calldata... || params || evmApi... || evmData || gas || status | 7 pad | out ptr || /// #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_callUserWasmRustImpl( +pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbos_programs_callProgramRustImpl( sp: usize, ) { let mut sp = GoStack::new(sp); diff --git a/arbos/programs/native.go b/arbos/programs/native.go index 9aa541c9c..71153d2cb 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -90,7 +90,7 @@ func activateProgram( return hash, footprint, err } -func callUserWasm( +func callProgram( address common.Address, program Program, scope *vm.ScopeContext, diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 7dd96f3a1..fcbeff3d7 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -292,7 +292,7 @@ func (p Programs) CallProgram( if contract.CodeAddr != nil { address = *contract.CodeAddr } - return callUserWasm(address, program, scope, statedb, interpreter, tracingInfo, calldata, evmData, params, model) + return callProgram(address, program, scope, statedb, interpreter, tracingInfo, calldata, evmData, params, model) } func getWasm(statedb vm.StateDB, program common.Address) ([]byte, error) { diff --git a/arbos/programs/raw.s b/arbos/programs/raw.s index 8e2dbf625..c0b3dc45e 100644 --- a/arbos/programs/raw.s +++ b/arbos/programs/raw.s @@ -10,7 +10,7 @@ TEXT ·activateProgramRustImpl(SB), NOSPLIT, $0 CallImport RET -TEXT ·callUserWasmRustImpl(SB), NOSPLIT, $0 +TEXT ·callProgramRustImpl(SB), NOSPLIT, $0 CallImport RET diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index b099a3f02..c46e450e3 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -35,7 +35,7 @@ func activateProgramRustImpl( wasm []byte, pageLimit, version u16, debugMode u32, moduleHash *hash, gas *u64, ) (footprint u16, err *rustVec) -func callUserWasmRustImpl( +func callProgramRustImpl( moduleHash *hash, calldata []byte, params *rustConfig, evmApi []byte, evmData *rustEvmData, gas *u64, ) (status userStatus, out *rustVec) @@ -79,7 +79,7 @@ func activateProgram( return moduleHash, footprint, nil } -func callUserWasm( +func callProgram( address common.Address, program Program, scope *vm.ScopeContext, @@ -95,7 +95,7 @@ func callUserWasm( defer evmApi.drop() debug := arbmath.UintToBool(params.debugMode) - status, output := callUserWasmRustImpl( + status, output := callProgramRustImpl( &program.moduleHash, calldata, params.encode(), From 6d95c61af59e61f732499fbd20b3168d5de254e1 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sun, 15 Oct 2023 14:59:44 -0600 Subject: [PATCH 50/55] reorder test-only Machine::run checks --- arbitrator/stylus/src/run.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arbitrator/stylus/src/run.rs b/arbitrator/stylus/src/run.rs index f50ba161a..a9ff1836f 100644 --- a/arbitrator/stylus/src/run.rs +++ b/arbitrator/stylus/src/run.rs @@ -44,12 +44,12 @@ impl RunProgram for Machine { self.set_stack(config.max_depth); let status: u32 = call!("user", STYLUS_ENTRY_POINT, vec![args_len], |error| { - if self.ink_left() == MachineMeter::Exhausted { - return UserOutcome::OutOfInk; - } if self.stack_left() == 0 { return UserOutcome::OutOfStack; } + if self.ink_left() == MachineMeter::Exhausted { + return UserOutcome::OutOfInk; + } UserOutcome::Failure(error) }); From aa636dcb58087b8cab5251fdb4166eefa63b0a16 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sun, 15 Oct 2023 22:55:49 -0600 Subject: [PATCH 51/55] address review comments --- arbitrator/jit/src/gostack.rs | 7 +++- arbitrator/jit/src/socket.rs | 2 +- arbitrator/jit/src/user/mod.rs | 8 ++-- arbitrator/jit/src/wavmio.rs | 2 +- arbitrator/prover/src/lib.rs | 2 +- arbitrator/prover/src/machine.rs | 23 ++++++------ arbitrator/prover/src/programs/mod.rs | 3 +- arbitrator/prover/test-cases/dynamic.wat | 3 ++ arbitrator/prover/test-cases/link.wat | 45 +++++++++++----------- arbitrator/stylus/src/evm_api.rs | 27 ++++++------- arbitrator/stylus/src/lib.rs | 12 +++--- arbitrator/tools/module_roots/Cargo.lock | 5 ++- arbitrator/tools/module_roots/src/main.rs | 2 +- arbos/burn/burn.go | 2 +- arbos/programs/native.go | 31 +++++++-------- arbos/programs/native_api.go | 20 +++++----- arbos/programs/programs.go | 46 +++++++++++------------ arbos/programs/wasm.go | 3 +- contracts | 2 +- go-ethereum | 2 +- validator/server_api/json.go | 13 ++----- 21 files changed, 134 insertions(+), 126 deletions(-) diff --git a/arbitrator/jit/src/gostack.rs b/arbitrator/jit/src/gostack.rs index 63cf2547b..ad166f4a5 100644 --- a/arbitrator/jit/src/gostack.rs +++ b/arbitrator/jit/src/gostack.rs @@ -243,8 +243,11 @@ impl GoStack { data } - pub fn write_slice>(&self, ptr: T, src: &[u8]) { - let ptr: u32 = ptr.try_into().map_err(|_| "Go pointer not a u32").unwrap(); + pub fn write_slice>(&self, ptr: T, src: &[u8]) + where + T::Error: Debug, + { + let ptr: u32 = ptr.try_into().expect("Go pointer not a u32"); self.view().write(ptr.into(), src).unwrap(); } diff --git a/arbitrator/jit/src/socket.rs b/arbitrator/jit/src/socket.rs index 634af3635..f63653ad4 100644 --- a/arbitrator/jit/src/socket.rs +++ b/arbitrator/jit/src/socket.rs @@ -42,7 +42,7 @@ pub fn read_bytes(reader: &mut BufReader) -> Result, io::Err Ok(buf) } -pub fn read_box(reader: &mut BufReader) -> Result, io::Error> { +pub fn read_boxed_slice(reader: &mut BufReader) -> Result, io::Error> { Ok(Vec::into_boxed_slice(read_bytes(reader)?)) } diff --git a/arbitrator/jit/src/user/mod.rs b/arbitrator/jit/src/user/mod.rs index 313dd9620..30e6d062f 100644 --- a/arbitrator/jit/src/user/mod.rs +++ b/arbitrator/jit/src/user/mod.rs @@ -12,9 +12,11 @@ use arbutil::{ format::DebugBytes, heapify, }; -use prover::programs::{config::PricingParams, prelude::*}; +use prover::{ + machine::Module, + programs::{config::PricingParams, prelude::*}, +}; use std::mem; -use stylus::native; mod evm_api; @@ -54,7 +56,7 @@ pub fn stylus_activate(env: WasmEnvMut, sp: u32) { } let gas_left = &mut sp.read_u64_raw(gas); - let (_, module, pages) = match native::activate(&wasm, version, page_limit, debug, gas_left) { + let (module, pages) = match Module::activate(&wasm, version, page_limit, debug, gas_left) { Ok(result) => result, Err(error) => error!(error), }; diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index 17ba6fa81..3831d16c7 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -315,7 +315,7 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { let programs_count = socket::read_u32(stream)?; for _ in 0..programs_count { let module_hash = socket::read_bytes32(stream)?; - let module_asm = socket::read_box(stream)?; + let module_asm = socket::read_boxed_slice(stream)?; env.module_asms.insert(module_hash, module_asm.into()); } diff --git a/arbitrator/prover/src/lib.rs b/arbitrator/prover/src/lib.rs index dbb3ba5d0..3e5267b8b 100644 --- a/arbitrator/prover/src/lib.rs +++ b/arbitrator/prover/src/lib.rs @@ -133,7 +133,7 @@ pub fn str_to_c_string(text: &str) -> *mut libc::c_char { panic!("Failed to allocate memory for error string"); } ptr::copy_nonoverlapping(text.as_ptr(), buf as *mut u8, text.len()); - *(buf.add(text.len()) as *mut u8) = 0; + *(buf as *mut u8).add(text.len()) = 0; buf as *mut libc::c_char } } diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 82a067d90..3e6ba1c2a 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -286,30 +286,29 @@ pub struct Module { } lazy_static! { - static ref USER_IMPORTS: Result> = Module::calc_user_imports(); + static ref USER_IMPORTS: HashMap = Module::calc_user_imports(); } impl Module { const FORWARDING_PREFIX: &str = "arbitrator_forward__"; - fn calc_user_imports() -> Result> { - let forward_bytes = include_bytes!("../../../target/machines/latest/forward_stub.wasm"); - let forward_bin = binary::parse(forward_bytes, Path::new("forward")).unwrap(); + fn calc_user_imports() -> HashMap { + let mut imports = HashMap::default(); - let mut available_imports = HashMap::default(); + let forward = include_bytes!("../../../target/machines/latest/forward_stub.wasm"); + let forward = binary::parse(forward, Path::new("forward")).unwrap(); - for (name, &(export, kind)) in &forward_bin.exports { + for (name, &(export, kind)) in &forward.exports { if kind == ExportKind::Func { - let ty = match forward_bin.get_function(FunctionIndex::from_u32(export)) { + let ty = match forward.get_function(FunctionIndex::from_u32(export)) { Ok(ty) => ty, - Err(error) => bail!("failed to read export {}: {}", name, error), + Err(error) => panic!("failed to read export {name}: {error:?}"), }; let import = AvailableImport::new(ty, 1, export); - available_imports.insert(name.to_owned(), import); + imports.insert(name.to_owned(), import); } } - - Ok(available_imports) + imports } fn from_binary( @@ -573,7 +572,7 @@ impl Module { ) -> Result { Self::from_binary( bin, - USER_IMPORTS.as_ref().unwrap(), + &USER_IMPORTS, &HashMap::default(), false, debug_funcs, diff --git a/arbitrator/prover/src/programs/mod.rs b/arbitrator/prover/src/programs/mod.rs index 5b2562822..423cb0974 100644 --- a/arbitrator/prover/src/programs/mod.rs +++ b/arbitrator/prover/src/programs/mod.rs @@ -5,6 +5,7 @@ use crate::{ binary::{ExportKind, WasmBinary}, machine::Module, memory::MemoryType, + programs::config::CompileConfig, value::{FunctionType as ArbFunctionType, Value}, }; use arbutil::Color; @@ -17,8 +18,6 @@ use wasmer_types::{ }; use wasmparser::{Operator, Type as WpType}; -use self::config::CompileConfig; - #[cfg(feature = "native")] use { super::value, diff --git a/arbitrator/prover/test-cases/dynamic.wat b/arbitrator/prover/test-cases/dynamic.wat index d284e0bd3..1f9c8d029 100644 --- a/arbitrator/prover/test-cases/dynamic.wat +++ b/arbitrator/prover/test-cases/dynamic.wat @@ -6,8 +6,11 @@ (import "hostio" "program_ink_left" (func $ink_left (param i32) (result i64))) (import "hostio" "program_ink_status" (func $ink_status (param i32) (result i32))) (import "hostio" "program_call_main" (func $user_func (param i32 i32) (result i32))) + + ;; WAVM Module hash (data (i32.const 0x0) "\97\0c\df\6a\a9\bf\d4\3c\03\80\7f\8a\7e\67\9a\5c\12\05\94\4f\c6\5e\39\9e\00\df\5c\b3\7d\de\55\ad") ;; 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 72ce0783a..f3f24fb28 100644 --- a/arbitrator/prover/test-cases/link.wat +++ b/arbitrator/prover/test-cases/link.wat @@ -4,32 +4,35 @@ (module (import "hostio" "wavm_link_module" (func $link (param i32) (result i32))) (import "hostio" "wavm_unlink_module" (func $unlink (param) (result))) - (data (i32.const 0x0) - "\6d\c0\9f\17\5f\5b\e8\73\64\bc\79\62\e8\13\fd\cb\09\2a\12\24\87\4a\af\15\f2\e1\2e\93\b0\95\30\9a") - (data (i32.const 0x20) - "\f5\6b\4c\c7\19\da\61\01\e4\e4\9a\f1\04\ca\29\97\fd\07\05\d6\c2\3b\e6\55\70\c5\54\65\a0\3f\3d\ee") - (data (i32.const 0x40) - "\57\27\40\77\40\da\77\f8\1f\fd\81\cb\00\e0\02\17\40\f0\be\e4\11\89\0a\56\ba\80\e4\b9\31\74\13\a2") - (data (i32.const 0x60) - "\53\36\71\e6\bf\90\0f\50\fd\18\5f\44\d6\18\77\2f\70\17\19\2a\1a\8d\b6\92\5a\3c\14\1a\af\86\81\d4") - (data (i32.const 0x80) - "\97\0c\df\6a\a9\bf\d4\3c\03\80\7f\8a\7e\67\9a\5c\12\05\94\4f\c6\5e\39\9e\00\df\5c\b3\7d\de\55\ad") - (data (i32.const 0xa0) - "\c7\db\9f\8e\ed\13\ac\66\72\62\76\65\93\1b\9a\64\03\c3\c8\21\44\92\5c\8d\bc\1a\d6\bd\65\f8\2b\20") - (data (i32.const 0xc0) - "\83\46\03\41\b4\5f\a6\e6\a3\0d\e9\fc\79\fc\3c\d6\c9\c3\c7\ac\97\42\bc\48\54\92\e6\84\08\37\07\a6") - (data (i32.const 0xe0) - "\42\1d\62\e9\9a\51\d4\71\ce\50\6e\b4\83\72\18\ea\f8\ab\ab\b9\29\b8\bd\6d\66\ea\52\b3\3d\50\26\34") + + ;; WAVM module hashes + (data (i32.const 0x000) + "\74\22\43\ad\22\2e\e5\6d\f4\bb\3f\0b\09\76\0a\bf\51\b7\17\a4\c5\50\c9\5b\45\be\ea\ed\4c\57\4d\17") ;; block + (data (i32.const 0x020) + "\53\36\71\e6\bf\90\0f\50\fd\18\5f\44\d6\18\77\2f\70\17\19\2a\1a\8d\b6\92\5a\3c\14\1a\af\86\81\d4") ;; call + (data (i32.const 0x040) + "\57\27\40\77\40\da\77\f8\1f\fd\81\cb\00\e0\02\17\40\f0\be\e4\11\89\0a\56\ba\80\e4\b9\31\74\13\a2") ;; indirect + (data (i32.const 0x060) + "\3f\c3\a1\eb\a6\62\70\2b\3b\fa\dc\5b\29\22\11\6f\58\4a\6e\e5\70\60\6f\cf\6c\66\d8\c9\77\c5\c9\23") ;; const + (data (i32.const 0x080) + "\83\46\03\41\b4\5f\a6\e6\a3\0d\e9\fc\79\fc\3c\d6\c9\c3\c7\ac\97\42\bc\48\54\92\e6\84\08\37\07\a6") ;; div + (data (i32.const 0x0a0) + "\16\90\98\f2\7f\8d\bf\73\90\b9\eb\94\9f\b9\41\cd\c3\93\2e\30\b8\12\1b\d5\87\98\18\26\f2\62\7d\2c") ;; globals + (data (i32.const 0x0c0) + "\f5\6b\4c\c7\19\da\61\01\e4\e4\9a\f1\04\ca\29\97\fd\07\05\d6\c2\3b\e6\55\70\c5\54\65\a0\3f\3d\ee") ;; if-else + (data (i32.const 0x0e0) + "\42\1d\62\e9\9a\51\d4\71\ce\50\6e\b4\83\72\18\ea\f8\ab\ab\b9\29\b8\bd\6d\66\ea\52\b3\3d\50\26\34") ;; locals (data (i32.const 0x100) - "\74\22\43\ad\22\2e\e5\6d\f4\bb\3f\0b\09\76\0a\bf\51\b7\17\a4\c5\50\c9\5b\45\be\ea\ed\4c\57\4d\17") + "\6d\c0\9f\17\5f\5b\e8\73\64\bc\79\62\e8\13\fd\cb\09\2a\12\24\87\4a\af\15\f2\e1\2e\93\b0\95\30\9a") ;; loop (data (i32.const 0x120) - "\16\90\98\f2\7f\8d\bf\73\90\b9\eb\94\9f\b9\41\cd\c3\93\2e\30\b8\12\1b\d5\87\98\18\26\f2\62\7d\2c") + "\a7\66\cb\0e\c4\31\ea\16\fd\c6\2f\d3\11\ca\4a\78\f8\48\6a\69\0a\4c\b9\1c\fc\47\f8\b6\63\6d\80\fa") ;; math (data (i32.const 0x140) - "\3f\c3\a1\eb\a6\62\70\2b\3b\fa\dc\5b\29\22\11\6f\58\4a\6e\e5\70\60\6f\cf\6c\66\d8\c9\77\c5\c9\23") + "\ea\02\78\f7\a3\b3\e0\0e\55\f6\8f\13\87\d6\6f\04\38\b3\6b\4c\d5\33\e2\3d\0b\36\71\9f\57\f5\f0\59") ;; iops (data (i32.const 0x160) - "\a7\66\cb\0e\c4\31\ea\16\fd\c6\2f\d3\11\ca\4a\78\f8\48\6a\69\0a\4c\b9\1c\fc\47\f8\b6\63\6d\80\fa") + "\97\0c\df\6a\a9\bf\d4\3c\03\80\7f\8a\7e\67\9a\5c\12\05\94\4f\c6\5e\39\9e\00\df\5c\b3\7d\de\55\ad") ;; user (data (i32.const 0x180) - "\ea\02\78\f7\a3\b3\e0\0e\55\f6\8f\13\87\d6\6f\04\38\b3\6b\4c\d5\33\e2\3d\0b\36\71\9f\57\f5\f0\59") + "\c7\db\9f\8e\ed\13\ac\66\72\62\76\65\93\1b\9a\64\03\c3\c8\21\44\92\5c\8d\bc\1a\d6\bd\65\f8\2b\20") ;; return + (func $start (local $counter i32) ;; add modules diff --git a/arbitrator/stylus/src/evm_api.rs b/arbitrator/stylus/src/evm_api.rs index 8cda407fc..3b1b261dc 100644 --- a/arbitrator/stylus/src/evm_api.rs +++ b/arbitrator/stylus/src/evm_api.rs @@ -1,7 +1,7 @@ // Copyright 2022-2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE -use crate::{RustSlice, RustVec}; +use crate::{RustBytes, RustSlice}; use arbutil::{ evm::{ api::{EvmApi, EvmApiStatus}, @@ -19,7 +19,7 @@ pub struct GoEvmApi { key: Bytes32, value: Bytes32, gas_cost: *mut u64, - error: *mut RustVec, + error: *mut RustBytes, ) -> EvmApiStatus, pub contract_call: unsafe extern "C" fn( id: usize, @@ -45,22 +45,23 @@ pub struct GoEvmApi { ) -> EvmApiStatus, pub create1: unsafe extern "C" fn( id: usize, - code: *mut RustVec, + code: *mut RustBytes, endowment: Bytes32, gas: *mut u64, return_data_len: *mut u32, ) -> EvmApiStatus, pub create2: unsafe extern "C" fn( id: usize, - code: *mut RustVec, + code: *mut RustBytes, endowment: Bytes32, salt: Bytes32, gas: *mut u64, return_data_len: *mut u32, ) -> EvmApiStatus, pub get_return_data: - unsafe extern "C" fn(id: usize, output: *mut RustVec, offset: u32, size: u32), - pub emit_log: unsafe extern "C" fn(id: usize, data: *mut RustVec, topics: u32) -> EvmApiStatus, + unsafe extern "C" fn(id: usize, output: *mut RustBytes, offset: u32, size: u32), + pub emit_log: + unsafe extern "C" fn(id: usize, data: *mut RustBytes, topics: u32) -> EvmApiStatus, pub account_balance: unsafe extern "C" fn(id: usize, address: Bytes20, gas_cost: *mut u64) -> Bytes32, // balance pub account_codehash: @@ -68,7 +69,7 @@ pub struct GoEvmApi { pub add_pages: unsafe extern "C" fn(id: usize, pages: u16) -> u64, // gas cost pub capture_hostio: unsafe extern "C" fn( id: usize, - name: *mut RustVec, + name: *mut RustBytes, args: *mut RustSlice, outs: *mut RustSlice, start_ink: u64, @@ -106,7 +107,7 @@ impl EvmApi for GoEvmApi { } fn set_bytes32(&mut self, key: Bytes32, value: Bytes32) -> Result { - let mut error = RustVec::new(vec![]); + let mut error = RustBytes::new(vec![]); let mut cost = 0; let api_status = call!(self, set_bytes32, key, value, ptr!(cost), ptr!(error)); let error = into_vec!(error); // done here to always drop @@ -183,7 +184,7 @@ impl EvmApi for GoEvmApi { ) -> (Result, u32, u64) { let mut call_gas = gas; // becomes the call's cost let mut return_data_len = 0; - let mut code = RustVec::new(code); + let mut code = RustBytes::new(code); let api_status = call!( self, create1, @@ -209,7 +210,7 @@ impl EvmApi for GoEvmApi { ) -> (Result, u32, u64) { let mut call_gas = gas; // becomes the call's cost let mut return_data_len = 0; - let mut code = RustVec::new(code); + let mut code = RustBytes::new(code); let api_status = call!( self, create2, @@ -228,13 +229,13 @@ impl EvmApi for GoEvmApi { } fn get_return_data(&mut self, offset: u32, size: u32) -> Vec { - let mut data = RustVec::new(vec![]); + let mut data = RustBytes::new(vec![]); call!(self, get_return_data, ptr!(data), offset, size); into_vec!(data) } fn emit_log(&mut self, data: Vec, topics: u32) -> Result<()> { - let mut data = RustVec::new(data); + let mut data = RustBytes::new(data); let api_status = call!(self, emit_log, ptr!(data), topics); let error = into_vec!(data); // done here to always drop match api_status { @@ -263,7 +264,7 @@ impl EvmApi for GoEvmApi { call!( self, capture_hostio, - ptr!(RustVec::new(name.as_bytes().to_vec())), + ptr!(RustBytes::new(name.as_bytes().to_vec())), ptr!(RustSlice::new(args)), ptr!(RustSlice::new(outs)), start_ink, diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index fd406ee1b..40aecdebd 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -60,13 +60,13 @@ impl<'a> RustSlice<'a> { } #[repr(C)] -pub struct RustVec { +pub struct RustBytes { ptr: *mut u8, len: usize, cap: usize, } -impl RustVec { +impl RustBytes { fn new(vec: Vec) -> Self { let mut rust_vec = Self { ptr: std::ptr::null_mut(), @@ -117,7 +117,7 @@ pub unsafe extern "C" fn stylus_activate( page_limit: u16, version: u16, debug: bool, - output: *mut RustVec, + output: *mut RustBytes, asm_len: *mut usize, module_hash: *mut Bytes32, footprint: *mut u16, @@ -156,7 +156,7 @@ pub unsafe extern "C" fn stylus_call( go_api: GoEvmApi, evm_data: EvmData, debug_chain: u32, - output: *mut RustVec, + output: *mut RustBytes, gas: *mut u64, ) -> UserOutcomeKind { let module = module.slice(); @@ -191,7 +191,7 @@ pub unsafe extern "C" fn stylus_call( /// /// Must only be called once per vec. #[no_mangle] -pub unsafe extern "C" fn stylus_drop_vec(vec: RustVec) { +pub unsafe extern "C" fn stylus_drop_vec(vec: RustBytes) { if !vec.ptr.is_null() { mem::drop(vec.into_vec()) } @@ -203,7 +203,7 @@ pub unsafe extern "C" fn stylus_drop_vec(vec: RustVec) { /// /// `rust` must not be null. #[no_mangle] -pub unsafe extern "C" fn stylus_vec_set_bytes(rust: *mut RustVec, data: GoSliceData) { +pub unsafe extern "C" fn stylus_vec_set_bytes(rust: *mut RustBytes, data: GoSliceData) { let rust = &mut *rust; let mut vec = Vec::from_raw_parts(rust.ptr, rust.len, rust.cap); vec.clear(); diff --git a/arbitrator/tools/module_roots/Cargo.lock b/arbitrator/tools/module_roots/Cargo.lock index 4bfaa7db1..b4ccea9ca 100644 --- a/arbitrator/tools/module_roots/Cargo.lock +++ b/arbitrator/tools/module_roots/Cargo.lock @@ -44,6 +44,7 @@ dependencies = [ "digest 0.9.0", "eyre", "hex", + "num-traits", "serde", "sha3 0.10.6", "siphasher", @@ -894,9 +895,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] diff --git a/arbitrator/tools/module_roots/src/main.rs b/arbitrator/tools/module_roots/src/main.rs index 2cc52d614..2a42a0131 100644 --- a/arbitrator/tools/module_roots/src/main.rs +++ b/arbitrator/tools/module_roots/src/main.rs @@ -35,7 +35,7 @@ fn main() -> Result<()> { for module in &opts.stylus_modules { let error = || format!("failed to read module at {}", module.to_string_lossy()); let wasm = file_bytes(module).wrap_err_with(error)?; - let hash = mach.add_program(&wasm, 1, true, None).wrap_err_with(error)?; + let hash = mach.add_program(&wasm, 1, true).wrap_err_with(error)?; let name = module.file_stem().unwrap().to_string_lossy(); stylus.push((name.to_owned(), hash)); println!("{} {}", name, hash); diff --git a/arbos/burn/burn.go b/arbos/burn/burn.go index 665ff4e58..0463588af 100644 --- a/arbos/burn/burn.go +++ b/arbos/burn/burn.go @@ -13,7 +13,7 @@ import ( type Burner interface { Burn(amount uint64) error Burned() uint64 - GasLeft() *uint64 + GasLeft() *uint64 // SystemBurner's panic BurnOut() error Restrict(err error) HandleError(err error) error diff --git a/arbos/programs/native.go b/arbos/programs/native.go index 71153d2cb..cd84caf48 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -41,7 +41,7 @@ type usize = C.size_t type cbool = C._Bool type bytes20 = C.Bytes20 type bytes32 = C.Bytes32 -type rustVec = C.RustVec +type rustBytes = C.RustBytes type rustSlice = C.RustSlice func activateProgram( @@ -53,7 +53,7 @@ func activateProgram( debug bool, burner burn.Burner, ) (common.Hash, uint16, error) { - output := &rustVec{} + output := &rustBytes{} asmLen := usize(0) moduleHash := &bytes32{} footprint := uint16(math.MaxUint16) @@ -93,6 +93,7 @@ func activateProgram( func callProgram( address common.Address, program Program, + moduleHash common.Hash, scope *vm.ScopeContext, db vm.StateDB, interpreter *vm.EVMInterpreter, @@ -103,14 +104,14 @@ func callProgram( memoryModel *MemoryModel, ) ([]byte, error) { if db, ok := db.(*state.StateDB); ok { - db.RecordProgram(program.moduleHash) + db.RecordProgram(moduleHash) } - asm := db.GetActivatedAsm(program.moduleHash) + asm := db.GetActivatedAsm(moduleHash) evmApi, id := newApi(interpreter, tracingInfo, scope, memoryModel) defer dropApi(id) - output := &rustVec{} + output := &rustBytes{} status := userStatus(C.stylus_call( goSlice(asm), goSlice(calldata), @@ -144,7 +145,7 @@ func getBytes32Impl(api usize, key bytes32, cost *u64) bytes32 { } //export setBytes32Impl -func setBytes32Impl(api usize, key, value bytes32, cost *u64, errVec *rustVec) apiStatus { +func setBytes32Impl(api usize, key, value bytes32, cost *u64, errVec *rustBytes) apiStatus { closures := getApi(api) gas, err := closures.setBytes32(key.toHash(), value.toHash()) @@ -193,7 +194,7 @@ func staticCallImpl(api usize, contract bytes20, data *rustSlice, evmGas *u64, l } //export create1Impl -func create1Impl(api usize, code *rustVec, endowment bytes32, evmGas *u64, len *u32) apiStatus { +func create1Impl(api usize, code *rustBytes, endowment bytes32, evmGas *u64, len *u32) apiStatus { closures := getApi(api) addr, ret_len, cost, err := closures.create1(code.read(), endowment.toBig(), uint64(*evmGas)) *evmGas = u64(cost) // evmGas becomes the call's cost @@ -207,7 +208,7 @@ func create1Impl(api usize, code *rustVec, endowment bytes32, evmGas *u64, len * } //export create2Impl -func create2Impl(api usize, code *rustVec, endowment, salt bytes32, evmGas *u64, len *u32) apiStatus { +func create2Impl(api usize, code *rustBytes, endowment, salt bytes32, evmGas *u64, len *u32) apiStatus { closures := getApi(api) addr, ret_len, cost, err := closures.create2(code.read(), endowment.toBig(), salt.toBig(), uint64(*evmGas)) *evmGas = u64(cost) // evmGas becomes the call's cost @@ -221,14 +222,14 @@ func create2Impl(api usize, code *rustVec, endowment, salt bytes32, evmGas *u64, } //export getReturnDataImpl -func getReturnDataImpl(api usize, output *rustVec, offset u32, size u32) { +func getReturnDataImpl(api usize, output *rustBytes, offset u32, size u32) { closures := getApi(api) returnData := closures.getReturnData(uint32(offset), uint32(size)) output.setBytes(returnData) } //export emitLogImpl -func emitLogImpl(api usize, data *rustVec, topics u32) apiStatus { +func emitLogImpl(api usize, data *rustBytes, topics u32) apiStatus { closures := getApi(api) err := closures.emitLog(data.read(), uint32(topics)) if err != nil { @@ -307,25 +308,25 @@ func (slice *rustSlice) read() []byte { return arbutil.PointerToSlice((*byte)(slice.ptr), int(slice.len)) } -func (vec *rustVec) read() []byte { +func (vec *rustBytes) read() []byte { return arbutil.PointerToSlice((*byte)(vec.ptr), int(vec.len)) } -func (vec *rustVec) intoBytes() []byte { +func (vec *rustBytes) intoBytes() []byte { slice := vec.read() vec.drop() return slice } -func (vec *rustVec) drop() { +func (vec *rustBytes) drop() { C.stylus_drop_vec(*vec) } -func (vec *rustVec) setString(data string) { +func (vec *rustBytes) setString(data string) { vec.setBytes([]byte(data)) } -func (vec *rustVec) setBytes(data []byte) { +func (vec *rustBytes) setBytes(data []byte) { C.stylus_vec_set_bytes(vec, goSlice(data)) } diff --git a/arbos/programs/native_api.go b/arbos/programs/native_api.go index 6956d493b..121ff79ea 100644 --- a/arbos/programs/native_api.go +++ b/arbos/programs/native_api.go @@ -21,8 +21,8 @@ Bytes32 getBytes32Wrap(usize api, Bytes32 key, u64 * cost) { return getBytes32Impl(api, key, cost); } -EvmApiStatus setBytes32Impl(usize api, Bytes32 key, Bytes32 value, u64 * cost, RustVec * error); -EvmApiStatus setBytes32Wrap(usize api, Bytes32 key, Bytes32 value, u64 * cost, RustVec * error) { +EvmApiStatus setBytes32Impl(usize api, Bytes32 key, Bytes32 value, u64 * cost, RustBytes * error); +EvmApiStatus setBytes32Wrap(usize api, Bytes32 key, Bytes32 value, u64 * cost, RustBytes * error) { return setBytes32Impl(api, key, value, cost, error); } @@ -41,23 +41,23 @@ EvmApiStatus staticCallWrap(usize api, Bytes20 contract, RustSlice * calldata, u return staticCallImpl(api, contract, calldata, gas, len); } -EvmApiStatus create1Impl(usize api, RustVec * code, Bytes32 endowment, u64 * gas, u32 * len); -EvmApiStatus create1Wrap(usize api, RustVec * code, Bytes32 endowment, u64 * gas, u32 * len) { +EvmApiStatus create1Impl(usize api, RustBytes * code, Bytes32 endowment, u64 * gas, u32 * len); +EvmApiStatus create1Wrap(usize api, RustBytes * code, Bytes32 endowment, u64 * gas, u32 * len) { return create1Impl(api, code, endowment, gas, len); } -EvmApiStatus create2Impl(usize api, RustVec * code, Bytes32 endowment, Bytes32 salt, u64 * gas, u32 * len); -EvmApiStatus create2Wrap(usize api, RustVec * code, Bytes32 endowment, Bytes32 salt, u64 * gas, u32 * len) { +EvmApiStatus create2Impl(usize api, RustBytes * code, Bytes32 endowment, Bytes32 salt, u64 * gas, u32 * len); +EvmApiStatus create2Wrap(usize api, RustBytes * code, Bytes32 endowment, Bytes32 salt, u64 * gas, u32 * len) { return create2Impl(api, code, endowment, salt, gas, len); } -void getReturnDataImpl(usize api, RustVec * data, u32 offset, u32 size); -void getReturnDataWrap(usize api, RustVec * data, u32 offset, u32 size) { +void getReturnDataImpl(usize api, RustBytes * data, u32 offset, u32 size); +void getReturnDataWrap(usize api, RustBytes * data, u32 offset, u32 size) { return getReturnDataImpl(api, data, offset, size); } -EvmApiStatus emitLogImpl(usize api, RustVec * data, usize topics); -EvmApiStatus emitLogWrap(usize api, RustVec * data, usize topics) { +EvmApiStatus emitLogImpl(usize api, RustBytes * data, usize topics); +EvmApiStatus emitLogWrap(usize api, RustBytes * data, usize topics) { return emitLogImpl(api, data, topics); } diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index fcbeff3d7..42cea9dff 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -30,14 +30,13 @@ type Programs struct { pageRamp storage.StorageBackedUint64 pageLimit storage.StorageBackedUint16 callScalar storage.StorageBackedUint16 - version storage.StorageBackedUint16 + version storage.StorageBackedUint16 // Must only be changed during ArbOS upgrades } type Program struct { - wasmSize uint16 // Unit is half of a kb - footprint uint16 - version uint16 - moduleHash common.Hash + wasmSize uint16 // Unit is half of a kb + footprint uint16 + version uint16 } type uint24 = arbmath.Uint24 @@ -202,15 +201,17 @@ func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, debugMode if err != nil { return 0, true, err } + if err := p.moduleHashes.Set(codeHash, moduleHash); err != nil { + return version, true, err + } // wasmSize is stored as half kb units, rounding up wasmSize := arbmath.SaturatingUCast[uint16]((len(wasm) + 511) / 512) programData := Program{ - wasmSize: wasmSize, - footprint: footprint, - version: version, - moduleHash: moduleHash, + wasmSize: wasmSize, + footprint: footprint, + version: version, } return version, false, p.setProgram(codeHash, programData) } @@ -241,6 +242,11 @@ func (p Programs) CallProgram( return nil, ProgramOutOfDateError(program.version) } + moduleHash, err := p.moduleHashes.Get(contract.CodeHash) + if err != nil { + return nil, err + } + debugMode := interpreter.Evm().ChainConfig().DebugMode() params, err := p.goParams(program.version, debugMode) if err != nil { @@ -292,7 +298,10 @@ func (p Programs) CallProgram( if contract.CodeAddr != nil { address = *contract.CodeAddr } - return callProgram(address, program, scope, statedb, interpreter, tracingInfo, calldata, evmData, params, model) + return callProgram( + address, program, moduleHash, scope, statedb, interpreter, + tracingInfo, calldata, evmData, params, model, + ) } func getWasm(statedb vm.StateDB, program common.Address) ([]byte, error) { @@ -316,15 +325,10 @@ func (p Programs) deserializeProgram(codeHash common.Hash) (Program, error) { if err != nil { return Program{}, err } - compiledHash, err := p.moduleHashes.Get(codeHash) - if err != nil { - return Program{}, err - } return Program{ - wasmSize: arbmath.BytesToUint16(data[26:28]), - footprint: arbmath.BytesToUint16(data[28:30]), - version: arbmath.BytesToUint16(data[30:]), - moduleHash: compiledHash, + wasmSize: arbmath.BytesToUint16(data[26:28]), + footprint: arbmath.BytesToUint16(data[28:30]), + version: arbmath.BytesToUint16(data[30:]), }, nil } @@ -333,11 +337,7 @@ func (p Programs) setProgram(codehash common.Hash, program Program) error { copy(data[26:], arbmath.Uint16ToBytes(program.wasmSize)) copy(data[28:], arbmath.Uint16ToBytes(program.footprint)) copy(data[30:], arbmath.Uint16ToBytes(program.version)) - err := p.programs.Set(codehash, data) - if err != nil { - return err - } - return p.moduleHashes.Set(codehash, program.moduleHash) + return p.programs.Set(codehash, data) } func (p Programs) CodehashVersion(codeHash common.Hash) (uint16, error) { diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index c46e450e3..44e9bb1c0 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -82,6 +82,7 @@ func activateProgram( func callProgram( address common.Address, program Program, + moduleHash common.Hash, scope *vm.ScopeContext, db vm.StateDB, interpreter *vm.EVMInterpreter, @@ -96,7 +97,7 @@ func callProgram( debug := arbmath.UintToBool(params.debugMode) status, output := callProgramRustImpl( - &program.moduleHash, + &moduleHash, calldata, params.encode(), evmApi.funcs, diff --git a/contracts b/contracts index 73a9a78b1..78ecfd5a8 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 73a9a78b194bd99eadc0f41d7b899f45ff53b786 +Subproject commit 78ecfd5a8cad6858cd18f75500284756bf854d16 diff --git a/go-ethereum b/go-ethereum index 548c77470..4ae0e6497 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 548c7747099b4feb21369b02941482e7cd79e1c5 +Subproject commit 4ae0e649726e0079d142d1050331c068edd9acfd diff --git a/validator/server_api/json.go b/validator/server_api/json.go index 07f39b345..d81fee176 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -29,7 +29,7 @@ type ValidationInputJson struct { DelayedMsgNr uint64 PreimagesB64 jsonapi.PreimagesMapJson BatchInfo []BatchInfoJson - UserWasms map[string]UserWasmJson + UserWasms map[common.Hash]UserWasmJson DelayedMsgB64 string StartState validator.GoGlobalState DebugChain bool @@ -43,7 +43,7 @@ func ValidationInputToJson(entry *validator.ValidationInput) *ValidationInputJso DelayedMsgB64: base64.StdEncoding.EncodeToString(entry.DelayedMsg), StartState: entry.StartState, PreimagesB64: jsonapi.NewPreimagesMapJson(entry.Preimages), - UserWasms: make(map[string]UserWasmJson), + UserWasms: make(map[common.Hash]UserWasmJson), DebugChain: entry.DebugChain, } for _, binfo := range entry.BatchInfo { @@ -51,12 +51,11 @@ func ValidationInputToJson(entry *validator.ValidationInput) *ValidationInputJso res.BatchInfo = append(res.BatchInfo, BatchInfoJson{binfo.Number, encData}) } for moduleHash, info := range entry.UserWasms { - encModuleHash := base64.StdEncoding.EncodeToString(moduleHash[:]) encWasm := UserWasmJson{ Asm: base64.StdEncoding.EncodeToString(info.Asm), Module: base64.StdEncoding.EncodeToString(info.Module), } - res.UserWasms[encModuleHash] = encWasm + res.UserWasms[moduleHash] = encWasm } return res } @@ -88,10 +87,6 @@ func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationI valInput.BatchInfo = append(valInput.BatchInfo, decInfo) } for moduleHash, info := range entry.UserWasms { - decModuleHash, err := base64.StdEncoding.DecodeString(moduleHash) - if err != nil { - return nil, err - } asm, err := base64.StdEncoding.DecodeString(info.Asm) if err != nil { return nil, err @@ -104,7 +99,7 @@ func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationI Asm: asm, Module: module, } - valInput.UserWasms[common.Hash(decModuleHash)] = decInfo + valInput.UserWasms[moduleHash] = decInfo } return valInput, nil } From 8a69f1f232934c1693ddf112628addf3a140eaaa Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sun, 15 Oct 2023 23:11:18 -0600 Subject: [PATCH 52/55] add activation event --- arbos/programs/programs.go | 20 ++++++++++--------- contracts | 2 +- precompiles/ArbWasm.go | 7 +++++-- system_tests/program_test.go | 37 ++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 12 deletions(-) diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 42cea9dff..8219a03e6 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -168,41 +168,43 @@ func (p Programs) SetCallScalar(scalar uint16) error { return p.callScalar.Set(scalar) } -func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, debugMode bool) (uint16, bool, error) { +func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, debugMode bool) ( + uint16, common.Hash, common.Hash, bool, error, +) { statedb := evm.StateDB codeHash := statedb.GetCodeHash(address) burner := p.programs.Burner() version, err := p.StylusVersion() if err != nil { - return 0, false, err + return 0, codeHash, common.Hash{}, false, err } latest, err := p.CodehashVersion(codeHash) if err != nil { - return 0, false, err + return 0, codeHash, common.Hash{}, false, err } // Already compiled and found in the machine versions mapping. if latest >= version { - return 0, false, ProgramUpToDateError() + return 0, codeHash, common.Hash{}, false, ProgramUpToDateError() } wasm, err := getWasm(statedb, address) if err != nil { - return 0, false, err + return 0, codeHash, common.Hash{}, false, err } // require the program's footprint not exceed the remaining memory budget pageLimit, err := p.PageLimit() if err != nil { - return 0, false, err + return 0, codeHash, common.Hash{}, false, err } pageLimit = arbmath.SaturatingUSub(pageLimit, statedb.GetStylusPagesOpen()) moduleHash, footprint, err := activateProgram(statedb, address, wasm, pageLimit, version, debugMode, burner) if err != nil { - return 0, true, err + return 0, codeHash, common.Hash{}, true, err } if err := p.moduleHashes.Set(codeHash, moduleHash); err != nil { - return version, true, err + return 0, codeHash, common.Hash{}, true, err } // wasmSize is stored as half kb units, rounding up @@ -213,7 +215,7 @@ func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, debugMode footprint: footprint, version: version, } - return version, false, p.setProgram(codeHash, programData) + return version, codeHash, moduleHash, false, p.setProgram(codeHash, programData) } func (p Programs) CallProgram( diff --git a/contracts b/contracts index 78ecfd5a8..70682f242 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 78ecfd5a8cad6858cd18f75500284756bf854d16 +Subproject commit 70682f242380296c18359a2e4e2b994e5e099cac diff --git a/precompiles/ArbWasm.go b/precompiles/ArbWasm.go index be4ad44fc..ff211bedf 100644 --- a/precompiles/ArbWasm.go +++ b/precompiles/ArbWasm.go @@ -6,6 +6,8 @@ package precompiles type ArbWasm struct { Address addr // 0x71 + ProgramActivated func(ctx, mech, hash, hash, addr, uint16) error + ProgramActivatedGasCost func(hash, hash, addr, uint16) (uint64, error) ProgramNotActivatedError func() error ProgramOutOfDateError func(version uint16) error ProgramUpToDateError func() error @@ -13,17 +15,18 @@ type ArbWasm struct { // Compile a wasm program with the latest instrumentation func (con ArbWasm) ActivateProgram(c ctx, evm mech, program addr) (uint16, error) { + debug := evm.ChainConfig().DebugMode() // charge 3 million up front to begin activation if err := c.Burn(3000000); err != nil { return 0, err } - version, takeAllGas, err := c.State.Programs().ActivateProgram(evm, program, evm.ChainConfig().DebugMode()) + version, codeHash, moduleHash, takeAllGas, err := c.State.Programs().ActivateProgram(evm, program, debug) if takeAllGas { _ = c.BurnOut() return version, err } - return version, err + return version, con.ProgramActivated(c, evm, codeHash, moduleHash, program, version) } // Gets the latest stylus version diff --git a/system_tests/program_test.go b/system_tests/program_test.go index d1d216538..3c89892bf 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -993,6 +993,43 @@ func testSdkStorage(t *testing.T, jit bool) { check() } +func TestProgramAcivationLogs(t *testing.T) { + t.Parallel() + ctx, _, _, l2client, auth, cleanup := setupProgramTest(t, true) + defer cleanup() + + wasm, _ := readWasmFile(t, watFile("memory")) + arbWasm, err := precompilesgen.NewArbWasm(types.ArbWasmAddress, l2client) + Require(t, err) + + nolimitAuth := auth + nolimitAuth.GasLimit = 32000000 + + programAddress := deployContract(t, ctx, nolimitAuth, l2client, wasm) + + tx, err := arbWasm.ActivateProgram(&auth, programAddress) + Require(t, err) + receipt, err := EnsureTxSucceeded(ctx, l2client, tx) + Require(t, err) + + if len(receipt.Logs) != 1 { + Fatal(t, "expected 1 log while activating, got ", len(receipt.Logs)) + } + log, err := arbWasm.ParseProgramActivated(*receipt.Logs[0]) + if err != nil { + Fatal(t, "parsing activated log: ", err) + } + if log.Version == 0 { + Fatal(t, "activated program with version 0") + } + if log.Program != programAddress { + Fatal(t, "unexpected program in activation log: ", log.Program) + } + if crypto.Keccak256Hash(wasm) != log.Codehash { + Fatal(t, "unexpected codehash in activation log: ", log.Codehash) + } +} + func setupProgramTest(t *testing.T, jit bool) ( context.Context, *arbnode.Node, *BlockchainTestInfo, *ethclient.Client, bind.TransactOpts, func(), ) { From 9dde38e5da3601fc721500f539be981da60f7908 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sun, 15 Oct 2023 23:33:00 -0600 Subject: [PATCH 53/55] move out err --- precompiles/ArbWasm.go | 2 ++ system_tests/program_test.go | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/precompiles/ArbWasm.go b/precompiles/ArbWasm.go index ff211bedf..902985543 100644 --- a/precompiles/ArbWasm.go +++ b/precompiles/ArbWasm.go @@ -24,6 +24,8 @@ func (con ArbWasm) ActivateProgram(c ctx, evm mech, program addr) (uint16, error version, codeHash, moduleHash, takeAllGas, err := c.State.Programs().ActivateProgram(evm, program, debug) if takeAllGas { _ = c.BurnOut() + } + if err != nil { return version, err } return version, con.ProgramActivated(c, evm, codeHash, moduleHash, program, version) diff --git a/system_tests/program_test.go b/system_tests/program_test.go index 3c89892bf..6339f0293 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -59,9 +59,9 @@ func keccakTest(t *testing.T, jit bool) { Require(t, err) colors.PrintBlue("program deployed to ", programAddress.Hex()) - timed(t, "compile same code", func() { + timed(t, "activate same code", func() { if _, err := arbWasm.ActivateProgram(&auth, otherAddressSameCode); err == nil || !strings.Contains(err.Error(), "ProgramUpToDate") { - Fatal(t, "compile should have failed with ProgramUpToDate") + Fatal(t, "activate should have failed with ProgramUpToDate", err) } }) From a44d8a221ebe60324544a21f4750fab77fcfd463 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sun, 15 Oct 2023 23:35:19 -0600 Subject: [PATCH 54/55] simplify --- arbos/programs/programs.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 8219a03e6..3adef75eb 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -324,14 +324,11 @@ func (p Programs) getProgram(contract *vm.Contract) (Program, error) { func (p Programs) deserializeProgram(codeHash common.Hash) (Program, error) { data, err := p.programs.Get(codeHash) - if err != nil { - return Program{}, err - } return Program{ wasmSize: arbmath.BytesToUint16(data[26:28]), footprint: arbmath.BytesToUint16(data[28:30]), version: arbmath.BytesToUint16(data[30:]), - }, nil + }, err } func (p Programs) setProgram(codehash common.Hash, program Program) error { From 40daaacecee9640c2821fe893baa58483ecdf1ef Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Sun, 15 Oct 2023 23:51:49 -0600 Subject: [PATCH 55/55] address review comments + simplify --- arbitrator/prover/src/machine.rs | 14 ++++++-------- arbos/burn/burn.go | 2 +- arbos/programs/native.go | 1 - arbos/programs/programs.go | 2 +- arbos/programs/wasm.go | 1 - 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 3e6ba1c2a..396744074 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -286,13 +286,7 @@ pub struct Module { } lazy_static! { - static ref USER_IMPORTS: HashMap = Module::calc_user_imports(); -} - -impl Module { - const FORWARDING_PREFIX: &str = "arbitrator_forward__"; - - fn calc_user_imports() -> HashMap { + static ref USER_IMPORTS: HashMap = { let mut imports = HashMap::default(); let forward = include_bytes!("../../../target/machines/latest/forward_stub.wasm"); @@ -309,7 +303,11 @@ impl Module { } } imports - } + }; +} + +impl Module { + const FORWARDING_PREFIX: &str = "arbitrator_forward__"; fn from_binary( bin: &WasmBinary, diff --git a/arbos/burn/burn.go b/arbos/burn/burn.go index 0463588af..7d30ad12e 100644 --- a/arbos/burn/burn.go +++ b/arbos/burn/burn.go @@ -13,7 +13,7 @@ import ( type Burner interface { Burn(amount uint64) error Burned() uint64 - GasLeft() *uint64 // SystemBurner's panic + GasLeft() *uint64 // `SystemBurner`s panic (no notion of GasLeft) BurnOut() error Restrict(err error) HandleError(err error) error diff --git a/arbos/programs/native.go b/arbos/programs/native.go index cd84caf48..f99ae8f4b 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -92,7 +92,6 @@ func activateProgram( func callProgram( address common.Address, - program Program, moduleHash common.Hash, scope *vm.ScopeContext, db vm.StateDB, diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 3adef75eb..2bddb929f 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -301,7 +301,7 @@ func (p Programs) CallProgram( address = *contract.CodeAddr } return callProgram( - address, program, moduleHash, scope, statedb, interpreter, + address, moduleHash, scope, statedb, interpreter, tracingInfo, calldata, evmData, params, model, ) } diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index 44e9bb1c0..cba3f8e1b 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -81,7 +81,6 @@ func activateProgram( func callProgram( address common.Address, - program Program, moduleHash common.Hash, scope *vm.ScopeContext, db vm.StateDB,