Skip to content

Commit

Permalink
Arbitrum Nitro Consensus v32
Browse files Browse the repository at this point in the history
  • Loading branch information
PlasmaPower committed Sep 24, 2024
1 parent dd8cf65 commit ce7d035
Show file tree
Hide file tree
Showing 30 changed files with 299 additions and 141 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ linters-settings:
gosec:
excludes:
- G404 # checks that random numbers are securely generated
- G115

govet:
enable-all: true
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ RUN apt-get update && \
FROM scratch as brotli-library-export
COPY --from=brotli-library-builder /workspace/install/ /

FROM node:16-bookworm-slim as contracts-builder
FROM node:18-bookworm-slim as contracts-builder
RUN apt-get update && \
apt-get install -y git python3 make g++ curl
RUN curl -L https://foundry.paradigm.xyz | bash && . ~/.bashrc && ~/.foundry/bin/foundryup
Expand Down
2 changes: 1 addition & 1 deletion arbitrator/arbutil/src/evm/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub trait EvmApi<D: DataReader>: 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) -> (Bytes32, u64);
fn get_bytes32(&mut self, key: Bytes32, evm_api_gas_to_use: u64) -> (Bytes32, u64);

/// 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`.
Expand Down
3 changes: 3 additions & 0 deletions arbitrator/arbutil/src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ pub const GASPRICE_GAS: u64 = GAS_QUICK_STEP;
// vm.GasQuickStep (see jump_table.go)
pub const ORIGIN_GAS: u64 = GAS_QUICK_STEP;

pub const ARBOS_VERSION_STYLUS_CHARGING_FIXES: u64 = 32;

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct EvmData {
pub arbos_version: u64,
pub block_basefee: Bytes32,
pub chainid: u64,
pub block_coinbase: Bytes20,
Expand Down
5 changes: 2 additions & 3 deletions arbitrator/arbutil/src/evm/req.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::{
user::UserOutcomeKind,
},
format::Utf8OrHex,
pricing::EVM_API_INK,
Bytes20, Bytes32,
};
use eyre::{bail, eyre, Result};
Expand Down Expand Up @@ -100,13 +99,13 @@ impl<D: DataReader, H: RequestHandler<D>> EvmApiRequestor<D, H> {
}

