Skip to content
This repository has been archived by the owner on Nov 26, 2024. It is now read-only.

Update hostios to use wasmer FunctionEnv method #56

Merged
merged 3 commits into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions arbitrator/arbutil/src/evm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright 2023, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE

// params.SstoreSentryGasEIP2200
pub const SSTORE_SENTRY_EVM_GAS: u64 = 2300;

// params.LogGas and params.LogDataGas
pub const LOG_TOPIC_GAS: u64 = 375;
pub const LOG_DATA_GAS: u64 = 8;

// params.CopyGas
pub const COPY_WORD_GAS: u64 = 3;
1 change: 1 addition & 0 deletions arbitrator/arbutil/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

pub mod color;
pub mod crypto;
pub mod evm;
pub mod format;
pub mod math;
pub mod operator;
Expand Down
172 changes: 7 additions & 165 deletions arbitrator/stylus/src/env.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// Copyright 2022-2023, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE

use crate::host;
use arbutil::evm;
use eyre::{eyre, ErrReport};
use ouroboros::self_referencing;
use prover::{
programs::{
config::{PricingParams, StylusConfig},
Expand All @@ -18,62 +17,9 @@ use std::{
};
use thiserror::Error;
use wasmer::{
AsStoreMut, AsStoreRef, FunctionEnvMut, Global, Memory, MemoryAccessError, MemoryView,
StoreMut, StoreRef, WasmPtr,
AsStoreRef, FunctionEnvMut, Global, Memory, MemoryAccessError, MemoryView, StoreMut, WasmPtr,
};

#[self_referencing]
pub struct MemoryViewContainer {
memory: Memory,
#[borrows(memory)]
#[covariant]
view: MemoryView<'this>,
}

impl MemoryViewContainer {
fn create(env: &WasmEnvMut<'_>) -> Self {
// this func exists to properly constrain the closure's type
fn closure<'a>(
store: &'a StoreRef,
) -> impl (for<'b> FnOnce(&'b Memory) -> MemoryView<'b>) + 'a {
move |memory: &Memory| memory.view(&store)
}

let store = env.as_store_ref();
let memory = env.data().memory.clone().unwrap();
let view_builder = closure(&store);
MemoryViewContainerBuilder {
memory,
view_builder,
}
.build()
}

pub fn view(&self) -> &MemoryView {
self.borrow_view()
}

pub fn read_slice(&self, ptr: u32, len: u32) -> Result<Vec<u8>, MemoryAccessError> {
let mut data = vec![0; len as usize];
self.view().read(ptr.into(), &mut data)?;
Ok(data)
}

pub fn read_bytes20(&self, ptr: u32) -> eyre::Result<Bytes20> {
let data = self.read_slice(ptr, 20)?;
Ok(data.try_into()?)
}

pub fn read_bytes32(&self, ptr: u32) -> eyre::Result<Bytes32> {
let data = self.read_slice(ptr, 32)?;
Ok(data.try_into()?)
}

pub fn write_slice(&self, ptr: u32, src: &[u8]) -> Result<(), MemoryAccessError> {
self.view().write(ptr.into(), src)
}
}

pub type WasmEnvMut<'a> = FunctionEnvMut<'a, WasmEnv>;

#[derive(Default)]
Expand Down Expand Up @@ -197,10 +143,6 @@ impl WasmEnv {
self.evm_data.as_ref().expect("no evm data")
}

pub fn memory(env: &mut WasmEnvMut<'_>) -> MemoryViewContainer {
MemoryViewContainer::create(env)
}

pub fn return_data_len(&self) -> u32 {
self.evm_ref().return_data_len
}
Expand All @@ -209,23 +151,6 @@ impl WasmEnv {
self.evm().return_data_len = len;
}

pub fn data<'a, 'b: 'a>(env: &'a mut WasmEnvMut<'b>) -> (&'a mut Self, MemoryViewContainer) {
let memory = MemoryViewContainer::create(env);
(env.data_mut(), memory)
}

pub fn meter<'a, 'b>(env: &'a mut WasmEnvMut<'b>) -> MeterState<'a> {
let state = env.data().meter.clone().unwrap();
let store = env.as_store_mut();
MeterState::new(state, store)
}

pub fn begin<'a, 'b>(env: &'a mut WasmEnvMut<'b>) -> Result<MeterState<'a>, Escape> {
let mut state = Self::meter(env);
state.buy_gas(state.pricing.hostio_cost)?;
Ok(state)
}

