1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
// Copyright 2022-2023, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md
use alloy_primitives::{FixedBytes, Signed, Uint, B256, U256};
use core::{
marker::PhantomData,
ops::{Deref, DerefMut},
ptr,
};
use derivative::Derivative;
/// Accessor trait that lets a type be used in persistent storage.
/// Users can implement this trait to add novel data structures to their contract definitions.
/// The Stylus SDK by default provides only solidity types, which are represented [`the same way`].
///
/// [`the same way`]: https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html
pub trait StorageType: Sized {
/// For primitive types, this is the type being stored.
/// For collections, this is the [`StorageType`] being collected.
type Wraps<'a>: 'a
where
Self: 'a;
/// Mutable accessor to the type being stored.
type WrapsMut<'a>: 'a
where
Self: 'a;
/// The number of bytes in a slot needed to represent the type. Must not exceed 32.
/// For types larger than 32 bytes that are stored inline with a struct's fields,
/// set this to 32 and return the full size in [`StorageType::new`].
///
/// For implementing collections, see how Solidity slots are assigned for [`Arrays and Maps`] and their
/// Stylus equivalents [`StorageVec`](super::StorageVec) and [`StorageMap`](super::StorageMap).
/// For multi-word, but still fixed-size types, see the implementations for structs
/// and [`StorageArray`](super::StorageArray).
///
/// [`Arrays and Maps`]: https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays
const SLOT_BYTES: usize = 32;
/// The number of words this type must fill. For primitives this is always 0.
/// For complex types requiring more than one inline word, set this to the total size.
const REQUIRED_SLOTS: usize = 0;
/// Where in persistent storage the type should live. Although useful for framework designers
/// creating new storage types, most user programs shouldn't call this.
/// Note: implementations will have to be `const` once [`generic_const_exprs`] stabilizes.
///
/// # Safety
///
/// 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) -> Self;
/// Load the wrapped type, consuming the accessor.
/// Note: most types have a `get` and/or `getter`, which don't consume `Self`.
fn load<'s>(self) -> Self::Wraps<'s>
where
Self: 's;
/// Load the wrapped mutable type, consuming the accessor.
/// Note: most types have a `set` and/or `setter`, which don't consume `Self`.
fn load_mut<'s>(self) -> Self::WrapsMut<'s>
where
Self: 's;
}
/// Trait for accessors that can be used to completely erase their underlying value.
/// Note that some collections, like [`StorageMap`](super::StorageMap), don't implement this trait.
pub trait Erase: StorageType {
/// Erase the value from persistent storage.
fn erase(&mut self);
}
/// Trait for simple accessors that store no more than their wrapped value.
/// The type's representation must be entirely inline, or storage leaks become possible.
/// Note: it is a logic error if erasure does anything more than writing the zero-value.
pub trait SimpleStorageType<'a>: StorageType + Erase + Into<Self::Wraps<'a>>
where
Self: 'a,
{
/// Write the value to persistent storage.
fn set_by_wrapped(&mut self, value: Self::Wraps<'a>);
}
/// Trait for top-level storage types, usually implemented by proc macros.
/// Top-level types are special in that their lifetimes track the entirety
/// of all the EVM state-changes throughout a contract invocation.
///
/// To prevent storage aliasing during reentrancy, you must hold a reference
/// to such a type when making an EVM call. This may change in the future
/// for programs that prevent reentrancy.
///
/// # Safety
///
/// The type must be top-level to prevent storage aliasing.
pub unsafe trait TopLevelStorage {}
/// Binds a storage accessor to a lifetime to prevent aliasing.
/// Because this type doesn't implement `DerefMut`, mutable methods on the accessor aren't available.
/// For a mutable accessor, see [`StorageGuardMut`].
#[derive(Derivative)]
#[derivative(Debug = "transparent")]
pub struct StorageGuard<'a, T: 'a> {
inner: T,
#[derivative(Debug = "ignore")]
marker: PhantomData<&'a T>,
}
impl<'a, T: 'a> StorageGuard<'a, T> {
/// Creates a new storage guard around an arbitrary type.
pub fn new(inner: T) -> Self {
Self {
inner,
marker: PhantomData,
}
}
/// Get the underlying `T` directly, bypassing the borrow checker.
///
/// # Safety
///
/// Enables storage aliasing.
pub unsafe fn into_raw(self) -> T {
self.inner
}
}
impl<'a, T: 'a> Deref for StorageGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
/// Binds a storage accessor to a lifetime to prevent aliasing.
pub struct StorageGuardMut<'a, T: 'a> {
inner: T,
marker: PhantomData<&'a T>,
}
impl<'a, T: 'a> StorageGuardMut<'a, T> {
/// Creates a new storage guard around an arbitrary type.
pub fn new(inner: T) -> Self {
Self {
inner,
marker: PhantomData,
}
}
/// Get the underlying `T` directly, bypassing the borrow checker.
///
/// # Safety
///
/// Enables storage aliasing.
pub unsafe fn into_raw(self) -> T {
self.inner
}
}
impl<'a, T: 'a> Deref for StorageGuardMut<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'a, T: 'a> DerefMut for StorageGuardMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
/// Trait for managing access to persistent storage.
/// Notable implementations include the [`StorageCache`](super::StorageCache)
/// and [`EagerStorage`](super::EagerStorage) types.
pub trait GlobalStorage {
/// Retrieves `N ≤ 32` bytes from persistent storage, performing [`SLOAD`]'s only as needed.
/// The bytes are read from slot `key`, starting `offset` bytes from the left.
/// Note that the bytes must exist within a single, 32-byte EVM word.
///
/// # Safety
///
/// UB if the read would cross a word boundary.
/// May become safe when Rust stabilizes [`generic_const_exprs`].
///
/// [`SLOAD`]: https://www.evm.codes/#54
/// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560
unsafe fn get<const N: usize>(key: U256, offset: usize) -> FixedBytes<N> {
debug_assert!(N + offset <= 32);
let word = Self::get_word(key);
let value = &word[offset..][..N];
FixedBytes::from_slice(value)
}
/// Retrieves a [`Uint`] from persistent storage, performing [`SLOAD`]'s only as needed.
/// The integer's bytes are read from slot `key`, starting `offset` bytes from the left.
/// Note that the bytes must exist within a single, 32-byte EVM word.
///
/// # Safety
///
/// UB if the read would cross a word boundary.
/// May become safe when Rust stabilizes [`generic_const_exprs`].
///
/// [`SLOAD`]: https://www.evm.codes/#54
/// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560
unsafe fn get_uint<const B: usize, const L: usize>(key: U256, offset: usize) -> Uint<B, L> {
debug_assert!(B / 8 + offset <= 32);
let word = Self::get_word(key);
let value = &word[offset..][..B / 8];
Uint::try_from_be_slice(value).unwrap()
}
/// Retrieves a [`Signed`] from persistent storage, performing [`SLOAD`]'s only as needed.
/// The integer's bytes are read from slot `key`, starting `offset` bytes from the left.
/// Note that the bytes must exist within a single, 32-byte EVM word.
///
/// # Safety
///
/// UB if the read would cross a word boundary.
/// May become safe when Rust stabilizes [`generic_const_exprs`].
///
/// [`SLOAD`]: https://www.evm.codes/#54
/// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560
unsafe fn get_signed<const B: usize, const L: usize>(key: U256, offset: usize) -> Signed<B, L> {
Signed::from_raw(Self::get_uint(key, offset))
}
/// Retrieves a [`u8`] from persistent storage, performing [`SLOAD`]'s only as needed.
/// The byte is read from slot `key`, starting `offset` bytes from the left.
///
/// # Safety
///
/// UB if the read is out of bounds.
/// May become safe when Rust stabilizes [`generic_const_exprs`].
///
/// [`SLOAD`]: https://www.evm.codes/#54
/// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560
unsafe fn get_byte(key: U256, offset: usize) -> u8 {
debug_assert!(offset <= 32);
let word = Self::get::<1>(key, offset);
word[0]
}
/// Retrieves a [`Signed`] from persistent storage, performing [`SLOAD`]'s only as needed.
/// The integer's bytes are read from slot `key`, starting `offset` bytes from the left.
/// Note that the bytes must exist within a single, 32-byte EVM word.
///
/// # Safety
///
/// UB if the read would cross a word boundary.
/// May become safe when Rust stabilizes [`generic_const_exprs`].
///
/// [`SLOAD`]: https://www.evm.codes/#54
/// [`generic_const_exprs`]: https://github.com/rust-lang/rust/issues/76560
fn get_word(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.
/// Note that the bytes must be written to a single, 32-byte EVM word.
///
/// # Safety
///
/// UB if the write would cross a word boundary.
/// Aliases if called during the lifetime an overlapping accessor.
///
/// [`SSTORE`]: https://www.evm.codes/#55
unsafe fn set<const N: usize>(key: U256, offset: usize, value: FixedBytes<N>) {
debug_assert!(N + offset <= 32);
if N == 32 {
return Self::set_word(key, FixedBytes::from_slice(value.as_slice()));
}
let mut word = Self::get_word(key);
let dest = word[offset..].as_mut_ptr();
ptr::copy(value.as_ptr(), dest, N);
Self::set_word(key, word);
}
/// Writes a [`Uint`] to persistent storage, performing [`SSTORE`]'s only as needed.
/// The integer's bytes are written to slot `key`, starting `offset` bytes from the left.
/// Note that the bytes must be written to a single, 32-byte EVM word.
///
/// # Safety
///
/// UB if the write would cross a word boundary.
/// Aliases if called during the lifetime an overlapping accessor.
///
/// [`SSTORE`]: https://www.evm.codes/#55
unsafe fn set_uint<const B: usize, const L: usize>(
key: U256,
offset: usize,
value: Uint<B, L>,
) {
debug_assert!(B / 8 + offset <= 32);
if B == 256 {
return Self::set_word(key, FixedBytes::from_slice(&value.to_be_bytes::<32>()));
}
let mut word = Self::get_word(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(key, word);
}
/// Writes a [`Signed`] to persistent storage, performing [`SSTORE`]'s only as needed.
/// The bytes are written to slot `key`, starting `offset` bytes from the left.
/// Note that the bytes must be written to a single, 32-byte EVM word.
///
/// # Safety
///
/// UB if the write would cross a word boundary.
/// Aliases if called during the lifetime an overlapping accessor.
///
/// [`SSTORE`]: https://www.evm.codes/#55
unsafe fn set_signed<const B: usize, const L: usize>(
key: U256,
offset: usize,
value: Signed<B, L>,
) {
Self::set_uint(key, offset, value.into_raw())
}
/// Writes a [`u8`] to persistent storage, performing [`SSTORE`]'s only as needed.
/// The byte is written to slot `key`, starting `offset` bytes from the left.
///
/// # Safety
///
/// UB if the write is out of bounds.
/// Aliases if called during the lifetime an overlapping accessor.
///
/// [`SSTORE`]: https://www.evm.codes/#55
unsafe fn set_byte(key: U256, offset: usize, value: u8) {
let fixed = FixedBytes::from_slice(&[value]);
Self::set::<1>(key, offset, fixed)
}
/// Stores a 32-byte EVM word to persistent storage, performing [`SSTORE`]'s only as needed.
///
/// # Safety
///
/// Aliases if called during the lifetime an overlapping accessor.
///
/// [`SSTORE`]: https://www.evm.codes/#55
unsafe fn set_word(key: U256, value: B256);
/// Clears the 32-byte word at the given key, performing [`SSTORE`]'s only as needed.
///
/// # Safety
///
/// Aliases if called during the lifetime an overlapping accessor.
///
/// [`SSTORE`]: https://www.evm.codes/#55
unsafe fn clear_word(key: U256) {
Self::set_word(key, B256::ZERO)
}
}