diff --git a/arbitrator/Cargo.lock b/arbitrator/Cargo.lock index a4166b341d..2d8d5b8cbf 100644 --- a/arbitrator/Cargo.lock +++ b/arbitrator/Cargo.lock @@ -50,13 +50,13 @@ dependencies = [ name = "arbutil" version = "0.1.0" dependencies = [ - "digest 0.9.0", + "digest", "eyre", "hex", "num-traits", "serde", - "sha3 0.10.8", "siphasher", + "tiny-keccak", "wasmparser", ] @@ -135,15 +135,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "block-padding" version = "0.2.1" @@ -397,14 +388,10 @@ dependencies = [ ] [[package]] -name = "crypto-common" -version = "0.1.6" +name = "crunchy" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "darling" @@ -508,16 +495,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", -] - [[package]] name = "dynasm" version = "1.2.3" @@ -805,7 +782,7 @@ dependencies = [ "prover", "rand", "rand_pcg", - "sha3 0.9.1", + "sha3", "structopt", "stylus", "thiserror", @@ -1174,7 +1151,7 @@ dependencies = [ "bincode", "brotli2", "derivative", - "digest 0.9.0", + "digest", "eyre", "fnv", "hex", @@ -1190,7 +1167,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sha3 0.9.1", + "sha3", "smallvec", "static_assertions", "structopt", @@ -1510,22 +1487,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", + "block-buffer", + "digest", "keccak", "opaque-debug", ] -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - [[package]] name = "shared-buffer" version = "0.1.3" @@ -1626,7 +1593,6 @@ dependencies = [ "parking_lot", "prover", "rand", - "sha3 0.10.8", "thiserror", "user-host-trait", "wasmer", @@ -1700,6 +1666,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/arbitrator/arbutil/Cargo.toml b/arbitrator/arbutil/Cargo.toml index c4b9c7035b..39bfe3277c 100644 --- a/arbitrator/arbutil/Cargo.toml +++ b/arbitrator/arbutil/Cargo.toml @@ -8,8 +8,8 @@ digest = "0.9.0" eyre = "0.6.5" hex = "0.4.3" num-traits = "0.2.17" -sha3 = "0.10.5" siphasher = "0.3.10" +tiny-keccak = { version = "2.0.2", features = ["keccak"] } wasmparser.workspace = true serde = { version = "1.0.130", features = ["derive", "rc"] } diff --git a/arbitrator/arbutil/src/crypto.rs b/arbitrator/arbutil/src/crypto.rs index c5afa2c329..3f5f57ca83 100644 --- a/arbitrator/arbutil/src/crypto.rs +++ b/arbitrator/arbutil/src/crypto.rs @@ -1,17 +1,24 @@ // Copyright 2022, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -use sha3::{Digest, Keccak256}; use siphasher::sip::SipHasher24; -use std::hash::Hasher; +use std::mem::MaybeUninit; +use tiny_keccak::{Hasher, Keccak}; pub fn keccak>(preimage: T) -> [u8; 32] { - let mut hasher = Keccak256::new(); - hasher.update(preimage); - hasher.finalize().into() + let mut output = MaybeUninit::<[u8; 32]>::uninit(); + let mut hasher = Keccak::v256(); + hasher.update(preimage.as_ref()); + + // SAFETY: finalize() writes 32 bytes + unsafe { + hasher.finalize(&mut *output.as_mut_ptr()); + output.assume_init() + } } pub fn siphash(preimage: &[u8], key: &[u8; 16]) -> u64 { + use std::hash::Hasher; let mut hasher = SipHasher24::new_with_key(key); hasher.write(preimage); hasher.finish() diff --git a/arbitrator/arbutil/src/pricing.rs b/arbitrator/arbutil/src/pricing.rs index dfbcc638d3..f779d6a9f6 100644 --- a/arbitrator/arbutil/src/pricing.rs +++ b/arbitrator/arbutil/src/pricing.rs @@ -2,10 +2,10 @@ // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE /// For hostios that may return something. -pub const HOSTIO_INK: u64 = 12513; +pub const HOSTIO_INK: u64 = 8400; /// For hostios that include pointers. -pub const PTR_INK: u64 = 22137 - HOSTIO_INK; +pub const PTR_INK: u64 = 13440 - HOSTIO_INK; /// For hostios that involve an API cost. pub const EVM_API_INK: u64 = 59673; diff --git a/arbitrator/prover/src/binary.rs b/arbitrator/prover/src/binary.rs index e4f7c2fc5a..a9f71d5aa0 100644 --- a/arbitrator/prover/src/binary.rs +++ b/arbitrator/prover/src/binary.rs @@ -510,7 +510,7 @@ impl<'a> Debug for WasmBinary<'a> { impl<'a> WasmBinary<'a> { /// Instruments a user wasm, producing a version bounded via configurable instrumentation. pub fn instrument(&mut self, compile: &CompileConfig) -> Result { - let meter = Meter::new(compile.pricing.costs); + let meter = Meter::new(&compile.pricing); let dygas = DynamicMeter::new(&compile.pricing); let depth = DepthChecker::new(compile.bounds); let bound = HeapBound::new(compile.bounds); diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 4cdb0d213a..69666e69b4 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -2507,7 +2507,7 @@ impl Machine { if !guards.is_empty() || self.guards.enabled { h.update(b"With guards:"); - h.update(&[self.guards.enabled as u8]); + h.update([self.guards.enabled as u8]); h.update(ErrorGuardProof::hash_guards(&guards)); } } diff --git a/arbitrator/prover/src/programs/config.rs b/arbitrator/prover/src/programs/config.rs index 4d4f331eff..63c281a7ae 100644 --- a/arbitrator/prover/src/programs/config.rs +++ b/arbitrator/prover/src/programs/config.rs @@ -112,6 +112,8 @@ pub struct CompilePricingParams { /// Associates opcodes to their ink costs #[derivative(Debug = "ignore")] pub costs: OpCosts, + /// Cost of checking the amount of ink left. + pub ink_header_cost: u64, /// Per-byte `MemoryFill` cost pub memory_fill_ink: u64, /// Per-byte `MemoryCopy` cost @@ -132,6 +134,7 @@ impl Default for CompilePricingParams { fn default() -> Self { Self { costs: |_, _| 0, + ink_header_cost: 0, memory_fill_ink: 0, memory_copy_ink: 0, } @@ -163,8 +166,9 @@ impl CompileConfig { config.bounds.max_frame_contention = 4096; config.pricing = CompilePricingParams { costs: meter::pricing_v1, - memory_fill_ink: 1000 / 8, - memory_copy_ink: 1000 / 8, + ink_header_cost: 2450, + memory_fill_ink: 800 / 8, + memory_copy_ink: 800 / 8, }; } _ => panic!("no config exists for Stylus version {version}"), @@ -186,7 +190,7 @@ impl CompileConfig { compiler.canonicalize_nans(true); compiler.enable_verifier(); - let meter = MiddlewareWrapper::new(Meter::new(self.pricing.costs)); + let meter = MiddlewareWrapper::new(Meter::new(&self.pricing)); let dygas = MiddlewareWrapper::new(DynamicMeter::new(&self.pricing)); let depth = MiddlewareWrapper::new(DepthChecker::new(self.bounds)); let bound = MiddlewareWrapper::new(HeapBound::new(self.bounds)); diff --git a/arbitrator/prover/src/programs/meter.rs b/arbitrator/prover/src/programs/meter.rs index 3c1031cb5b..149fb81ce1 100644 --- a/arbitrator/prover/src/programs/meter.rs +++ b/arbitrator/prover/src/programs/meter.rs @@ -2,7 +2,10 @@ // For license information, see https://github.com/nitro/blob/master/LICENSE use crate::{ - programs::{config::PricingParams, FuncMiddleware, Middleware, ModuleMod}, + programs::{ + config::{CompilePricingParams, PricingParams, SigMap}, + FuncMiddleware, Middleware, ModuleMod, + }, value::FunctionType, Machine, }; @@ -18,7 +21,7 @@ use std::{ use wasmer_types::{GlobalIndex, GlobalInit, LocalFunctionIndex, SignatureIndex, Type}; use wasmparser::{BlockType, Operator}; -use super::config::SigMap; +use super::config::OpCosts; pub const STYLUS_INK_LEFT: &str = "stylus_ink_left"; pub const STYLUS_INK_STATUS: &str = "stylus_ink_status"; @@ -30,22 +33,29 @@ impl OpcodePricer for T where T: Fn(&Operator, &SigMap) -> u64 + Send + Sync #[derive(Derivative)] #[derivative(Debug)] pub struct Meter { + /// Associates opcodes to their ink costs. #[derivative(Debug = "ignore")] costs: F, + /// Cost of checking the amount of ink left. + header_cost: u64, + /// Ink and ink status globals. globals: RwLock>, /// The types of the module being instrumented sigs: RwLock>>, } -impl Meter { - pub fn new(costs: F) -> Self { +impl Meter { + pub fn new(pricing: &CompilePricingParams) -> Meter { Self { - costs, + costs: pricing.costs, + header_cost: pricing.ink_header_cost, globals: RwLock::default(), sigs: RwLock::default(), } } +} +impl Meter { pub fn globals(&self) -> [GlobalIndex; 2] { self.globals.read().expect("missing globals") } @@ -75,6 +85,7 @@ where ink, status, self.costs.clone(), + self.header_cost, sigs.clone(), )) } @@ -87,18 +98,20 @@ where #[derive(Derivative)] #[derivative(Debug)] pub struct FuncMeter<'a, F: OpcodePricer> { - /// Represents the amount of ink left for consumption + /// Represents the amount of ink left for consumption. ink_global: GlobalIndex, - /// Represents whether the machine is out of ink + /// Represents whether the machine is out of ink. status_global: GlobalIndex, - /// Instructions of the current basic block + /// Instructions of the current basic block. block: Vec>, - /// The accumulated cost of the current basic block + /// The accumulated cost of the current basic block. block_cost: u64, - /// Associates opcodes to their ink costs + /// Cost of checking the amount of ink left. + header_cost: u64, + /// Associates opcodes to their ink costs. #[derivative(Debug = "ignore")] costs: F, - /// The types of the module being instrumented + /// The types of the module being instrumented. sigs: Arc, } @@ -107,6 +120,7 @@ impl<'a, F: OpcodePricer> FuncMeter<'a, F> { ink_global: GlobalIndex, status_global: GlobalIndex, costs: F, + header_cost: u64, sigs: Arc, ) -> Self { Self { @@ -114,6 +128,7 @@ impl<'a, F: OpcodePricer> FuncMeter<'a, F> { status_global, block: vec![], block_cost: 0, + header_cost, costs, sigs, } @@ -139,7 +154,10 @@ impl<'a, F: OpcodePricer> FuncMiddleware<'a> for FuncMeter<'a, F> { let status = self.status_global.as_u32(); let blockty = BlockType::Empty; - let mut header = [ + // include the cost of executing the header + cost = cost.saturating_add(self.header_cost); + + out.extend([ // if ink < cost => panic with status = 1 GlobalGet { global_index: ink }, I64Const { value: cost as i64 }, @@ -156,16 +174,7 @@ impl<'a, F: OpcodePricer> FuncMiddleware<'a> for FuncMeter<'a, F> { I64Const { value: cost as i64 }, I64Sub, GlobalSet { global_index: ink }, - ]; - - // include the cost of executing the header - for op in &header { - cost = cost.saturating_add((self.costs)(op, &self.sigs)) - } - header[1] = I64Const { value: cost as i64 }; - header[9] = I64Const { value: cost as i64 }; - - out.extend(header); + ]); out.extend(self.block.drain(..)); self.block_cost = 0; } @@ -268,17 +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(18287, 31, bytes.saturating_sub(32))) + self.buy_ink(sat_add_mul(5040, 25, 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(40423, 61, bytes.saturating_sub(32))) + self.buy_ink(sat_add_mul(16381, 54, bytes.saturating_sub(32))) } /// Pays for both I/O and keccak. fn pay_for_keccak(&mut self, bytes: u32) -> Result<(), OutOfInkError> { - self.buy_ink(sat_add_mul(268527, 41920, evm::evm_words(bytes))) + let words = evm::evm_words(bytes).saturating_sub(2); + self.buy_ink(sat_add_mul(121800, 21000, words)) } /// Pays for copying bytes from geth. @@ -364,41 +374,42 @@ pub fn pricing_v1(op: &Operator, tys: &HashMap) -> op!(Unreachable, Return) => 1, op!(Nop, Drop) | dot!(I32Const, I64Const) => 1, dot!(Block, Loop) | op!(Else, End) => 1, - dot!(Br, BrIf, If) => 2400, - dot!(Select) => 4000, // TODO: improve wasmer codegen - dot!(Call) => 13750, - dot!(LocalGet, LocalTee) => 200, - dot!(LocalSet) => 375, - dot!(GlobalGet) => 300, - dot!(GlobalSet) => 990, - dot!(I32Load, I32Load8S, I32Load8U, I32Load16S, I32Load16U) => 2200, - dot!(I64Load, I64Load8S, I64Load8U, I64Load16S, I64Load16U, I64Load32S, I64Load32U) => 2750, - dot!(I32Store, I32Store8, I32Store16) => 2400, - dot!(I64Store, I64Store8, I64Store16, I64Store32) => 3100, - dot!(MemorySize) => 13500, + dot!(Br, BrIf, If) => 765, + dot!(Select) => 1250, // TODO: improve wasmer codegen + dot!(Call) => 3800, + dot!(LocalGet, LocalTee) => 75, + dot!(LocalSet) => 210, + dot!(GlobalGet) => 225, + dot!(GlobalSet) => 575, + dot!(I32Load, I32Load8S, I32Load8U, I32Load16S, I32Load16U) => 670, + dot!(I64Load, I64Load8S, I64Load8U, I64Load16S, I64Load16U, I64Load32S, I64Load32U) => 680, + dot!(I32Store, I32Store8, I32Store16) => 825, + dot!(I64Store, I64Store8, I64Store16, I64Store32) => 950, + dot!(MemorySize) => 3000, dot!(MemoryGrow) => 1, // cost handled by memory pricer - op!(I32Eqz, I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU) => 570, - op!(I64Eqz, I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU) => 760, - - op!(I32Clz, I32Ctz) => 750, - op!(I32Popcnt) => 500, - op!(I32Add, I32Sub) => 200, - op!(I32Mul) => 550, - op!(I32DivS, I32DivU, I32RemS, I32RemU) => 2500, - op!(I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr) => 200, - - op!(I64Clz, I64Ctz) => 750, - op!(I64Popcnt) => 750, - op!(I64Add, I64Sub) => 200, - op!(I64Mul) => 550, - op!(I64DivS, I64DivU, I64RemS, I64RemU) => 2900, - op!(I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr) => 200, - - op!(I32WrapI64, I64ExtendI32S, I64ExtendI32U) => 200, - op!(I32Extend8S, I32Extend16S, I64Extend8S, I64Extend16S, I64Extend32S) => 200, - dot!(MemoryCopy) => 3100, - dot!(MemoryFill) => 3100, + op!(I32Eqz, I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU) => 170, + op!(I64Eqz, I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU) => 225, + + op!(I32Clz, I32Ctz) => 210, + op!(I32Add, I32Sub) => 70, + op!(I32Mul) => 160, + op!(I32DivS, I32DivU, I32RemS, I32RemU) => 1120, + op!(I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr) => 70, + + op!(I64Clz, I64Ctz) => 210, + op!(I64Add, I64Sub) => 100, + op!(I64Mul) => 160, + op!(I64DivS, I64DivU, I64RemS, I64RemU) => 1270, + op!(I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr) => 100, + + op!(I32Popcnt) => 2650, // slow on ARM, fast on x86 + op!(I64Popcnt) => 6000, // slow on ARM, fast on x86 + + op!(I32WrapI64, I64ExtendI32S, I64ExtendI32U) => 100, + op!(I32Extend8S, I32Extend16S, I64Extend8S, I64Extend16S, I64Extend32S) => 100, + dot!(MemoryCopy) => 950, + dot!(MemoryFill) => 950, BrTable { targets } => { 2400 + 325 * targets.len() as u64 diff --git a/arbitrator/stylus/Cargo.toml b/arbitrator/stylus/Cargo.toml index 8621e4b80f..f57f200f3d 100644 --- a/arbitrator/stylus/Cargo.toml +++ b/arbitrator/stylus/Cargo.toml @@ -21,7 +21,6 @@ libc = "0.2.108" eyre = "0.6.5" rand = "0.8.5" fnv = "1.0.7" -sha3 = "0.10.5" hex = "0.4.3" [dev-dependencies] diff --git a/arbitrator/wasm-libraries/Cargo.lock b/arbitrator/wasm-libraries/Cargo.lock index e9019773f9..2cd7b611d6 100644 --- a/arbitrator/wasm-libraries/Cargo.lock +++ b/arbitrator/wasm-libraries/Cargo.lock @@ -26,13 +26,13 @@ dependencies = [ name = "arbutil" version = "0.1.0" dependencies = [ - "digest 0.9.0", + "digest", "eyre", "hex", "num-traits", "serde", - "sha3 0.10.6", "siphasher", + "tiny-keccak", "wasmparser", ] @@ -96,15 +96,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] - [[package]] name = "block-padding" version = "0.2.1" @@ -172,14 +163,10 @@ dependencies = [ ] [[package]] -name = "crypto-common" -version = "0.1.6" +name = "crunchy" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "darling" @@ -270,16 +257,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.3", - "crypto-common", -] - [[package]] name = "either" version = "1.8.1" @@ -743,7 +720,7 @@ dependencies = [ "arbutil", "bincode", "derivative", - "digest 0.9.0", + "digest", "eyre", "fnv", "hex", @@ -758,7 +735,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sha3 0.9.1", + "sha3", "smallvec", "static_assertions", "structopt", @@ -956,22 +933,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", + "block-buffer", + "digest", "keccak", "opaque-debug", ] -[[package]] -name = "sha3" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" -dependencies = [ - "digest 0.10.6", - "keccak", -] - [[package]] name = "simdutf8" version = "0.1.4" @@ -1098,6 +1065,15 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/arbitrator/wasm-libraries/user-host-trait/src/lib.rs b/arbitrator/wasm-libraries/user-host-trait/src/lib.rs index c96aa25a45..83114d8ba9 100644 --- a/arbitrator/wasm-libraries/user-host-trait/src/lib.rs +++ b/arbitrator/wasm-libraries/user-host-trait/src/lib.rs @@ -90,6 +90,7 @@ pub trait UserHost: GasMeteredMachine { fn write_result(&mut self, ptr: u32, len: u32) -> Result<(), Self::Err> { self.buy_ink(HOSTIO_INK)?; self.pay_for_read(len)?; + self.pay_for_geth_bytes(len)?; // returned after call *self.outs() = self.read_slice(ptr, len)?; trace!("write_result", self, &*self.outs(), &[]) } @@ -232,6 +233,7 @@ pub trait UserHost: GasMeteredMachine { { self.buy_ink(HOSTIO_INK + 3 * PTR_INK + EVM_API_INK)?; self.pay_for_read(calldata_len)?; + self.pay_for_geth_bytes(calldata_len)?; let gas_passed = gas; gas = gas.min(self.gas_left()?); // provide no more than what the user has @@ -360,6 +362,7 @@ pub trait UserHost: GasMeteredMachine { { self.buy_ink(HOSTIO_INK + cost)?; self.pay_for_read(code_len)?; + self.pay_for_geth_bytes(code_len)?; let code = self.read_slice(code, code_len)?; let code_copy = self.evm_data().tracing.then(|| code.clone()); diff --git a/arbitrator/wasm-libraries/user-test/src/host.rs b/arbitrator/wasm-libraries/user-test/src/host.rs index d5b29bf8a6..391d994f82 100644 --- a/arbitrator/wasm-libraries/user-test/src/host.rs +++ b/arbitrator/wasm-libraries/user-test/src/host.rs @@ -25,6 +25,7 @@ pub unsafe extern "C" fn vm_hooks__read_args(ptr: u32) { pub unsafe extern "C" fn vm_hooks__write_result(ptr: u32, len: u32) { let mut program = Program::start(0); program.pay_for_read(len).unwrap(); + program.pay_for_geth_bytes(len).unwrap(); OUTS = wavm::read_slice_u32(ptr, len); } diff --git a/arbos/programs/native.go b/arbos/programs/native.go index cb6cab0d09..584854d794 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -70,7 +70,7 @@ func activateProgram( (*u64)(burner.GasLeft()), )) - data, msg, err := status.toResult(output.intoBytes(), debug) + data, msg, err := status.toResult(output.takeBytes(), debug) if err != nil { if debug { log.Warn("activation failed", "err", err, "msg", msg, "program", program) @@ -124,7 +124,7 @@ func callProgram( depth := interpreter.Depth() debug := stylusParams.debugMode != 0 - data, msg, err := status.toResult(output.intoBytes(), debug) + data, msg, err := status.toResult(output.takeBytes(), debug) if status == userFailure && debug { log.Warn("program failure", "err", err, "msg", msg, "program", address, "depth", depth) } @@ -312,7 +312,7 @@ func (vec *rustBytes) read() []byte { return arbutil.PointerToSlice((*byte)(vec.ptr), int(vec.len)) } -func (vec *rustBytes) intoBytes() []byte { +func (vec *rustBytes) takeBytes() []byte { slice := vec.read() vec.drop() return slice diff --git a/arbutil/unsafe.go b/arbutil/unsafe.go index b29ac605ed..67b3e0a7d3 100644 --- a/arbutil/unsafe.go +++ b/arbutil/unsafe.go @@ -12,9 +12,16 @@ func SliceToPointer[T any](slice []T) *T { return &slice[0] } +// does a defensive copy due to Go's lake of immutable types func PointerToSlice[T any](pointer *T, length int) []T { output := make([]T, length) source := unsafe.Slice(pointer, length) copy(output, source) return output } + +func CopySlice[T any](slice []T) []T { + output := make([]T, len(slice)) + copy(output, slice) + return output +} diff --git a/system_tests/benchmarks_test.go b/system_tests/benchmarks_test.go index 05f685e1c1..174a217d55 100644 --- a/system_tests/benchmarks_test.go +++ b/system_tests/benchmarks_test.go @@ -49,6 +49,9 @@ func TestBenchmarkGas(t *testing.T) { bench("ecrecover", func() *types.Receipt { return ensure(programTest.FillBlockRecover(&auth)) }) + bench("mulmod", func() *types.Receipt { + return ensure(programTest.FillBlockMulMod(&auth)) + }) bench("keccak", func() *types.Receipt { return ensure(programTest.FillBlockHash(&auth)) })