From 698b07653ab6ee11f4ebb853c69453a5401d0165 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Wed, 13 Mar 2024 02:56:20 -0600 Subject: [PATCH 1/5] VM storage cache --- arbitrator/arbutil/src/evm/api.rs | 31 ++- arbitrator/arbutil/src/evm/mod.rs | 8 +- arbitrator/arbutil/src/evm/req.rs | 56 +++- arbitrator/arbutil/src/evm/storage.rs | 64 +++++ arbitrator/arbutil/src/format.rs | 13 + arbitrator/stylus/cbindgen.toml | 2 +- arbitrator/stylus/src/env.rs | 2 +- arbitrator/stylus/src/evm_api.rs | 7 +- arbitrator/stylus/src/host.rs | 11 +- arbitrator/stylus/src/native.rs | 6 +- arbitrator/stylus/src/test/api.rs | 10 +- arbitrator/wasm-libraries/Cargo.lock | 1 + arbitrator/wasm-libraries/forward/src/main.rs | 5 +- .../wasm-libraries/user-host-trait/src/lib.rs | 44 ++- .../wasm-libraries/user-host/src/host.rs | 9 +- .../wasm-libraries/user-test/Cargo.toml | 1 + .../user-test/src/caller_env.rs | 21 -- .../wasm-libraries/user-test/src/host.rs | 252 ++++++++++++++---- .../wasm-libraries/user-test/src/ink.rs | 18 +- .../wasm-libraries/user-test/src/lib.rs | 8 +- .../wasm-libraries/user-test/src/program.rs | 209 +++++++++++++++ arbos/programs/api.go | 58 ++-- arbos/programs/native.go | 8 +- arbos/programs/native_api.go | 4 +- arbos/programs/testconstants.go | 28 +- 25 files changed, 690 insertions(+), 186 deletions(-) create mode 100644 arbitrator/arbutil/src/evm/storage.rs delete mode 100644 arbitrator/wasm-libraries/user-test/src/caller_env.rs create mode 100644 arbitrator/wasm-libraries/user-test/src/program.rs diff --git a/arbitrator/arbutil/src/evm/api.rs b/arbitrator/arbutil/src/evm/api.rs index a7968fcc8..e9886d0cd 100644 --- a/arbitrator/arbutil/src/evm/api.rs +++ b/arbitrator/arbutil/src/evm/api.rs @@ -3,28 +3,24 @@ use crate::{evm::user::UserOutcomeKind, Bytes20, Bytes32}; use eyre::Result; +use num_enum::IntoPrimitive; use std::sync::Arc; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, IntoPrimitive)] #[repr(u8)] pub enum EvmApiStatus { Success, Failure, -} - -impl From for UserOutcomeKind { - fn from(value: EvmApiStatus) -> Self { - match value { - EvmApiStatus::Success => UserOutcomeKind::Success, - EvmApiStatus::Failure => UserOutcomeKind::Revert, - } - } + OutOfGas, + WriteProtection, } impl From for EvmApiStatus { fn from(value: u8) -> Self { match value { 0 => Self::Success, + 2 => Self::OutOfGas, + 3 => Self::WriteProtection, _ => Self::Failure, } } @@ -34,7 +30,7 @@ impl From for EvmApiStatus { #[repr(u32)] pub enum EvmApiMethod { GetBytes32, - SetBytes32, + SetTrieSlots, ContractCall, DelegateCall, StaticCall, @@ -81,10 +77,13 @@ pub trait EvmApi: Send + 'static { /// Analogous to `vm.SLOAD`. fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64); - /// Stores the given value at the given key in the EVM state trie. - /// Returns the access cost on success. - /// Analogous to `vm.SSTORE`. - fn set_bytes32(&mut self, key: Bytes32, value: Bytes32) -> Result; + /// Stores the given value at the given key in Stylus VM's cache of the EVM state trie. + /// Note that the actual values only get written after calls to `set_trie_slots`. + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64; + + /// Persists any dirty values in the storage cache to the EVM state trie, dropping the cache entirely if requested. + /// Analogous to repeated invocations of `vm.SSTORE`. + fn flush_storage_cache(&mut self, clear: bool, gas_left: u64) -> Result; /// Calls the contract at the given address. /// Returns the EVM return data's length, the gas cost, and whether the call succeeded. @@ -141,7 +140,7 @@ pub trait EvmApi: Send + 'static { ) -> (eyre::Result, u32, u64); /// Returns the EVM return data. - /// Analogous to `vm.RETURNDATASIZE`. + /// Analogous to `vm.RETURNDATA`. fn get_return_data(&self) -> D; /// Emits an EVM log with the given number of topics and data, the first bytes of which should be the topic data. diff --git a/arbitrator/arbutil/src/evm/mod.rs b/arbitrator/arbutil/src/evm/mod.rs index c99e488d3..72d0ec6e9 100644 --- a/arbitrator/arbutil/src/evm/mod.rs +++ b/arbitrator/arbutil/src/evm/mod.rs @@ -1,10 +1,11 @@ -// Copyright 2023, Offchain Labs, Inc. +// Copyright 2023-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE use crate::{Bytes20, Bytes32}; pub mod api; pub mod req; +pub mod storage; pub mod user; // params.SstoreSentryGasEIP2200 @@ -13,9 +14,12 @@ pub const SSTORE_SENTRY_GAS: u64 = 2300; // params.ColdAccountAccessCostEIP2929 pub const COLD_ACCOUNT_GAS: u64 = 2600; -// params.ColdSloadCostEIP2929 +// params.WarmStorageReadCostEIP2929 pub const COLD_SLOAD_GAS: u64 = 2100; +// params.WarmSloadCostEIP2929; +pub const WARM_SLOAD_GAS: u64 = 100; + // params.LogGas and params.LogDataGas pub const LOG_TOPIC_GAS: u64 = 375; pub const LOG_DATA_GAS: u64 = 8; diff --git a/arbitrator/arbutil/src/evm/req.rs b/arbitrator/arbutil/src/evm/req.rs index bafd0eb73..d7cab0071 100644 --- a/arbitrator/arbutil/src/evm/req.rs +++ b/arbitrator/arbutil/src/evm/req.rs @@ -3,12 +3,16 @@ use crate::{ evm::{ - api::{DataReader, EvmApi, EvmApiMethod}, + api::{DataReader, EvmApi, EvmApiMethod, EvmApiStatus}, + storage::{StorageCache, StorageWord}, user::UserOutcomeKind, }, + format::Utf8OrHex, + pricing::EVM_API_INK, Bytes20, Bytes32, }; use eyre::{bail, eyre, Result}; +use std::collections::hash_map::Entry; pub trait RequestHandler: Send + 'static { fn handle_request(&mut self, req_type: EvmApiMethod, req_data: &[u8]) -> (Vec, D, u64); @@ -18,6 +22,7 @@ pub struct EvmApiRequestor> { handler: H, last_code: Option<(Bytes20, D)>, last_return_data: Option, + storage_cache: StorageCache, } impl> EvmApiRequestor { @@ -26,6 +31,7 @@ impl> EvmApiRequestor { handler, last_code: None, last_return_data: None, + storage_cache: StorageCache::default(), } } @@ -93,20 +99,45 @@ impl> EvmApiRequestor { impl> EvmApi for EvmApiRequestor { fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64) { - let (res, _, cost) = self.handle_request(EvmApiMethod::GetBytes32, key.as_slice()); - (res.try_into().unwrap(), cost) + let cache = &mut self.storage_cache; + let mut cost = cache.read_gas(); + + let value = cache.entry(key).or_insert_with(|| { + let (res, _, gas) = self + .handler + .handle_request(EvmApiMethod::GetBytes32, key.as_slice()); + cost = cost.saturating_add(gas).saturating_add(EVM_API_INK); + StorageWord::known(res.try_into().unwrap()) + }); + (value.value, cost) } - fn set_bytes32(&mut self, key: Bytes32, value: Bytes32) -> Result { - let mut request = Vec::with_capacity(64); - request.extend(key); - request.extend(value); - let (res, _, cost) = self.handle_request(EvmApiMethod::SetBytes32, &request); - if res.len() != 1 { - bail!("bad response from set_bytes32") + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64 { + match self.storage_cache.entry(key) { + Entry::Occupied(mut key) => key.get_mut().value = value, + Entry::Vacant(slot) => drop(slot.insert(StorageWord::unknown(value))), + }; + self.storage_cache.write_gas() + } + + fn flush_storage_cache(&mut self, clear: bool, gas_left: u64) -> Result { + let mut data = Vec::with_capacity(64 * self.storage_cache.len() + 8); + data.extend(gas_left.to_be_bytes()); + + for (key, value) in &mut self.storage_cache.slots { + if value.dirty() { + data.extend(*key); + data.extend(*value.value); + value.known = Some(value.value); + } } - if res[0] != 1 { - bail!("write protected") + if clear { + self.storage_cache.clear(); + } + + let (res, _, cost) = self.handle_request(EvmApiMethod::SetTrieSlots, &data); + if res[0] != EvmApiStatus::Success.into() { + bail!("{}", String::from_utf8_or_hex(res)); } Ok(cost) } @@ -175,6 +206,7 @@ impl> EvmApi for EvmApiRequestor { } fn emit_log(&mut self, data: Vec, topics: u32) -> Result<()> { + // TODO: remove copy let mut request = Vec::with_capacity(4 + data.len()); request.extend(topics.to_be_bytes()); request.extend(data); diff --git a/arbitrator/arbutil/src/evm/storage.rs b/arbitrator/arbutil/src/evm/storage.rs new file mode 100644 index 000000000..7fc4a6911 --- /dev/null +++ b/arbitrator/arbutil/src/evm/storage.rs @@ -0,0 +1,64 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use crate::Bytes32; +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, +}; + +/// Represents the EVM word at a given key. +pub struct StorageWord { + /// The current value of the slot. + pub value: Bytes32, + /// The value in Geth, if known. + pub known: Option, +} + +impl StorageWord { + pub fn known(value: Bytes32) -> Self { + let known = Some(value); + Self { value, known } + } + + pub fn unknown(value: Bytes32) -> Self { + Self { value, known: None } + } + + pub fn dirty(&self) -> bool { + Some(self.value) != self.known + } +} + +#[derive(Default)] +pub struct StorageCache { + pub(crate) slots: HashMap, +} + +impl StorageCache { + pub const REQUIRED_ACCESS_GAS: u64 = crate::evm::COLD_SLOAD_GAS; + + pub fn read_gas(&self) -> u64 { + //self.slots.len().ilog2() as u64 + self.slots.len() as u64 + } + + pub fn write_gas(&self) -> u64 { + //self.slots.len().ilog2() as u64 + self.slots.len() as u64 + } +} + +impl Deref for StorageCache { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.slots + } +} + +impl DerefMut for StorageCache { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.slots + } +} diff --git a/arbitrator/arbutil/src/format.rs b/arbitrator/arbutil/src/format.rs index 069421256..99e8b31b5 100644 --- a/arbitrator/arbutil/src/format.rs +++ b/arbitrator/arbutil/src/format.rs @@ -50,3 +50,16 @@ impl DebugBytes for T { format!("{:?}", self).as_bytes().to_vec() } } + +pub trait Utf8OrHex { + fn from_utf8_or_hex(data: impl Into>) -> String; +} + +impl Utf8OrHex for String { + fn from_utf8_or_hex(data: impl Into>) -> String { + match String::from_utf8(data.into()) { + Ok(string) => string, + Err(error) => hex::encode(error.as_bytes()), + } + } +} diff --git a/arbitrator/stylus/cbindgen.toml b/arbitrator/stylus/cbindgen.toml index b9afbe840..466972da7 100644 --- a/arbitrator/stylus/cbindgen.toml +++ b/arbitrator/stylus/cbindgen.toml @@ -10,4 +10,4 @@ extra_bindings = ["arbutil", "prover"] prefix_with_name = true [export] -include = ["EvmApiMethod"] +include = ["EvmApiMethod", "EvmApiStatus"] diff --git a/arbitrator/stylus/src/env.rs b/arbitrator/stylus/src/env.rs index edf8cfb55..69d542070 100644 --- a/arbitrator/stylus/src/env.rs +++ b/arbitrator/stylus/src/env.rs @@ -1,4 +1,4 @@ -// Copyright 2022-2023, Offchain Labs, Inc. +// Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE use arbutil::{ diff --git a/arbitrator/stylus/src/evm_api.rs b/arbitrator/stylus/src/evm_api.rs index 752410d32..fdcc5b16a 100644 --- a/arbitrator/stylus/src/evm_api.rs +++ b/arbitrator/stylus/src/evm_api.rs @@ -3,7 +3,7 @@ use crate::{GoSliceData, RustSlice}; use arbutil::evm::{ - api::{EvmApiMethod, EvmApiStatus, EVM_API_METHOD_REQ_OFFSET}, + api::{EvmApiMethod, EVM_API_METHOD_REQ_OFFSET}, req::RequestHandler, }; @@ -16,7 +16,7 @@ pub struct NativeRequestHandler { gas_cost: *mut u64, result: *mut GoSliceData, raw_data: *mut GoSliceData, - ) -> EvmApiStatus, + ), pub id: usize, } @@ -35,7 +35,7 @@ impl RequestHandler for NativeRequestHandler { let mut result = GoSliceData::null(); let mut raw_data = GoSliceData::null(); let mut cost = 0; - let status = unsafe { + unsafe { (self.handle_request_fptr)( self.id, req_type as u32 + EVM_API_METHOD_REQ_OFFSET, @@ -45,7 +45,6 @@ impl RequestHandler for NativeRequestHandler { ptr!(raw_data), ) }; - assert_eq!(status, EvmApiStatus::Success); (result.slice().to_vec(), raw_data, cost) } } diff --git a/arbitrator/stylus/src/host.rs b/arbitrator/stylus/src/host.rs index 6bf4a9045..130b84a51 100644 --- a/arbitrator/stylus/src/host.rs +++ b/arbitrator/stylus/src/host.rs @@ -126,12 +126,19 @@ pub(crate) fn storage_load_bytes32>( hostio!(env, storage_load_bytes32(key, dest)) } -pub(crate) fn storage_store_bytes32>( +pub(crate) fn storage_cache_bytes32>( mut env: WasmEnvMut, key: GuestPtr, value: GuestPtr, ) -> MaybeEscape { - hostio!(env, storage_store_bytes32(key, value)) + hostio!(env, storage_cache_bytes32(key, value)) +} + +pub(crate) fn storage_flush_cache>( + mut env: WasmEnvMut, + clear: u32, +) -> MaybeEscape { + hostio!(env, storage_flush_cache(clear != 0)) } pub(crate) fn call_contract>( diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index c2def7b0a..1b14763c3 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -130,7 +130,8 @@ impl> NativeInstance { "write_result" => func!(host::write_result), "exit_early" => func!(host::exit_early), "storage_load_bytes32" => func!(host::storage_load_bytes32), - "storage_store_bytes32" => func!(host::storage_store_bytes32), + "storage_cache_bytes32" => func!(host::storage_cache_bytes32), + "storage_flush_cache" => func!(host::storage_flush_cache), "call_contract" => func!(host::call_contract), "delegate_call_contract" => func!(host::delegate_call_contract), "static_call_contract" => func!(host::static_call_contract), @@ -339,7 +340,8 @@ pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { "write_result" => stub!(|_: u32, _: u32|), "exit_early" => stub!(|_: u32|), "storage_load_bytes32" => stub!(|_: u32, _: u32|), - "storage_store_bytes32" => stub!(|_: u32, _: u32|), + "storage_cache_bytes32" => stub!(|_: u32, _: u32|), + "storage_flush_cache" => stub!(|_: u32|), "call_contract" => stub!(u8 <- |_: u32, _: u32, _: u32, _: u32, _: u64, _: u32|), "delegate_call_contract" => stub!(u8 <- |_: u32, _: u32, _: u32, _: u64, _: u32|), "static_call_contract" => stub!(u8 <- |_: u32, _: u32, _: u32, _: u64, _: u32|), diff --git a/arbitrator/stylus/src/test/api.rs b/arbitrator/stylus/src/test/api.rs index 1c418ab65..798fee79d 100644 --- a/arbitrator/stylus/src/test/api.rs +++ b/arbitrator/stylus/src/test/api.rs @@ -74,11 +74,17 @@ impl EvmApi for TestEvmApi { (value, 2100) // pretend worst case } - fn set_bytes32(&mut self, key: Bytes32, value: Bytes32) -> Result { + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64 { let storage = &mut self.storage.lock(); let storage = storage.get_mut(&self.program).unwrap(); storage.insert(key, value); - Ok(22100) // pretend worst case + 0 + } + + fn flush_storage_cache(&mut self, _clear: bool, _gas_left: u64) -> Result { + let storage = &mut self.storage.lock(); + let storage = storage.get_mut(&self.program).unwrap(); + Ok(22100 * storage.len() as u64) // pretend worst case } /// Simulates a contract call. diff --git a/arbitrator/wasm-libraries/Cargo.lock b/arbitrator/wasm-libraries/Cargo.lock index 67beb7c93..c54553da3 100644 --- a/arbitrator/wasm-libraries/Cargo.lock +++ b/arbitrator/wasm-libraries/Cargo.lock @@ -1254,6 +1254,7 @@ dependencies = [ "lazy_static", "parking_lot", "prover", + "user-host-trait", ] [[package]] diff --git a/arbitrator/wasm-libraries/forward/src/main.rs b/arbitrator/wasm-libraries/forward/src/main.rs index 7f6f24699..632054bcb 100644 --- a/arbitrator/wasm-libraries/forward/src/main.rs +++ b/arbitrator/wasm-libraries/forward/src/main.rs @@ -6,12 +6,13 @@ use std::{fs::File, io::Write, path::PathBuf}; use structopt::StructOpt; /// order matters! -const HOSTIOS: [[&str; 3]; 34] = [ +const HOSTIOS: [[&str; 3]; 35] = [ ["read_args", "i32", ""], ["write_result", "i32 i32", ""], ["exit_early", "i32", ""], ["storage_load_bytes32", "i32 i32", ""], - ["storage_store_bytes32", "i32 i32", ""], + ["storage_cache_bytes32", "i32 i32", ""], + ["storage_flush_cache", "i32", ""], ["call_contract", "i32 i32 i32 i32 i64 i32", "i32"], ["delegate_call_contract", "i32 i32 i32 i64 i32", "i32"], ["static_call_contract", "i32 i32 i32 i64 i32", "i32"], diff --git a/arbitrator/wasm-libraries/user-host-trait/src/lib.rs b/arbitrator/wasm-libraries/user-host-trait/src/lib.rs index 74f8d2924..c9e1e049b 100644 --- a/arbitrator/wasm-libraries/user-host-trait/src/lib.rs +++ b/arbitrator/wasm-libraries/user-host-trait/src/lib.rs @@ -6,6 +6,7 @@ use arbutil::{ evm::{ self, api::{DataReader, EvmApi}, + storage::StorageCache, user::UserOutcomeKind, EvmData, }, @@ -132,10 +133,14 @@ pub trait UserHost: GasMeteredMachine { /// value stored in the EVM state trie at offset `key`, which will be `0` when not previously /// set. The semantics, then, are equivalent to that of the EVM's [`SLOAD`] opcode. /// + /// Note: the Stylus VM implements storage caching. This means that repeated calls to the same key + /// will cost less than in the EVM. + /// /// [`SLOAD`]: https://www.evm.codes/#54 fn storage_load_bytes32(&mut self, key: GuestPtr, dest: GuestPtr) -> Result<(), Self::Err> { - self.buy_ink(HOSTIO_INK + 2 * PTR_INK + EVM_API_INK)?; - self.require_gas(evm::COLD_SLOAD_GAS)?; + self.buy_ink(HOSTIO_INK + 2 * PTR_INK)?; + self.require_gas(evm::COLD_SLOAD_GAS + EVM_API_INK + StorageCache::REQUIRED_ACCESS_GAS)?; // cache-miss case + let key = self.read_bytes32(key)?; let (value, gas_cost) = self.evm_api().get_bytes32(key); @@ -144,25 +149,40 @@ pub trait UserHost: GasMeteredMachine { trace!("storage_load_bytes32", self, key, value) } - /// Stores a 32-byte value to permanent storage. Stylus's storage format is identical to that - /// of the EVM. This means that, under the hood, this hostio is storing a 32-byte value into - /// the EVM state trie at offset `key`. Furthermore, refunds are tabulated exactly as in the - /// EVM. The semantics, then, are equivalent to that of the EVM's [`SSTORE`] opcode. + /// Writes a 32-byte value to the permanent storage cache. Stylus's storage format is identical to that + /// of the EVM. This means that, under the hood, this hostio represents storing a 32-byte value into + /// the EVM state trie at offset `key`. Refunds are tabulated exactly as in the EVM. The semantics, then, + /// are equivalent to that of the EVM's [`SSTORE`] opcode. + /// + /// Note: because this value is cached, one must call `storage_flush_cache` to persist the value. /// - /// Note: we require the [`SSTORE`] sentry per EVM rules. The `gas_cost` returned by the EVM API + /// Auditor's note: we require the [`SSTORE`] sentry per EVM rules. The `gas_cost` returned by the EVM API /// may exceed this amount, but that's ok because the predominant cost is due to state bloat concerns. /// /// [`SSTORE`]: https://www.evm.codes/#55 - fn storage_store_bytes32(&mut self, key: GuestPtr, value: GuestPtr) -> Result<(), Self::Err> { - self.buy_ink(HOSTIO_INK + 2 * PTR_INK + EVM_API_INK)?; - self.require_gas(evm::SSTORE_SENTRY_GAS)?; // see operations_acl_arbitrum.go + fn storage_cache_bytes32(&mut self, key: GuestPtr, value: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 2 * PTR_INK)?; + self.require_gas(evm::SSTORE_SENTRY_GAS + StorageCache::REQUIRED_ACCESS_GAS)?; // see operations_acl_arbitrum.go let key = self.read_bytes32(key)?; let value = self.read_bytes32(value)?; - let gas_cost = self.evm_api().set_bytes32(key, value)?; + let gas_cost = self.evm_api().cache_bytes32(key, value); self.buy_gas(gas_cost)?; - trace!("storage_store_bytes32", self, [key, value], &[]) + trace!("storage_cache_bytes32", self, [key, value], &[]) + } + + /// Persists any dirty values in the storage cache to the EVM state trie, dropping the cache entirely if requested. + /// Analogous to repeated invocations of [`SSTORE`]. + /// + /// [`SSTORE`]: https://www.evm.codes/#55 + fn storage_flush_cache(&mut self, clear: bool) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + EVM_API_INK)?; + self.require_gas(evm::SSTORE_SENTRY_GAS)?; // see operations_acl_arbitrum.go + + let gas_left = self.gas_left()?; + self.evm_api().flush_storage_cache(clear, gas_left)?; + trace!("storage_flush_cache", self, [be!(clear as u8)], &[]) } /// Calls the contract at the given address with options for passing value and to limit the diff --git a/arbitrator/wasm-libraries/user-host/src/host.rs b/arbitrator/wasm-libraries/user-host/src/host.rs index 8b4d12240..64320b61a 100644 --- a/arbitrator/wasm-libraries/user-host/src/host.rs +++ b/arbitrator/wasm-libraries/user-host/src/host.rs @@ -49,8 +49,13 @@ pub unsafe extern "C" fn user_host__storage_load_bytes32(key: GuestPtr, dest: Gu } #[no_mangle] -pub unsafe extern "C" fn user_host__storage_store_bytes32(key: GuestPtr, value: GuestPtr) { - hostio!(storage_store_bytes32(key, value)) +pub unsafe extern "C" fn user_host__storage_cache_bytes32(key: GuestPtr, value: GuestPtr) { + hostio!(storage_cache_bytes32(key, value)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__storage_flush_cache(clear: u32) { + hostio!(storage_flush_cache(clear != 0)) } #[no_mangle] diff --git a/arbitrator/wasm-libraries/user-test/Cargo.toml b/arbitrator/wasm-libraries/user-test/Cargo.toml index ee4577d4b..aad9d8ec2 100644 --- a/arbitrator/wasm-libraries/user-test/Cargo.toml +++ b/arbitrator/wasm-libraries/user-test/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["cdylib"] arbutil = { path = "../../arbutil/" } caller-env = { path = "../../caller-env/", features = ["static_caller"] } prover = { path = "../../prover/", default-features = false } +user-host-trait = { path = "../user-host-trait" } eyre = "0.6.5" fnv = "1.0.7" hex = "0.4.3" diff --git a/arbitrator/wasm-libraries/user-test/src/caller_env.rs b/arbitrator/wasm-libraries/user-test/src/caller_env.rs deleted file mode 100644 index 04555d579..000000000 --- a/arbitrator/wasm-libraries/user-test/src/caller_env.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2024, Offchain Labs, Inc. -// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE - -use arbutil::Bytes32; -use caller_env::{static_caller::STATIC_MEM, GuestPtr, MemAccess}; - -pub struct UserMem; - -impl UserMem { - pub fn read_bytes32(ptr: GuestPtr) -> Bytes32 { - unsafe { STATIC_MEM.read_fixed(ptr).into() } - } - - pub fn read_slice(ptr: GuestPtr, len: u32) -> Vec { - unsafe { STATIC_MEM.read_slice(ptr, len as usize) } - } - - pub fn write_slice(ptr: GuestPtr, src: &[u8]) { - unsafe { STATIC_MEM.write_slice(ptr, src) } - } -} diff --git a/arbitrator/wasm-libraries/user-test/src/host.rs b/arbitrator/wasm-libraries/user-test/src/host.rs index d7b4869d5..a4f7912f5 100644 --- a/arbitrator/wasm-libraries/user-test/src/host.rs +++ b/arbitrator/wasm-libraries/user-test/src/host.rs @@ -1,94 +1,232 @@ // Copyright 2022-2024, Offchain Labs, Inc. -// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE +// For license information, see https://github.com/nitro/blob/master/LICENSE -#![allow(clippy::missing_safety_doc)] - -use crate::{caller_env::UserMem, Program, ARGS, EVER_PAGES, KEYS, LOGS, OPEN_PAGES, OUTS}; -use arbutil::{ - crypto, evm, - pricing::{EVM_API_INK, HOSTIO_INK, PTR_INK}, -}; +use crate::program::Program; use caller_env::GuestPtr; -use prover::programs::{ - memory::MemoryModel, - prelude::{GasMeteredMachine, MeteredMachine}, -}; +use user_host_trait::UserHost; + +macro_rules! hostio { + ($($func:tt)*) => { + match Program::current().$($func)* { + Ok(value) => value, + Err(error) => panic!("{error}"), + } + }; +} #[no_mangle] pub unsafe extern "C" fn vm_hooks__read_args(ptr: GuestPtr) { - let mut program = Program::start(0); - program.pay_for_write(ARGS.len() as u32).unwrap(); - UserMem::write_slice(ptr, &ARGS); + hostio!(read_args(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__exit_early(status: u32) { + hostio!(exit_early(status)); } #[no_mangle] pub unsafe extern "C" fn vm_hooks__write_result(ptr: GuestPtr, len: u32) { - let mut program = Program::start(0); - program.pay_for_read(len).unwrap(); - program.pay_for_geth_bytes(len).unwrap(); - OUTS = UserMem::read_slice(ptr, len); + hostio!(write_result(ptr, len)) } #[no_mangle] pub unsafe extern "C" fn vm_hooks__storage_load_bytes32(key: GuestPtr, dest: GuestPtr) { - let mut program = Program::start(2 * PTR_INK + EVM_API_INK); - let key = UserMem::read_bytes32(key); + hostio!(storage_load_bytes32(key, dest)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__storage_cache_bytes32(key: GuestPtr, value: GuestPtr) { + hostio!(storage_cache_bytes32(key, value)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__storage_flush_cache(clear: u32) { + hostio!(storage_flush_cache(clear != 0)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__call_contract( + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + value: GuestPtr, + gas: u64, + ret_len: GuestPtr, +) -> u8 { + hostio!(call_contract(contract, data, data_len, value, gas, ret_len)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__delegate_call_contract( + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + gas: u64, + ret_len: GuestPtr, +) -> u8 { + hostio!(delegate_call_contract( + contract, data, data_len, gas, ret_len + )) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__static_call_contract( + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + gas: u64, + ret_len: GuestPtr, +) -> u8 { + hostio!(static_call_contract(contract, data, data_len, gas, ret_len)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__create1( + code: GuestPtr, + code_len: u32, + value: GuestPtr, + contract: GuestPtr, + revert_len: GuestPtr, +) { + hostio!(create1(code, code_len, value, contract, revert_len)) +} - let value = KEYS.lock().get(&key).cloned().unwrap_or_default(); - program.buy_gas(2100).unwrap(); // pretend it was cold - UserMem::write_slice(dest, &value.0); +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__create2( + code: GuestPtr, + code_len: u32, + value: GuestPtr, + salt: GuestPtr, + contract: GuestPtr, + revert_len: GuestPtr, +) { + hostio!(create2(code, code_len, value, salt, contract, revert_len)) } #[no_mangle] -pub unsafe extern "C" fn vm_hooks__storage_store_bytes32(key: GuestPtr, value: GuestPtr) { - let mut program = Program::start(2 * PTR_INK + EVM_API_INK); - program.require_gas(evm::SSTORE_SENTRY_GAS).unwrap(); - program.buy_gas(22100).unwrap(); // pretend the worst case +pub unsafe extern "C" fn vm_hooks__read_return_data( + dest: GuestPtr, + offset: u32, + size: u32, +) -> u32 { + hostio!(read_return_data(dest, offset, size)) +} - let key = UserMem::read_bytes32(key); - let value = UserMem::read_bytes32(value); - KEYS.lock().insert(key, value); +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__return_data_size() -> u32 { + hostio!(return_data_size()) } #[no_mangle] pub unsafe extern "C" fn vm_hooks__emit_log(data: GuestPtr, len: u32, topics: u32) { - let mut program = Program::start(EVM_API_INK); - if topics > 4 || len < topics * 32 { - panic!("bad topic data"); - } - program.pay_for_read(len).unwrap(); - program.pay_for_evm_log(topics, len - topics * 32).unwrap(); + hostio!(emit_log(data, len, topics)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__account_balance(address: GuestPtr, ptr: GuestPtr) { + hostio!(account_balance(address, ptr)) +} +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__account_code( + address: GuestPtr, + offset: u32, + size: u32, + dest: GuestPtr, +) -> u32 { + hostio!(account_code(address, offset, size, dest)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__account_code_size(address: GuestPtr) -> u32 { + hostio!(account_code_size(address)) +} - let data = UserMem::read_slice(data, len); - LOGS.push(data) +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__account_codehash(address: GuestPtr, ptr: GuestPtr) { + hostio!(account_codehash(address, ptr)) } #[no_mangle] -pub unsafe extern "C" fn vm_hooks__pay_for_memory_grow(pages: u16) { - let mut program = Program::start_free(); - if pages == 0 { - return program.buy_ink(HOSTIO_INK).unwrap(); - } - let model = MemoryModel::new(2, 1000); +pub unsafe extern "C" fn vm_hooks__block_basefee(ptr: GuestPtr) { + hostio!(block_basefee(ptr)) +} - let (open, ever) = (OPEN_PAGES, EVER_PAGES); - OPEN_PAGES = OPEN_PAGES.saturating_add(pages); - EVER_PAGES = EVER_PAGES.max(OPEN_PAGES); - program.buy_gas(model.gas_cost(pages, open, ever)).unwrap(); +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__block_coinbase(ptr: GuestPtr) { + hostio!(block_coinbase(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__block_gas_limit() -> u64 { + hostio!(block_gas_limit()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__block_number() -> u64 { + hostio!(block_number()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__block_timestamp() -> u64 { + hostio!(block_timestamp()) } #[no_mangle] -pub unsafe extern "C" fn vm_hooks__native_keccak256(bytes: GuestPtr, len: u32, output: GuestPtr) { - let mut program = Program::start(0); - program.pay_for_keccak(len).unwrap(); +pub unsafe extern "C" fn vm_hooks__chainid() -> u64 { + hostio!(chainid()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__contract_address(ptr: GuestPtr) { + hostio!(contract_address(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__evm_gas_left() -> u64 { + hostio!(evm_gas_left()) +} - let preimage = UserMem::read_slice(bytes, len); - let digest = crypto::keccak(preimage); - UserMem::write_slice(output, &digest); +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__evm_ink_left() -> u64 { + hostio!(evm_ink_left()) } #[no_mangle] pub unsafe extern "C" fn vm_hooks__msg_reentrant() -> u32 { - let _ = Program::start(0); - 0 + hostio!(msg_reentrant()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__msg_sender(ptr: GuestPtr) { + hostio!(msg_sender(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__msg_value(ptr: GuestPtr) { + hostio!(msg_value(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__native_keccak256(input: GuestPtr, len: u32, output: GuestPtr) { + hostio!(native_keccak256(input, len, output)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__tx_gas_price(ptr: GuestPtr) { + hostio!(tx_gas_price(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__tx_ink_price() -> u32 { + hostio!(tx_ink_price()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__tx_origin(ptr: GuestPtr) { + hostio!(tx_origin(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__pay_for_memory_grow(pages: u16) { + hostio!(pay_for_memory_grow(pages)) } diff --git a/arbitrator/wasm-libraries/user-test/src/ink.rs b/arbitrator/wasm-libraries/user-test/src/ink.rs index ab9a5045f..fca658e59 100644 --- a/arbitrator/wasm-libraries/user-test/src/ink.rs +++ b/arbitrator/wasm-libraries/user-test/src/ink.rs @@ -1,14 +1,12 @@ -// Copyright 2022-2023, Offchain Labs, Inc. +// Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE -use arbutil::pricing; +use crate::{program::Program, CONFIG}; use prover::programs::{ config::PricingParams, prelude::{GasMeteredMachine, MachineMeter, MeteredMachine}, }; -use crate::{Program, CONFIG}; - #[link(wasm_import_module = "hostio")] extern "C" { fn user_ink_left() -> u64; @@ -38,15 +36,3 @@ impl GasMeteredMachine for Program { unsafe { CONFIG.unwrap().pricing } } } - -impl Program { - pub fn start(cost: u64) -> Self { - let mut program = Self::start_free(); - program.buy_ink(pricing::HOSTIO_INK + cost).unwrap(); - program - } - - pub fn start_free() -> Self { - Self - } -} diff --git a/arbitrator/wasm-libraries/user-test/src/lib.rs b/arbitrator/wasm-libraries/user-test/src/lib.rs index 21464d658..7fd771cf3 100644 --- a/arbitrator/wasm-libraries/user-test/src/lib.rs +++ b/arbitrator/wasm-libraries/user-test/src/lib.rs @@ -3,15 +3,15 @@ #![allow(clippy::missing_safety_doc)] -use arbutil::Bytes32; +use arbutil::{Bytes32, evm::EvmData}; use fnv::FnvHashMap as HashMap; use lazy_static::lazy_static; use parking_lot::Mutex; use prover::programs::prelude::StylusConfig; -mod caller_env; pub mod host; mod ink; +mod program; pub(crate) static mut ARGS: Vec = vec![]; pub(crate) static mut OUTS: Vec = vec![]; @@ -22,11 +22,9 @@ pub(crate) static mut EVER_PAGES: u16 = 0; lazy_static! { static ref KEYS: Mutex> = Mutex::new(HashMap::default()); + static ref EVM_DATA: EvmData = EvmData::default(); } -/// Mock type representing a `user_host::Program` -pub struct Program; - #[no_mangle] pub unsafe extern "C" fn user_test__prepare( len: usize, diff --git a/arbitrator/wasm-libraries/user-test/src/program.rs b/arbitrator/wasm-libraries/user-test/src/program.rs new file mode 100644 index 000000000..592317719 --- /dev/null +++ b/arbitrator/wasm-libraries/user-test/src/program.rs @@ -0,0 +1,209 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{ARGS, EVER_PAGES, KEYS, LOGS, OPEN_PAGES, OUTS, EVM_DATA}; +use arbutil::{ + evm::{ + api::{EvmApi, VecReader}, + user::UserOutcomeKind, + EvmData, + }, + Bytes20, Bytes32, Color, +}; +use caller_env::{static_caller::STATIC_MEM, GuestPtr, MemAccess}; +use eyre::{eyre, Result}; +use prover::programs::memory::MemoryModel; +use std::fmt::Display; +use user_host_trait::UserHost; + +/// Signifies an out-of-bounds memory access was requested. +pub struct MemoryBoundsError; + +impl From for eyre::ErrReport { + fn from(_: MemoryBoundsError) -> Self { + eyre!("memory access out of bounds") + } +} + +/// Mock type representing a `user_host::Program` +pub struct Program { + evm_api: MockEvmApi, +} + +#[allow(clippy::unit_arg)] +impl UserHost for Program { + type Err = eyre::ErrReport; + type MemoryErr = MemoryBoundsError; + type A = MockEvmApi; + + fn args(&self) -> &[u8] { + unsafe { &ARGS } + } + + fn outs(&mut self) -> &mut Vec { + unsafe { &mut OUTS } + } + + fn evm_api(&mut self) -> &mut Self::A { + &mut self.evm_api + } + + fn evm_data(&self) -> &EvmData { + &EVM_DATA + } + + fn evm_return_data_len(&mut self) -> &mut u32 { + unimplemented!() + } + + fn read_slice(&self, ptr: GuestPtr, len: u32) -> Result, MemoryBoundsError> { + self.check_memory_access(ptr, len)?; + unsafe { Ok(STATIC_MEM.read_slice(ptr, len as usize)) } + } + + fn read_fixed(&self, ptr: GuestPtr) -> Result<[u8; N], MemoryBoundsError> { + self.read_slice(ptr, N as u32) + .map(|x| x.try_into().unwrap()) + } + + fn write_u32(&mut self, ptr: GuestPtr, x: u32) -> Result<(), MemoryBoundsError> { + self.check_memory_access(ptr, 4)?; + unsafe { Ok(STATIC_MEM.write_u32(ptr, x)) } + } + + fn write_slice(&self, ptr: GuestPtr, src: &[u8]) -> Result<(), MemoryBoundsError> { + self.check_memory_access(ptr, src.len() as u32)?; + unsafe { Ok(STATIC_MEM.write_slice(ptr, src)) } + } + + fn say(&self, text: D) { + println!("{} {text}", "Stylus says:".yellow()); + } + + fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], _end_ink: u64) { + let args = hex::encode(args); + let outs = hex::encode(outs); + println!("Error: unexpected hostio tracing info for {name} while proving: {args}, {outs}"); + } +} + +impl Program { + pub fn current() -> Self { + Self { + evm_api: MockEvmApi, + } + } + + fn check_memory_access(&self, _ptr: GuestPtr, _bytes: u32) -> Result<(), MemoryBoundsError> { + Ok(()) // pretend we did a check + } +} + +pub struct MockEvmApi; + +impl EvmApi for MockEvmApi { + fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64) { + let value = KEYS.lock().get(&key).cloned().unwrap_or_default(); + (value, 2100) // pretend worst case + } + + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64 { + KEYS.lock().insert(key, value); + 0 + } + + fn flush_storage_cache(&mut self, _clear: bool, _gas_left: u64) -> Result { + Ok(22100 * KEYS.lock().len() as u64) // pretend worst case + } + + /// Simulates a contract call. + /// Note: this call function is for testing purposes only and deviates from onchain behavior. + fn contract_call( + &mut self, + _contract: Bytes20, + _calldata: &[u8], + _gas: u64, + _value: Bytes32, + ) -> (u32, u64, UserOutcomeKind) { + unimplemented!() + } + + fn delegate_call( + &mut self, + _contract: Bytes20, + _calldata: &[u8], + _gas: u64, + ) -> (u32, u64, UserOutcomeKind) { + unimplemented!() + } + + fn static_call( + &mut self, + _contract: Bytes20, + _calldata: &[u8], + _gas: u64, + ) -> (u32, u64, UserOutcomeKind) { + unimplemented!() + } + + fn create1( + &mut self, + _code: Vec, + _endowment: Bytes32, + _gas: u64, + ) -> (Result, u32, u64) { + unimplemented!() + } + + fn create2( + &mut self, + _code: Vec, + _endowment: Bytes32, + _salt: Bytes32, + _gas: u64, + ) -> (Result, u32, u64) { + unimplemented!() + } + + fn get_return_data(&self) -> VecReader { + unimplemented!() + } + + fn emit_log(&mut self, data: Vec, _topics: u32) -> Result<()> { + unsafe { LOGS.push(data) }; + Ok(()) // pretend a log was emitted + } + + fn account_balance(&mut self, _address: Bytes20) -> (Bytes32, u64) { + unimplemented!() + } + + fn account_code(&mut self, _address: Bytes20, _gas_left: u64) -> (VecReader, u64) { + unimplemented!() + } + + fn account_codehash(&mut self, _address: Bytes20) -> (Bytes32, u64) { + unimplemented!() + } + + fn add_pages(&mut self, pages: u16) -> u64 { + let model = MemoryModel::new(2, 1000); + unsafe { + let (open, ever) = (OPEN_PAGES, EVER_PAGES); + OPEN_PAGES = OPEN_PAGES.saturating_add(pages); + EVER_PAGES = EVER_PAGES.max(OPEN_PAGES); + model.gas_cost(pages, open, ever) + } + } + + fn capture_hostio( + &mut self, + _name: &str, + _args: &[u8], + _outs: &[u8], + _start_ink: u64, + _end_ink: u64, + ) { + unimplemented!() + } +} diff --git a/arbos/programs/api.go b/arbos/programs/api.go index 9369cc626..73c1915da 100644 --- a/arbos/programs/api.go +++ b/arbos/programs/api.go @@ -24,7 +24,7 @@ type RequestType int const ( GetBytes32 RequestType = iota - SetBytes32 + SetTrieSlots ContractCall DelegateCall StaticCall @@ -38,6 +38,19 @@ const ( CaptureHostIO ) +type apiStatus uint8 + +const ( + Success apiStatus = iota + Failure + OutOfGas + WriteProtection +) + +func (s apiStatus) to_slice() []byte { + return []byte{uint8(s)} +} + const EvmApiMethodReqOffset = 0x10000000 func newApiClosures( @@ -60,16 +73,28 @@ func newApiClosures( cost := vm.WasmStateLoadCost(db, actingAddress, key) return db.GetState(actingAddress, key), cost } - setBytes32 := func(key, value common.Hash) (uint64, error) { - if tracingInfo != nil { - tracingInfo.RecordStorageSet(key, value) - } - if readOnly { - return 0, vm.ErrWriteProtection + setTrieSlots := func(data []byte, gasLeft *uint64) apiStatus { + for len(data) > 0 { + key := common.BytesToHash(data[:32]) + value := common.BytesToHash(data[32:64]) + data = data[64:] + + if tracingInfo != nil { + tracingInfo.RecordStorageSet(key, value) + } + if readOnly { + return WriteProtection + } + + cost := vm.WasmStateStoreCost(db, actingAddress, key, value) + if cost > *gasLeft { + *gasLeft = 0 + return OutOfGas + } + *gasLeft -= cost + db.SetState(actingAddress, key, value) } - cost := vm.WasmStateStoreCost(db, actingAddress, key, value) - db.SetState(actingAddress, key, value) - return cost, nil + return Success } doCall := func( contract common.Address, opcode vm.OpCode, input []byte, gas uint64, value *big.Int, @@ -286,14 +311,11 @@ func newApiClosures( key := takeHash() out, cost := getBytes32(key) return out[:], nil, cost - case SetBytes32: - key := takeHash() - value := takeHash() - cost, err := setBytes32(key, value) - if err != nil { - return []byte{0}, nil, 0 - } - return []byte{1}, nil, cost + case SetTrieSlots: + gasLeft := takeU64() + gas := gasLeft + status := setTrieSlots(takeRest(), &gas) + return status.to_slice(), nil, gasLeft - gas case ContractCall, DelegateCall, StaticCall: var opcode vm.OpCode switch req { diff --git a/arbos/programs/native.go b/arbos/programs/native.go index 198e3cb80..a41606366 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -135,13 +135,8 @@ func callProgram( return data, err } -type apiStatus = C.EvmApiStatus - -const apiSuccess C.EvmApiStatus = C.EvmApiStatus_Success -const apiFailure C.EvmApiStatus = C.EvmApiStatus_Failure - //export handleReqImpl -func handleReqImpl(apiId usize, req_type u32, data *rustSlice, costPtr *u64, out_response *C.GoSliceData, out_raw_data *C.GoSliceData) apiStatus { +func handleReqImpl(apiId usize, req_type u32, data *rustSlice, costPtr *u64, out_response *C.GoSliceData, out_raw_data *C.GoSliceData) { api := getApi(apiId) reqData := data.read() reqType := RequestType(req_type - EvmApiMethodReqOffset) @@ -149,7 +144,6 @@ func handleReqImpl(apiId usize, req_type u32, data *rustSlice, costPtr *u64, out *costPtr = u64(cost) api.pinAndRef(response, out_response) api.pinAndRef(raw_data, out_raw_data) - return apiSuccess } func (value bytes32) toHash() common.Hash { diff --git a/arbos/programs/native_api.go b/arbos/programs/native_api.go index e66cf07fc..136f74c96 100644 --- a/arbos/programs/native_api.go +++ b/arbos/programs/native_api.go @@ -16,8 +16,8 @@ typedef uint32_t u32; typedef uint64_t u64; typedef size_t usize; -EvmApiStatus handleReqImpl(usize api, u32 req_type, RustSlice *data, u64 *out_cost, GoSliceData *out_result, GoSliceData *out_raw_data); -EvmApiStatus handleReqWrap(usize api, u32 req_type, RustSlice *data, u64 *out_cost, GoSliceData *out_result, GoSliceData *out_raw_data) { +void handleReqImpl(usize api, u32 req_type, RustSlice *data, u64 *out_cost, GoSliceData *out_result, GoSliceData *out_raw_data); +void handleReqWrap(usize api, u32 req_type, RustSlice *data, u64 *out_cost, GoSliceData *out_result, GoSliceData *out_raw_data) { return handleReqImpl(api, req_type, data, out_cost, out_result, out_raw_data); } */ diff --git a/arbos/programs/testconstants.go b/arbos/programs/testconstants.go index 04f40395d..f37ccb4b0 100644 --- a/arbos/programs/testconstants.go +++ b/arbos/programs/testconstants.go @@ -25,7 +25,7 @@ func testConstants() error { if err := errIfNotEq(1, GetBytes32, C.EvmApiMethod_GetBytes32); err != nil { return err } - if err := errIfNotEq(2, SetBytes32, C.EvmApiMethod_SetBytes32); err != nil { + if err := errIfNotEq(2, SetTrieSlots, C.EvmApiMethod_SetTrieSlots); err != nil { return err } if err := errIfNotEq(3, ContractCall, C.EvmApiMethod_ContractCall); err != nil { @@ -61,5 +61,29 @@ func testConstants() error { if err := errIfNotEq(14, CaptureHostIO, C.EvmApiMethod_CaptureHostIO); err != nil { return err } - return errIfNotEq(15, EvmApiMethodReqOffset, C.EVM_API_METHOD_REQ_OFFSET) + if err := errIfNotEq(15, EvmApiMethodReqOffset, C.EVM_API_METHOD_REQ_OFFSET); err != nil { + return err + } + + assertEq := func(index int, a apiStatus, b uint32) error { + if uint32(a) != b { + return fmt.Errorf("constant test %d failed! %d != %d", index, a, b) + } + return nil + } + + if err := assertEq(0, Success, C.EvmApiStatus_Success); err != nil { + return err + } + if err := assertEq(1, Failure, C.EvmApiStatus_Failure); err != nil { + return err + } + if err := assertEq(2, OutOfGas, C.EvmApiStatus_OutOfGas); err != nil { + return err + } + if err := assertEq(3, WriteProtection, C.EvmApiStatus_WriteProtection); err != nil { + return err + } + + return nil } From cc2728fe2ae1292a1ede31ba965609f34a2b0d10 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Wed, 13 Mar 2024 15:54:54 -0600 Subject: [PATCH 2/5] cleanup --- arbitrator/arbutil/src/evm/mod.rs | 4 +-- arbitrator/arbutil/src/evm/req.rs | 32 +++++++++---------- arbitrator/arbutil/src/evm/storage.rs | 1 + arbitrator/jit/src/stylus_backend.rs | 6 ++-- arbitrator/langs/c | 2 +- arbitrator/langs/rust | 2 +- arbitrator/stylus/src/evm_api.rs | 6 ++-- .../wasm-libraries/user-host/src/program.rs | 6 ++-- arbos/programs/api.go | 1 + arbos/programs/testconstants.go | 1 - 10 files changed, 31 insertions(+), 30 deletions(-) diff --git a/arbitrator/arbutil/src/evm/mod.rs b/arbitrator/arbutil/src/evm/mod.rs index 72d0ec6e9..86ce2accc 100644 --- a/arbitrator/arbutil/src/evm/mod.rs +++ b/arbitrator/arbutil/src/evm/mod.rs @@ -14,10 +14,10 @@ pub const SSTORE_SENTRY_GAS: u64 = 2300; // params.ColdAccountAccessCostEIP2929 pub const COLD_ACCOUNT_GAS: u64 = 2600; -// params.WarmStorageReadCostEIP2929 +// params.ColdSloadCostEIP2929; pub const COLD_SLOAD_GAS: u64 = 2100; -// params.WarmSloadCostEIP2929; +// params.WarmStorageReadCostEIP2929 pub const WARM_SLOAD_GAS: u64 = 100; // params.LogGas and params.LogDataGas diff --git a/arbitrator/arbutil/src/evm/req.rs b/arbitrator/arbutil/src/evm/req.rs index d7cab0071..0243c408e 100644 --- a/arbitrator/arbutil/src/evm/req.rs +++ b/arbitrator/arbutil/src/evm/req.rs @@ -15,7 +15,7 @@ use eyre::{bail, eyre, Result}; use std::collections::hash_map::Entry; pub trait RequestHandler: Send + 'static { - fn handle_request(&mut self, req_type: EvmApiMethod, req_data: &[u8]) -> (Vec, D, u64); + fn request(&mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>) -> (Vec, D, u64); } pub struct EvmApiRequestor> { @@ -35,8 +35,8 @@ impl> EvmApiRequestor { } } - fn handle_request(&mut self, req_type: EvmApiMethod, req_data: &[u8]) -> (Vec, D, u64) { - self.handler.handle_request(req_type, req_data) + fn request(&mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>) -> (Vec, D, u64) { + self.handler.request(req_type, req_data) } /// Call out to a contract. @@ -54,7 +54,7 @@ impl> EvmApiRequestor { request.extend(gas.to_be_bytes()); request.extend(input); - let (res, data, cost) = self.handle_request(call_type, &request); + let (res, data, cost) = self.request(call_type, &request); let status: UserOutcomeKind = res[0].try_into().expect("unknown outcome"); let data_len = data.slice().len() as u32; self.last_return_data = Some(data); @@ -81,7 +81,7 @@ impl> EvmApiRequestor { } request.extend(code); - let (mut res, data, cost) = self.handle_request(create_type, &request); + let (mut res, data, cost) = self.request(create_type, request); if res.len() != 21 || res[0] == 0 { if !res.is_empty() { res.remove(0); @@ -103,9 +103,7 @@ impl> EvmApi for EvmApiRequestor { let mut cost = cache.read_gas(); let value = cache.entry(key).or_insert_with(|| { - let (res, _, gas) = self - .handler - .handle_request(EvmApiMethod::GetBytes32, key.as_slice()); + let (res, _, gas) = self.handler.request(EvmApiMethod::GetBytes32, key); cost = cost.saturating_add(gas).saturating_add(EVM_API_INK); StorageWord::known(res.try_into().unwrap()) }); @@ -134,8 +132,11 @@ impl> EvmApi for EvmApiRequestor { if clear { self.storage_cache.clear(); } + if data.len() == 8 { + return Ok(0); // no need to make request + } - let (res, _, cost) = self.handle_request(EvmApiMethod::SetTrieSlots, &data); + let (res, _, cost) = self.request(EvmApiMethod::SetTrieSlots, data); if res[0] != EvmApiStatus::Success.into() { bail!("{}", String::from_utf8_or_hex(res)); } @@ -211,7 +212,7 @@ impl> EvmApi for EvmApiRequestor { request.extend(topics.to_be_bytes()); request.extend(data); - let (res, _, _) = self.handle_request(EvmApiMethod::EmitLog, &request); + let (res, _, _) = self.request(EvmApiMethod::EmitLog, request); if !res.is_empty() { bail!(String::from_utf8(res).unwrap_or("malformed emit-log response".into())) } @@ -219,7 +220,7 @@ impl> EvmApi for EvmApiRequestor { } fn account_balance(&mut self, address: Bytes20) -> (Bytes32, u64) { - let (res, _, cost) = self.handle_request(EvmApiMethod::AccountBalance, address.as_slice()); + let (res, _, cost) = self.request(EvmApiMethod::AccountBalance, address); (res.try_into().unwrap(), cost) } @@ -233,19 +234,18 @@ impl> EvmApi for EvmApiRequestor { req.extend(address); req.extend(gas_left.to_be_bytes()); - let (_, data, cost) = self.handle_request(EvmApiMethod::AccountCode, &req); + let (_, data, cost) = self.request(EvmApiMethod::AccountCode, req); self.last_code = Some((address, data.clone())); (data, cost) } fn account_codehash(&mut self, address: Bytes20) -> (Bytes32, u64) { - let (res, _, cost) = self.handle_request(EvmApiMethod::AccountCodeHash, address.as_slice()); + let (res, _, cost) = self.request(EvmApiMethod::AccountCodeHash, address); (res.try_into().unwrap(), cost) } fn add_pages(&mut self, pages: u16) -> u64 { - self.handle_request(EvmApiMethod::AddPages, &pages.to_be_bytes()) - .2 + self.request(EvmApiMethod::AddPages, pages.to_be_bytes()).2 } fn capture_hostio( @@ -265,6 +265,6 @@ impl> EvmApi for EvmApiRequestor { request.extend(name.as_bytes()); request.extend(args); request.extend(outs); - self.handle_request(EvmApiMethod::CaptureHostIO, &request); + self.request(EvmApiMethod::CaptureHostIO, request); } } diff --git a/arbitrator/arbutil/src/evm/storage.rs b/arbitrator/arbutil/src/evm/storage.rs index 7fc4a6911..10d1ab799 100644 --- a/arbitrator/arbutil/src/evm/storage.rs +++ b/arbitrator/arbutil/src/evm/storage.rs @@ -8,6 +8,7 @@ use std::{ }; /// Represents the EVM word at a given key. +#[derive(Debug)] pub struct StorageWord { /// The current value of the slot. pub value: Bytes32, diff --git a/arbitrator/jit/src/stylus_backend.rs b/arbitrator/jit/src/stylus_backend.rs index 74a8d6ae1..61dbf258d 100644 --- a/arbitrator/jit/src/stylus_backend.rs +++ b/arbitrator/jit/src/stylus_backend.rs @@ -43,14 +43,14 @@ struct CothreadRequestor { } impl RequestHandler for CothreadRequestor { - fn handle_request( + fn request( &mut self, req_type: EvmApiMethod, - req_data: &[u8], + req_data: impl AsRef<[u8]>, ) -> (Vec, VecReader, u64) { let msg = MessageFromCothread { req_type: req_type as u32 + EVM_API_METHOD_REQ_OFFSET, - req_data: req_data.to_vec(), + req_data: req_data.as_ref().to_vec(), }; if let Err(error) = self.tx.send(msg) { diff --git a/arbitrator/langs/c b/arbitrator/langs/c index c7bbff75d..29fe05d68 160000 --- a/arbitrator/langs/c +++ b/arbitrator/langs/c @@ -1 +1 @@ -Subproject commit c7bbff75d5e3d4a49a722c4d029817f21a28dc27 +Subproject commit 29fe05d68672797572080084b0f5f0a282e298ef diff --git a/arbitrator/langs/rust b/arbitrator/langs/rust index c8951eab9..1fcef8387 160000 --- a/arbitrator/langs/rust +++ b/arbitrator/langs/rust @@ -1 +1 @@ -Subproject commit c8951eab9b5bd61b264d192241642bf316aa466e +Subproject commit 1fcef8387e5f337901f18f44250756c5bf08db40 diff --git a/arbitrator/stylus/src/evm_api.rs b/arbitrator/stylus/src/evm_api.rs index fdcc5b16a..d26737282 100644 --- a/arbitrator/stylus/src/evm_api.rs +++ b/arbitrator/stylus/src/evm_api.rs @@ -27,10 +27,10 @@ macro_rules! ptr { } impl RequestHandler for NativeRequestHandler { - fn handle_request( + fn request( &mut self, req_type: EvmApiMethod, - req_data: &[u8], + req_data: impl AsRef<[u8]>, ) -> (Vec, GoSliceData, u64) { let mut result = GoSliceData::null(); let mut raw_data = GoSliceData::null(); @@ -39,7 +39,7 @@ impl RequestHandler for NativeRequestHandler { (self.handle_request_fptr)( self.id, req_type as u32 + EVM_API_METHOD_REQ_OFFSET, - ptr!(RustSlice::new(req_data)), + ptr!(RustSlice::new(req_data.as_ref())), ptr!(cost), ptr!(result), ptr!(raw_data), diff --git a/arbitrator/wasm-libraries/user-host/src/program.rs b/arbitrator/wasm-libraries/user-host/src/program.rs index a6ec966c0..b43e632b9 100644 --- a/arbitrator/wasm-libraries/user-host/src/program.rs +++ b/arbitrator/wasm-libraries/user-host/src/program.rs @@ -145,15 +145,15 @@ impl UserHostRequester { } impl RequestHandler for UserHostRequester { - fn handle_request( + fn request( &mut self, req_type: EvmApiMethod, - req_data: &[u8], + req_data: impl AsRef<[u8]>, ) -> (Vec, VecReader, u64) { unsafe { self.send_request( req_type as u32 + EVM_API_METHOD_REQ_OFFSET, - req_data.to_vec(), + req_data.as_ref().to_vec(), ) } } diff --git a/arbos/programs/api.go b/arbos/programs/api.go index 73c1915da..390a3f43e 100644 --- a/arbos/programs/api.go +++ b/arbos/programs/api.go @@ -312,6 +312,7 @@ func newApiClosures( out, cost := getBytes32(key) return out[:], nil, cost case SetTrieSlots: + println("setting trie slots", len(input)) gasLeft := takeU64() gas := gasLeft status := setTrieSlots(takeRest(), &gas) diff --git a/arbos/programs/testconstants.go b/arbos/programs/testconstants.go index f37ccb4b0..cfaf42d88 100644 --- a/arbos/programs/testconstants.go +++ b/arbos/programs/testconstants.go @@ -84,6 +84,5 @@ func testConstants() error { if err := assertEq(3, WriteProtection, C.EvmApiStatus_WriteProtection); err != nil { return err } - return nil } From 704ce1bcbad394b84558aa695a7bff05344d6750 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 14 Mar 2024 03:01:15 -0600 Subject: [PATCH 3/5] SDK support --- .dockerignore | 1 + arbitrator/Cargo.lock | 1 + arbitrator/Cargo.toml | 1 + arbitrator/arbutil/Cargo.toml | 1 + arbitrator/arbutil/src/evm/req.rs | 3 +- arbitrator/arbutil/src/evm/storage.rs | 30 ++++++++++++------- arbitrator/langs/rust | 2 +- arbitrator/stylus/tests/create/Cargo.lock | 7 ----- arbitrator/stylus/tests/erc20/Cargo.lock | 1 - arbitrator/stylus/tests/erc20/src/main.rs | 2 +- arbitrator/stylus/tests/evm-data/Cargo.lock | 7 ----- arbitrator/stylus/tests/fallible/Cargo.lock | 7 ----- arbitrator/stylus/tests/keccak-100/Cargo.lock | 7 ----- arbitrator/stylus/tests/keccak/Cargo.lock | 7 ----- arbitrator/stylus/tests/log/Cargo.lock | 7 ----- arbitrator/stylus/tests/multicall/Cargo.lock | 7 ----- arbitrator/stylus/tests/multicall/src/main.rs | 2 +- .../stylus/tests/read-return-data/Cargo.lock | 7 ----- .../stylus/tests/sdk-storage/Cargo.lock | 16 +++++----- .../stylus/tests/sdk-storage/Cargo.toml | 10 +++---- .../stylus/tests/sdk-storage/src/main.rs | 5 ++-- arbitrator/stylus/tests/storage/Cargo.lock | 7 ----- arbitrator/stylus/tests/storage/src/main.rs | 8 ++--- arbitrator/wasm-libraries/Cargo.lock | 1 + arbos/programs/api.go | 1 - 25 files changed, 49 insertions(+), 99 deletions(-) diff --git a/.dockerignore b/.dockerignore index aa0896303..840538730 100644 --- a/.dockerignore +++ b/.dockerignore @@ -30,6 +30,7 @@ arbitrator/wasm-testsuite/target/ arbitrator/wasm-libraries/target/ arbitrator/tools/wasmer/target/ arbitrator/tools/wasm-tools/ +arbitrator/tools/pricers/ arbitrator/tools/module_roots/ arbitrator/langs/rust/target/ arbitrator/langs/bf/target/ diff --git a/arbitrator/Cargo.lock b/arbitrator/Cargo.lock index d54403153..94aace7a7 100644 --- a/arbitrator/Cargo.lock +++ b/arbitrator/Cargo.lock @@ -52,6 +52,7 @@ version = "0.1.0" dependencies = [ "digest", "eyre", + "fnv", "hex", "num-traits", "num_enum", diff --git a/arbitrator/Cargo.toml b/arbitrator/Cargo.toml index 51c278d3b..2d76c17b5 100644 --- a/arbitrator/Cargo.toml +++ b/arbitrator/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "arbutil", + "caller-env", "prover", "stylus", "jit", diff --git a/arbitrator/arbutil/Cargo.toml b/arbitrator/arbutil/Cargo.toml index f9404ddb8..332369601 100644 --- a/arbitrator/arbutil/Cargo.toml +++ b/arbitrator/arbutil/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] digest = "0.9.0" eyre = "0.6.5" +fnv = "1.0.7" hex = "0.4.3" num-traits = "0.2.17" siphasher = "0.3.10" diff --git a/arbitrator/arbutil/src/evm/req.rs b/arbitrator/arbutil/src/evm/req.rs index 0243c408e..0eeb9a2f6 100644 --- a/arbitrator/arbutil/src/evm/req.rs +++ b/arbitrator/arbutil/src/evm/req.rs @@ -111,11 +111,12 @@ impl> EvmApi for EvmApiRequestor { } fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64 { + let cost = self.storage_cache.write_gas(); match self.storage_cache.entry(key) { Entry::Occupied(mut key) => key.get_mut().value = value, Entry::Vacant(slot) => drop(slot.insert(StorageWord::unknown(value))), }; - self.storage_cache.write_gas() + cost } fn flush_storage_cache(&mut self, clear: bool, gas_left: u64) -> Result { diff --git a/arbitrator/arbutil/src/evm/storage.rs b/arbitrator/arbutil/src/evm/storage.rs index 10d1ab799..32b60dd21 100644 --- a/arbitrator/arbutil/src/evm/storage.rs +++ b/arbitrator/arbutil/src/evm/storage.rs @@ -2,10 +2,8 @@ // For license information, see https://github.com/nitro/blob/master/LICENSE use crate::Bytes32; -use std::{ - collections::HashMap, - ops::{Deref, DerefMut}, -}; +use fnv::FnvHashMap as HashMap; +use std::ops::{Deref, DerefMut}; /// Represents the EVM word at a given key. #[derive(Debug)] @@ -34,19 +32,29 @@ impl StorageWord { #[derive(Default)] pub struct StorageCache { pub(crate) slots: HashMap, + reads: usize, + writes: usize, } impl StorageCache { - pub const REQUIRED_ACCESS_GAS: u64 = crate::evm::COLD_SLOAD_GAS; + pub const REQUIRED_ACCESS_GAS: u64 = 10; - pub fn read_gas(&self) -> u64 { - //self.slots.len().ilog2() as u64 - self.slots.len() as u64 + pub fn read_gas(&mut self) -> u64 { + self.reads += 1; + match self.reads { + 0..=32 => 0, + 33..=128 => 2, + _ => 10, + } } - pub fn write_gas(&self) -> u64 { - //self.slots.len().ilog2() as u64 - self.slots.len() as u64 + pub fn write_gas(&mut self) -> u64 { + self.writes += 1; + match self.writes { + 0..=8 => 0, + 9..=64 => 7, + _ => 10, + } } } diff --git a/arbitrator/langs/rust b/arbitrator/langs/rust index 1fcef8387..0686ba111 160000 --- a/arbitrator/langs/rust +++ b/arbitrator/langs/rust @@ -1 +1 @@ -Subproject commit 1fcef8387e5f337901f18f44250756c5bf08db40 +Subproject commit 0686ba111232dfcb76bd4cf71b59648555f1248d diff --git a/arbitrator/stylus/tests/create/Cargo.lock b/arbitrator/stylus/tests/create/Cargo.lock index 3a32d390a..ca6be1f23 100644 --- a/arbitrator/stylus/tests/create/Cargo.lock +++ b/arbitrator/stylus/tests/create/Cargo.lock @@ -192,12 +192,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.7" @@ -459,7 +453,6 @@ dependencies = [ "alloy-sol-types", "cfg-if", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/erc20/Cargo.lock b/arbitrator/stylus/tests/erc20/Cargo.lock index 2a7c1ba86..c3e215978 100644 --- a/arbitrator/stylus/tests/erc20/Cargo.lock +++ b/arbitrator/stylus/tests/erc20/Cargo.lock @@ -669,7 +669,6 @@ dependencies = [ "alloy-sol-types", "cfg-if 1.0.0", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/erc20/src/main.rs b/arbitrator/stylus/tests/erc20/src/main.rs index 730f9f6f3..7cbda7ef3 100644 --- a/arbitrator/stylus/tests/erc20/src/main.rs +++ b/arbitrator/stylus/tests/erc20/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2023, Offchain Labs, Inc. +// Copyright 2023-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE // Warning: this code is for testing only and has not been audited diff --git a/arbitrator/stylus/tests/evm-data/Cargo.lock b/arbitrator/stylus/tests/evm-data/Cargo.lock index dcc206a09..c78abc9f1 100644 --- a/arbitrator/stylus/tests/evm-data/Cargo.lock +++ b/arbitrator/stylus/tests/evm-data/Cargo.lock @@ -192,12 +192,6 @@ dependencies = [ "stylus-sdk", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.7" @@ -459,7 +453,6 @@ dependencies = [ "alloy-sol-types", "cfg-if", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/fallible/Cargo.lock b/arbitrator/stylus/tests/fallible/Cargo.lock index ddbd9b787..252edfbbf 100644 --- a/arbitrator/stylus/tests/fallible/Cargo.lock +++ b/arbitrator/stylus/tests/fallible/Cargo.lock @@ -191,12 +191,6 @@ dependencies = [ "stylus-sdk", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.7" @@ -458,7 +452,6 @@ dependencies = [ "alloy-sol-types", "cfg-if", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/keccak-100/Cargo.lock b/arbitrator/stylus/tests/keccak-100/Cargo.lock index a8a06076c..d3ff2a09a 100644 --- a/arbitrator/stylus/tests/keccak-100/Cargo.lock +++ b/arbitrator/stylus/tests/keccak-100/Cargo.lock @@ -184,12 +184,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.6" @@ -459,7 +453,6 @@ dependencies = [ "alloy-sol-types", "cfg-if", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/keccak/Cargo.lock b/arbitrator/stylus/tests/keccak/Cargo.lock index 0e2aead75..5b5344e94 100644 --- a/arbitrator/stylus/tests/keccak/Cargo.lock +++ b/arbitrator/stylus/tests/keccak/Cargo.lock @@ -184,12 +184,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.6" @@ -459,7 +453,6 @@ dependencies = [ "alloy-sol-types", "cfg-if", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/log/Cargo.lock b/arbitrator/stylus/tests/log/Cargo.lock index bd01923ca..0bb2ca333 100644 --- a/arbitrator/stylus/tests/log/Cargo.lock +++ b/arbitrator/stylus/tests/log/Cargo.lock @@ -184,12 +184,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.7" @@ -459,7 +453,6 @@ dependencies = [ "alloy-sol-types", "cfg-if", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/multicall/Cargo.lock b/arbitrator/stylus/tests/multicall/Cargo.lock index a277df269..67b375d74 100644 --- a/arbitrator/stylus/tests/multicall/Cargo.lock +++ b/arbitrator/stylus/tests/multicall/Cargo.lock @@ -184,12 +184,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.7" @@ -459,7 +453,6 @@ dependencies = [ "alloy-sol-types", "cfg-if", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/multicall/src/main.rs b/arbitrator/stylus/tests/multicall/src/main.rs index ebb784e04..1f255cd99 100644 --- a/arbitrator/stylus/tests/multicall/src/main.rs +++ b/arbitrator/stylus/tests/multicall/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2023, Offchain Labs, Inc. +// Copyright 2023-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE #![no_main] diff --git a/arbitrator/stylus/tests/read-return-data/Cargo.lock b/arbitrator/stylus/tests/read-return-data/Cargo.lock index 7f5dfe25a..2d551af6e 100644 --- a/arbitrator/stylus/tests/read-return-data/Cargo.lock +++ b/arbitrator/stylus/tests/read-return-data/Cargo.lock @@ -184,12 +184,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.7" @@ -459,7 +453,6 @@ dependencies = [ "alloy-sol-types", "cfg-if", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/sdk-storage/Cargo.lock b/arbitrator/stylus/tests/sdk-storage/Cargo.lock index 7ec98393a..778a091be 100644 --- a/arbitrator/stylus/tests/sdk-storage/Cargo.lock +++ b/arbitrator/stylus/tests/sdk-storage/Cargo.lock @@ -190,12 +190,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.7" @@ -275,6 +269,14 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" +[[package]] +name = "mini-alloc" +version = "0.4.2" +dependencies = [ + "cfg-if 1.0.0", + "wee_alloc", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -421,6 +423,7 @@ name = "sdk-storage" version = "0.1.0" dependencies = [ "hex", + "mini-alloc", "stylus-sdk", "wee_alloc", ] @@ -472,7 +475,6 @@ dependencies = [ "alloy-sol-types", "cfg-if 1.0.0", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/sdk-storage/Cargo.toml b/arbitrator/stylus/tests/sdk-storage/Cargo.toml index da14332da..3946a0f36 100644 --- a/arbitrator/stylus/tests/sdk-storage/Cargo.toml +++ b/arbitrator/stylus/tests/sdk-storage/Cargo.toml @@ -4,7 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -stylus-sdk = { path = "../../../langs/rust/stylus-sdk" } +stylus-sdk = { path = "../../../langs/rust/stylus-sdk", default-features = false, features = [] } +#stylus-sdk = { path = "../../../langs/rust/stylus-sdk", default-features = false, features = ["storage-cache"] } +mini-alloc.path = "../../../langs/rust/mini-alloc" hex = "0.4.3" wee_alloc = "0.4.5" @@ -16,10 +18,6 @@ panic = "abort" # uncomment to optimize for size # opt-level = "z" - -# TODO: move to .cargo/ and add nightly to build process and CI -[unstable] -build-std = ["std", "panic_abort"] -build-std-features = ["panic_immediate_abort"] +opt-level = "s" [workspace] diff --git a/arbitrator/stylus/tests/sdk-storage/src/main.rs b/arbitrator/stylus/tests/sdk-storage/src/main.rs index 15ec72816..4bfe8b602 100644 --- a/arbitrator/stylus/tests/sdk-storage/src/main.rs +++ b/arbitrator/stylus/tests/sdk-storage/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2023, Offchain Labs, Inc. +// Copyright 2023-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE #![no_main] @@ -7,9 +7,10 @@ use stylus_sdk::{ alloy_primitives::{Address, Signed, Uint, B256, I32, U16, U256, U64, U8}, prelude::*, }; +use mini_alloc::MiniAlloc; #[global_allocator] -static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; +static ALLOC: MiniAlloc = MiniAlloc::INIT; sol_storage! { pub struct Contract { diff --git a/arbitrator/stylus/tests/storage/Cargo.lock b/arbitrator/stylus/tests/storage/Cargo.lock index bffed4f41..a686950b2 100644 --- a/arbitrator/stylus/tests/storage/Cargo.lock +++ b/arbitrator/stylus/tests/storage/Cargo.lock @@ -184,12 +184,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.7" @@ -458,7 +452,6 @@ dependencies = [ "alloy-sol-types", "cfg-if", "derivative", - "fnv", "hex", "keccak-const", "lazy_static", diff --git a/arbitrator/stylus/tests/storage/src/main.rs b/arbitrator/stylus/tests/storage/src/main.rs index b737dcd09..6cb0518a6 100644 --- a/arbitrator/stylus/tests/storage/src/main.rs +++ b/arbitrator/stylus/tests/storage/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2023, Offchain Labs, Inc. +// Copyright 2023-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE #![no_main] @@ -6,7 +6,7 @@ use stylus_sdk::{ alloy_primitives::B256, console, - storage::{load_bytes32, store_bytes32}, + storage::{StorageCache, GlobalStorage}, stylus_proc::entrypoint, }; @@ -17,13 +17,13 @@ fn user_main(input: Vec) -> Result, Vec> { Ok(if read { console!("read {slot}"); - let data = unsafe { load_bytes32(slot.into()) }; + let data = StorageCache::get_word(slot.into()); console!("value {data}"); data.0.into() } else { console!("write {slot}"); let data = B256::try_from(&input[33..]).unwrap(); - unsafe { store_bytes32(slot.into(), data) }; + unsafe { StorageCache::set_word(slot.into(), data) }; console!(("value {data}")); vec![] }) diff --git a/arbitrator/wasm-libraries/Cargo.lock b/arbitrator/wasm-libraries/Cargo.lock index c54553da3..f7bc33d46 100644 --- a/arbitrator/wasm-libraries/Cargo.lock +++ b/arbitrator/wasm-libraries/Cargo.lock @@ -28,6 +28,7 @@ version = "0.1.0" dependencies = [ "digest", "eyre", + "fnv", "hex", "num-traits", "num_enum", diff --git a/arbos/programs/api.go b/arbos/programs/api.go index 390a3f43e..73c1915da 100644 --- a/arbos/programs/api.go +++ b/arbos/programs/api.go @@ -312,7 +312,6 @@ func newApiClosures( out, cost := getBytes32(key) return out[:], nil, cost case SetTrieSlots: - println("setting trie slots", len(input)) gasLeft := takeU64() gas := gasLeft status := setTrieSlots(takeRest(), &gas) From a2d98c2be948ba80029e07d8f3c334946fc1852e Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 14 Mar 2024 03:13:25 -0600 Subject: [PATCH 4/5] repin rust --- arbitrator/langs/rust | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrator/langs/rust b/arbitrator/langs/rust index 0686ba111..7bb07e556 160000 --- a/arbitrator/langs/rust +++ b/arbitrator/langs/rust @@ -1 +1 @@ -Subproject commit 0686ba111232dfcb76bd4cf71b59648555f1248d +Subproject commit 7bb07e556d2da4e623f13bfb099a99f9d85cc297 From 35aeb1742ae7cdb025efb71a871b692f0675a2da Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 14 Mar 2024 03:21:56 -0600 Subject: [PATCH 5/5] cleanup --- arbitrator/arbutil/src/evm/mod.rs | 2 +- arbitrator/stylus/tests/sdk-storage/Cargo.toml | 6 +----- arbitrator/wasm-libraries/user-test/src/program.rs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/arbitrator/arbutil/src/evm/mod.rs b/arbitrator/arbutil/src/evm/mod.rs index 86ce2accc..ae5eefeca 100644 --- a/arbitrator/arbutil/src/evm/mod.rs +++ b/arbitrator/arbutil/src/evm/mod.rs @@ -14,7 +14,7 @@ pub const SSTORE_SENTRY_GAS: u64 = 2300; // params.ColdAccountAccessCostEIP2929 pub const COLD_ACCOUNT_GAS: u64 = 2600; -// params.ColdSloadCostEIP2929; +// params.ColdSloadCostEIP2929 pub const COLD_SLOAD_GAS: u64 = 2100; // params.WarmStorageReadCostEIP2929 diff --git a/arbitrator/stylus/tests/sdk-storage/Cargo.toml b/arbitrator/stylus/tests/sdk-storage/Cargo.toml index 3946a0f36..c136762b5 100644 --- a/arbitrator/stylus/tests/sdk-storage/Cargo.toml +++ b/arbitrator/stylus/tests/sdk-storage/Cargo.toml @@ -4,8 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -stylus-sdk = { path = "../../../langs/rust/stylus-sdk", default-features = false, features = [] } -#stylus-sdk = { path = "../../../langs/rust/stylus-sdk", default-features = false, features = ["storage-cache"] } +stylus-sdk.path = "../../../langs/rust/stylus-sdk" mini-alloc.path = "../../../langs/rust/mini-alloc" hex = "0.4.3" wee_alloc = "0.4.5" @@ -15,9 +14,6 @@ codegen-units = 1 strip = true lto = true panic = "abort" - -# uncomment to optimize for size -# opt-level = "z" opt-level = "s" [workspace] diff --git a/arbitrator/wasm-libraries/user-test/src/program.rs b/arbitrator/wasm-libraries/user-test/src/program.rs index 592317719..63afbdfe7 100644 --- a/arbitrator/wasm-libraries/user-test/src/program.rs +++ b/arbitrator/wasm-libraries/user-test/src/program.rs @@ -171,7 +171,7 @@ impl EvmApi for MockEvmApi { fn emit_log(&mut self, data: Vec, _topics: u32) -> Result<()> { unsafe { LOGS.push(data) }; - Ok(()) // pretend a log was emitted + Ok(()) } fn account_balance(&mut self, _address: Bytes20) -> (Bytes32, u64) {