Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions substrate/frame/revive/fixtures/contracts/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,25 @@ pub extern "C" fn call() {
assert_eq!(existing, None);
unwrap_output!(val, [0u8; 32], api::take_storage, StorageFlags::empty(), &KEY);
assert_eq!(**val, VALUE_3);

const VALUE_A: [u8; 32] = [4u8; 32];
const ZERO: [u8; 32] = [0u8; 32];

api::clear_storage(StorageFlags::empty(), &KEY);
assert_eq!(api::contains_storage(StorageFlags::empty(), &KEY), None);
let existing = api::set_storage_or_clear(StorageFlags::empty(), &KEY, &VALUE_A);
assert_eq!(existing, None);
unwrap_output!(val, [0u8; 32], api::get_storage, StorageFlags::empty(), &KEY);
assert_eq!(**val, VALUE_A);

let mut stored: [u8; 32] = [0u8; 32];
let _ = api::get_storage_or_zero(StorageFlags::empty(), &KEY, &mut stored);
assert_eq!(stored, VALUE_A);

let existing = api::set_storage_or_clear(StorageFlags::empty(), &KEY, &ZERO);
assert_eq!(existing, Some(32));

let mut cleared: [u8; 32] = [1u8; 32];
let _ = api::get_storage_or_zero(StorageFlags::empty(), &KEY, &mut cleared);
assert_eq!(cleared, ZERO);
}
45 changes: 45 additions & 0 deletions substrate/frame/revive/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2152,4 +2152,49 @@ pub mod env {
already_charged,
)?)
}

/// Sets the storage at a fixed 256-bit key with a fixed 256-bit value.
/// If the provided value is all zeros then the key is cleared (i.e. deleted),
/// similar to Ethereum’s SSTORE semantics.
#[stable]
#[mutating]
fn set_storage_or_clear(
&mut self,
memory: &mut M,
flags: u32,
key_ptr: u32,
value_ptr: u32,
) -> Result<u32, TrapReason> {
let value = memory.read(value_ptr, 32)?;
if value.iter().all(|&b| b == 0) {
self.clear_storage(memory, flags, key_ptr, 32)
} else {
self.set_storage(memory, flags, key_ptr, 32, value_ptr, 32)
}
}

/// Reads the storage at a fixed 256-bit key and writes back a fixed 256-bit value.
/// If the key does not exist, writes back 32 bytes of zero.
#[stable]
fn get_storage_or_zero(
&mut self,
memory: &mut M,
key_ptr: u32,
out_ptr: u32,
) -> Result<(), TrapReason> {
let key = self.decode_key(memory, key_ptr, 32)?;
let value_opt = self.ext.get_storage(&key);
let value = match value_opt {
Some(v) => v,
None => vec![0u8; 32],
};
let mut fixed_value = [0u8; 32];
if value.len() >= 32 {
fixed_value.copy_from_slice(&value[..32]);
} else {
fixed_value[..value.len()].copy_from_slice(&value);
}
memory.write(out_ptr, &fixed_value)?;
Ok(())
}
}
9 changes: 9 additions & 0 deletions substrate/frame/revive/uapi/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,15 @@ pub trait HostFn: private::Sealed {
/// Returns the size of the pre-existing value at the specified key if any.
fn set_storage(flags: StorageFlags, key: &[u8], value: &[u8]) -> Option<u32>;

/// Sets the storage for a fixed 256‑bit key with a fixed 256‑bit value.
/// If the provided 32‑byte value is all zeros then the key is cleared (i.e. deleted)
/// mimicking Ethereum’s SSTORE behavior.
fn set_storage_or_clear(flags: StorageFlags, key: &[u8; 32], value: &[u8; 32]) -> Option<u32>;

/// Retrieves the storage value for a fixed 256‑bit key.
/// If the key does not exist, the output buffer is filled with 32 zero bytes.
fn get_storage_or_zero(flags: StorageFlags, key: &[u8; 32], output: &mut [u8; 32]) -> Result;

/// Stores the value transferred along with this call/instantiate into the supplied buffer.
///
/// # Parameters
Expand Down
31 changes: 31 additions & 0 deletions substrate/frame/revive/uapi/src/host/riscv64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ mod sys {
value_ptr: *const u8,
value_len: u32,
) -> ReturnCode;
pub fn set_storage_or_clear(
flags: u32,
key_ptr: *const u8,
value_ptr: *const u8,
) -> ReturnCode;
pub fn clear_storage(flags: u32, key_ptr: *const u8, key_len: u32) -> ReturnCode;
pub fn get_storage(
flags: u32,
Expand All @@ -51,6 +56,7 @@ mod sys {
out_ptr: *mut u8,
out_len_ptr: *mut u32,
) -> ReturnCode;
pub fn get_storage_or_zero(key_ptr: *const u8, out_ptr: *mut u8) -> ReturnCode;
pub fn contains_storage(flags: u32, key_ptr: *const u8, key_len: u32) -> ReturnCode;
pub fn take_storage(
flags: u32,
Expand Down Expand Up @@ -314,6 +320,31 @@ impl HostFn for HostFnImpl {
ret_code.into()
}

fn set_storage_or_clear(
flags: StorageFlags,
key: &[u8; 32],
encoded_value: &[u8; 32],
) -> Option<u32> {
let ret_code = unsafe {
sys::set_storage_or_clear(
flags.bits(),
key.as_ptr(), // key is expected to be 32 bytes
encoded_value.as_ptr(), // value is expected to be 32 bytes
)
};
ret_code.into()
}

fn get_storage_or_zero(_flags: StorageFlags, key: &[u8; 32], output: &mut [u8; 32]) -> Result {
let ret_code = unsafe {
sys::get_storage_or_zero(
key.as_ptr(), // key (32 bytes)
output.as_mut_ptr(), // pointer where 32 bytes are written
)
};
ret_code.into()
}

fn get_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result {
let mut output_len = output.len() as u32;
let ret_code = {
Expand Down
Loading