pub fn start<'a, 'b>(env: &'a mut WasmEnvMut<'b>) -> Result<HostioInfo<'a>, Escape> {
let (env, store) = env.data_and_store_mut();
let memory = env.memory.clone().unwrap();
Expand Down Expand Up @@ -282,15 +207,15 @@ impl<'a> HostioInfo<'a> {

pub fn pay_for_evm_copy(&mut self, bytes: u64) -> MaybeEscape {
let evm_words = |count: u64| count.saturating_mul(31) / 32;
let evm_gas = evm_words(bytes).saturating_mul(host::COPY_WORD_GAS);
let evm_gas = evm_words(bytes).saturating_mul(evm::COPY_WORD_GAS);
self.buy_evm_gas(evm_gas)
}

pub fn view(&self) -> MemoryView {
self.memory.view(&self.store.as_store_ref())
}

pub fn write_u8(&mut self, ptr: u32, x: u8) -> Result<&mut Self, MemoryAccessError> {
pub fn _write_u8(&mut self, ptr: u32, x: u8) -> Result<&mut Self, MemoryAccessError> {
let ptr: WasmPtr<u8> = WasmPtr::new(ptr);
ptr.deref(&self.view()).write(x)?;
Ok(self)
Expand All @@ -302,7 +227,7 @@ impl<'a> HostioInfo<'a> {
Ok(self)
}

pub fn write_u64(&mut self, ptr: u32, x: u64) -> Result<&mut Self, MemoryAccessError> {
pub fn _write_u64(&mut self, ptr: u32, x: u64) -> Result<&mut Self, MemoryAccessError> {
let ptr: WasmPtr<u64> = WasmPtr::new(ptr);
ptr.deref(&self.view()).write(x)?;
Ok(self)
Expand Down Expand Up @@ -333,7 +258,7 @@ impl<'a> HostioInfo<'a> {
Ok(())
}

pub fn write_bytes32(&self, ptr: u32, src: Bytes32) -> eyre::Result<()> {
pub fn _write_bytes32(&self, ptr: u32, src: Bytes32) -> eyre::Result<()> {
self.write_slice(ptr, &src.0)?;
Ok(())
}
Expand Down Expand Up @@ -376,89 +301,6 @@ impl<'a> DerefMut for HostioInfo<'a> {
}
}

pub struct MeterState<'a> {
state: MeterData,
store: StoreMut<'a>,
}

impl<'a> Deref for MeterState<'a> {
type Target = MeterData;

fn deref(&self) -> &Self::Target {
&self.state
}
}

impl<'a> DerefMut for MeterState<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.state
}
}

impl<'a> MeterState<'a> {
pub fn new(state: MeterData, store: StoreMut<'a>) -> Self {
Self { state, store }
}

pub fn buy_gas(&mut self, gas: u64) -> MaybeEscape {
let MachineMeter::Ready(gas_left) = self.gas_left() else {
return Escape::out_of_gas();
};
if gas_left < gas {
return Escape::out_of_gas();
}
self.set_gas(gas_left - gas);
Ok(())
}

pub fn buy_evm_gas(&mut self, evm: u64) -> MaybeEscape {
let wasm_gas = self.pricing.evm_to_wasm(evm);
self.buy_gas(wasm_gas)
}

/// Checks if the user has enough evm gas, but doesn't burn any
pub fn require_evm_gas(&mut self, evm: u64) -> MaybeEscape {
let wasm_gas = self.pricing.evm_to_wasm(evm);
let MachineMeter::Ready(gas_left) = self.gas_left() else {
return Escape::out_of_gas();
};
match gas_left < wasm_gas {
true => Escape::out_of_gas(),
false => Ok(()),
}
}

pub fn pay_for_evm_copy(&mut self, bytes: u64) -> MaybeEscape {
let evm_words = |count: u64| count.saturating_mul(31) / 32;
let evm_gas = evm_words(bytes).saturating_mul(host::COPY_WORD_GAS);
self.buy_evm_gas(evm_gas)
}
}

impl<'a> MeteredMachine for MeterState<'a> {
fn gas_left(&mut self) -> MachineMeter {
let store = &mut self.store;
let state = &self.state;

let status = state.gas_status.get(store);
let status = status.try_into().expect("type mismatch");
let gas = state.gas_left.get(store);
let gas = gas.try_into().expect("type mismatch");

match status {
0_u32 => MachineMeter::Ready(gas),
_ => MachineMeter::Exhausted,
}
}

fn set_gas(&mut self, gas: u64) {
let store = &mut self.store;
let state = &self.state;
state.gas_left.set(store, gas.into()).unwrap();
state.gas_status.set(store, 0.into()).unwrap();
}
}

impl EvmAPI {
pub fn load_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64) {
(self.get_bytes32)(key)
Expand Down Expand Up @@ -539,7 +381,7 @@ pub enum Escape {
}

impl Escape {
pub fn internal<T>(error: &'static str) -> Result<T, Escape> {
pub fn _internal<T>(error: &'static str) -> Result<T, Escape> {
Err(Self::Internal(eyre!(error)))
}

Expand Down
64 changes: 22 additions & 42 deletions arbitrator/stylus/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,39 @@
// For license information, see https://github.com/nitro/blob/master/LICENSE

use crate::env::{Escape, MaybeEscape, WasmEnv, WasmEnvMut};
use arbutil::Color;
use arbutil::{evm, Color};
use prover::programs::prelude::*;

// params.SstoreSentryGasEIP2200 (see operations_acl_arbitrum.go)
pub const SSTORE_SENTRY_EVM_GAS: u64 = 2300;

// params.LogGas and params.LogDataGas
pub const LOG_TOPIC_GAS: u64 = 375;
pub const LOG_DATA_GAS: u64 = 8;

// params.CopyGas
pub const COPY_WORD_GAS: u64 = 3;

pub(crate) fn read_args(mut env: WasmEnvMut, ptr: u32) -> MaybeEscape {
WasmEnv::begin(&mut env)?;

let (env, memory) = WasmEnv::data(&mut env);
memory.write_slice(ptr, &env.args)?;
let mut env = WasmEnv::start(&mut env)?;
env.pay_for_evm_copy(env.args.len() as u64)?;
env.write_slice(ptr, &env.args)?;
Ok(())
}

pub(crate) fn return_data(mut env: WasmEnvMut, ptr: u32, len: u32) -> MaybeEscape {
let mut meter = WasmEnv::begin(&mut env)?;
meter.pay_for_evm_copy(len.into())?;

let (env, memory) = WasmEnv::data(&mut env);
env.outs = memory.read_slice(ptr, len)?;
let mut env = WasmEnv::start(&mut env)?;
env.pay_for_evm_copy(len.into())?;
env.outs = env.read_slice(ptr, len)?;
Ok(())
}

pub(crate) fn account_load_bytes32(mut env: WasmEnvMut, key: u32, dest: u32) -> MaybeEscape {
WasmEnv::begin(&mut env)?;

let (data, memory) = WasmEnv::data(&mut env);
let key = memory.read_bytes32(key)?;
let (value, cost) = data.evm().load_bytes32(key);
memory.write_slice(dest, &value.0)?;

let mut meter = WasmEnv::meter(&mut env);
meter.buy_evm_gas(cost)
let mut env = WasmEnv::start(&mut env)?;
let key = env.read_bytes32(key)?;
let (value, cost) = env.evm().load_bytes32(key);
env.write_slice(dest, &value.0)?;
env.buy_evm_gas(cost)
}

pub(crate) fn account_store_bytes32(mut env: WasmEnvMut, key: u32, value: u32) -> MaybeEscape {
let mut meter = WasmEnv::begin(&mut env)?;
meter.require_evm_gas(SSTORE_SENTRY_EVM_GAS)?;

let (data, memory) = WasmEnv::data(&mut env);
let key = memory.read_bytes32(key)?;
let value = memory.read_bytes32(value)?;
let cost = data.evm().store_bytes32(key, value)?;
let mut env = WasmEnv::start(&mut env)?;
env.require_evm_gas(evm::SSTORE_SENTRY_EVM_GAS)?; // see operations_acl_arbitrum.go

let mut meter = WasmEnv::meter(&mut env);
meter.buy_evm_gas(cost)
let key = env.read_bytes32(key)?;
let value = env.read_bytes32(value)?;
let cost = env.evm().store_bytes32(key, value)?;
env.buy_evm_gas(cost)
}

pub(crate) fn call_contract(
Expand Down Expand Up @@ -200,8 +180,8 @@ pub(crate) fn emit_log(mut env: WasmEnvMut, data: u32, len: u32, topics: u32) ->
if length < topics * 32 || topics > 4 {
return Escape::logical("bad topic data");
}
env.buy_evm_gas((1 + topics) * LOG_TOPIC_GAS)?;
env.buy_evm_gas((length - topics * 32) * LOG_DATA_GAS)?;
env.buy_evm_gas((1 + topics) * evm::LOG_TOPIC_GAS)?;
env.buy_evm_gas((length - topics * 32) * evm::LOG_DATA_GAS)?;

let data = env.read_slice(data, len)?;
env.evm().emit_log(data, topics as usize)?;
Expand All @@ -216,8 +196,8 @@ pub(crate) fn tx_origin(mut env: WasmEnvMut, data: u32) -> MaybeEscape {
}

pub(crate) fn debug_println(mut env: WasmEnvMut, ptr: u32, len: u32) -> MaybeEscape {
let memory = WasmEnv::memory(&mut env);
let text = memory.read_slice(ptr, len)?;
let env = WasmEnv::start(&mut env)?;
let text = env.read_slice(ptr, len)?;
println!(
"{} {}",
"Stylus says:".yellow(),
Expand Down
7 changes: 7 additions & 0 deletions arbitrator/wasm-libraries/user-host/src/gas.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2022-2023, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE

use arbutil::evm;
use prover::programs::config::PricingParams;
use std::ops::Deref;

Expand Down Expand Up @@ -44,4 +45,10 @@ impl Pricing {
let wasm_gas = evm.saturating_mul(100_00) / self.wasm_gas_price;
self.buy_gas(wasm_gas)
}

pub fn pay_for_evm_copy(&self, bytes: usize) {
let evm_words = |count: u64| count.saturating_mul(31) / 32;
let evm_gas = evm_words(bytes as u64).saturating_mul(evm::COPY_WORD_GAS);
self.buy_evm_gas(evm_gas)
}
}
Loading