From 9e037b8d83435cdf5c4fb0dac1a76ebfeddecd27 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Tue, 5 Sep 2023 10:56:55 -0600 Subject: [PATCH] implement hostio caching --- stylus-sdk/src/block.rs | 12 +++---- stylus-sdk/src/call/raw.rs | 6 ++-- stylus-sdk/src/contract.rs | 8 ++--- stylus-sdk/src/deploy/raw.rs | 6 ++-- stylus-sdk/src/hostio.rs | 66 +++++++++++++++++------------------- stylus-sdk/src/msg.rs | 6 ++-- stylus-sdk/src/tx.rs | 16 ++++----- 7 files changed, 58 insertions(+), 62 deletions(-) diff --git a/stylus-sdk/src/block.rs b/stylus-sdk/src/block.rs index 9fca675..517894a 100644 --- a/stylus-sdk/src/block.rs +++ b/stylus-sdk/src/block.rs @@ -6,23 +6,23 @@ use alloy_primitives::{Address, B256, U256}; wrap_hostio!( /// Gets the basefee of the current block. - basefee block_basefee U256 + basefee BASEFEE block_basefee U256 ); wrap_hostio!( /// Gets the unique chain identifier of the Arbitrum chain. - chainid chainid u64 + chainid CHAINID chainid u64 ); wrap_hostio!( /// Gets the coinbase of the current block, which on Arbitrum chains is the L1 batch poster's /// address. - coinbase block_coinbase Address + coinbase COINBASE block_coinbase Address ); wrap_hostio!( /// Gets the gas limit of the current block. - gas_limit block_gas_limit u64 + gas_limit GAS_LIMIT block_gas_limit u64 ); wrap_hostio!( @@ -31,7 +31,7 @@ wrap_hostio!( /// determined. /// /// [`Block Numbers and Time`]: https://developer.arbitrum.io/time - number block_number u64 + number NUMBER block_number u64 ); wrap_hostio!( @@ -40,5 +40,5 @@ wrap_hostio!( /// determined. /// /// [`Block Numbers and Time`]: https://developer.arbitrum.io/time - timestamp block_timestamp u64 + timestamp TIMESTAMP block_timestamp u64 ); diff --git a/stylus-sdk/src/call/raw.rs b/stylus-sdk/src/call/raw.rs index 70f30e7..91865e6 100644 --- a/stylus-sdk/src/call/raw.rs +++ b/stylus-sdk/src/call/raw.rs @@ -2,8 +2,8 @@ // For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md use crate::{ - contract::read_return_data, - hostio::{self, RETURN_DATA_SIZE}, + contract::{read_return_data, RETURN_DATA_LEN}, + hostio, storage::StorageCache, tx, ArbResult, }; @@ -180,7 +180,7 @@ impl RawCall { }; unsafe { - RETURN_DATA_SIZE.set(outs_len); + RETURN_DATA_LEN.set(outs_len); } let outs = read_return_data(self.offset, self.size); diff --git a/stylus-sdk/src/contract.rs b/stylus-sdk/src/contract.rs index de9dfa4..7008037 100644 --- a/stylus-sdk/src/contract.rs +++ b/stylus-sdk/src/contract.rs @@ -2,7 +2,7 @@ // For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md use crate::{ - hostio::{self, wrap_hostio, RETURN_DATA_SIZE}, + hostio::{self, wrap_hostio}, types::AddressVM, }; use alloy_primitives::{Address, U256}; @@ -29,7 +29,7 @@ pub fn output(data: &[u8]) { /// Copies the bytes of the last EVM call or deployment return result. /// Note: this function does not revert if out of bounds, but rather will copy the overlapping portion. pub fn read_return_data(offset: usize, size: Option) -> Vec { - let size = unsafe { size.unwrap_or_else(|| RETURN_DATA_SIZE.get().saturating_sub(offset)) }; + let size = size.unwrap_or_else(|| return_data_len().saturating_sub(offset)); let mut data = Vec::with_capacity(size); if size > 0 { @@ -45,12 +45,12 @@ pub fn read_return_data(offset: usize, size: Option) -> Vec { wrap_hostio!( /// Returns the length of the last EVM call or deployment return result, or `0` if neither have /// happened during the program's execution. - return_data_len return_data_size usize + return_data_len RETURN_DATA_LEN return_data_size usize ); wrap_hostio!( /// Gets the address of the current program. - address contract_address Address + address ADDRESS contract_address Address ); /// Gets the balance of the current program. diff --git a/stylus-sdk/src/deploy/raw.rs b/stylus-sdk/src/deploy/raw.rs index 58a2611..fef0f14 100644 --- a/stylus-sdk/src/deploy/raw.rs +++ b/stylus-sdk/src/deploy/raw.rs @@ -4,8 +4,8 @@ use alloy_primitives::{Address, B256, U256}; use crate::{ - contract::read_return_data, - hostio::{self, RETURN_DATA_SIZE}, + contract::{read_return_data, RETURN_DATA_LEN}, + hostio, }; /// Mechanism for performing raw deploys of other contracts. @@ -89,7 +89,7 @@ impl RawDeploy { &mut revert_data_len as *mut _, ); } - RETURN_DATA_SIZE.set(revert_data_len); + RETURN_DATA_LEN.set(revert_data_len); if contract.is_zero() { return Err(read_return_data(0, None)); diff --git a/stylus-sdk/src/hostio.rs b/stylus-sdk/src/hostio.rs index d2077d4..58f0847 100644 --- a/stylus-sdk/src/hostio.rs +++ b/stylus-sdk/src/hostio.rs @@ -323,23 +323,26 @@ extern "C" { } macro_rules! wrap_hostio { - ($(#[$meta:meta])* $name:ident $hostio:ident bool) => { - wrap_hostio!(@simple $(#[$meta])* $name, $hostio, bool); + ($(#[$meta:meta])* $name:ident $hostio:ident u64) => { + wrap_hostio!(@simple $(#[$meta])* $name, $hostio, u64); // uncached }; - ($(#[$meta:meta])* $name:ident $hostio:ident usize) => { - wrap_hostio!(@simple $(#[$meta])* $name, $hostio, usize); + ($(#[$meta:meta])* $name:ident $cache:ident $hostio:ident bool) => { + wrap_hostio!(@simple $(#[$meta])* $name, $cache, $hostio, bool); }; - ($(#[$meta:meta])* $name:ident $hostio:ident u64) => { - wrap_hostio!(@simple $(#[$meta])* $name, $hostio, u64); + ($(#[$meta:meta])* $name:ident $cache:ident $hostio:ident u32) => { + wrap_hostio!(@simple $(#[$meta])* $name, $cache, $hostio, u32); + }; + ($(#[$meta:meta])* $name:ident $cache:ident $hostio:ident u64) => { + wrap_hostio!(@simple $(#[$meta])* $name, $cache, $hostio, u64); }; - ($(#[$meta:meta])* $name:ident $hostio:ident Address) => { - wrap_hostio!(@arg $(#[$meta])* $name, $hostio, Address); + ($(#[$meta:meta])* $name:ident $cache:ident $hostio:ident usize) => { + wrap_hostio!(@simple $(#[$meta])* $name, $cache, $hostio, usize); }; - ($(#[$meta:meta])* $name:ident $hostio:ident B256) => { - wrap_hostio!(@arg $(#[$meta])* $name, $hostio, B256); + ($(#[$meta:meta])* $name:ident $cache:ident $hostio:ident Address) => { + wrap_hostio!(@convert $(#[$meta])* $name, $cache, $hostio, Address, Address); }; - ($(#[$meta:meta])* $name:ident $hostio:ident U256) => { - wrap_hostio!(@convert $(#[$meta])* $name, $hostio, B256, U256); + ($(#[$meta:meta])* $name:ident $cache:ident $hostio:ident U256) => { + wrap_hostio!(@convert $(#[$meta])* $name, $cache, $hostio, B256, U256); }; (@simple $(#[$meta:meta])* $name:ident, $hostio:ident, $ty:ident) => { $(#[$meta])* @@ -347,55 +350,48 @@ macro_rules! wrap_hostio { unsafe { $ty::from(hostio::$hostio()) } } }; - (@arg $(#[$meta:meta])* $name:ident, $hostio:ident, $ty:ident) => { + (@simple $(#[$meta:meta])* $name:ident, $cache:ident, $hostio:ident, $ty:ident) => { $(#[$meta])* pub fn $name() -> $ty { - let mut data = $ty::ZERO; - unsafe { hostio::$hostio(data.as_mut_ptr()) }; - data + unsafe{ $cache.get() } } + pub(crate) static mut $cache: hostio::CachedOption<$ty> = hostio::CachedOption::new(|| unsafe { hostio::$hostio() }); }; - (@convert $(#[$meta:meta])* $name:ident, $hostio:ident, $from:ident, $ty:ident) => { + (@convert $(#[$meta:meta])* $name:ident, $cache:ident, $hostio:ident, $from:ident, $ty:ident) => { $(#[$meta])* pub fn $name() -> $ty { + unsafe{ $cache.get() } + } + pub(crate) static mut $cache: hostio::CachedOption<$ty> = hostio::CachedOption::new(|| { let mut data = $from::ZERO; unsafe { hostio::$hostio(data.as_mut_ptr()) }; data.into() - } + }); }; } pub(crate) use wrap_hostio; -/// Caches the length of the most recent EVM return data -pub(crate) static mut RETURN_DATA_SIZE: CachedOption = CachedOption::new(return_data_size); - -/// Caches the current price of ink -pub(crate) static mut CACHED_INK_PRICE: CachedOption = CachedOption::new(tx_ink_price); - /// Caches a value to avoid paying for hostio invocations. pub(crate) struct CachedOption { value: Option, - loader: unsafe extern "C" fn() -> T, + loader: fn() -> T, } impl CachedOption { - const fn new(loader: unsafe extern "C" fn() -> T) -> Self { + /// Creates a new [`CachedOption`], which will use the `loader` during `get`. + pub const fn new(loader: fn() -> T) -> Self { let value = None; Self { value, loader } } - pub(crate) fn set(&mut self, value: T) { + /// Sets and overwrites the cached value. + pub fn set(&mut self, value: T) { self.value = Some(value); } - pub(crate) fn get(&mut self) -> T { - if let Some(value) = &self.value { - return *value; - } - - let value = unsafe { (self.loader)() }; - self.value = Some(value); - value + /// Gets the value, writing it to the cache if necessary. + pub fn get(&mut self) -> T { + *self.value.get_or_insert_with(|| (self.loader)()) } } diff --git a/stylus-sdk/src/msg.rs b/stylus-sdk/src/msg.rs index e580ff4..08ee183 100644 --- a/stylus-sdk/src/msg.rs +++ b/stylus-sdk/src/msg.rs @@ -6,7 +6,7 @@ use alloy_primitives::{Address, B256, U256}; wrap_hostio!( /// Whether the current call is reentrant. - reentrant msg_reentrant bool + reentrant REENTRANT msg_reentrant bool ); wrap_hostio!( @@ -20,10 +20,10 @@ wrap_hostio!( /// [`CALLER`]: https://www.evm.codes/#33 /// [`DELEGATE_CALL`]: https://www.evm.codes/#f4 /// [`Retryable Ticket Address Aliasing`]: https://developer.arbitrum.io/arbos/l1-to-l2-messaging#address-aliasing - sender msg_sender Address + sender SENDER msg_sender Address ); wrap_hostio!( /// Get the ETH value in wei sent to the program. - value msg_value U256 + value VALUE msg_value U256 ); diff --git a/stylus-sdk/src/tx.rs b/stylus-sdk/src/tx.rs index 23ea0a7..705b891 100644 --- a/stylus-sdk/src/tx.rs +++ b/stylus-sdk/src/tx.rs @@ -4,12 +4,12 @@ use crate::hostio::{self, wrap_hostio}; use alloy_primitives::{Address, B256, U256}; -/// Gets the price of ink in evm gas basis points. See [`Ink and Gas`] for more information on -/// Stylus's compute-pricing model. -/// -/// [`Ink and Gas`]: https://developer.arbitrum.io/TODO -pub fn ink_price() -> u32 { - unsafe { hostio::CACHED_INK_PRICE.get() } +wrap_hostio! { + /// Gets the price of ink in evm gas basis points. See [`Ink and Gas`] for more information on + /// Stylus's compute-pricing model. + /// + /// [`Ink and Gas`]: https://developer.arbitrum.io/TODO + ink_price INK_PRICE tx_ink_price u32 } /// Converts evm gas to ink. See [`Ink and Gas`] for more information on @@ -30,7 +30,7 @@ pub fn ink_to_gas(ink: u64) -> u64 { wrap_hostio!( /// Gets the gas price in wei per gas, which on Arbitrum chains equals the basefee. - gas_price tx_gas_price U256 + gas_price GAS_PRICE tx_gas_price U256 ); wrap_hostio!( @@ -38,5 +38,5 @@ wrap_hostio!( /// EVM's [`ORIGIN`] opcode. /// /// [`ORIGIN`]: https://www.evm.codes/#32 - origin tx_origin Address + origin ORIGIN tx_origin Address );