Skip to content

Commit

Permalink
Merge pull request #32 from OffchainLabs/storage_cache_feature
Browse files Browse the repository at this point in the history
stylus-sdk: make storage_cache a configurable feature
  • Loading branch information
rachel-bousfield authored Sep 5, 2023
2 parents 07693ed + 3b095ac commit 6354576
Show file tree
Hide file tree
Showing 11 changed files with 521 additions and 416 deletions.
8 changes: 6 additions & 2 deletions stylus-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ derivative.workspace = true
hex = { workspace = true, default-features = false, features = ["alloc"] }
keccak-const.workspace = true
lazy_static.workspace = true

# export-abi
regex = { workspace = true, optional = true }

# data structures
fnv.workspace = true
# storage-cache
fnv = { workspace = true, optional = true }

# local deps
stylus-proc.workspace = true
Expand All @@ -30,5 +32,7 @@ paste.workspace = true
sha3.workspace = true

[features]
default = ["storage-cache"]
export-abi = ["debug", "regex", "stylus-proc/export-abi"]
debug = []
storage-cache = ["fnv"]
17 changes: 12 additions & 5 deletions stylus-sdk/src/call/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// Copyright 2022-2023, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md

use crate::storage::{StorageCache, TopLevelStorage};
use crate::storage::TopLevelStorage;
use alloy_primitives::{Address, U256};
use core::sync::atomic::{AtomicBool, Ordering};

pub use self::{context::Context, error::Error, raw::RawCall, traits::*};

pub(crate) use raw::CachePolicy;

#[cfg(feature = "storage-cache")]
use crate::storage::Storage;