impl<D: DataReader, H: RequestHandler<D>> EvmApi<D> for EvmApiRequestor<D, H> {
fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64) {
fn get_bytes32(&mut self, key: Bytes32, evm_api_gas_to_use: u64) -> (Bytes32, u64) {
let cache = &mut self.storage_cache;
let mut cost = cache.read_gas();

let value = cache.entry(key).or_insert_with(|| {
let (res, _, gas) = self.handler.request(EvmApiMethod::GetBytes32, key);
cost = cost.saturating_add(gas).saturating_add(EVM_API_INK);
cost = cost.saturating_add(gas).saturating_add(evm_api_gas_to_use);
StorageWord::known(res.try_into().unwrap())
});
(value.value, cost)
Expand Down
2 changes: 2 additions & 0 deletions arbitrator/jit/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ pub fn create(opts: &Opts, env: WasmEnv) -> (Instance, FunctionEnv<WasmEnv>, Sto
"send_response" => func!(program::send_response),
"create_stylus_config" => func!(program::create_stylus_config),
"create_evm_data" => func!(program::create_evm_data),
"create_evm_data_v2" => func!(program::create_evm_data_v2),
"activate" => func!(program::activate),
"activate_v2" => func!(program::activate_v2),
},
};

Expand Down
93 changes: 89 additions & 4 deletions arbitrator/jit/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,54 @@ use prover::{
programs::{config::PricingParams, prelude::*},
};

/// activates a user program
const DEFAULT_STYLUS_ARBOS_VERSION: u64 = 31;

pub fn activate(
env: WasmEnvMut,
wasm_ptr: GuestPtr,
wasm_size: u32,
pages_ptr: GuestPtr,
asm_estimate_ptr: GuestPtr,
init_cost_ptr: GuestPtr,
cached_init_cost_ptr: GuestPtr,
stylus_version: u16,
debug: u32,
codehash: GuestPtr,
module_hash_ptr: GuestPtr,
gas_ptr: GuestPtr,
err_buf: GuestPtr,
err_buf_len: u32,
) -> Result<u32, Escape> {
activate_v2(
env,
wasm_ptr,
wasm_size,
pages_ptr,
asm_estimate_ptr,
init_cost_ptr,
cached_init_cost_ptr,
stylus_version,
DEFAULT_STYLUS_ARBOS_VERSION,
debug,
codehash,
module_hash_ptr,
gas_ptr,
err_buf,
err_buf_len,
)
}

/// activates a user program
pub fn activate_v2(
mut env: WasmEnvMut,
wasm_ptr: GuestPtr,
wasm_size: u32,
pages_ptr: GuestPtr,
asm_estimate_ptr: GuestPtr,
init_cost_ptr: GuestPtr,
cached_init_cost_ptr: GuestPtr,
version: u16,
stylus_version: u16,
arbos_version_for_gas: u64,
debug: u32,
codehash: GuestPtr,
module_hash_ptr: GuestPtr,
Expand All @@ -40,7 +78,15 @@ pub fn activate(

let page_limit = mem.read_u16(pages_ptr);
let gas_left = &mut mem.read_u64(gas_ptr);
match Module::activate(&wasm, codehash, version, page_limit, debug, gas_left) {
match Module::activate(
&wasm,
codehash,
stylus_version,
arbos_version_for_gas,
page_limit,
debug,
gas_left,
) {
Ok((module, data)) => {
mem.write_u64(gas_ptr, *gas_left);
mem.write_u16(pages_ptr, data.footprint);
Expand Down Expand Up @@ -222,9 +268,47 @@ pub fn create_stylus_config(
Ok(res as u64)
}

/// Creates an `EvmData` handler from its component parts.
pub fn create_evm_data(
env: WasmEnvMut,
block_basefee_ptr: GuestPtr,
chainid: u64,
block_coinbase_ptr: GuestPtr,
block_gas_limit: u64,
block_number: u64,
block_timestamp: u64,
contract_address_ptr: GuestPtr,
module_hash_ptr: GuestPtr,
msg_sender_ptr: GuestPtr,
msg_value_ptr: GuestPtr,
tx_gas_price_ptr: GuestPtr,
tx_origin_ptr: GuestPtr,
cached: u32,
reentrant: u32,
) -> Result<u64, Escape> {
create_evm_data_v2(
env,
DEFAULT_STYLUS_ARBOS_VERSION,
block_basefee_ptr,
chainid,
block_coinbase_ptr,
block_gas_limit,
block_number,
block_timestamp,
contract_address_ptr,
module_hash_ptr,
msg_sender_ptr,
msg_value_ptr,
tx_gas_price_ptr,
tx_origin_ptr,
cached,
reentrant,
)
}

/// Creates an `EvmData` handler from its component parts.
pub fn create_evm_data_v2(
mut env: WasmEnvMut,
arbos_version: u64,
block_basefee_ptr: GuestPtr,
chainid: u64,
block_coinbase_ptr: GuestPtr,
Expand All @@ -243,6 +327,7 @@ pub fn create_evm_data(
let (mut mem, _) = env.jit_env();

let evm_data = EvmData {
arbos_version,
block_basefee: mem.read_bytes32(block_basefee_ptr),
cached: cached != 0,
chainid,
Expand Down
9 changes: 8 additions & 1 deletion arbitrator/prover/src/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use crate::{
},
value::{ArbValueType, FunctionType, IntegerValType, Value},
};
use arbutil::{math::SaturatingSum, Bytes32, Color, DebugColor};
use arbutil::{
evm::ARBOS_VERSION_STYLUS_CHARGING_FIXES, math::SaturatingSum, Bytes32, Color, DebugColor,
};
use eyre::{bail, ensure, eyre, Result, WrapErr};
use fnv::{FnvHashMap as HashMap, FnvHashSet as HashSet};
use nom::{
Expand Down Expand Up @@ -641,6 +643,7 @@ impl<'a> WasmBinary<'a> {
/// Parses and instruments a user wasm
pub fn parse_user(
wasm: &'a [u8],
arbos_version_for_gas: u64,
page_limit: u16,
compile: &CompileConfig,
codehash: &Bytes32,
Expand Down Expand Up @@ -678,6 +681,10 @@ impl<'a> WasmBinary<'a> {
limit!(65536, code.expr.len(), "opcodes in func body");
}

if arbos_version_for_gas >= ARBOS_VERSION_STYLUS_CHARGING_FIXES {
limit!(513, bin.imports.len(), "imports")
}

let table_entries = bin.tables.iter().map(|x| x.initial).saturating_sum();
limit!(4096, table_entries, "table entries");

Expand Down
15 changes: 9 additions & 6 deletions arbitrator/prover/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,13 +350,16 @@ impl Module {
for import in &bin.imports {
let module = import.module;
let have_ty = &bin.types[import.offset as usize];
let (forward, import_name) = match import.name.strip_prefix(Module::FORWARDING_PREFIX) {
Some(name) => (true, name),
None => (false, import.name),
};
// allow_hostapi is only set for system modules like the
// forwarder. We restrict stripping the prefix for user modules.
let (forward, import_name) =
if allow_hostapi && import.name.starts_with(Self::FORWARDING_PREFIX) {
(true, &import.name[Self::FORWARDING_PREFIX.len()..])
} else {
(false, import.name)
};

let mut qualified_name = format!("{module}__{import_name}");
qualified_name = qualified_name.replace(&['/', '.', '-'] as &[char], "_");
let qualified_name = format!("{module}__{import_name}");

let func = if let Some(import) = available_imports.get(&qualified_name) {
let call = match forward {
Expand Down
100 changes: 53 additions & 47 deletions arbitrator/prover/src/programs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
programs::config::CompileConfig,
value::{FunctionType as ArbFunctionType, Value},
};
use arbutil::{math::SaturatingSum, Bytes32, Color};
use arbutil::{evm::ARBOS_VERSION_STYLUS_CHARGING_FIXES, math::SaturatingSum, Bytes32, Color};
use eyre::{bail, eyre, Report, Result, WrapErr};
use fnv::FnvHashMap as HashMap;
use std::fmt::Debug;
Expand Down Expand Up @@ -418,58 +418,64 @@ impl Module {
pub fn activate(
wasm: &[u8],
codehash: &Bytes32,
version: u16,
stylus_version: u16,
arbos_version_for_gas: u64, // must only be used for activation gas
page_limit: u16,
debug: bool,
gas: &mut u64,
) -> Result<(Self, StylusData)> {
// converts a number of microseconds to gas
// TODO: collapse to a single value after finalizing factors
let us_to_gas = |us: u64| {
let fudge = 2;
let sync_rate = 1_000_000 / 2;
let speed = 7_000_000;
us.saturating_mul(fudge * speed) / sync_rate
};

macro_rules! pay {
($us:expr) => {
let amount = us_to_gas($us);
if *gas < amount {
*gas = 0;
bail!("out of gas");
}
*gas -= amount;
let compile = CompileConfig::version(stylus_version, debug);
let (bin, stylus_data) =
WasmBinary::parse_user(wasm, arbos_version_for_gas, page_limit, &compile, codehash)
.wrap_err("failed to parse wasm")?;

if arbos_version_for_gas > 0 {
// converts a number of microseconds to gas
// TODO: collapse to a single value after finalizing factors
let us_to_gas = |us: u64| {
let fudge = 2;
let sync_rate = 1_000_000 / 2;
let speed = 7_000_000;
us.saturating_mul(fudge * speed) / sync_rate
};
}

// pay for wasm
let wasm_len = wasm.len() as u64;
pay!(wasm_len.saturating_mul(31_733 / 100_000));

let compile = CompileConfig::version(version, debug);
let (bin, stylus_data) = WasmBinary::parse_user(wasm, page_limit, &compile, codehash)
.wrap_err("failed to parse wasm")?;

// pay for funcs
let funcs = bin.functions.len() as u64;
pay!(funcs.saturating_mul(17_263) / 100_000);

// pay for data
let data = bin.datas.iter().map(|x| x.data.len()).saturating_sum() as u64;
pay!(data.saturating_mul(17_376) / 100_000);

// pay for elements
let elems = bin.elements.iter().map(|x| x.range.len()).saturating_sum() as u64;
pay!(elems.saturating_mul(17_376) / 100_000);

// pay for memory
let mem = bin.memories.first().map(|x| x.initial).unwrap_or_default();
pay!(mem.saturating_mul(2217));

// pay for code
let code = bin.codes.iter().map(|x| x.expr.len()).saturating_sum() as u64;
pay!(code.saturating_mul(535) / 1_000);
macro_rules! pay {
($us:expr) => {
let amount = us_to_gas($us);
if *gas < amount {
*gas = 0;
bail!("out of gas");
}
*gas -= amount;
};
}

// pay for wasm
if arbos_version_for_gas >= ARBOS_VERSION_STYLUS_CHARGING_FIXES {
let wasm_len = wasm.len() as u64;
pay!(wasm_len.saturating_mul(31_733) / 100_000);
}

// pay for funcs
let funcs = bin.functions.len() as u64;
pay!(funcs.saturating_mul(17_263) / 100_000);

// pay for data
let data = bin.datas.iter().map(|x| x.data.len()).saturating_sum() as u64;
pay!(data.saturating_mul(17_376) / 100_000);

// pay for elements
let elems = bin.elements.iter().map(|x| x.range.len()).saturating_sum() as u64;
pay!(elems.saturating_mul(17_376) / 100_000);

// pay for memory
let mem = bin.memories.first().map(|x| x.initial).unwrap_or_default();
pay!(mem.saturating_mul(2217));

// pay for code
let code = bin.codes.iter().map(|x| x.expr.len()).saturating_sum() as u64;
pay!(code.saturating_mul(535) / 1_000);
}

let module = Self::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data))
.wrap_err("failed to build user module")?;
Expand Down
Loading

0 comments on commit ce7d035

Please sign in to comment.