From cbca272af9b491483230db7aa0717487055173f0 Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Fri, 27 Dec 2024 14:24:25 -0600 Subject: [PATCH] Parametrizing SDK Storage by a Host --- stylus-proc/src/macros/entrypoint.rs | 6 +- stylus-proc/src/macros/storage.rs | 24 +++- stylus-sdk/src/abi/mod.rs | 3 +- stylus-sdk/src/host/mod.rs | 4 + stylus-sdk/src/storage/array.rs | 19 +++- stylus-sdk/src/storage/bytes.rs | 60 +++++----- stylus-sdk/src/storage/map.rs | 35 ++++-- stylus-sdk/src/storage/mod.rs | 158 ++++++++++++++++++--------- stylus-sdk/src/storage/traits.rs | 41 +++---- stylus-sdk/src/storage/vec.rs | 30 +++-- 10 files changed, 242 insertions(+), 138 deletions(-) diff --git a/stylus-proc/src/macros/entrypoint.rs b/stylus-proc/src/macros/entrypoint.rs index b358342..b9180d6 100644 --- a/stylus-proc/src/macros/entrypoint.rs +++ b/stylus-proc/src/macros/entrypoint.rs @@ -126,7 +126,7 @@ fn top_level_storage_impl(item: &syn::ItemStruct) -> syn::ItemImpl { fn struct_entrypoint_fn(name: &Ident) -> syn::ItemFn { parse_quote! { - fn #STRUCT_ENTRYPOINT_FN(input: alloc::vec::Vec, host: Rc) -> stylus_sdk::ArbResult { + fn #STRUCT_ENTRYPOINT_FN(input: alloc::vec::Vec, host: &H) -> stylus_sdk::ArbResult { stylus_sdk::abi::router_entrypoint::<#name, #name, H>(input, host) } } @@ -155,12 +155,12 @@ fn user_entrypoint_fn(user_fn: Ident) -> syn::ItemFn { parse_quote! { #[no_mangle] pub extern "C" fn user_entrypoint(len: usize) -> usize { - let host = Rc::new(WasmHost{}); + let host = WasmHost{}; #deny_reentrant host.pay_for_memory_grow(0); let input = host.args(len); - let (data, status) = match #user_fn(input, Rc::clone(&host)) { + let (data, status) = match #user_fn(input, &host) { Ok(data) => (data, 0), Err(data) => (data, 1), }; diff --git a/stylus-proc/src/macros/storage.rs b/stylus-proc/src/macros/storage.rs index 69dcffa..8dd142e 100644 --- a/stylus-proc/src/macros/storage.rs +++ b/stylus-proc/src/macros/storage.rs @@ -73,7 +73,7 @@ impl Storage { const SLOT_BYTES: usize = 32; const REQUIRED_SLOTS: usize = Self::required_slots(); - unsafe fn new(mut root: stylus_sdk::alloy_primitives::U256, offset: u8, host: Rc) -> Self { + unsafe fn new(mut root: stylus_sdk::alloy_primitives::U256, offset: u8, host: *const H) -> Self { use stylus_sdk::{storage, alloy_primitives}; debug_assert!(offset == 0); @@ -96,6 +96,20 @@ impl Storage { } } } + + fn impl_host_access(&self) -> syn::ItemImpl { + let name = &self.name; + let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); + parse_quote! { + impl #impl_generics stylus_sdk::host::HostAccess for #name #ty_generics #where_clause { + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } + } + } + } } impl From<&mut syn::ItemStruct> for Storage { @@ -126,6 +140,7 @@ impl ToTokens for Storage { fn to_tokens(&self, tokens: &mut TokenStream) { self.item_impl().to_tokens(tokens); self.impl_storage_type().to_tokens(tokens); + self.impl_host_access().to_tokens(tokens); for field in &self.fields { field.impl_borrow(&self.name).to_tokens(tokens); field.impl_borrow_mut(&self.name).to_tokens(tokens); @@ -166,7 +181,7 @@ impl StorageField { return None; }; let ty = &self.ty; - if ty.to_token_stream().to_string() == "Rc < H >".to_string() { + if ty.to_token_stream().to_string() == "*const H".to_string() { return None; } Some(parse_quote! { @@ -180,7 +195,7 @@ impl StorageField { space -= bytes; let root = root + alloy_primitives::U256::from(slot); - let field = <#ty as storage::StorageType>::new(root, space as u8, Rc::clone(&host)); + let field = <#ty as storage::StorageType>::new(root, space as u8, host); if words > 0 { slot += words; space = 32; @@ -192,7 +207,8 @@ impl StorageField { fn size(&self) -> TokenStream { let ty = &self.ty; - if ty.to_token_stream().to_string() == "Rc < H >".to_string() { + println!("{}", ty.to_token_stream().to_string()); + if ty.to_token_stream().to_string() == "*const H".to_string() { return quote! {}; } quote! { diff --git a/stylus-sdk/src/abi/mod.rs b/stylus-sdk/src/abi/mod.rs index 7aa9e13..15d0e96 100644 --- a/stylus-sdk/src/abi/mod.rs +++ b/stylus-sdk/src/abi/mod.rs @@ -18,7 +18,6 @@ use alloc::vec::Vec; use alloy_primitives::U256; use core::borrow::BorrowMut; -use rclite::Rc; use alloy_sol_types::{abi::TokenSeq, private::SolTypeValue, SolType}; @@ -98,7 +97,7 @@ where // if no value is received in the transaction. It is implicitly payable. // - Fallback is called when no other function matches a selector. If a receive function is not // defined, then calls with no input calldata will be routed to the fallback function. -pub fn router_entrypoint(input: alloc::vec::Vec, host: Rc) -> ArbResult +pub fn router_entrypoint(input: alloc::vec::Vec, host: &H) -> ArbResult where R: Router, S: StorageType + TopLevelStorage + BorrowMut, diff --git a/stylus-sdk/src/host/mod.rs b/stylus-sdk/src/host/mod.rs index c079535..662d76a 100644 --- a/stylus-sdk/src/host/mod.rs +++ b/stylus-sdk/src/host/mod.rs @@ -27,6 +27,10 @@ pub trait Host: { } +pub trait HostAccess { + fn get_host(&self) -> &H; +} + /// TODO pub trait CryptographyAccess { /// TODO diff --git a/stylus-sdk/src/storage/array.rs b/stylus-sdk/src/storage/array.rs index 4f38ad1..a9e6ee0 100644 --- a/stylus-sdk/src/storage/array.rs +++ b/stylus-sdk/src/storage/array.rs @@ -1,18 +1,17 @@ // Copyright 2023-2024, Offchain Labs, Inc. // For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/licenses/COPYRIGHT.md -use crate::host::Host; +use crate::host::{Host, HostAccess}; use super::{Erase, StorageGuard, StorageGuardMut, StorageType}; use alloy_primitives::U256; use core::marker::PhantomData; -use rclite::Rc; /// Accessor for a storage-backed array. pub struct StorageArray, const N: usize> { slot: U256, marker: PhantomData, - host: Rc, + host: *const H, } impl, const N: usize> StorageType for StorageArray { @@ -27,7 +26,7 @@ impl, const N: usize> StorageType for StorageArray const REQUIRED_SLOTS: usize = Self::required_slots(); - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self { debug_assert!(offset == 0); Self { slot, @@ -45,6 +44,14 @@ impl, const N: usize> StorageType for StorageArray } } +impl, const N: usize> HostAccess for StorageArray { + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } +} + impl, const N: usize> StorageArray { /// Gets the number of elements stored. /// @@ -82,7 +89,7 @@ impl, const N: usize> StorageArray { return None; } let (slot, offset) = self.index_slot(index); - Some(S::new(slot, offset, Rc::clone(&self.host))) + Some(S::new(slot, offset, self.get_host())) } /// Gets the underlying accessor to the element at a given index, even if out of bounds. @@ -92,7 +99,7 @@ impl, const N: usize> StorageArray { /// Enables aliasing. UB if out of bounds. unsafe fn accessor_unchecked(&self, index: usize) -> S { let (slot, offset) = self.index_slot(index); - S::new(slot, offset, Rc::clone(&self.host)) + S::new(slot, offset, self.get_host()) } /// Gets the element at the given index, if it exists. diff --git a/stylus-sdk/src/storage/bytes.rs b/stylus-sdk/src/storage/bytes.rs index e8c08d2..145516d 100644 --- a/stylus-sdk/src/storage/bytes.rs +++ b/stylus-sdk/src/storage/bytes.rs @@ -2,20 +2,22 @@ // For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/licenses/COPYRIGHT.md use super::{Erase, GlobalStorage, Storage, StorageB8, StorageGuard, StorageGuardMut, StorageType}; -use crate::{crypto, host::Host}; +use crate::{ + crypto, + host::{Host, HostAccess}, +}; use alloc::{ string::{String, ToString}, vec::Vec, }; use alloy_primitives::{U256, U8}; use core::cell::OnceCell; -use rclite::Rc; /// Accessor for storage-backed bytes. pub struct StorageBytes { root: U256, base: OnceCell, - host: Rc, + host: *const H, } impl StorageType for StorageBytes { @@ -28,7 +30,7 @@ impl StorageType for StorageBytes { where Self: 'a; - unsafe fn new(root: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(root: U256, offset: u8, host: *const H) -> Self { debug_assert!(offset == 0); Self { root, @@ -46,6 +48,14 @@ impl StorageType for StorageBytes { } } +impl HostAccess for StorageBytes { + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } +} + impl StorageBytes { /// Returns `true` if the collection contains no elements. pub fn is_empty(&self) -> bool { @@ -54,7 +64,7 @@ impl StorageBytes { /// Gets the number of bytes stored. pub fn len(&self) -> usize { - let word = Storage::get_word(Rc::clone(&self.host), self.root); + let word = Storage::get_word(self.get_host(), self.root); // check if the data is short let slot: &[u8] = word.as_ref(); @@ -83,15 +93,15 @@ impl StorageBytes { // if shrinking, pull data in if (len < 32) && (old > 32) { - let word = Storage::get_word(Rc::clone(&self.host), *self.base()); - Storage::set_word(Rc::clone(&self.host), self.root, word); + let word = Storage::get_word(self.get_host(), *self.base()); + Storage::set_word(self.get_host(), self.root, word); return self.write_len(len); } // if growing, push data out - let mut word = Storage::get_word(Rc::clone(&self.host), self.root); + let mut word = Storage::get_word(self.get_host(), self.root); word[31] = 0; // clear len byte - Storage::set_word(Rc::clone(&self.host), *self.base(), word); + Storage::set_word(self.get_host(), *self.base(), word); self.write_len(len) } @@ -99,14 +109,10 @@ impl StorageBytes { unsafe fn write_len(&mut self, len: usize) { if len < 32 { // place the len in the last byte of the root with the long bit low - Storage::set_uint(Rc::clone(&self.host), self.root, 31, U8::from(len * 2)); + Storage::set_uint(self.get_host(), self.root, 31, U8::from(len * 2)); } else { // place the len in the root with the long bit high - Storage::set_word( - Rc::clone(&self.host), - self.root, - U256::from(len * 2 + 1).into(), - ) + Storage::set_word(self.get_host(), self.root, U256::from(len * 2 + 1).into()) } } @@ -118,7 +124,7 @@ impl StorageBytes { macro_rules! assign { ($slot:expr) => { unsafe { - Storage::set_uint(Rc::clone(&self.host), $slot, index % 32, value); // pack value + Storage::set_uint(self.get_host(), $slot, index % 32, value); // pack value self.write_len(index + 1); } }; @@ -131,8 +137,8 @@ impl StorageBytes { // convert to multi-word representation if index == 31 { // copy content over (len byte will be overwritten) - let word = Storage::get_word(Rc::clone(&self.host), self.root); - unsafe { Storage::set_word(Rc::clone(&self.host), *self.base(), word) }; + let word = Storage::get_word(self.get_host(), self.root); + unsafe { Storage::set_word(self.get_host(), *self.base(), word) }; } let slot = self.base() + U256::from(index / 32); @@ -152,13 +158,13 @@ impl StorageBytes { let clean = index % 32 == 0; let byte = self.get(index)?; - let clear = |slot| unsafe { Storage::clear_word(Rc::clone(&self.host), slot) }; + let clear = |slot| unsafe { Storage::clear_word(self.get_host(), slot) }; // convert to single-word representation if len == 32 { // copy content over - let word = Storage::get_word(Rc::clone(&self.host), *self.base()); - unsafe { Storage::set_word(Rc::clone(&self.host), self.root, word) }; + let word = Storage::get_word(self.get_host(), *self.base()); + unsafe { Storage::set_word(self.get_host(), self.root, word) }; clear(*self.base()); } @@ -169,7 +175,7 @@ impl StorageBytes { // clear the value if len < 32 { - unsafe { Storage::set_byte(Rc::clone(&self.host), self.root, index, 0) }; + unsafe { Storage::set_byte(self.get_host(), self.root, index, 0) }; } // set the new length @@ -193,7 +199,7 @@ impl StorageBytes { return None; } let (slot, offset) = self.index_slot(index); - let value = unsafe { StorageB8::new(slot, offset, Rc::clone(&self.host)) }; + let value = unsafe { StorageB8::new(slot, offset, self.get_host()) }; Some(StorageGuardMut::new(value)) } @@ -204,7 +210,7 @@ impl StorageBytes { /// UB if index is out of bounds. pub unsafe fn get_unchecked(&self, index: usize) -> u8 { let (slot, offset) = self.index_slot(index); - unsafe { Storage::get_byte(Rc::clone(&self.host), slot, offset.into()) } + unsafe { Storage::get_byte(self.get_host(), slot, offset.into()) } } /// Gets the full contents of the collection. @@ -247,11 +253,11 @@ impl Erase for StorageBytes { if len > 31 { while len > 0 { let slot = self.index_slot(len as usize - 1).0; - unsafe { Storage::clear_word(Rc::clone(&self.host), slot) }; + unsafe { Storage::clear_word(self.get_host(), slot) }; len -= 32; } } - unsafe { Storage::clear_word(Rc::clone(&self.host), self.root) }; + unsafe { Storage::clear_word(self.get_host(), self.root) }; } } @@ -284,7 +290,7 @@ impl StorageType for StorageString { where Self: 'a; - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self { Self(StorageBytes::new(slot, offset, host)) } diff --git a/stylus-sdk/src/storage/map.rs b/stylus-sdk/src/storage/map.rs index 66df923..62c7544 100644 --- a/stylus-sdk/src/storage/map.rs +++ b/stylus-sdk/src/storage/map.rs @@ -1,19 +1,21 @@ // Copyright 2023-2024, Offchain Labs, Inc. // For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/licenses/COPYRIGHT.md -use crate::{crypto, host::Host}; +use crate::{ + crypto, + host::{Host, HostAccess}, +}; use super::{Erase, SimpleStorageType, StorageGuard, StorageGuardMut, StorageType}; use alloc::{string::String, vec::Vec}; use alloy_primitives::{Address, FixedBytes, Signed, Uint, B256, U160, U256}; use core::marker::PhantomData; -use rclite::Rc; /// Accessor for a storage-backed map. pub struct StorageMap> { slot: U256, marker: PhantomData<(K, V)>, - host: Rc, + host: *const H, } impl StorageType for StorageMap @@ -31,7 +33,7 @@ where where Self: 'a; - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self { debug_assert!(offset == 0); Self { slot, @@ -49,6 +51,19 @@ where } } +impl HostAccess for StorageMap +where + K: StorageKey, + V: StorageType, + H: Host, +{ + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } +} + impl StorageMap where K: StorageKey, @@ -63,7 +78,7 @@ where /// to that of `&self`. pub fn getter(&self, key: K) -> StorageGuard { let slot = key.to_slot(self.slot.into()); - unsafe { StorageGuard::new(V::new(slot, Self::CHILD_OFFSET, Rc::clone(&self.host))) } + unsafe { StorageGuard::new(V::new(slot, Self::CHILD_OFFSET, self.get_host())) } } /// Gets a mutable accessor to the element at the given key, or the zero-value is none is there. @@ -71,7 +86,7 @@ where /// to that of `&mut self`. pub fn setter(&mut self, key: K) -> StorageGuardMut { let slot = key.to_slot(self.slot.into()); - unsafe { StorageGuardMut::new(V::new(slot, Self::CHILD_OFFSET, Rc::clone(&self.host))) } + unsafe { StorageGuardMut::new(V::new(slot, Self::CHILD_OFFSET, self.get_host())) } } /// Gets the element at the given key, or the zero value if none is there. @@ -99,8 +114,8 @@ where let slot = key.to_slot(self.slot.into()); // intentionally alias so that we can erase after load unsafe { - let store = V::new(slot, Self::CHILD_OFFSET, Rc::clone(&self.host)); - let mut alias = V::new(slot, Self::CHILD_OFFSET, Rc::clone(&self.host)); + let store = V::new(slot, Self::CHILD_OFFSET, self.get_host()); + let mut alias = V::new(slot, Self::CHILD_OFFSET, self.get_host()); let prior = store.load(); alias.set_by_wrapped(value); prior @@ -113,8 +128,8 @@ where let slot = key.to_slot(self.slot.into()); // intentionally alias so that we can erase after load unsafe { - let store = V::new(slot, Self::CHILD_OFFSET, Rc::clone(&self.host)); - let mut alias = V::new(slot, Self::CHILD_OFFSET, Rc::clone(&self.host)); + let store = V::new(slot, Self::CHILD_OFFSET, self.get_host()); + let mut alias = V::new(slot, Self::CHILD_OFFSET, self.get_host()); let value = store.load(); alias.erase(); value diff --git a/stylus-sdk/src/storage/mod.rs b/stylus-sdk/src/storage/mod.rs index 924a1d3..ca2d3af 100644 --- a/stylus-sdk/src/storage/mod.rs +++ b/stylus-sdk/src/storage/mod.rs @@ -22,11 +22,13 @@ //! //! [overview]: https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#storage -use crate::{host::Host, hostio}; +use crate::{ + host::{Host, HostAccess}, + hostio, +}; use alloy_primitives::{Address, BlockHash, BlockNumber, FixedBytes, Signed, Uint, B256, U256}; use alloy_sol_types::sol_data::{ByteCount, IntBitCount, SupportedFixedBytes, SupportedInt}; use core::{cell::OnceCell, marker::PhantomData, ops::Deref}; -use rclite::Rc; pub use array::StorageArray; pub use bytes::{StorageBytes, StorageString}; @@ -52,10 +54,8 @@ pub struct StorageCache; impl GlobalStorage for StorageCache { /// Retrieves a 32-byte EVM word from persistent storage. - fn get_word(host: Rc, key: U256) -> B256 { - let mut data = B256::ZERO; - unsafe { hostio::storage_load_bytes32(B256::from(key).as_ptr(), data.as_mut_ptr()) }; - data + fn get_word(host: &H, key: U256) -> B256 { + host.load(key) } /// Stores a 32-byte EVM word to persistent storage. @@ -63,8 +63,8 @@ impl GlobalStorage for StorageCache { /// # Safety /// /// May alias storage. - unsafe fn set_word(host: Rc, key: U256, value: B256) { - hostio::storage_cache_bytes32(B256::from(key).as_ptr(), value.as_ptr()) + unsafe fn set_word(host: &H, key: U256, value: B256) { + host.cache(key, value) } } @@ -150,7 +150,7 @@ where slot: U256, offset: u8, cached: OnceCell>, - host: Rc, + host: *const H, } impl StorageUint @@ -166,7 +166,19 @@ where /// Sets the underlying [`alloy_primitives::Uint`] in persistent storage. pub fn set(&mut self, value: Uint) { overwrite_cell(&mut self.cached, value); - unsafe { Storage::set_uint(Rc::clone(&self.host), self.slot, self.offset.into(), value) }; + unsafe { Storage::set_uint(self.get_host(), self.slot, self.offset.into(), value) }; + } +} + +impl HostAccess for StorageUint +where + IntBitCount: SupportedInt, + H: Host, +{ + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } } } @@ -186,7 +198,7 @@ where const SLOT_BYTES: usize = (B / 8); - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self { debug_assert!(B <= 256); Self { slot, @@ -238,7 +250,7 @@ where fn deref(&self) -> &Self::Target { self.cached.get_or_init(|| unsafe { - Storage::get_uint(Rc::clone(&self.host), self.slot, self.offset.into()) + Storage::get_uint(self.get_host(), self.slot, self.offset.into()) }) } } @@ -267,7 +279,7 @@ where slot: U256, offset: u8, cached: OnceCell>, - host: Rc, + host: *const H, } impl StorageSigned @@ -283,7 +295,7 @@ where /// Gets the underlying [`Signed`] in persistent storage. pub fn set(&mut self, value: Signed) { overwrite_cell(&mut self.cached, value); - unsafe { Storage::set_signed(Rc::clone(&self.host), self.slot, self.offset.into(), value) }; + unsafe { Storage::set_signed(self.get_host(), self.slot, self.offset.into(), value) }; } } @@ -303,7 +315,7 @@ where const SLOT_BYTES: usize = (B / 8); - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self { Self { slot, offset, @@ -324,6 +336,18 @@ where } } +impl HostAccess for StorageSigned +where + IntBitCount: SupportedInt, + H: Host, +{ + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } +} + impl<'a, H, const B: usize, const L: usize> SimpleStorageType<'a, H> for StorageSigned where IntBitCount: SupportedInt, @@ -357,7 +381,7 @@ where fn deref(&self) -> &Self::Target { self.cached.get_or_init(|| unsafe { - Storage::get_signed(Rc::clone(&self.host), self.slot, self.offset.into()) + Storage::get_signed(self.get_host(), self.slot, self.offset.into()) }) } } @@ -378,7 +402,7 @@ pub struct StorageFixedBytes { slot: U256, offset: u8, cached: OnceCell>, - host: Rc, + host: *const H, } impl StorageFixedBytes { @@ -390,7 +414,7 @@ impl StorageFixedBytes { /// Gets the underlying [`FixedBytes`] in persistent storage. pub fn set(&mut self, value: FixedBytes) { overwrite_cell(&mut self.cached, value); - unsafe { Storage::set(Rc::clone(&self.host), self.slot, self.offset.into(), value) } + unsafe { Storage::set(self.get_host(), self.slot, self.offset.into(), value) } } } @@ -410,7 +434,7 @@ where const SLOT_BYTES: usize = N; - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self { Self { slot, offset, @@ -431,6 +455,17 @@ where } } +impl HostAccess for StorageFixedBytes +where + H: Host, +{ + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } +} + impl<'a, H, const N: usize> SimpleStorageType<'a, H> for StorageFixedBytes where ByteCount: SupportedFixedBytes, @@ -456,9 +491,8 @@ impl Deref for StorageFixedBytes { type Target = FixedBytes; fn deref(&self) -> &Self::Target { - self.cached.get_or_init(|| unsafe { - Storage::get(Rc::clone(&self.host), self.slot, self.offset.into()) - }) + self.cached + .get_or_init(|| unsafe { Storage::get(self.get_host(), self.slot, self.offset.into()) }) } } @@ -474,7 +508,7 @@ pub struct StorageBool { slot: U256, offset: u8, cached: OnceCell, - host: Rc, + host: *const H, } impl StorageBool { @@ -486,14 +520,7 @@ impl StorageBool { /// Gets the underlying [`bool`] in persistent storage. pub fn set(&mut self, value: bool) { overwrite_cell(&mut self.cached, value); - unsafe { - Storage::set_byte( - Rc::clone(&self.host), - self.slot, - self.offset.into(), - value as u8, - ) - } + unsafe { Storage::set_byte(self.get_host(), self.slot, self.offset.into(), value as u8) } } } @@ -509,7 +536,7 @@ impl StorageType for StorageBool { const SLOT_BYTES: usize = 1; - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self { Self { slot, offset, @@ -530,6 +557,14 @@ impl StorageType for StorageBool { } } +impl HostAccess for StorageBool { + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } +} + impl<'a, H: Host> SimpleStorageType<'a, H> for StorageBool where Self: 'a, @@ -553,7 +588,7 @@ impl Deref for StorageBool { fn deref(&self) -> &Self::Target { self.cached.get_or_init(|| unsafe { - let data = Storage::get_byte(Rc::clone(&self.host), self.slot, self.offset.into()); + let data = Storage::get_byte(self.get_host(), self.slot, self.offset.into()); data != 0 }) } @@ -571,7 +606,7 @@ pub struct StorageAddress { slot: U256, offset: u8, cached: OnceCell
, - host: Rc, + host: *const H, } impl StorageAddress { @@ -583,14 +618,7 @@ impl StorageAddress { /// Gets the underlying [`Address`] in persistent storage. pub fn set(&mut self, value: Address) { overwrite_cell(&mut self.cached, value); - unsafe { - Storage::set::<20>( - Rc::clone(&self.host), - self.slot, - self.offset.into(), - value.into(), - ) - } + unsafe { Storage::set::<20>(self.get_host(), self.slot, self.offset.into(), value.into()) } } } @@ -606,7 +634,7 @@ impl StorageType for StorageAddress { const SLOT_BYTES: usize = 20; - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self { Self { slot, offset, @@ -627,6 +655,14 @@ impl StorageType for StorageAddress { } } +impl HostAccess for StorageAddress { + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } +} + impl<'a, H: Host> SimpleStorageType<'a, H> for StorageAddress where Self: 'a, @@ -647,7 +683,7 @@ impl Deref for StorageAddress { fn deref(&self) -> &Self::Target { self.cached.get_or_init(|| unsafe { - Storage::get::<20>(Rc::clone(&self.host), self.slot, self.offset.into()).into() + Storage::get::<20>(self.get_host(), self.slot, self.offset.into()).into() }) } } @@ -667,7 +703,7 @@ pub struct StorageBlockNumber { slot: U256, offset: u8, cached: OnceCell, - host: Rc, + host: *const H, } impl StorageBlockNumber { @@ -680,7 +716,7 @@ impl StorageBlockNumber { pub fn set(&mut self, value: BlockNumber) { overwrite_cell(&mut self.cached, value); let value = FixedBytes::from(value.to_be_bytes()); - unsafe { Storage::set::<8>(Rc::clone(&self.host), self.slot, self.offset.into(), value) }; + unsafe { Storage::set::<8>(self.get_host(), self.slot, self.offset.into(), value) }; } } @@ -696,7 +732,7 @@ impl StorageType for StorageBlockNumber { const SLOT_BYTES: usize = 8; - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self { Self { slot, offset, @@ -717,6 +753,14 @@ impl StorageType for StorageBlockNumber { } } +impl HostAccess for StorageBlockNumber { + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } +} + impl<'a, H: Host> SimpleStorageType<'a, H> for StorageBlockNumber where Self: 'a, @@ -737,7 +781,7 @@ impl Deref for StorageBlockNumber { fn deref(&self) -> &Self::Target { self.cached.get_or_init(|| unsafe { - let data = Storage::get::<8>(Rc::clone(&self.host), self.slot, self.offset.into()); + let data = Storage::get::<8>(self.get_host(), self.slot, self.offset.into()); u64::from_be_bytes(data.0) }) } @@ -757,7 +801,7 @@ impl From> for BlockNumber { pub struct StorageBlockHash { slot: U256, cached: OnceCell, - host: Rc, + host: *const H, } impl StorageBlockHash { @@ -769,7 +813,7 @@ impl StorageBlockHash { /// Sets the underlying [`BlockHash`] in persistent storage. pub fn set(&mut self, value: BlockHash) { overwrite_cell(&mut self.cached, value); - unsafe { Storage::set_word(Rc::clone(&self.host), self.slot, value) } + unsafe { Storage::set_word(self.get_host(), self.slot, value) } } } @@ -783,7 +827,7 @@ impl StorageType for StorageBlockHash { where Self: 'a; - unsafe fn new(slot: U256, _offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, _offset: u8, host: *const H) -> Self { let cached = OnceCell::new(); Self { slot, cached, host } } @@ -800,6 +844,14 @@ impl StorageType for StorageBlockHash { } } +impl HostAccess for StorageBlockHash { + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } +} + impl<'a, H: Host> SimpleStorageType<'a, H> for StorageBlockHash where Self: 'a, @@ -820,7 +872,7 @@ impl Deref for StorageBlockHash { fn deref(&self) -> &Self::Target { self.cached - .get_or_init(|| Storage::get_word(Rc::clone(&self.host), self.slot)) + .get_or_init(|| Storage::get_word(self.get_host(), self.slot)) } } @@ -844,7 +896,7 @@ impl StorageType for PhantomData { const REQUIRED_SLOTS: usize = 0; const SLOT_BYTES: usize = 0; - unsafe fn new(_slot: U256, _offset: u8, _host: Rc) -> Self { + unsafe fn new(_slot: U256, _offset: u8, _host: *const H) -> Self { Self } diff --git a/stylus-sdk/src/storage/traits.rs b/stylus-sdk/src/storage/traits.rs index d71b438..66ea4d1 100644 --- a/stylus-sdk/src/storage/traits.rs +++ b/stylus-sdk/src/storage/traits.rs @@ -8,7 +8,6 @@ use core::{ ptr, }; use derivative::Derivative; -use rclite::Rc; use crate::host::Host; @@ -54,7 +53,7 @@ pub trait StorageType: Sized { /// Aliases storage if two calls to the same slot and offset occur within the same lifetime. /// /// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560 - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self; + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self; /// Load the wrapped type, consuming the accessor. /// Note: most types have a `get` and/or `getter`, which don't consume `Self`. @@ -192,7 +191,7 @@ pub trait GlobalStorage { /// /// [`SLOAD`]: https://www.evm.codes/#54 /// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560 - unsafe fn get(host: Rc, key: U256, offset: usize) -> FixedBytes { + unsafe fn get(host: &H, key: U256, offset: usize) -> FixedBytes { debug_assert!(N + offset <= 32); let word = Self::get_word(host, key); let value = &word[offset..][..N]; @@ -211,7 +210,7 @@ pub trait GlobalStorage { /// [`SLOAD`]: https://www.evm.codes/#54 /// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560 unsafe fn get_uint( - host: Rc, + host: &H, key: U256, offset: usize, ) -> Uint { @@ -233,7 +232,7 @@ pub trait GlobalStorage { /// [`SLOAD`]: https://www.evm.codes/#54 /// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560 unsafe fn get_signed( - host: Rc, + host: &H, key: U256, offset: usize, ) -> Signed { @@ -250,7 +249,7 @@ pub trait GlobalStorage { /// /// [`SLOAD`]: https://www.evm.codes/#54 /// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560 - unsafe fn get_byte(host: Rc, key: U256, offset: usize) -> u8 { + unsafe fn get_byte(host: &H, key: U256, offset: usize) -> u8 { debug_assert!(offset <= 32); let word = Self::get::<1>(host, key, offset); word[0] @@ -267,7 +266,7 @@ pub trait GlobalStorage { /// /// [`SLOAD`]: https://www.evm.codes/#54 /// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560 - fn get_word(host: Rc, key: U256) -> B256; + fn get_word(host: &H, key: U256) -> B256; /// Writes `N ≤ 32` bytes to persistent storage, performing [`SSTORE`]'s only as needed. /// The bytes are written to slot `key`, starting `offset` bytes from the left. @@ -279,23 +278,19 @@ pub trait GlobalStorage { /// Aliases if called during the lifetime an overlapping accessor. /// /// [`SSTORE`]: https://www.evm.codes/#55 - unsafe fn set(host: Rc, key: U256, offset: usize, value: FixedBytes) { + unsafe fn set(host: &H, key: U256, offset: usize, value: FixedBytes) { debug_assert!(N + offset <= 32); if N == 32 { - return Self::set_word( - Rc::clone(&host), - key, - FixedBytes::from_slice(value.as_slice()), - ); + return Self::set_word(host, key, FixedBytes::from_slice(value.as_slice())); } - let mut word = Self::get_word(Rc::clone(&host), key); + let mut word = Self::get_word(host, key); let dest = word[offset..].as_mut_ptr(); ptr::copy(value.as_ptr(), dest, N); - Self::set_word(Rc::clone(&host), key, word); + Self::set_word(host, key, word); } /// Writes a [`Uint`] to persistent storage, performing [`SSTORE`]'s only as needed. @@ -309,7 +304,7 @@ pub trait GlobalStorage { /// /// [`SSTORE`]: https://www.evm.codes/#55 unsafe fn set_uint( - host: Rc, + host: &H, key: U256, offset: usize, value: Uint, @@ -319,18 +314,18 @@ pub trait GlobalStorage { if B == 256 { return Self::set_word( - Rc::clone(&host), + host, key, FixedBytes::from_slice(&value.to_be_bytes::<32>()), ); } - let mut word = Self::get_word(Rc::clone(&host), key); + let mut word = Self::get_word(host, key); let value = value.to_be_bytes_vec(); let dest = word[offset..].as_mut_ptr(); ptr::copy(value.as_ptr(), dest, B / 8); - Self::set_word(Rc::clone(&host), key, word); + Self::set_word(host, key, word); } /// Writes a [`Signed`] to persistent storage, performing [`SSTORE`]'s only as needed. @@ -344,7 +339,7 @@ pub trait GlobalStorage { /// /// [`SSTORE`]: https://www.evm.codes/#55 unsafe fn set_signed( - host: Rc, + host: &H, key: U256, offset: usize, value: Signed, @@ -361,7 +356,7 @@ pub trait GlobalStorage { /// Aliases if called during the lifetime an overlapping accessor. /// /// [`SSTORE`]: https://www.evm.codes/#55 - unsafe fn set_byte(host: Rc, key: U256, offset: usize, value: u8) { + unsafe fn set_byte(host: &H, key: U256, offset: usize, value: u8) { let fixed = FixedBytes::from_slice(&[value]); Self::set::<1>(host, key, offset, fixed) } @@ -373,7 +368,7 @@ pub trait GlobalStorage { /// Aliases if called during the lifetime an overlapping accessor. /// /// [`SSTORE`]: https://www.evm.codes/#55 - unsafe fn set_word(host: Rc, key: U256, value: B256); + unsafe fn set_word(host: &H, key: U256, value: B256); /// Clears the 32-byte word at the given key, performing [`SSTORE`]'s only as needed. /// @@ -382,7 +377,7 @@ pub trait GlobalStorage { /// Aliases if called during the lifetime an overlapping accessor. /// /// [`SSTORE`]: https://www.evm.codes/#55 - unsafe fn clear_word(host: Rc, key: U256) { + unsafe fn clear_word(host: &H, key: U256) { Self::set_word(host, key, B256::ZERO) } } diff --git a/stylus-sdk/src/storage/vec.rs b/stylus-sdk/src/storage/vec.rs index 922b138..d2c3045 100644 --- a/stylus-sdk/src/storage/vec.rs +++ b/stylus-sdk/src/storage/vec.rs @@ -4,17 +4,19 @@ use super::{ Erase, GlobalStorage, SimpleStorageType, Storage, StorageGuard, StorageGuardMut, StorageType, }; -use crate::{crypto, host::Host}; +use crate::{ + crypto, + host::{Host, HostAccess}, +}; use alloy_primitives::U256; use core::{cell::OnceCell, marker::PhantomData}; -use rclite::Rc; /// Accessor for a storage-backed vector. pub struct StorageVec> { slot: U256, base: OnceCell, marker: PhantomData, - host: Rc, + host: *const H, } impl> StorageType for StorageVec { @@ -27,7 +29,7 @@ impl> StorageType for StorageVec { where Self: 'a; - unsafe fn new(slot: U256, offset: u8, host: Rc) -> Self { + unsafe fn new(slot: U256, offset: u8, host: *const H) -> Self { debug_assert!(offset == 0); Self { slot, @@ -46,6 +48,14 @@ impl> StorageType for StorageVec { } } +impl> HostAccess for StorageVec { + fn get_host(&self) -> &H { + // SAFETY: Host is guaranteed to be valid and non-null for the lifetime of the storage + // as injected by the Stylus entrypoint function. + unsafe { &*self.host } + } +} + impl> StorageVec { /// Returns `true` if the collection contains no elements. pub fn is_empty(&self) -> bool { @@ -54,7 +64,7 @@ impl> StorageVec { /// Gets the number of elements stored. pub fn len(&self) -> usize { - let word: U256 = Storage::get_word(Rc::clone(&self.host), self.slot).into(); + let word: U256 = Storage::get_word(self.get_host(), self.slot).into(); word.try_into().unwrap() } @@ -66,7 +76,7 @@ impl> StorageVec { /// or any junk data left over from prior dirty operations. /// Note that [`StorageVec`] has unlimited capacity, so all lengths are valid. pub unsafe fn set_len(&mut self, len: usize) { - Storage::set_word(Rc::clone(&self.host), self.slot, U256::from(len).into()) + Storage::set_word(self.get_host(), self.slot, U256::from(len).into()) } /// Gets an accessor to the element at a given index, if it exists. @@ -98,7 +108,7 @@ impl> StorageVec { return None; } let (slot, offset) = self.index_slot(index); - Some(S::new(slot, offset, Rc::clone(&self.host))) + Some(S::new(slot, offset, self.get_host())) } /// Gets the underlying accessor to the element at a given index, even if out of bounds. @@ -108,7 +118,7 @@ impl> StorageVec { /// Enables aliasing. UB if out of bounds. unsafe fn accessor_unchecked(&self, index: usize) -> S { let (slot, offset) = self.index_slot(index); - S::new(slot, offset, Rc::clone(&self.host)) + S::new(slot, offset, self.get_host()) } /// Gets the element at the given index, if it exists. @@ -147,7 +157,7 @@ impl> StorageVec { unsafe { self.set_len(index + 1) }; let (slot, offset) = self.index_slot(index); - let store = unsafe { S::new(slot, offset, Rc::clone(&self.host)) }; + let store = unsafe { S::new(slot, offset, self.get_host()) }; StorageGuardMut::new(store) } @@ -216,7 +226,7 @@ impl<'a, H: Host, S: SimpleStorageType<'a, H>> StorageVec { let slot = self.index_slot(index).0; let words = S::REQUIRED_SLOTS.max(1); for i in 0..words { - unsafe { Storage::clear_word(Rc::clone(&self.host), slot + U256::from(i)) }; + unsafe { Storage::clear_word(self.get_host(), slot + U256::from(i)) }; } } Some(value)