mod context;
mod error;
mod raw;
Expand Down Expand Up @@ -45,9 +50,10 @@ pub fn static_call(
to: Address,
data: &[u8],
) -> Result<Vec<u8>, Error> {
// flush storage to persist changes, but don't invalidate the cache
#[cfg(feature = "storage-cache")]
if reentrancy_enabled() {
StorageCache::flush();
// flush storage to persist changes, but don't invalidate the cache
Storage::flush();
}
unsafe {
RawCall::new_static()
Expand All @@ -59,9 +65,10 @@ pub fn static_call(

/// Calls the contract at the given address.
pub fn call(context: impl MutatingCallContext, to: Address, data: &[u8]) -> Result<Vec<u8>, Error> {
// clear the storage to persist changes, invalidating the cache
#[cfg(feature = "storage-cache")]
if reentrancy_enabled() {
StorageCache::clear();
// clear the storage to persist changes, invalidating the cache
Storage::clear();
}
unsafe {
RawCall::new_with_value(context.value())
Expand Down
21 changes: 13 additions & 8 deletions stylus-sdk/src/call/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@

use crate::{
contract::{read_return_data, RETURN_DATA_LEN},
hostio,
storage::StorageCache,
tx, ArbResult,
hostio, tx, ArbResult,
};
use alloy_primitives::{Address, B256, U256};

#[cfg(feature = "storage-cache")]
use crate::storage::StorageCache;

/// Mechanism for performing raw calls to other contracts.
#[derive(Clone, Default)]
#[must_use]
Expand All @@ -18,6 +19,7 @@ pub struct RawCall {
gas: Option<u64>,
offset: usize,
size: Option<usize>,
#[allow(unused)]
cache_policy: CachePolicy,
}

Expand All @@ -30,8 +32,10 @@ enum CallKind {
Static,
}

#[derive(Clone, Default, PartialEq, PartialOrd)]
enum CachePolicy {
/// How to manage the storage cache, if enabled.
#[allow(unused)]
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum CachePolicy {
#[default]
DoNothing,
Flush,
Expand Down Expand Up @@ -122,14 +126,14 @@ impl RawCall {
}

/// Write all cached values to persistent storage before the call.
#[cfg(feature = "storage-cache")]
pub fn flush_storage_cache(mut self) -> Self {
if self.cache_policy < CachePolicy::Flush {
self.cache_policy = CachePolicy::Flush;
}
self.cache_policy = self.cache_policy.max(CachePolicy::Flush);
self
}

/// Flush and clear the storage cache before the call.
#[cfg(feature = "storage-cache")]
pub fn clear_storage_cache(mut self) -> Self {
self.cache_policy = CachePolicy::Clear;
self
Expand All @@ -148,6 +152,7 @@ impl RawCall {
let gas = self.gas.unwrap_or(u64::MAX); // will be clamped by 63/64 rule
let value = B256::from(self.callvalue);
let status = unsafe {
#[cfg(feature = "storage-cache")]
match self.cache_policy {
CachePolicy::Clear => StorageCache::clear(),
CachePolicy::Flush => StorageCache::flush(),
Expand Down
30 changes: 28 additions & 2 deletions stylus-sdk/src/deploy/raw.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// Copyright 2023, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md

use alloy_primitives::{Address, B256, U256};

use crate::{
call::CachePolicy,
contract::{read_return_data, RETURN_DATA_LEN},
hostio,
};
use alloy_primitives::{Address, B256, U256};

#[cfg(feature = "storage-cache")]
use crate::storage::StorageCache;

/// Mechanism for performing raw deploys of other contracts.
#[derive(Clone, Default)]
Expand All @@ -15,6 +18,8 @@ pub struct RawDeploy {
salt: Option<B256>,
offset: usize,
size: Option<usize>,
#[allow(unused)]
cache_policy: CachePolicy,
}

impl RawDeploy {
Expand Down Expand Up @@ -55,6 +60,20 @@ impl RawDeploy {
self.limit_revert_data(0, 0)
}

/// Write all cached values to persistent storage before the init code.
#[cfg(feature = "storage-cache")]
pub fn flush_storage_cache(mut self) -> Self {
self.cache_policy = self.cache_policy.max(CachePolicy::Flush);
self
}

/// Flush and clear the storage cache before the init code.
#[cfg(feature = "storage-cache")]
pub fn clear_storage_cache(mut self) -> Self {
self.cache_policy = CachePolicy::Clear;
self
}

/// Performs a raw deploy of another contract with the given `endowment` and init `code`.
/// Returns the address of the newly deployed contract, or the error data in case of failure.
///
Expand All @@ -67,6 +86,13 @@ impl RawDeploy {
/// For extra flexibility, this method does not clear the global storage cache.
/// See [`StorageCache::flush`] and [`StorageCache::clear`] for more information.
pub unsafe fn deploy(self, code: &[u8], endowment: U256) -> Result<Address, Vec<u8>> {
#[cfg(feature = "storage-cache")]
match self.cache_policy {
CachePolicy::Clear => StorageCache::clear(),
CachePolicy::Flush => StorageCache::flush(),
CachePolicy::DoNothing => {}
}

let mut contract = Address::default();
let mut revert_data_len = 0;

Expand Down
36 changes: 18 additions & 18 deletions stylus-sdk/src/storage/bytes.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2022-2023, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md

use super::{Erase, StorageB8, StorageCache, StorageGuard, StorageGuardMut, StorageType};
use super::{Erase, GlobalStorage, Storage, StorageB8, StorageGuard, StorageGuardMut, StorageType};
use crate::crypto;
use alloy_primitives::{U256, U8};
use core::cell::OnceCell;
Expand Down Expand Up @@ -41,7 +41,7 @@ impl StorageBytes {

/// Gets the number of bytes stored.
pub fn len(&self) -> usize {
let word = StorageCache::get_word(self.root);
let word = Storage::get_word(self.root);

// check if the data is short
let slot: &[u8] = word.as_ref();
Expand Down Expand Up @@ -70,26 +70,26 @@ impl StorageBytes {

// if shrinking, pull data in
if (len < 32) && (old > 32) {
let word = StorageCache::get_word(*self.base());
StorageCache::set_word(self.root, word);
let word = Storage::get_word(*self.base());
Storage::set_word(self.root, word);
return self.write_len(len);
}

// if growing, push data out
let mut word = StorageCache::get_word(self.root);
let mut word = Storage::get_word(self.root);
word[31] = 0; // clear len byte
StorageCache::set_word(*self.base(), word);
Storage::set_word(*self.base(), word);
self.write_len(len)
}

/// Updates the length while being concious of representation.
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
StorageCache::set_uint(self.root, 31, U8::from(len * 2));
Storage::set_uint(self.root, 31, U8::from(len * 2));
} else {
// place the len in the root with the long bit high
StorageCache::set_word(self.root, U256::from(len * 2 + 1).into())
Storage::set_word(self.root, U256::from(len * 2 + 1).into())
}
}

Expand All @@ -101,7 +101,7 @@ impl StorageBytes {
macro_rules! assign {
($slot:expr) => {
unsafe {
StorageCache::set_uint($slot, index % 32, value); // pack value
Storage::set_uint($slot, index % 32, value); // pack value
self.write_len(index + 1);
}
};
Expand All @@ -114,8 +114,8 @@ impl StorageBytes {
// convert to multi-word representation
if index == 31 {
// copy content over (len byte will be overwritten)
let word = StorageCache::get_word(self.root);
unsafe { StorageCache::set_word(*self.base(), word) };
let word = Storage::get_word(self.root);
unsafe { Storage::set_word(*self.base(), word) };
}

let slot = self.base() + U256::from(index / 32);
Expand All @@ -135,13 +135,13 @@ impl StorageBytes {
let clean = index % 32 == 0;
let byte = self.get(index)?;

let clear = |slot| unsafe { StorageCache::clear_word(slot) };
let clear = |slot| unsafe { Storage::clear_word(slot) };

// convert to single-word representation
if len == 32 {
// copy content over
let word = StorageCache::get_word(*self.base());
unsafe { StorageCache::set_word(self.root, word) };
let word = Storage::get_word(*self.base());
unsafe { Storage::set_word(self.root, word) };
clear(*self.base());
}

Expand All @@ -152,7 +152,7 @@ impl StorageBytes {

// clear the value
if len < 32 {
unsafe { StorageCache::set_byte(self.root, index, 0) };
unsafe { Storage::set_byte(self.root, index, 0) };
}

// set the new length
Expand Down Expand Up @@ -187,7 +187,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 { StorageCache::get_byte(slot, offset.into()) }
unsafe { Storage::get_byte(slot, offset.into()) }
}

/// Gets the full contents of the collection.
Expand Down Expand Up @@ -231,11 +231,11 @@ impl Erase for StorageBytes {
if len > 31 {
while len > 0 {
let slot = self.index_slot(len as usize - 1).0;
unsafe { StorageCache::clear_word(slot) };
unsafe { Storage::clear_word(slot) };
len -= 32;
}
}
unsafe { StorageCache::clear_word(self.root) };
unsafe { Storage::clear_word(self.root) };
}
}

Expand Down
Loading

0 comments on commit 6354576

Please sign in to comment.