Skip to content

Commit

Permalink
Merge pull request #2672 from OffchainLabs/stylus_activation_pricing
Browse files Browse the repository at this point in the history
Stylus activation pricing
  • Loading branch information
tsahee authored Sep 20, 2024
2 parents 8fd0ff4 + c5bff79 commit 3befb69
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 71 deletions.
13 changes: 11 additions & 2 deletions arbitrator/jit/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ pub fn activate(
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 +41,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
95 changes: 50 additions & 45 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,63 @@ 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;
};
}

// 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 compile = CompileConfig::version(stylus_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));
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 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
13 changes: 11 additions & 2 deletions arbitrator/stylus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ impl RustBytes {
pub unsafe extern "C" fn stylus_activate(
wasm: GoSliceData,
page_limit: u16,
version: u16,
stylus_version: u16,
arbos_version_for_gas: u64,
debug: bool,
output: *mut RustBytes,
codehash: *const Bytes32,
Expand All @@ -153,7 +154,15 @@ pub unsafe extern "C" fn stylus_activate(
let codehash = &*codehash;
let gas = &mut *gas;

let (module, info) = match native::activate(wasm, codehash, version, page_limit, debug, gas) {
let (module, info) = match native::activate(
wasm,
codehash,
stylus_version,
arbos_version_for_gas,
page_limit,
debug,
gas,
) {
Ok(val) => val,
Err(err) => return output.write_err(err),
};
Expand Down
14 changes: 11 additions & 3 deletions arbitrator/stylus/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,13 +351,21 @@ pub fn module(wasm: &[u8], compile: CompileConfig, target: Target) -> Result<Vec
pub fn activate(
wasm: &[u8],
codehash: &Bytes32,
version: u16,
stylus_version: u16,
arbos_version_for_gas: u64,
page_limit: u16,
debug: bool,
gas: &mut u64,
) -> Result<(ProverModule, StylusData)> {
let (module, stylus_data) =
ProverModule::activate(wasm, codehash, version, page_limit, debug, gas)?;
let (module, stylus_data) = ProverModule::activate(
wasm,
codehash,
stylus_version,
arbos_version_for_gas,
page_limit,
debug,
gas,
)?;

Ok((module, stylus_data))
}
Expand Down
13 changes: 11 additions & 2 deletions arbitrator/wasm-libraries/user-host/src/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ pub unsafe extern "C" fn programs__activate(
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 @@ -58,7 +59,15 @@ pub unsafe extern "C" fn programs__activate(

let page_limit = STATIC_MEM.read_u16(pages_ptr);
let gas_left = &mut STATIC_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)) => {
STATIC_MEM.write_u64(gas_ptr, *gas_left);
STATIC_MEM.write_u16(pages_ptr, data.footprint);
Expand Down
20 changes: 13 additions & 7 deletions arbos/programs/native.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ func activateProgram(
codehash common.Hash,
wasm []byte,
page_limit uint16,
version uint16,
stylusVersion uint16,
arbosVersionForGas uint64,
debug bool,
burner burn.Burner,
) (*activationInfo, error) {
info, asmMap, err := activateProgramInternal(db, program, codehash, wasm, page_limit, version, debug, burner.GasLeft())
info, asmMap, err := activateProgramInternal(db, program, codehash, wasm, page_limit, stylusVersion, arbosVersionForGas, debug, burner.GasLeft())
if err != nil {
return nil, err
}
Expand All @@ -69,7 +70,8 @@ func activateProgramInternal(
codehash common.Hash,
wasm []byte,
page_limit uint16,
version uint16,
stylusVersion uint16,
arbosVersionForGas uint64,
debug bool,
gasLeft *uint64,
) (*activationInfo, map[ethdb.WasmTarget][]byte, error) {
Expand All @@ -81,7 +83,8 @@ func activateProgramInternal(
status_mod := userStatus(C.stylus_activate(
goSlice(wasm),
u16(page_limit),
u16(version),
u16(stylusVersion),
u64(arbosVersionForGas),
cbool(debug),
output,
&codeHash,
Expand Down Expand Up @@ -116,7 +119,7 @@ func activateProgramInternal(
output := &rustBytes{}
status_asm := C.stylus_compile(
goSlice(wasm),
u16(version),
u16(stylusVersion),
cbool(debug),
goSlice([]byte(target)),
output,
Expand Down Expand Up @@ -168,9 +171,12 @@ func getLocalAsm(statedb vm.StateDB, moduleHash common.Hash, addressForLogging c
return nil, fmt.Errorf("failed to reactivate program address: %v err: %w", addressForLogging, err)
}

unlimitedGas := uint64(0xffffffffffff)
// don't charge gas
zeroArbosVersion := uint64(0)
zeroGas := uint64(0)

// we know program is activated, so it must be in correct version and not use too much memory
info, asmMap, err := activateProgramInternal(statedb, addressForLogging, codeHash, wasm, pagelimit, program.version, debugMode, &unlimitedGas)
info, asmMap, err := activateProgramInternal(statedb, addressForLogging, codeHash, wasm, pagelimit, program.version, zeroArbosVersion, debugMode, &zeroGas)
if err != nil {
log.Error("failed to reactivate program", "address", addressForLogging, "expected moduleHash", moduleHash, "err", err)
return nil, fmt.Errorf("failed to reactivate program address: %v err: %w", addressForLogging, err)
Expand Down
4 changes: 2 additions & 2 deletions arbos/programs/programs.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (p Programs) CacheManagers() *addressSet.AddressSet {
return p.cacheManagers
}

func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, runMode core.MessageRunMode, debugMode bool) (
func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, arbosVersion uint64, runMode core.MessageRunMode, debugMode bool) (
uint16, common.Hash, common.Hash, *big.Int, bool, error,
) {
statedb := evm.StateDB
Expand Down Expand Up @@ -116,7 +116,7 @@ func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, runMode c
// require the program's footprint not exceed the remaining memory budget
pageLimit := am.SaturatingUSub(params.PageLimit, statedb.GetStylusPagesOpen())

info, err := activateProgram(statedb, address, codeHash, wasm, pageLimit, stylusVersion, debugMode, burner)
info, err := activateProgram(statedb, address, codeHash, wasm, pageLimit, stylusVersion, arbosVersion, debugMode, burner)
if err != nil {
return 0, codeHash, common.Hash{}, nil, true, err
}
Expand Down
9 changes: 6 additions & 3 deletions arbos/programs/wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ func programActivate(
asm_estimation_ptr unsafe.Pointer,
init_gas_ptr unsafe.Pointer,
cached_init_gas_ptr unsafe.Pointer,
version uint32,
stylusVersion uint32,
arbosVersion uint64,
debug uint32,
codehash unsafe.Pointer,
module_hash_ptr unsafe.Pointer,
Expand All @@ -59,7 +60,8 @@ func activateProgram(
codehash common.Hash,
wasm []byte,
pageLimit u16,
version u16,
stylusVersion u16,
arbosVersion uint64,
debug bool,
burner burn.Burner,
) (*activationInfo, error) {
Expand All @@ -79,7 +81,8 @@ func activateProgram(
unsafe.Pointer(&asmEstimate),
unsafe.Pointer(&initGas),
unsafe.Pointer(&cachedInitGas),
uint32(version),
uint32(stylusVersion),
arbosVersion,
debugMode,
arbutil.SliceToUnsafePointer(codehash[:]),
arbutil.SliceToUnsafePointer(moduleHash[:]),
Expand Down
11 changes: 7 additions & 4 deletions arbos/programs/wasmstorehelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ import (

// SaveActiveProgramToWasmStore is used to save active stylus programs to wasm store during rebuilding
func (p Programs) SaveActiveProgramToWasmStore(statedb *state.StateDB, codeHash common.Hash, code []byte, time uint64, debugMode bool, rebuildingStartBlockTime uint64) error {
params, err := p.Params()
progParams, err := p.Params()
if err != nil {
return err
}

program, err := p.getActiveProgram(codeHash, time, params)
program, err := p.getActiveProgram(codeHash, time, progParams)
if err != nil {
// The program is not active so return early
log.Info("program is not active, getActiveProgram returned error, hence do not include in rebuilding", "err", err)
Expand Down Expand Up @@ -56,10 +56,13 @@ func (p Programs) SaveActiveProgramToWasmStore(statedb *state.StateDB, codeHash
return fmt.Errorf("failed to reactivate program while rebuilding wasm store: %w", err)
}

unlimitedGas := uint64(0xffffffffffff)
// don't charge gas
zeroArbosVersion := uint64(0)
zeroGas := uint64(0)

// We know program is activated, so it must be in correct version and not use too much memory
// Empty program address is supplied because we dont have access to this during rebuilding of wasm store
info, asmMap, err := activateProgramInternal(statedb, common.Address{}, codeHash, wasm, params.PageLimit, program.version, debugMode, &unlimitedGas)
info, asmMap, err := activateProgramInternal(statedb, common.Address{}, codeHash, wasm, progParams.PageLimit, program.version, zeroArbosVersion, debugMode, &zeroGas)
if err != nil {
log.Error("failed to reactivate program while rebuilding wasm store", "expected moduleHash", moduleHash, "err", err)
return fmt.Errorf("failed to reactivate program while rebuilding wasm store: %w", err)
Expand Down
3 changes: 2 additions & 1 deletion precompiles/ArbWasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ func (con ArbWasm) ActivateProgram(c ctx, evm mech, value huge, program addr) (u
debug := evm.ChainConfig().DebugMode()
runMode := c.txProcessor.RunMode()
programs := c.State.Programs()
arbosVersion := c.State.ArbOSVersion()

// charge a fixed cost up front to begin activation
if err := c.Burn(1659168); err != nil {
return 0, nil, err
}
version, codeHash, moduleHash, dataFee, takeAllGas, err := programs.ActivateProgram(evm, program, runMode, debug)
version, codeHash, moduleHash, dataFee, takeAllGas, err := programs.ActivateProgram(evm, program, arbosVersion, runMode, debug)
if takeAllGas {
_ = c.BurnOut()
}
Expand Down

0 comments on commit 3befb69

Please sign in to comment.