From 4a12f9c8620260a7a388aad9ee747bf49693ba4a Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 18 Sep 2024 10:13:59 -0500 Subject: [PATCH 1/5] Add Gas and Ink types in rust --- arbitrator/arbutil/src/evm/api.rs | 118 ++++++++++++++---- arbitrator/arbutil/src/evm/mod.rs | 49 ++++---- arbitrator/arbutil/src/evm/req.rs | 67 +++++----- arbitrator/arbutil/src/evm/storage.rs | 20 +-- arbitrator/arbutil/src/pricing.rs | 14 ++- arbitrator/jit/src/program.rs | 5 +- arbitrator/jit/src/stylus_backend.rs | 12 +- arbitrator/prover/src/machine.rs | 2 +- arbitrator/prover/src/merkle.rs | 5 +- arbitrator/prover/src/programs/config.rs | 9 +- arbitrator/prover/src/programs/memory.rs | 22 ++-- arbitrator/prover/src/programs/meter.rs | 40 +++--- arbitrator/prover/src/test.rs | 4 +- arbitrator/stylus/src/env.rs | 16 +-- arbitrator/stylus/src/evm_api.rs | 6 +- arbitrator/stylus/src/host.rs | 14 +-- arbitrator/stylus/src/lib.rs | 8 +- arbitrator/stylus/src/native.rs | 4 +- arbitrator/stylus/src/run.rs | 8 +- arbitrator/stylus/src/test/api.rs | 54 ++++---- arbitrator/stylus/src/test/mod.rs | 16 +-- arbitrator/stylus/src/test/native.rs | 22 ++-- arbitrator/stylus/src/test/wavm.rs | 11 +- .../wasm-libraries/user-host-trait/src/lib.rs | 28 ++--- 24 files changed, 319 insertions(+), 235 deletions(-) diff --git a/arbitrator/arbutil/src/evm/api.rs b/arbitrator/arbutil/src/evm/api.rs index 9d4c78c0de..a9f8d927fe 100644 --- a/arbitrator/arbutil/src/evm/api.rs +++ b/arbitrator/arbutil/src/evm/api.rs @@ -73,19 +73,93 @@ impl DataReader for VecReader { } } +macro_rules! derive_math { + ($t:ident) => { + impl std::ops::Add for $t { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) + } + } + + impl std::ops::AddAssign for $t { + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + } + } + + impl std::ops::Sub for $t { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Self(self.0 - rhs.0) + } + } + + impl std::ops::SubAssign for $t { + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0; + } + } + + impl std::ops::Mul for $t { + type Output = Self; + + fn mul(self, rhs: u64) -> Self { + Self(self.0 * rhs) + } + } + + impl std::ops::Mul<$t> for u64 { + type Output = $t; + + fn mul(self, rhs: $t) -> $t { + $t(self * rhs.0) + } + } + + impl $t { + pub const fn saturating_add(self, rhs: Self) -> Self { + Self(self.0.saturating_add(rhs.0)) + } + + pub const fn saturating_sub(self, rhs: Self) -> Self { + Self(self.0.saturating_sub(rhs.0)) + } + + pub fn to_be_bytes(self) -> [u8; 8] { + self.0.to_be_bytes() + } + } + }; +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[must_use] +pub struct Gas(pub u64); + +derive_math!(Gas); + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[must_use] +pub struct Ink(pub u64); + +derive_math!(Ink); + pub trait EvmApi: Send + 'static { /// Reads the 32-byte value in the EVM state trie at offset `key`. /// Returns the value and the access cost in gas. /// Analogous to `vm.SLOAD`. - fn get_bytes32(&mut self, key: Bytes32, evm_api_gas_to_use: u64) -> (Bytes32, u64); + fn get_bytes32(&mut self, key: Bytes32, evm_api_gas_to_use: Gas) -> (Bytes32, Gas); /// 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; + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> Gas; /// 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; + fn flush_storage_cache(&mut self, clear: bool, gas_left: Gas) -> Result; /// Reads the 32-byte value in the EVM's transient state trie at offset `key`. /// Analogous to `vm.TLOAD`. @@ -102,10 +176,10 @@ pub trait EvmApi: Send + 'static { &mut self, contract: Bytes20, calldata: &[u8], - gas_left: u64, - gas_req: u64, + gas_left: Gas, + gas_req: Gas, value: Bytes32, - ) -> (u32, u64, UserOutcomeKind); + ) -> (u32, Gas, UserOutcomeKind); /// Delegate-calls the contract at the given address. /// Returns the EVM return data's length, the gas cost, and whether the call succeeded. @@ -114,9 +188,9 @@ pub trait EvmApi: Send + 'static { &mut self, contract: Bytes20, calldata: &[u8], - gas_left: u64, - gas_req: u64, - ) -> (u32, u64, UserOutcomeKind); + gas_left: Gas, + gas_req: Gas, + ) -> (u32, Gas, UserOutcomeKind); /// Static-calls the contract at the given address. /// Returns the EVM return data's length, the gas cost, and whether the call succeeded. @@ -125,9 +199,9 @@ pub trait EvmApi: Send + 'static { &mut self, contract: Bytes20, calldata: &[u8], - gas_left: u64, - gas_req: u64, - ) -> (u32, u64, UserOutcomeKind); + gas_left: Gas, + gas_req: Gas, + ) -> (u32, Gas, UserOutcomeKind); /// Deploys a new contract using the init code provided. /// Returns the new contract's address on success, or the error reason on failure. @@ -137,8 +211,8 @@ pub trait EvmApi: Send + 'static { &mut self, code: Vec, endowment: Bytes32, - gas: u64, - ) -> (eyre::Result, u32, u64); + gas: Gas, + ) -> (eyre::Result, u32, Gas); /// Deploys a new contract using the init code provided, with an address determined in part by the `salt`. /// Returns the new contract's address on success, or the error reason on failure. @@ -149,8 +223,8 @@ pub trait EvmApi: Send + 'static { code: Vec, endowment: Bytes32, salt: Bytes32, - gas: u64, - ) -> (eyre::Result, u32, u64); + gas: Gas, + ) -> (eyre::Result, u32, Gas); /// Returns the EVM return data. /// Analogous to `vm.RETURNDATACOPY`. @@ -164,21 +238,21 @@ pub trait EvmApi: Send + 'static { /// Gets the balance of the given account. /// Returns the balance and the access cost in gas. /// Analogous to `vm.BALANCE`. - fn account_balance(&mut self, address: Bytes20) -> (Bytes32, u64); + fn account_balance(&mut self, address: Bytes20) -> (Bytes32, Gas); /// Returns the code and the access cost in gas. /// Analogous to `vm.EXTCODECOPY`. - fn account_code(&mut self, address: Bytes20, gas_left: u64) -> (D, u64); + fn account_code(&mut self, address: Bytes20, gas_left: Gas) -> (D, Gas); /// Gets the hash of the given address's code. /// Returns the hash and the access cost in gas. /// Analogous to `vm.EXTCODEHASH`. - fn account_codehash(&mut self, address: Bytes20) -> (Bytes32, u64); + fn account_codehash(&mut self, address: Bytes20) -> (Bytes32, Gas); /// Determines the cost in gas of allocating additional wasm pages. /// Note: has the side effect of updating Geth's memory usage tracker. /// Not analogous to any EVM opcode. - fn add_pages(&mut self, pages: u16) -> u64; + fn add_pages(&mut self, pages: u16) -> Gas; /// Captures tracing information for hostio invocations during native execution. fn capture_hostio( @@ -186,7 +260,7 @@ pub trait EvmApi: Send + 'static { name: &str, args: &[u8], outs: &[u8], - start_ink: u64, - end_ink: u64, + start_ink: Ink, + end_ink: Ink, ); } diff --git a/arbitrator/arbutil/src/evm/mod.rs b/arbitrator/arbutil/src/evm/mod.rs index 36dadd906a..063194b0c6 100644 --- a/arbitrator/arbutil/src/evm/mod.rs +++ b/arbitrator/arbutil/src/evm/mod.rs @@ -2,6 +2,7 @@ // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE use crate::{Bytes20, Bytes32}; +use api::Gas; pub mod api; pub mod req; @@ -9,70 +10,70 @@ pub mod storage; pub mod user; // params.SstoreSentryGasEIP2200 -pub const SSTORE_SENTRY_GAS: u64 = 2300; +pub const SSTORE_SENTRY_GAS: Gas = Gas(2300); // params.ColdAccountAccessCostEIP2929 -pub const COLD_ACCOUNT_GAS: u64 = 2600; +pub const COLD_ACCOUNT_GAS: Gas = Gas(2600); // params.ColdSloadCostEIP2929 -pub const COLD_SLOAD_GAS: u64 = 2100; +pub const COLD_SLOAD_GAS: Gas = Gas(2100); // params.WarmStorageReadCostEIP2929 -pub const WARM_SLOAD_GAS: u64 = 100; +pub const WARM_SLOAD_GAS: Gas = Gas(100); // params.WarmStorageReadCostEIP2929 (see enable1153 in jump_table.go) -pub const TLOAD_GAS: u64 = WARM_SLOAD_GAS; -pub const TSTORE_GAS: u64 = WARM_SLOAD_GAS; +pub const TLOAD_GAS: Gas = WARM_SLOAD_GAS; +pub const TSTORE_GAS: Gas = WARM_SLOAD_GAS; // params.LogGas and params.LogDataGas -pub const LOG_TOPIC_GAS: u64 = 375; -pub const LOG_DATA_GAS: u64 = 8; +pub const LOG_TOPIC_GAS: Gas = Gas(375); +pub const LOG_DATA_GAS: Gas = Gas(8); // params.CopyGas -pub const COPY_WORD_GAS: u64 = 3; +pub const COPY_WORD_GAS: Gas = Gas(3); // params.Keccak256Gas -pub const KECCAK_256_GAS: u64 = 30; -pub const KECCAK_WORD_GAS: u64 = 6; +pub const KECCAK_256_GAS: Gas = Gas(30); +pub const KECCAK_WORD_GAS: Gas = Gas(6); // vm.GasQuickStep (see gas.go) -pub const GAS_QUICK_STEP: u64 = 2; +pub const GAS_QUICK_STEP: Gas = Gas(2); // vm.GasQuickStep (see jump_table.go) -pub const ADDRESS_GAS: u64 = GAS_QUICK_STEP; +pub const ADDRESS_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see eips.go) -pub const BASEFEE_GAS: u64 = GAS_QUICK_STEP; +pub const BASEFEE_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see eips.go) -pub const CHAINID_GAS: u64 = GAS_QUICK_STEP; +pub const CHAINID_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see jump_table.go) -pub const COINBASE_GAS: u64 = GAS_QUICK_STEP; +pub const COINBASE_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see jump_table.go) -pub const GASLIMIT_GAS: u64 = GAS_QUICK_STEP; +pub const GASLIMIT_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see jump_table.go) -pub const NUMBER_GAS: u64 = GAS_QUICK_STEP; +pub const NUMBER_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see jump_table.go) -pub const TIMESTAMP_GAS: u64 = GAS_QUICK_STEP; +pub const TIMESTAMP_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see jump_table.go) -pub const GASLEFT_GAS: u64 = GAS_QUICK_STEP; +pub const GASLEFT_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see jump_table.go) -pub const CALLER_GAS: u64 = GAS_QUICK_STEP; +pub const CALLER_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see jump_table.go) -pub const CALLVALUE_GAS: u64 = GAS_QUICK_STEP; +pub const CALLVALUE_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see jump_table.go) -pub const GASPRICE_GAS: u64 = GAS_QUICK_STEP; +pub const GASPRICE_GAS: Gas = GAS_QUICK_STEP; // vm.GasQuickStep (see jump_table.go) -pub const ORIGIN_GAS: u64 = GAS_QUICK_STEP; +pub const ORIGIN_GAS: Gas = GAS_QUICK_STEP; pub const ARBOS_VERSION_STYLUS_CHARGING_FIXES: u64 = 32; diff --git a/arbitrator/arbutil/src/evm/req.rs b/arbitrator/arbutil/src/evm/req.rs index 0304f2d378..621f41e951 100644 --- a/arbitrator/arbutil/src/evm/req.rs +++ b/arbitrator/arbutil/src/evm/req.rs @@ -12,8 +12,10 @@ use crate::{ use eyre::{bail, eyre, Result}; use std::collections::hash_map::Entry; +use super::api::{Gas, Ink}; + pub trait RequestHandler: Send + 'static { - fn request(&mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>) -> (Vec, D, u64); + fn request(&mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>) -> (Vec, D, Gas); } pub struct EvmApiRequestor> { @@ -33,7 +35,7 @@ impl> EvmApiRequestor { } } - fn request(&mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>) -> (Vec, D, u64) { + fn request(&mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>) -> (Vec, D, Gas) { self.handler.request(req_type, req_data) } @@ -43,10 +45,10 @@ impl> EvmApiRequestor { call_type: EvmApiMethod, contract: Bytes20, input: &[u8], - gas_left: u64, - gas_req: u64, + gas_left: Gas, + gas_req: Gas, value: Bytes32, - ) -> (u32, u64, UserOutcomeKind) { + ) -> (u32, Gas, UserOutcomeKind) { let mut request = Vec::with_capacity(20 + 32 + 8 + 8 + input.len()); request.extend(contract); request.extend(value); @@ -71,8 +73,8 @@ impl> EvmApiRequestor { code: Vec, endowment: Bytes32, salt: Option, - gas: u64, - ) -> (Result, u32, u64) { + gas: Gas, + ) -> (Result, u32, Gas) { let mut request = Vec::with_capacity(8 + 2 * 32 + code.len()); request.extend(gas.to_be_bytes()); request.extend(endowment); @@ -98,7 +100,7 @@ impl> EvmApiRequestor { } impl> EvmApi for EvmApiRequestor { - fn get_bytes32(&mut self, key: Bytes32, evm_api_gas_to_use: u64) -> (Bytes32, u64) { + fn get_bytes32(&mut self, key: Bytes32, evm_api_gas_to_use: Gas) -> (Bytes32, Gas) { let cache = &mut self.storage_cache; let mut cost = cache.read_gas(); @@ -110,7 +112,7 @@ impl> EvmApi for EvmApiRequestor { (value.value, cost) } - fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64 { + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> Gas { let cost = self.storage_cache.write_gas(); match self.storage_cache.entry(key) { Entry::Occupied(mut key) => key.get_mut().value = value, @@ -119,7 +121,7 @@ impl> EvmApi for EvmApiRequestor { cost } - fn flush_storage_cache(&mut self, clear: bool, gas_left: u64) -> Result { + fn flush_storage_cache(&mut self, clear: bool, gas_left: Gas) -> Result { let mut data = Vec::with_capacity(64 * self.storage_cache.len() + 8); data.extend(gas_left.to_be_bytes()); @@ -134,7 +136,7 @@ impl> EvmApi for EvmApiRequestor { self.storage_cache.clear(); } if data.len() == 8 { - return Ok(0); // no need to make request + return Ok(Gas(0)); // no need to make request } let (res, _, cost) = self.request(EvmApiMethod::SetTrieSlots, data); @@ -174,10 +176,10 @@ impl> EvmApi for EvmApiRequestor { &mut self, contract: Bytes20, input: &[u8], - gas_left: u64, - gas_req: u64, + gas_left: Gas, + gas_req: Gas, value: Bytes32, - ) -> (u32, u64, UserOutcomeKind) { + ) -> (u32, Gas, UserOutcomeKind) { self.call_request( EvmApiMethod::ContractCall, contract, @@ -192,9 +194,9 @@ impl> EvmApi for EvmApiRequestor { &mut self, contract: Bytes20, input: &[u8], - gas_left: u64, - gas_req: u64, - ) -> (u32, u64, UserOutcomeKind) { + gas_left: Gas, + gas_req: Gas, + ) -> (u32, Gas, UserOutcomeKind) { self.call_request( EvmApiMethod::DelegateCall, contract, @@ -209,9 +211,9 @@ impl> EvmApi for EvmApiRequestor { &mut self, contract: Bytes20, input: &[u8], - gas_left: u64, - gas_req: u64, - ) -> (u32, u64, UserOutcomeKind) { + gas_left: Gas, + gas_req: Gas, + ) -> (u32, Gas, UserOutcomeKind) { self.call_request( EvmApiMethod::StaticCall, contract, @@ -226,8 +228,8 @@ impl> EvmApi for EvmApiRequestor { &mut self, code: Vec, endowment: Bytes32, - gas: u64, - ) -> (Result, u32, u64) { + gas: Gas, + ) -> (Result, u32, Gas) { self.create_request(EvmApiMethod::Create1, code, endowment, None, gas) } @@ -236,8 +238,8 @@ impl> EvmApi for EvmApiRequestor { code: Vec, endowment: Bytes32, salt: Bytes32, - gas: u64, - ) -> (Result, u32, u64) { + gas: Gas, + ) -> (Result, u32, Gas) { self.create_request(EvmApiMethod::Create2, code, endowment, Some(salt), gas) } @@ -258,15 +260,15 @@ impl> EvmApi for EvmApiRequestor { Ok(()) } - fn account_balance(&mut self, address: Bytes20) -> (Bytes32, u64) { + fn account_balance(&mut self, address: Bytes20) -> (Bytes32, Gas) { let (res, _, cost) = self.request(EvmApiMethod::AccountBalance, address); (res.try_into().unwrap(), cost) } - fn account_code(&mut self, address: Bytes20, gas_left: u64) -> (D, u64) { + fn account_code(&mut self, address: Bytes20, gas_left: Gas) -> (D, Gas) { if let Some((stored_address, data)) = self.last_code.as_ref() { if address == *stored_address { - return (data.clone(), 0); + return (data.clone(), Gas(0)); } } let mut req = Vec::with_capacity(20 + 8); @@ -278,12 +280,12 @@ impl> EvmApi for EvmApiRequestor { (data, cost) } - fn account_codehash(&mut self, address: Bytes20) -> (Bytes32, u64) { + fn account_codehash(&mut self, address: Bytes20) -> (Bytes32, Gas) { let (res, _, cost) = self.request(EvmApiMethod::AccountCodeHash, address); (res.try_into().unwrap(), cost) } - fn add_pages(&mut self, pages: u16) -> u64 { + fn add_pages(&mut self, pages: u16) -> Gas { self.request(EvmApiMethod::AddPages, pages.to_be_bytes()).2 } @@ -292,8 +294,8 @@ impl> EvmApi for EvmApiRequestor { name: &str, args: &[u8], outs: &[u8], - start_ink: u64, - end_ink: u64, + start_ink: Ink, + end_ink: Ink, ) { let mut request = Vec::with_capacity(2 * 8 + 3 * 2 + name.len() + args.len() + outs.len()); request.extend(start_ink.to_be_bytes()); @@ -305,6 +307,7 @@ impl> EvmApi for EvmApiRequestor { request.extend(name.as_bytes()); request.extend(args); request.extend(outs); - self.request(EvmApiMethod::CaptureHostIO, request); + // ignore response (including gas) as we're just tracing + _ = self.request(EvmApiMethod::CaptureHostIO, request); } } diff --git a/arbitrator/arbutil/src/evm/storage.rs b/arbitrator/arbutil/src/evm/storage.rs index 32b60dd21b..5f688364d7 100644 --- a/arbitrator/arbutil/src/evm/storage.rs +++ b/arbitrator/arbutil/src/evm/storage.rs @@ -5,6 +5,8 @@ use crate::Bytes32; use fnv::FnvHashMap as HashMap; use std::ops::{Deref, DerefMut}; +use super::api::Gas; + /// Represents the EVM word at a given key. #[derive(Debug)] pub struct StorageWord { @@ -37,23 +39,23 @@ pub struct StorageCache { } impl StorageCache { - pub const REQUIRED_ACCESS_GAS: u64 = 10; + pub const REQUIRED_ACCESS_GAS: Gas = Gas(10); - pub fn read_gas(&mut self) -> u64 { + pub fn read_gas(&mut self) -> Gas { self.reads += 1; match self.reads { - 0..=32 => 0, - 33..=128 => 2, - _ => 10, + 0..=32 => Gas(0), + 33..=128 => Gas(2), + _ => Gas(10), } } - pub fn write_gas(&mut self) -> u64 { + pub fn write_gas(&mut self) -> Gas { self.writes += 1; match self.writes { - 0..=8 => 0, - 9..=64 => 7, - _ => 10, + 0..=8 => Gas(0), + 9..=64 => Gas(7), + _ => Gas(10), } } } diff --git a/arbitrator/arbutil/src/pricing.rs b/arbitrator/arbutil/src/pricing.rs index 4614b02a2a..91de739303 100644 --- a/arbitrator/arbutil/src/pricing.rs +++ b/arbitrator/arbutil/src/pricing.rs @@ -1,20 +1,22 @@ // Copyright 2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE +use crate::evm::api::Ink; + /// For hostios that may return something. -pub const HOSTIO_INK: u64 = 8400; +pub const HOSTIO_INK: Ink = Ink(8400); /// For hostios that include pointers. -pub const PTR_INK: u64 = 13440 - HOSTIO_INK; +pub const PTR_INK: Ink = Ink(13440 - HOSTIO_INK.0); /// For hostios that involve an API cost. -pub const EVM_API_INK: u64 = 59673; +pub const EVM_API_INK: Ink = Ink(59673); /// For hostios that involve a div or mod. -pub const DIV_INK: u64 = 20000; +pub const DIV_INK: Ink = Ink(20000); /// For hostios that involve a mulmod. -pub const MUL_MOD_INK: u64 = 24100; +pub const MUL_MOD_INK: Ink = Ink(24100); /// For hostios that involve an addmod. -pub const ADD_MOD_INK: u64 = 21000; +pub const ADD_MOD_INK: Ink = Ink(21000); diff --git a/arbitrator/jit/src/program.rs b/arbitrator/jit/src/program.rs index 084afe96bc..f10a059748 100644 --- a/arbitrator/jit/src/program.rs +++ b/arbitrator/jit/src/program.rs @@ -6,6 +6,7 @@ use crate::caller_env::JitEnv; use crate::machine::{Escape, MaybeEscape, WasmEnvMut}; use crate::stylus_backend::exec_wasm; +use arbutil::evm::api::Gas; use arbutil::Bytes32; use arbutil::{evm::EvmData, format::DebugBytes, heapify}; use caller_env::{GuestPtr, MemAccess}; @@ -131,7 +132,7 @@ pub fn new_program( // buy ink let pricing = config.stylus.pricing; - let ink = pricing.gas_to_ink(gas); + let ink = pricing.gas_to_ink(Gas(gas)); let Some(module) = exec.module_asms.get(&compiled_hash).cloned() else { return Err(Escape::Failure(format!( @@ -217,7 +218,7 @@ pub fn set_response( let raw_data = mem.read_slice(raw_data_ptr, raw_data_len as usize); let thread = exec.threads.last_mut().unwrap(); - thread.set_response(id, result, raw_data, gas) + thread.set_response(id, result, raw_data, Gas(gas)) } /// sends previos response diff --git a/arbitrator/jit/src/stylus_backend.rs b/arbitrator/jit/src/stylus_backend.rs index 61dbf258d4..0d8c477c6c 100644 --- a/arbitrator/jit/src/stylus_backend.rs +++ b/arbitrator/jit/src/stylus_backend.rs @@ -4,7 +4,7 @@ #![allow(clippy::too_many_arguments)] use crate::machine::{Escape, MaybeEscape}; -use arbutil::evm::api::VecReader; +use arbutil::evm::api::{Gas, Ink, VecReader}; use arbutil::evm::{ api::{EvmApiMethod, EVM_API_METHOD_REQ_OFFSET}, req::EvmApiRequestor, @@ -28,7 +28,7 @@ use stylus::{native::NativeInstance, run::RunProgram}; struct MessageToCothread { result: Vec, raw_data: Vec, - cost: u64, + cost: Gas, } #[derive(Clone)] @@ -47,7 +47,7 @@ impl RequestHandler for CothreadRequestor { &mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>, - ) -> (Vec, VecReader, u64) { + ) -> (Vec, VecReader, Gas) { let msg = MessageFromCothread { req_type: req_type as u32 + EVM_API_METHOD_REQ_OFFSET, req_data: req_data.as_ref().to_vec(), @@ -104,7 +104,7 @@ impl CothreadHandler { id: u32, result: Vec, raw_data: Vec, - cost: u64, + cost: Gas, ) -> MaybeEscape { let Some(msg) = self.last_request.clone() else { return Escape::hostio("trying to set response but no message pending"); @@ -131,7 +131,7 @@ pub fn exec_wasm( compile: CompileConfig, config: StylusConfig, evm_data: EvmData, - ink: u64, + ink: Ink, ) -> Result { let (tothread_tx, tothread_rx) = mpsc::sync_channel::(0); let (fromthread_tx, fromthread_rx) = mpsc::sync_channel::(0); @@ -150,7 +150,7 @@ pub fn exec_wasm( let outcome = instance.run_main(&calldata, config, ink); let ink_left = match outcome.as_ref() { - Ok(UserOutcome::OutOfStack) => 0, // take all ink when out of stack + Ok(UserOutcome::OutOfStack) => Ink(0), // take all ink when out of stack _ => instance.ink_left().into(), }; diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 4ece1f7bf2..66992019f9 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -1816,7 +1816,7 @@ impl Machine { } #[cfg(feature = "native")] - pub fn call_user_func(&mut self, func: &str, args: Vec, ink: u64) -> Result> { + pub fn call_user_func(&mut self, func: &str, args: Vec, ink: arbutil::evm::api::Ink) -> Result> { self.set_ink(ink); self.call_function("user", func, args) } diff --git a/arbitrator/prover/src/merkle.rs b/arbitrator/prover/src/merkle.rs index 4a1278b4cb..fbd704dfc6 100644 --- a/arbitrator/prover/src/merkle.rs +++ b/arbitrator/prover/src/merkle.rs @@ -549,8 +549,7 @@ mod test { let mut empty_node = Bytes32([ 57, 29, 211, 154, 252, 227, 18, 99, 65, 126, 203, 166, 252, 232, 32, 3, 98, 194, 254, 186, 118, 14, 139, 192, 101, 156, 55, 194, 101, 11, 11, 168, - ]) - .clone(); + ]); for _ in 0..64 { print!("Bytes32(["); for i in 0..32 { @@ -607,7 +606,7 @@ mod test { for layer in 0..64 { // empty_hash_at is just a lookup, but empty_hash is calculated iteratively. assert_eq!(empty_hash_at(ty, layer), &empty_hash); - empty_hash = hash_node(ty, &empty_hash, &empty_hash); + empty_hash = hash_node(ty, empty_hash, empty_hash); } } } diff --git a/arbitrator/prover/src/programs/config.rs b/arbitrator/prover/src/programs/config.rs index 0353589358..bd6fb3a843 100644 --- a/arbitrator/prover/src/programs/config.rs +++ b/arbitrator/prover/src/programs/config.rs @@ -4,6 +4,7 @@ #![allow(clippy::field_reassign_with_default)] use crate::{programs::meter, value::FunctionType}; +use arbutil::evm::api::{Gas, Ink}; use derivative::Derivative; use fnv::FnvHashMap as HashMap; use std::fmt::Debug; @@ -72,12 +73,12 @@ impl PricingParams { Self { ink_price } } - pub fn gas_to_ink(&self, gas: u64) -> u64 { - gas.saturating_mul(self.ink_price.into()) + pub fn gas_to_ink(&self, gas: Gas) -> Ink { + Ink(gas.0.saturating_mul(self.ink_price.into())) } - pub fn ink_to_gas(&self, ink: u64) -> u64 { - ink / self.ink_price as u64 // never 0 + pub fn ink_to_gas(&self, ink: Ink) -> Gas { + Gas(ink.0 / self.ink_price as u64) // ink_price is never 0 } } diff --git a/arbitrator/prover/src/programs/memory.rs b/arbitrator/prover/src/programs/memory.rs index 7253b59dc4..758f2f3e82 100644 --- a/arbitrator/prover/src/programs/memory.rs +++ b/arbitrator/prover/src/programs/memory.rs @@ -1,6 +1,8 @@ // Copyright 2023, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE +use arbutil::evm::api::Gas; + #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct MemoryModel { @@ -28,20 +30,20 @@ impl MemoryModel { } /// Determines the gas cost of allocating `new` pages given `open` are active and `ever` have ever been. - pub fn gas_cost(&self, new: u16, open: u16, ever: u16) -> u64 { + pub fn gas_cost(&self, new: u16, open: u16, ever: u16) -> Gas { let new_open = open.saturating_add(new); let new_ever = ever.max(new_open); // free until expansion beyond the first few if new_ever <= self.free_pages { - return 0; + return Gas(0); } let credit = |pages: u16| pages.saturating_sub(self.free_pages); let adding = credit(new_open).saturating_sub(credit(open)) as u64; let linear = adding.saturating_mul(self.page_gas.into()); let expand = Self::exp(new_ever) - Self::exp(ever); - linear.saturating_add(expand) + Gas(linear.saturating_add(expand)) } fn exp(pages: u16) -> u64 { @@ -85,7 +87,7 @@ fn test_model() { let mut pages = 0; while pages < 128 { let jump = jump.min(128 - pages); - total += model.gas_cost(jump, pages, pages); + total += model.gas_cost(jump, pages, pages).0; pages += jump; } assert_eq!(total, 31999998); @@ -98,7 +100,7 @@ fn test_model() { let mut adds = 0; while ever < 128 { let jump = jump.min(128 - open); - total += model.gas_cost(jump, open, ever); + total += model.gas_cost(jump, open, ever).0; open += jump; ever = ever.max(open); @@ -114,12 +116,12 @@ fn test_model() { } // check saturation - assert_eq!(u64::MAX, model.gas_cost(129, 0, 0)); - assert_eq!(u64::MAX, model.gas_cost(u16::MAX, 0, 0)); + assert_eq!(Gas(u64::MAX), model.gas_cost(129, 0, 0)); + assert_eq!(Gas(u64::MAX), model.gas_cost(u16::MAX, 0, 0)); // check free pages let model = MemoryModel::new(128, 1000); - assert_eq!(0, model.gas_cost(128, 0, 0)); - assert_eq!(0, model.gas_cost(128, 0, 128)); - assert_eq!(u64::MAX, model.gas_cost(129, 0, 0)); + assert_eq!(Gas(0), model.gas_cost(128, 0, 0)); + assert_eq!(Gas(0), model.gas_cost(128, 0, 128)); + assert_eq!(Gas(u64::MAX), model.gas_cost(129, 0, 0)); } diff --git a/arbitrator/prover/src/programs/meter.rs b/arbitrator/prover/src/programs/meter.rs index ab069fd911..fe98bbcbc1 100644 --- a/arbitrator/prover/src/programs/meter.rs +++ b/arbitrator/prover/src/programs/meter.rs @@ -9,7 +9,7 @@ use crate::{ value::FunctionType, Machine, }; -use arbutil::{evm, operator::OperatorInfo, Bytes32}; +use arbutil::{evm::{self, api::{Gas, Ink}}, operator::OperatorInfo, Bytes32}; use derivative::Derivative; use eyre::Result; use fnv::FnvHashMap as HashMap; @@ -188,15 +188,15 @@ impl<'a, F: OpcodePricer> FuncMiddleware<'a> for FuncMeter<'a, F> { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MachineMeter { - Ready(u64), + Ready(Ink), Exhausted, } impl MachineMeter { - pub fn ink(self) -> u64 { + pub fn ink(self) -> Ink { match self { Self::Ready(ink) => ink, - Self::Exhausted => 0, + Self::Exhausted => Ink(0), } } @@ -210,8 +210,8 @@ impl MachineMeter { /// We don't implement `From` since it's unclear what 0 would map to #[allow(clippy::from_over_into)] -impl Into for MachineMeter { - fn into(self) -> u64 { +impl Into for MachineMeter { + fn into(self) -> Ink { self.ink() } } @@ -219,7 +219,7 @@ impl Into for MachineMeter { impl Display for MachineMeter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Ready(ink) => write!(f, "{ink} ink"), + Self::Ready(ink) => write!(f, "{} ink", ink.0), Self::Exhausted => write!(f, "exhausted"), } } @@ -241,7 +241,7 @@ pub trait MeteredMachine { fn ink_left(&self) -> MachineMeter; fn set_meter(&mut self, meter: MachineMeter); - fn set_ink(&mut self, ink: u64) { + fn set_ink(&mut self, ink: Ink) { self.set_meter(MachineMeter::Ready(ink)); } @@ -250,14 +250,14 @@ pub trait MeteredMachine { Err(OutOfInkError) } - fn ink_ready(&mut self) -> Result { + fn ink_ready(&mut self) -> Result { let MachineMeter::Ready(ink_left) = self.ink_left() else { return self.out_of_ink(); }; Ok(ink_left) } - fn buy_ink(&mut self, ink: u64) -> Result<(), OutOfInkError> { + fn buy_ink(&mut self, ink: Ink) -> Result<(), OutOfInkError> { let ink_left = self.ink_ready()?; if ink_left < ink { return self.out_of_ink(); @@ -267,7 +267,7 @@ pub trait MeteredMachine { } /// Checks if the user has enough ink, but doesn't burn any - fn require_ink(&mut self, ink: u64) -> Result<(), OutOfInkError> { + fn require_ink(&mut self, ink: Ink) -> Result<(), OutOfInkError> { let ink_left = self.ink_ready()?; if ink_left < ink { return self.out_of_ink(); @@ -277,18 +277,18 @@ pub trait MeteredMachine { /// Pays for a write into the client. fn pay_for_write(&mut self, bytes: u32) -> Result<(), OutOfInkError> { - self.buy_ink(sat_add_mul(5040, 30, bytes.saturating_sub(32))) + self.buy_ink(Ink(sat_add_mul(5040, 30, bytes.saturating_sub(32)))) } /// Pays for a read into the host. fn pay_for_read(&mut self, bytes: u32) -> Result<(), OutOfInkError> { - self.buy_ink(sat_add_mul(16381, 55, bytes.saturating_sub(32))) + self.buy_ink(Ink(sat_add_mul(16381, 55, bytes.saturating_sub(32)))) } /// Pays for both I/O and keccak. fn pay_for_keccak(&mut self, bytes: u32) -> Result<(), OutOfInkError> { let words = evm::evm_words(bytes).saturating_sub(2); - self.buy_ink(sat_add_mul(121800, 21000, words)) + self.buy_ink(Ink(sat_add_mul(121800, 21000, words))) } /// Pays for copying bytes from geth. @@ -305,14 +305,14 @@ pub trait MeteredMachine { false => break, } } - self.buy_ink(3000 + exp * 17500) + self.buy_ink(Ink(3000 + exp * 17500)) } } pub trait GasMeteredMachine: MeteredMachine { fn pricing(&self) -> PricingParams; - fn gas_left(&self) -> Result { + fn gas_left(&self) -> Result { let pricing = self.pricing(); match self.ink_left() { MachineMeter::Ready(ink) => Ok(pricing.ink_to_gas(ink)), @@ -320,13 +320,13 @@ pub trait GasMeteredMachine: MeteredMachine { } } - fn buy_gas(&mut self, gas: u64) -> Result<(), OutOfInkError> { + fn buy_gas(&mut self, gas: Gas) -> Result<(), OutOfInkError> { let pricing = self.pricing(); self.buy_ink(pricing.gas_to_ink(gas)) } /// Checks if the user has enough gas, but doesn't burn any - fn require_gas(&mut self, gas: u64) -> Result<(), OutOfInkError> { + fn require_gas(&mut self, gas: Gas) -> Result<(), OutOfInkError> { let pricing = self.pricing(); self.require_ink(pricing.gas_to_ink(gas)) } @@ -350,7 +350,7 @@ impl MeteredMachine for Machine { }}; } - let ink = || convert!(self.get_global(STYLUS_INK_LEFT)); + let ink = || Ink(convert!(self.get_global(STYLUS_INK_LEFT))); let status: u32 = convert!(self.get_global(STYLUS_INK_STATUS)); match status { @@ -362,7 +362,7 @@ impl MeteredMachine for Machine { fn set_meter(&mut self, meter: MachineMeter) { let ink = meter.ink(); let status = meter.status(); - self.set_global(STYLUS_INK_LEFT, ink.into()).unwrap(); + self.set_global(STYLUS_INK_LEFT, ink.0.into()).unwrap(); self.set_global(STYLUS_INK_STATUS, status.into()).unwrap(); } } diff --git a/arbitrator/prover/src/test.rs b/arbitrator/prover/src/test.rs index 97170441ff..4fd739342e 100644 --- a/arbitrator/prover/src/test.rs +++ b/arbitrator/prover/src/test.rs @@ -1,8 +1,6 @@ // Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -#![cfg(test)] - use crate::binary; use brotli::Dictionary; use eyre::Result; @@ -64,7 +62,7 @@ pub fn test_compress() -> Result<()> { let deflate = brotli::compress(data, 11, 22, dict).unwrap(); let inflate = brotli::decompress(&deflate, dict).unwrap(); assert_eq!(hex::encode(inflate), hex::encode(data)); - assert!(&deflate != &last); + assert!(deflate != last); last = deflate; } Ok(()) diff --git a/arbitrator/stylus/src/env.rs b/arbitrator/stylus/src/env.rs index 69d542070d..a153fb5bf1 100644 --- a/arbitrator/stylus/src/env.rs +++ b/arbitrator/stylus/src/env.rs @@ -3,7 +3,7 @@ use arbutil::{ evm::{ - api::{DataReader, EvmApi}, + api::{DataReader, EvmApi, Ink}, EvmData, }, pricing, @@ -74,7 +74,7 @@ impl> WasmEnv { pub fn start<'a>( env: &'a mut WasmEnvMut<'_, D, E>, - ink: u64, + ink: Ink, ) -> Result, Escape> { let mut info = Self::program(env)?; info.buy_ink(pricing::HOSTIO_INK + ink)?; @@ -88,7 +88,7 @@ impl> WasmEnv { env, memory, store, - start_ink: 0, + start_ink: Ink(0), }; if info.env.evm_data.tracing { info.start_ink = info.ink_ready()?; @@ -114,16 +114,16 @@ pub struct MeterData { } impl MeterData { - pub fn ink(&self) -> u64 { - unsafe { self.ink_left.as_ref().val.u64 } + pub fn ink(&self) -> Ink { + Ink(unsafe { self.ink_left.as_ref().val.u64 }) } pub fn status(&self) -> u32 { unsafe { self.ink_status.as_ref().val.u32 } } - pub fn set_ink(&mut self, ink: u64) { - unsafe { self.ink_left.as_mut().val = RawValue { u64: ink } } + pub fn set_ink(&mut self, ink: Ink) { + unsafe { self.ink_left.as_mut().val = RawValue { u64: ink.0 } } } pub fn set_status(&mut self, status: u32) { @@ -140,7 +140,7 @@ pub struct HostioInfo<'a, D: DataReader, E: EvmApi> { pub env: &'a mut WasmEnv, pub memory: Memory, pub store: StoreMut<'a>, - pub start_ink: u64, + pub start_ink: Ink, } impl<'a, D: DataReader, E: EvmApi> HostioInfo<'a, D, E> { diff --git a/arbitrator/stylus/src/evm_api.rs b/arbitrator/stylus/src/evm_api.rs index d267372827..0dd27e3f8c 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, EVM_API_METHOD_REQ_OFFSET}, + api::{EvmApiMethod, Gas, EVM_API_METHOD_REQ_OFFSET}, req::RequestHandler, }; @@ -31,7 +31,7 @@ impl RequestHandler for NativeRequestHandler { &mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>, - ) -> (Vec, GoSliceData, u64) { + ) -> (Vec, GoSliceData, Gas) { let mut result = GoSliceData::null(); let mut raw_data = GoSliceData::null(); let mut cost = 0; @@ -45,6 +45,6 @@ impl RequestHandler for NativeRequestHandler { ptr!(raw_data), ) }; - (result.slice().to_vec(), raw_data, cost) + (result.slice().to_vec(), raw_data, Gas(cost)) } } diff --git a/arbitrator/stylus/src/host.rs b/arbitrator/stylus/src/host.rs index 1afc1b4e51..c72cafc316 100644 --- a/arbitrator/stylus/src/host.rs +++ b/arbitrator/stylus/src/host.rs @@ -6,7 +6,7 @@ use crate::env::{Escape, HostioInfo, MaybeEscape, WasmEnv, WasmEnvMut}; use arbutil::{ evm::{ - api::{DataReader, EvmApi}, + api::{DataReader, EvmApi, Gas, Ink}, EvmData, }, Color, @@ -82,7 +82,7 @@ where println!("{} {text}", "Stylus says:".yellow()); } - fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], end_ink: u64) { + fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], end_ink: Ink) { let start_ink = self.start_ink; self.evm_api .capture_hostio(name, args, outs, start_ink, end_ink); @@ -168,7 +168,7 @@ pub(crate) fn call_contract>( ) -> Result { hostio!( env, - call_contract(contract, data, data_len, value, gas, ret_len) + call_contract(contract, data, data_len, value, Gas(gas), ret_len) ) } @@ -182,7 +182,7 @@ pub(crate) fn delegate_call_contract>( ) -> Result { hostio!( env, - delegate_call_contract(contract, data, data_len, gas, ret_len) + delegate_call_contract(contract, data, data_len, Gas(gas), ret_len) ) } @@ -196,7 +196,7 @@ pub(crate) fn static_call_contract>( ) -> Result { hostio!( env, - static_call_contract(contract, data, data_len, gas, ret_len) + static_call_contract(contract, data, data_len, Gas(gas), ret_len) ) } @@ -334,13 +334,13 @@ pub(crate) fn contract_address>( pub(crate) fn evm_gas_left>( mut env: WasmEnvMut, ) -> Result { - hostio!(env, evm_gas_left()) + hostio!(env, evm_gas_left()).map(|g| g.0) } pub(crate) fn evm_ink_left>( mut env: WasmEnvMut, ) -> Result { - hostio!(env, evm_ink_left()) + hostio!(env, evm_ink_left()).map(|i| i.0) } pub(crate) fn math_div>( diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index 5962817d78..e7f10c2400 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -3,7 +3,7 @@ use arbutil::{ evm::{ - api::DataReader, + api::{DataReader, Gas, Ink}, req::EvmApiRequestor, user::{UserOutcome, UserOutcomeKind}, EvmData, @@ -279,7 +279,7 @@ pub unsafe extern "C" fn stylus_call( let evm_api = EvmApiRequestor::new(req_handler); let pricing = config.pricing; let output = &mut *output; - let ink = pricing.gas_to_ink(*gas); + let ink = pricing.gas_to_ink(Gas(*gas)); // Safety: module came from compile_user_wasm and we've paid for memory expansion let instance = unsafe { @@ -302,10 +302,10 @@ pub unsafe extern "C" fn stylus_call( Ok(outcome) => output.write_outcome(outcome), }; let ink_left = match status { - UserOutcomeKind::OutOfStack => 0, // take all gas when out of stack + UserOutcomeKind::OutOfStack => Ink(0), // take all gas when out of stack _ => instance.ink_left().into(), }; - *gas = pricing.ink_to_gas(ink_left); + *gas = pricing.ink_to_gas(ink_left).0; status } diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index c751a670cc..0fbdb342f3 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -8,7 +8,7 @@ use crate::{ }; use arbutil::{ evm::{ - api::{DataReader, EvmApi}, + api::{DataReader, EvmApi, Ink}, EvmData, }, operator::OperatorCode, @@ -270,7 +270,7 @@ impl> NativeInstance { global.set(store, value.into()).map_err(ErrReport::msg) } - pub fn call_func(&mut self, func: TypedFunction<(), R>, ink: u64) -> Result + pub fn call_func(&mut self, func: TypedFunction<(), R>, ink: Ink) -> Result where R: WasmTypeList, { diff --git a/arbitrator/stylus/src/run.rs b/arbitrator/stylus/src/run.rs index 8e673a25e5..6cbb0cfb42 100644 --- a/arbitrator/stylus/src/run.rs +++ b/arbitrator/stylus/src/run.rs @@ -4,18 +4,18 @@ #![allow(clippy::redundant_closure_call)] use crate::{env::Escape, native::NativeInstance}; -use arbutil::evm::api::{DataReader, EvmApi}; +use arbutil::evm::api::{DataReader, EvmApi, Ink}; use arbutil::evm::user::UserOutcome; use eyre::{eyre, Result}; use prover::machine::Machine; use prover::programs::{prelude::*, STYLUS_ENTRY_POINT}; pub trait RunProgram { - fn run_main(&mut self, args: &[u8], config: StylusConfig, ink: u64) -> Result; + fn run_main(&mut self, args: &[u8], config: StylusConfig, ink: Ink) -> Result; } impl RunProgram for Machine { - fn run_main(&mut self, args: &[u8], config: StylusConfig, ink: u64) -> Result { + fn run_main(&mut self, args: &[u8], config: StylusConfig, ink: Ink) -> Result { macro_rules! call { ($module:expr, $func:expr, $args:expr) => { call!($module, $func, $args, |error| UserOutcome::Failure(error)) @@ -65,7 +65,7 @@ impl RunProgram for Machine { } impl> RunProgram for NativeInstance { - fn run_main(&mut self, args: &[u8], config: StylusConfig, ink: u64) -> Result { + fn run_main(&mut self, args: &[u8], config: StylusConfig, ink: Ink) -> Result { use UserOutcome::*; self.set_ink(ink); diff --git a/arbitrator/stylus/src/test/api.rs b/arbitrator/stylus/src/test/api.rs index 66d600a6f7..7a5af6f89f 100644 --- a/arbitrator/stylus/src/test/api.rs +++ b/arbitrator/stylus/src/test/api.rs @@ -4,7 +4,7 @@ use crate::{native, run::RunProgram}; use arbutil::{ evm::{ - api::{EvmApi, VecReader}, + api::{EvmApi, Gas, Ink, VecReader}, user::UserOutcomeKind, EvmData, }, @@ -68,24 +68,24 @@ impl TestEvmApi { } impl EvmApi for TestEvmApi { - fn get_bytes32(&mut self, key: Bytes32, _evm_api_gas_to_use: u64) -> (Bytes32, u64) { + fn get_bytes32(&mut self, key: Bytes32, _evm_api_gas_to_use: Gas) -> (Bytes32, Gas) { let storage = &mut self.storage.lock(); let storage = storage.get_mut(&self.program).unwrap(); let value = storage.get(&key).cloned().unwrap_or_default(); - (value, 2100) // pretend worst case + (value, Gas(2100)) // pretend worst case } - fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64 { + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> Gas { let storage = &mut self.storage.lock(); let storage = storage.get_mut(&self.program).unwrap(); storage.insert(key, value); - 0 + Gas(0) } - fn flush_storage_cache(&mut self, _clear: bool, _gas_left: u64) -> Result { + fn flush_storage_cache(&mut self, _clear: bool, _gas_left: Gas) -> Result { let storage = &mut self.storage.lock(); let storage = storage.get_mut(&self.program).unwrap(); - Ok(22100 * storage.len() as u64) // pretend worst case + Ok(Gas(22100) * storage.len() as u64) // pretend worst case } fn get_transient_bytes32(&mut self, _key: Bytes32) -> Bytes32 { @@ -102,10 +102,10 @@ impl EvmApi for TestEvmApi { &mut self, contract: Bytes20, calldata: &[u8], - _gas_left: u64, - gas_req: u64, + _gas_left: Gas, + gas_req: Gas, _value: Bytes32, - ) -> (u32, u64, UserOutcomeKind) { + ) -> (u32, Gas, UserOutcomeKind) { let compile = self.compile.clone(); let evm_data = self.evm_data; let config = *self.configs.lock().get(&contract).unwrap(); @@ -122,7 +122,7 @@ impl EvmApi for TestEvmApi { let (status, outs) = outcome.into_data(); let outs_len = outs.len() as u32; - let ink_left: u64 = native.ink_left().into(); + let ink_left: Ink = native.ink_left().into(); let gas_left = config.pricing.ink_to_gas(ink_left); *self.write_result.lock() = outs; (outs_len, gas - gas_left, status) @@ -132,9 +132,9 @@ impl EvmApi for TestEvmApi { &mut self, _contract: Bytes20, _calldata: &[u8], - _gas_left: u64, - _gas_req: u64, - ) -> (u32, u64, UserOutcomeKind) { + _gas_left: Gas, + _gas_req: Gas, + ) -> (u32, Gas, UserOutcomeKind) { todo!("delegate call not yet supported") } @@ -142,9 +142,9 @@ impl EvmApi for TestEvmApi { &mut self, contract: Bytes20, calldata: &[u8], - gas_left: u64, - gas_req: u64, - ) -> (u32, u64, UserOutcomeKind) { + gas_left: Gas, + gas_req: Gas, + ) -> (u32, Gas, UserOutcomeKind) { println!("note: overriding static call with call"); self.contract_call(contract, calldata, gas_left, gas_req, Bytes32::default()) } @@ -153,8 +153,8 @@ impl EvmApi for TestEvmApi { &mut self, _code: Vec, _endowment: Bytes32, - _gas: u64, - ) -> (Result, u32, u64) { + _gas: Gas, + ) -> (Result, u32, Gas) { unimplemented!("create1 not supported") } @@ -163,8 +163,8 @@ impl EvmApi for TestEvmApi { _code: Vec, _endowment: Bytes32, _salt: Bytes32, - _gas: u64, - ) -> (Result, u32, u64) { + _gas: Gas, + ) -> (Result, u32, Gas) { unimplemented!("create2 not supported") } @@ -176,19 +176,19 @@ impl EvmApi for TestEvmApi { Ok(()) // pretend a log was emitted } - fn account_balance(&mut self, _address: Bytes20) -> (Bytes32, u64) { + fn account_balance(&mut self, _address: Bytes20) -> (Bytes32, Gas) { unimplemented!() } - fn account_code(&mut self, _address: Bytes20, _gas_left: u64) -> (VecReader, u64) { + fn account_code(&mut self, _address: Bytes20, _gas_left: Gas) -> (VecReader, Gas) { unimplemented!() } - fn account_codehash(&mut self, _address: Bytes20) -> (Bytes32, u64) { + fn account_codehash(&mut self, _address: Bytes20) -> (Bytes32, Gas) { unimplemented!() } - fn add_pages(&mut self, new: u16) -> u64 { + fn add_pages(&mut self, new: u16) -> Gas { let model = MemoryModel::new(2, 1000); let (open, ever) = *self.pages.lock(); @@ -203,8 +203,8 @@ impl EvmApi for TestEvmApi { _name: &str, _args: &[u8], _outs: &[u8], - _start_ink: u64, - _end_ink: u64, + _start_ink: Ink, + _end_ink: Ink, ) { unimplemented!() } diff --git a/arbitrator/stylus/src/test/mod.rs b/arbitrator/stylus/src/test/mod.rs index 00c9c62ae4..830b9cd23f 100644 --- a/arbitrator/stylus/src/test/mod.rs +++ b/arbitrator/stylus/src/test/mod.rs @@ -3,7 +3,7 @@ use crate::{env::WasmEnv, native::NativeInstance, run::RunProgram, test::api::TestEvmApi}; use arbutil::{ - evm::{api::VecReader, user::UserOutcome}, + evm::{api::{Ink, VecReader}, user::UserOutcome}, Bytes20, Bytes32, Color, }; use eyre::{bail, Result}; @@ -41,7 +41,7 @@ impl TestInstance { }; let mut native = Self::new_from_store(path, store, imports)?; native.set_meter_data(); - native.set_ink(u64::MAX); + native.set_ink(Ink(u64::MAX)); native.set_stack(u32::MAX); Ok(native) } @@ -107,8 +107,8 @@ fn expensive_add(op: &Operator, _tys: &SigMap) -> u64 { } } -pub fn random_ink(min: u64) -> u64 { - rand::thread_rng().gen_range(min..=u64::MAX) +pub fn random_ink(min: u64) -> Ink { + Ink(rand::thread_rng().gen_range(min..=u64::MAX)) } pub fn random_bytes20() -> Bytes20 { @@ -135,7 +135,7 @@ fn uniform_cost_config() -> StylusConfig { stylus_config } -fn test_configs() -> (CompileConfig, StylusConfig, u64) { +fn test_configs() -> (CompileConfig, StylusConfig, Ink) { ( test_compile_config(), uniform_cost_config(), @@ -165,12 +165,12 @@ fn new_test_machine(path: &str, compile: &CompileConfig) -> Result { Arc::new(|_, _, _| panic!("tried to read preimage")), Some(stylus_data), )?; - mach.set_ink(u64::MAX); + mach.set_ink(Ink(u64::MAX)); mach.set_stack(u32::MAX); Ok(mach) } -fn run_native(native: &mut TestInstance, args: &[u8], ink: u64) -> Result> { +fn run_native(native: &mut TestInstance, args: &[u8], ink: Ink) -> Result> { let config = native.env().config.expect("no config"); match native.run_main(args, config, ink)? { UserOutcome::Success(output) => Ok(output), @@ -182,7 +182,7 @@ fn run_machine( machine: &mut Machine, args: &[u8], config: StylusConfig, - ink: u64, + ink: Ink, ) -> Result> { match machine.run_main(args, config, ink)? { UserOutcome::Success(output) => Ok(output), diff --git a/arbitrator/stylus/src/test/native.rs b/arbitrator/stylus/src/test/native.rs index 9669932a03..672bdd179c 100644 --- a/arbitrator/stylus/src/test/native.rs +++ b/arbitrator/stylus/src/test/native.rs @@ -16,7 +16,7 @@ use crate::{ use arbutil::{ crypto, evm::{ - api::EvmApi, + api::{EvmApi, Gas, Ink}, user::{UserOutcome, UserOutcomeKind}, }, format, Bytes20, Bytes32, Color, @@ -48,8 +48,8 @@ fn test_ink() -> Result<()> { macro_rules! exhaust { ($ink:expr) => { - native.set_ink($ink); - assert_eq!(native.ink_left(), MachineMeter::Ready($ink)); + native.set_ink(Ink($ink)); + assert_eq!(native.ink_left(), MachineMeter::Ready(Ink($ink))); assert!(add_one.call(&mut native.store, 32).is_err()); assert_eq!(native.ink_left(), MachineMeter::Exhausted); }; @@ -59,12 +59,12 @@ fn test_ink() -> Result<()> { exhaust!(50); exhaust!(99); - let mut ink_left = 500; + let mut ink_left = Ink(500); native.set_ink(ink_left); - while ink_left > 0 { + while ink_left > Ink(0) { assert_eq!(native.ink_left(), MachineMeter::Ready(ink_left)); assert_eq!(add_one.call(&mut native.store, 64)?, 65); - ink_left -= 100; + ink_left -= Ink(100); } assert!(add_one.call(&mut native.store, 32).is_err()); assert_eq!(native.ink_left(), MachineMeter::Exhausted); @@ -198,7 +198,7 @@ fn test_import_export_safety() -> Result<()> { let mut bin = bin?; assert!(bin.clone().instrument(&compile, codehash).is_err()); compile.debug.debug_info = false; - assert!(bin.instrument(&compile, &codehash).is_err()); + assert!(bin.instrument(&compile, codehash).is_err()); if both { assert!(TestInstance::new_test(file, compile).is_err()); @@ -268,7 +268,7 @@ fn test_heap() -> Result<()> { assert_eq!(pages, 128); let used = config.pricing.ink_to_gas(ink - native.ink_ready()?); - ensure!((used as i64 - 32_000_000).abs() < 3_000, "wrong ink"); + ensure!((used.0 as i64 - 32_000_000).abs() < 3_000, "wrong ink"); assert_eq!(native.memory_size(), Pages(128)); if step == extra { @@ -283,7 +283,7 @@ fn test_heap() -> Result<()> { // the cost should exceed a maximum u32, consuming more gas than can ever be bought let (mut native, _) = TestInstance::new_with_evm("tests/memory2.wat", &compile, config)?; - let outcome = native.run_main(&[], config, config.pricing.ink_to_gas(u32::MAX.into()))?; + let outcome = native.run_main(&[], config, config.pricing.gas_to_ink(Gas(u32::MAX.into())))?; assert_eq!(outcome.kind(), UserOutcomeKind::OutOfInk); // ensure we reject programs with excessive footprints @@ -381,7 +381,7 @@ fn test_storage() -> Result<()> { let (mut native, mut evm) = TestInstance::new_with_evm(filename, &compile, config)?; run_native(&mut native, &store_args, ink)?; - assert_eq!(evm.get_bytes32(key.into(), 0).0, Bytes32(value)); + assert_eq!(evm.get_bytes32(key.into(), Gas(0)).0, Bytes32(value)); assert_eq!(run_native(&mut native, &load_args, ink)?, value); let mut machine = Machine::from_user_path(Path::new(filename), &compile)?; @@ -465,7 +465,7 @@ fn test_calls() -> Result<()> { run_native(&mut native, &args, ink)?; for (key, value) in slots { - assert_eq!(evm.get_bytes32(key, 0).0, value); + assert_eq!(evm.get_bytes32(key, Gas(0)).0, value); } Ok(()) } diff --git a/arbitrator/stylus/src/test/wavm.rs b/arbitrator/stylus/src/test/wavm.rs index e707cf490a..729cfebf2f 100644 --- a/arbitrator/stylus/src/test/wavm.rs +++ b/arbitrator/stylus/src/test/wavm.rs @@ -2,6 +2,7 @@ // For license information, see https://github.com/nitro/blob/master/LICENSE use crate::test::{new_test_machine, test_compile_config}; +use arbutil::evm::api::Ink; use eyre::Result; use prover::{programs::prelude::*, Machine}; @@ -15,8 +16,8 @@ fn test_ink() -> Result<()> { macro_rules! exhaust { ($ink:expr) => { - machine.set_ink($ink); - assert_eq!(machine.ink_left(), MachineMeter::Ready($ink)); + machine.set_ink(Ink($ink)); + assert_eq!(machine.ink_left(), MachineMeter::Ready(Ink($ink))); assert!(call(machine, 32).is_err()); assert_eq!(machine.ink_left(), MachineMeter::Exhausted); }; @@ -26,12 +27,12 @@ fn test_ink() -> Result<()> { exhaust!(50); exhaust!(99); - let mut ink_left = 500; + let mut ink_left = Ink(500); machine.set_ink(ink_left); - while ink_left > 0 { + while ink_left > Ink(0) { assert_eq!(machine.ink_left(), MachineMeter::Ready(ink_left)); assert_eq!(call(machine, 64)?, vec![65_u32.into()]); - ink_left -= 100; + ink_left -= Ink(100); } assert!(call(machine, 32).is_err()); assert_eq!(machine.ink_left(), MachineMeter::Exhausted); diff --git a/arbitrator/wasm-libraries/user-host-trait/src/lib.rs b/arbitrator/wasm-libraries/user-host-trait/src/lib.rs index 12a6bdbed2..35a4a31347 100644 --- a/arbitrator/wasm-libraries/user-host-trait/src/lib.rs +++ b/arbitrator/wasm-libraries/user-host-trait/src/lib.rs @@ -5,7 +5,7 @@ use arbutil::{ crypto, evm::{ self, - api::{DataReader, EvmApi}, + api::{DataReader, EvmApi, Gas, Ink}, storage::StorageCache, user::UserOutcomeKind, EvmData, ARBOS_VERSION_STYLUS_CHARGING_FIXES, @@ -88,7 +88,7 @@ pub trait UserHost: GasMeteredMachine { } fn say(&self, text: D); - fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], end_ink: u64); + fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], end_ink: Ink); fn write_bytes20(&self, ptr: GuestPtr, src: Bytes20) -> Result<(), Self::MemoryErr> { self.write_slice(ptr, &src.0) @@ -147,7 +147,7 @@ pub trait UserHost: GasMeteredMachine { // require for cache-miss case, preserve wrong behavior for old arbos let evm_api_gas_to_use = if arbos_version < ARBOS_VERSION_STYLUS_CHARGING_FIXES { - EVM_API_INK + Gas(EVM_API_INK.0) } else { self.pricing().ink_to_gas(EVM_API_INK) }; @@ -253,7 +253,7 @@ pub trait UserHost: GasMeteredMachine { data: GuestPtr, data_len: u32, value: GuestPtr, - gas: u64, + gas: Gas, ret_len: GuestPtr, ) -> Result { let value = Some(value); @@ -282,7 +282,7 @@ pub trait UserHost: GasMeteredMachine { contract: GuestPtr, data: GuestPtr, data_len: u32, - gas: u64, + gas: Gas, ret_len: GuestPtr, ) -> Result { let call = |api: &mut Self::A, contract, data: &_, left, req, _| { @@ -312,7 +312,7 @@ pub trait UserHost: GasMeteredMachine { contract: GuestPtr, data: GuestPtr, data_len: u32, - gas: u64, + gas: Gas, ret_len: GuestPtr, ) -> Result { let call = |api: &mut Self::A, contract, data: &_, left, req, _| { @@ -329,7 +329,7 @@ pub trait UserHost: GasMeteredMachine { calldata: GuestPtr, calldata_len: u32, value: Option, - gas: u64, + gas: Gas, return_data_len: GuestPtr, call: F, name: &str, @@ -339,10 +339,10 @@ pub trait UserHost: GasMeteredMachine { &mut Self::A, Address, &[u8], - u64, - u64, + Gas, + Gas, Option, - ) -> (u32, u64, UserOutcomeKind), + ) -> (u32, Gas, UserOutcomeKind), { self.buy_ink(HOSTIO_INK + 3 * PTR_INK + EVM_API_INK)?; self.pay_for_read(calldata_len)?; @@ -465,12 +465,12 @@ pub trait UserHost: GasMeteredMachine { salt: Option, contract: GuestPtr, revert_data_len: GuestPtr, - cost: u64, + cost: Ink, call: F, name: &str, ) -> Result<(), Self::Err> where - F: FnOnce(&mut Self::A, Vec, Bytes32, Option, u64) -> (Result
, u32, u64), + F: FnOnce(&mut Self::A, Vec, Bytes32, Option, Gas) -> (Result
, u32, Gas), { self.buy_ink(HOSTIO_INK + cost)?; self.pay_for_read(code_len)?; @@ -745,7 +745,7 @@ pub trait UserHost: GasMeteredMachine { /// equivalent to that of the EVM's [`GAS`] opcode. /// /// [`GAS`]: https://www.evm.codes/#5a - fn evm_gas_left(&mut self) -> Result { + fn evm_gas_left(&mut self) -> Result { self.buy_ink(HOSTIO_INK)?; let gas = self.gas_left()?; trace!("evm_gas_left", self, &[], be!(gas), gas) @@ -757,7 +757,7 @@ pub trait UserHost: GasMeteredMachine { /// /// [`GAS`]: https://www.evm.codes/#5a /// [`Ink and Gas`]: https://developer.arbitrum.io/TODO - fn evm_ink_left(&mut self) -> Result { + fn evm_ink_left(&mut self) -> Result { self.buy_ink(HOSTIO_INK)?; let ink = self.ink_ready()?; trace!("evm_ink_left", self, &[], be!(ink), ink) From 788de6ed78a4c2626302a4a65c6b287d9848bc85 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Mon, 14 Oct 2024 14:59:34 -0500 Subject: [PATCH 2/5] Fix user-host compilation --- arbitrator/arbutil/src/evm/api.rs | 4 +-- .../wasm-libraries/user-host/src/host.rs | 29 +++++++++++++++---- .../wasm-libraries/user-host/src/ink.rs | 5 ++-- .../wasm-libraries/user-host/src/link.rs | 18 +++++++----- .../wasm-libraries/user-host/src/program.rs | 12 ++++---- 5 files changed, 45 insertions(+), 23 deletions(-) diff --git a/arbitrator/arbutil/src/evm/api.rs b/arbitrator/arbutil/src/evm/api.rs index a9f8d927fe..8715f5ac8c 100644 --- a/arbitrator/arbutil/src/evm/api.rs +++ b/arbitrator/arbutil/src/evm/api.rs @@ -135,13 +135,13 @@ macro_rules! derive_math { }; } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] #[must_use] pub struct Gas(pub u64); derive_math!(Gas); -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] #[must_use] pub struct Ink(pub u64); diff --git a/arbitrator/wasm-libraries/user-host/src/host.rs b/arbitrator/wasm-libraries/user-host/src/host.rs index abe55b8c12..5ec2ece2c3 100644 --- a/arbitrator/wasm-libraries/user-host/src/host.rs +++ b/arbitrator/wasm-libraries/user-host/src/host.rs @@ -2,7 +2,7 @@ // For license information, see https://github.com/nitro/blob/master/LICENSE use crate::program::Program; -use arbutil::evm::user::UserOutcomeKind; +use arbutil::evm::{api::Gas, user::UserOutcomeKind}; use caller_env::GuestPtr; use user_host_trait::UserHost; @@ -77,7 +77,14 @@ pub unsafe extern "C" fn user_host__call_contract( gas: u64, ret_len: GuestPtr, ) -> u8 { - hostio!(call_contract(contract, data, data_len, value, gas, ret_len)) + hostio!(call_contract( + contract, + data, + data_len, + value, + Gas(gas), + ret_len + )) } #[no_mangle] @@ -89,7 +96,11 @@ pub unsafe extern "C" fn user_host__delegate_call_contract( ret_len: GuestPtr, ) -> u8 { hostio!(delegate_call_contract( - contract, data, data_len, gas, ret_len + contract, + data, + data_len, + Gas(gas), + ret_len )) } @@ -101,7 +112,13 @@ pub unsafe extern "C" fn user_host__static_call_contract( gas: u64, ret_len: GuestPtr, ) -> u8 { - hostio!(static_call_contract(contract, data, data_len, gas, ret_len)) + hostio!(static_call_contract( + contract, + data, + data_len, + Gas(gas), + ret_len + )) } #[no_mangle] @@ -207,12 +224,12 @@ pub unsafe extern "C" fn user_host__contract_address(ptr: GuestPtr) { #[no_mangle] pub unsafe extern "C" fn user_host__evm_gas_left() -> u64 { - hostio!(evm_gas_left()) + hostio!(evm_gas_left()).0 } #[no_mangle] pub unsafe extern "C" fn user_host__evm_ink_left() -> u64 { - hostio!(evm_ink_left()) + hostio!(evm_ink_left()).0 } #[no_mangle] diff --git a/arbitrator/wasm-libraries/user-host/src/ink.rs b/arbitrator/wasm-libraries/user-host/src/ink.rs index e01e616e07..bde7cfc1c0 100644 --- a/arbitrator/wasm-libraries/user-host/src/ink.rs +++ b/arbitrator/wasm-libraries/user-host/src/ink.rs @@ -2,6 +2,7 @@ // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE use crate::program::Program; +use arbutil::evm::api::Ink; use prover::programs::{ config::PricingParams, prelude::{GasMeteredMachine, MachineMeter, MeteredMachine}, @@ -18,7 +19,7 @@ impl MeteredMachine for Program { fn ink_left(&self) -> MachineMeter { unsafe { match user_ink_status() { - 0 => MachineMeter::Ready(user_ink_left()), + 0 => MachineMeter::Ready(Ink(user_ink_left())), _ => MachineMeter::Exhausted, } } @@ -26,7 +27,7 @@ impl MeteredMachine for Program { fn set_meter(&mut self, meter: MachineMeter) { unsafe { - user_set_ink(meter.ink(), meter.status()); + user_set_ink(meter.ink().0, meter.status()); } } } diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs index f4c402fd97..cb9f046cdb 100644 --- a/arbitrator/wasm-libraries/user-host/src/link.rs +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -3,7 +3,11 @@ use crate::program::Program; use arbutil::{ - evm::{user::UserOutcomeKind, EvmData}, + evm::{ + api::{Gas, Ink}, + user::UserOutcomeKind, + EvmData, + }, format::DebugBytes, heapify, Bytes20, Bytes32, }; @@ -120,11 +124,11 @@ pub unsafe extern "C" fn programs__new_program( // buy ink let pricing = config.pricing; - let ink = pricing.gas_to_ink(gas); + let ink = pricing.gas_to_ink(Gas(gas)); // link the program and ready its instrumentation let module = wavm_link_module(&MemoryLeaf(*module_hash)); - program_set_ink(module, ink); + program_set_ink(module, ink.0); program_set_stack(module, config.max_depth); // provide arguments @@ -175,7 +179,7 @@ pub unsafe extern "C" fn programs__set_response( id, STATIC_MEM.read_slice(result_ptr, result_len), STATIC_MEM.read_slice(raw_data_ptr, raw_data_len), - gas, + Gas(gas), ); } @@ -207,7 +211,7 @@ pub unsafe extern "C" fn program_internal__set_done(mut status: UserOutcomeKind) let program = Program::current(); let module = program.module; let mut outs = program.outs.as_slice(); - let mut ink_left = program_ink_left(module); + let mut ink_left = Ink(program_ink_left(module)); // apply any early exit codes if let Some(early) = program.early_exit { @@ -218,12 +222,12 @@ pub unsafe extern "C" fn program_internal__set_done(mut status: UserOutcomeKind) if program_ink_status(module) != 0 { status = OutOfInk; outs = &[]; - ink_left = 0; + ink_left = Ink(0); } if program_stack_left(module) == 0 { status = OutOfStack; outs = &[]; - ink_left = 0; + ink_left = Ink(0); } let gas_left = program.config.pricing.ink_to_gas(ink_left); diff --git a/arbitrator/wasm-libraries/user-host/src/program.rs b/arbitrator/wasm-libraries/user-host/src/program.rs index 4199a691f7..7b3782b2e5 100644 --- a/arbitrator/wasm-libraries/user-host/src/program.rs +++ b/arbitrator/wasm-libraries/user-host/src/program.rs @@ -3,7 +3,7 @@ use arbutil::{ evm::{ - api::{EvmApiMethod, VecReader, EVM_API_METHOD_REQ_OFFSET}, + api::{EvmApiMethod, Gas, Ink, VecReader, EVM_API_METHOD_REQ_OFFSET}, req::{EvmApiRequestor, RequestHandler}, user::UserOutcomeKind, EvmData, @@ -49,7 +49,7 @@ static mut LAST_REQUEST_ID: u32 = 0x10000; #[derive(Clone)] pub(crate) struct UserHostRequester { data: Option>, - answer: Option<(Vec, VecReader, u64)>, + answer: Option<(Vec, VecReader, Gas)>, req_type: u32, id: u32, } @@ -95,7 +95,7 @@ impl UserHostRequester { req_id: u32, result: Vec, raw_data: Vec, - gas: u64, + gas: Gas, ) { self.answer = Some((result, VecReader::new(raw_data), gas)); if req_id != self.id { @@ -130,7 +130,7 @@ impl UserHostRequester { } #[no_mangle] - unsafe fn send_request(&mut self, req_type: u32, data: Vec) -> (Vec, VecReader, u64) { + unsafe fn send_request(&mut self, req_type: u32, data: Vec) -> (Vec, VecReader, Gas) { let req_id = self.set_request(req_type, &data); compiler_fence(Ordering::SeqCst); @@ -149,7 +149,7 @@ impl RequestHandler for UserHostRequester { &mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>, - ) -> (Vec, VecReader, u64) { + ) -> (Vec, VecReader, Gas) { unsafe { self.send_request( req_type as u32 + EVM_API_METHOD_REQ_OFFSET, @@ -265,7 +265,7 @@ impl UserHost for Program { println!("{} {text}", "Stylus says:".yellow()); } - fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], _end_ink: u64) { + fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], _end_ink: Ink) { let args = hex::encode(args); let outs = hex::encode(outs); println!("Error: unexpected hostio tracing info for {name} while proving: {args}, {outs}"); From faadf7968161c1f8b30757af4e6386be64343264 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Mon, 14 Oct 2024 16:49:58 -0500 Subject: [PATCH 3/5] Fix user-test compilation --- .../wasm-libraries/user-test/src/host.rs | 28 ++++++++-- .../wasm-libraries/user-test/src/ink.rs | 5 +- .../wasm-libraries/user-test/src/program.rs | 54 +++++++++---------- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/arbitrator/wasm-libraries/user-test/src/host.rs b/arbitrator/wasm-libraries/user-test/src/host.rs index f2912eaae3..f1b4506414 100644 --- a/arbitrator/wasm-libraries/user-test/src/host.rs +++ b/arbitrator/wasm-libraries/user-test/src/host.rs @@ -2,6 +2,7 @@ // For license information, see https://github.com/nitro/blob/master/LICENSE use crate::program::Program; +use arbutil::evm::api::Gas; use caller_env::GuestPtr; use user_host_trait::UserHost; @@ -63,7 +64,14 @@ pub unsafe extern "C" fn vm_hooks__call_contract( gas: u64, ret_len: GuestPtr, ) -> u8 { - hostio!(call_contract(contract, data, data_len, value, gas, ret_len)) + hostio!(call_contract( + contract, + data, + data_len, + value, + Gas(gas), + ret_len + )) } #[no_mangle] @@ -75,7 +83,11 @@ pub unsafe extern "C" fn vm_hooks__delegate_call_contract( ret_len: GuestPtr, ) -> u8 { hostio!(delegate_call_contract( - contract, data, data_len, gas, ret_len + contract, + data, + data_len, + Gas(gas), + ret_len )) } @@ -87,7 +99,13 @@ pub unsafe extern "C" fn vm_hooks__static_call_contract( gas: u64, ret_len: GuestPtr, ) -> u8 { - hostio!(static_call_contract(contract, data, data_len, gas, ret_len)) + hostio!(static_call_contract( + contract, + data, + data_len, + Gas(gas), + ret_len + )) } #[no_mangle] @@ -189,12 +207,12 @@ pub unsafe extern "C" fn vm_hooks__contract_address(ptr: GuestPtr) { #[no_mangle] pub unsafe extern "C" fn vm_hooks__evm_gas_left() -> u64 { - hostio!(evm_gas_left()) + hostio!(evm_gas_left()).0 } #[no_mangle] pub unsafe extern "C" fn vm_hooks__evm_ink_left() -> u64 { - hostio!(evm_ink_left()) + hostio!(evm_ink_left()).0 } #[no_mangle] diff --git a/arbitrator/wasm-libraries/user-test/src/ink.rs b/arbitrator/wasm-libraries/user-test/src/ink.rs index fca658e59b..72ecfadd96 100644 --- a/arbitrator/wasm-libraries/user-test/src/ink.rs +++ b/arbitrator/wasm-libraries/user-test/src/ink.rs @@ -2,6 +2,7 @@ // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE use crate::{program::Program, CONFIG}; +use arbutil::evm::api::Ink; use prover::programs::{ config::PricingParams, prelude::{GasMeteredMachine, MachineMeter, MeteredMachine}, @@ -18,7 +19,7 @@ impl MeteredMachine for Program { fn ink_left(&self) -> MachineMeter { unsafe { match user_ink_status() { - 0 => MachineMeter::Ready(user_ink_left()), + 0 => MachineMeter::Ready(Ink(user_ink_left())), _ => MachineMeter::Exhausted, } } @@ -26,7 +27,7 @@ impl MeteredMachine for Program { fn set_meter(&mut self, meter: MachineMeter) { unsafe { - user_set_ink(meter.ink(), meter.status()); + user_set_ink(meter.ink().0, meter.status()); } } } diff --git a/arbitrator/wasm-libraries/user-test/src/program.rs b/arbitrator/wasm-libraries/user-test/src/program.rs index 85b522ee74..299fca08c3 100644 --- a/arbitrator/wasm-libraries/user-test/src/program.rs +++ b/arbitrator/wasm-libraries/user-test/src/program.rs @@ -4,7 +4,7 @@ use crate::{ARGS, EVER_PAGES, EVM_DATA, KEYS, LOGS, OPEN_PAGES, OUTS}; use arbutil::{ evm::{ - api::{EvmApi, VecReader}, + api::{EvmApi, Gas, Ink, VecReader}, user::UserOutcomeKind, EvmData, }, @@ -80,7 +80,7 @@ impl UserHost for Program { println!("{} {text}", "Stylus says:".yellow()); } - fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], _end_ink: u64) { + fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], _end_ink: Ink) { let args = hex::encode(args); let outs = hex::encode(outs); println!("Error: unexpected hostio tracing info for {name} while proving: {args}, {outs}"); @@ -102,18 +102,18 @@ impl Program { pub struct MockEvmApi; impl EvmApi for MockEvmApi { - fn get_bytes32(&mut self, key: Bytes32, _evm_api_gas_to_use: u64) -> (Bytes32, u64) { + fn get_bytes32(&mut self, key: Bytes32, _evm_api_gas_to_use: Gas) -> (Bytes32, Gas) { let value = KEYS.lock().get(&key).cloned().unwrap_or_default(); - (value, 2100) // pretend worst case + (value, Gas(2100)) // pretend worst case } - fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64 { + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> Gas { KEYS.lock().insert(key, value); - 0 + Gas(0) } - fn flush_storage_cache(&mut self, _clear: bool, _gas_left: u64) -> Result { - Ok(22100 * KEYS.lock().len() as u64) // pretend worst case + fn flush_storage_cache(&mut self, _clear: bool, _gas_left: Gas) -> Result { + Ok(Gas(22100) * KEYS.lock().len() as u64) // pretend worst case } fn get_transient_bytes32(&mut self, _key: Bytes32) -> Bytes32 { @@ -130,10 +130,10 @@ impl EvmApi for MockEvmApi { &mut self, _contract: Bytes20, _calldata: &[u8], - _gas_left: u64, - _gas_req: u64, + _gas_left: Gas, + _gas_req: Gas, _value: Bytes32, - ) -> (u32, u64, UserOutcomeKind) { + ) -> (u32, Gas, UserOutcomeKind) { unimplemented!() } @@ -141,9 +141,9 @@ impl EvmApi for MockEvmApi { &mut self, _contract: Bytes20, _calldata: &[u8], - _gas_left: u64, - _gas_req: u64, - ) -> (u32, u64, UserOutcomeKind) { + _gas_left: Gas, + _gas_req: Gas, + ) -> (u32, Gas, UserOutcomeKind) { unimplemented!() } @@ -151,9 +151,9 @@ impl EvmApi for MockEvmApi { &mut self, _contract: Bytes20, _calldata: &[u8], - _gas_left: u64, - _gas_req: u64, - ) -> (u32, u64, UserOutcomeKind) { + _gas_left: Gas, + _gas_req: Gas, + ) -> (u32, Gas, UserOutcomeKind) { unimplemented!() } @@ -161,8 +161,8 @@ impl EvmApi for MockEvmApi { &mut self, _code: Vec, _endowment: Bytes32, - _gas: u64, - ) -> (Result, u32, u64) { + _gas: Gas, + ) -> (Result, u32, Gas) { unimplemented!() } @@ -171,8 +171,8 @@ impl EvmApi for MockEvmApi { _code: Vec, _endowment: Bytes32, _salt: Bytes32, - _gas: u64, - ) -> (Result, u32, u64) { + _gas: Gas, + ) -> (Result, u32, Gas) { unimplemented!() } @@ -185,19 +185,19 @@ impl EvmApi for MockEvmApi { Ok(()) } - fn account_balance(&mut self, _address: Bytes20) -> (Bytes32, u64) { + fn account_balance(&mut self, _address: Bytes20) -> (Bytes32, Gas) { unimplemented!() } - fn account_code(&mut self, _address: Bytes20, _gas_left: u64) -> (VecReader, u64) { + fn account_code(&mut self, _address: Bytes20, _gas_left: Gas) -> (VecReader, Gas) { unimplemented!() } - fn account_codehash(&mut self, _address: Bytes20) -> (Bytes32, u64) { + fn account_codehash(&mut self, _address: Bytes20) -> (Bytes32, Gas) { unimplemented!() } - fn add_pages(&mut self, pages: u16) -> u64 { + fn add_pages(&mut self, pages: u16) -> Gas { let model = MemoryModel::new(2, 1000); unsafe { let (open, ever) = (OPEN_PAGES, EVER_PAGES); @@ -212,8 +212,8 @@ impl EvmApi for MockEvmApi { _name: &str, _args: &[u8], _outs: &[u8], - _start_ink: u64, - _end_ink: u64, + _start_ink: Ink, + _end_ink: Ink, ) { unimplemented!() } From 7bc39d3e0feb879d8ea0f1daacb1c2806d8babdf Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Mon, 14 Oct 2024 20:09:06 -0500 Subject: [PATCH 4/5] Fix formatting --- arbitrator/prover/src/machine.rs | 7 ++++++- arbitrator/prover/src/programs/meter.rs | 9 ++++++++- arbitrator/stylus/src/test/mod.rs | 5 ++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 66992019f9..dec355ac7c 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -1816,7 +1816,12 @@ impl Machine { } #[cfg(feature = "native")] - pub fn call_user_func(&mut self, func: &str, args: Vec, ink: arbutil::evm::api::Ink) -> Result> { + pub fn call_user_func( + &mut self, + func: &str, + args: Vec, + ink: arbutil::evm::api::Ink, + ) -> Result> { self.set_ink(ink); self.call_function("user", func, args) } diff --git a/arbitrator/prover/src/programs/meter.rs b/arbitrator/prover/src/programs/meter.rs index fe98bbcbc1..0d7b3151d7 100644 --- a/arbitrator/prover/src/programs/meter.rs +++ b/arbitrator/prover/src/programs/meter.rs @@ -9,7 +9,14 @@ use crate::{ value::FunctionType, Machine, }; -use arbutil::{evm::{self, api::{Gas, Ink}}, operator::OperatorInfo, Bytes32}; +use arbutil::{ + evm::{ + self, + api::{Gas, Ink}, + }, + operator::OperatorInfo, + Bytes32, +}; use derivative::Derivative; use eyre::Result; use fnv::FnvHashMap as HashMap; diff --git a/arbitrator/stylus/src/test/mod.rs b/arbitrator/stylus/src/test/mod.rs index 830b9cd23f..3fd0faede8 100644 --- a/arbitrator/stylus/src/test/mod.rs +++ b/arbitrator/stylus/src/test/mod.rs @@ -3,7 +3,10 @@ use crate::{env::WasmEnv, native::NativeInstance, run::RunProgram, test::api::TestEvmApi}; use arbutil::{ - evm::{api::{Ink, VecReader}, user::UserOutcome}, + evm::{ + api::{Ink, VecReader}, + user::UserOutcome, + }, Bytes20, Bytes32, Color, }; use eyre::{bail, Result}; From 087e59b4982eaed7d517ae55368777ef27928812 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 15 Oct 2024 21:57:23 -0500 Subject: [PATCH 5/5] Address PR comments --- arbitrator/arbutil/src/evm/api.rs | 10 ++++++++++ arbitrator/arbutil/src/pricing.rs | 2 +- arbitrator/prover/src/programs/memory.rs | 6 +++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/arbitrator/arbutil/src/evm/api.rs b/arbitrator/arbutil/src/evm/api.rs index 8715f5ac8c..0a603a3bb2 100644 --- a/arbitrator/arbutil/src/evm/api.rs +++ b/arbitrator/arbutil/src/evm/api.rs @@ -120,6 +120,16 @@ macro_rules! derive_math { } impl $t { + /// Equivalent to the Add trait, but const. + pub const fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) + } + + /// Equivalent to the Sub trait, but const. + pub const fn sub(self, rhs: Self) -> Self { + Self(self.0 - rhs.0) + } + pub const fn saturating_add(self, rhs: Self) -> Self { Self(self.0.saturating_add(rhs.0)) } diff --git a/arbitrator/arbutil/src/pricing.rs b/arbitrator/arbutil/src/pricing.rs index 91de739303..4d6bf827be 100644 --- a/arbitrator/arbutil/src/pricing.rs +++ b/arbitrator/arbutil/src/pricing.rs @@ -7,7 +7,7 @@ use crate::evm::api::Ink; pub const HOSTIO_INK: Ink = Ink(8400); /// For hostios that include pointers. -pub const PTR_INK: Ink = Ink(13440 - HOSTIO_INK.0); +pub const PTR_INK: Ink = Ink(13440).sub(HOSTIO_INK); /// For hostios that involve an API cost. pub const EVM_API_INK: Ink = Ink(59673); diff --git a/arbitrator/prover/src/programs/memory.rs b/arbitrator/prover/src/programs/memory.rs index 758f2f3e82..82c4d4469d 100644 --- a/arbitrator/prover/src/programs/memory.rs +++ b/arbitrator/prover/src/programs/memory.rs @@ -83,14 +83,14 @@ fn test_model() { let model = MemoryModel::new(2, 1000); for jump in 1..=128 { - let mut total = 0; + let mut total = Gas(0); let mut pages = 0; while pages < 128 { let jump = jump.min(128 - pages); - total += model.gas_cost(jump, pages, pages).0; + total += model.gas_cost(jump, pages, pages); pages += jump; } - assert_eq!(total, 31999998); + assert_eq!(total, Gas(31999998)); } for jump in 1..=128 {