mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-20 08:11:02 +00:00
[contracts] Implement transparent hashing for contract storage (#11501)
* save * builds and old tests pass save: temporary value dropped while borrowed save: finally builds test updated but still fails * type names enhanced * VarSizedKey bounded to new Config param * improved wasm runtime updated funcs * unstable-interface tests fixed * benchmarks fixed * Apply suggestions from code review Co-authored-by: Alexander Theißen <alex.theissen@me.com> * fixes on feedback * fixes on feedback applied + make it build * benchmarks build but fail (old) * "Original code too large" * seal_clear_storage bench fixed (code size workaround hack removal tbd) * bench_seal_clear_storage pass * bench_seal_take_storage ... ok * added new seal_set_storage + updated benchmarks * added new seal_get_storage + updated benchmarks * added new seal_contains_storage + updated benchmarks * added tests for _transparent exec functions * wasm test for clear_storage * wasm test for take_storage * wasm test for new set_storage * wasm test for new get_storage * wasm test for new contains_storage * CI fix * ci fix * ci fix * ci fix * cargo run --quiet --profile=production --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark pallet --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * fixes according to the review feedback * tests & benchmarks fixed * cargo run --quiet --profile=production --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark pallet --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * refactoring * fix to runtime api * ci fix * ctx.get_storage() factored out * ctx.contains_storage() factored out * number of batches reduced for transparent hashing storage benchmarks * contracts RPC & pallet::get_storage to use transparent hashing * node and rpc updated to use get_storage with VarSizedKey * refactored (more concize) * refactored contains_storage (DRYed) * refactored contains_storage (DRYed) * fix rpc * fmt fix * more fixes in rpc * rollback `Pallet:get_storage` to Vec<u8> and rpc and node parts related to it * added `KeyDecodingFailed` error * Revert weird "fmt fix" This reverts commit c582cfff4b5cb2c9929fd5e3b45519bb24aeb657. * node-executor basic test update * fix node-executor basic test * benchmarks fix * more benchmarks fix * FixedSizedKey is hidden from pub, VarSizedKey is exported as StorageKey * ci fix * set_storage benchmark fix * ci fix * ci fix * comments improved * new error code to rpc: KEY_DECODING_FAILED * Put `rusty-cachier` before PR merge into `master` for `cargo-check-benches` job * cargo run --quiet --profile=production --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark pallet --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * minor optimization Co-authored-by: Alexander Theißen <alex.theissen@me.com> Co-authored-by: Parity Bot <admin@parity.io> Co-authored-by: Vladimir Istyufeev <vladimir@parity.io> Co-authored-by: command-bot <>
This commit is contained in:
committed by
GitHub
parent
7d2ecc8995
commit
68ea2ab039
@@ -275,7 +275,8 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
exec::{
|
||||
AccountIdOf, BlockNumberOf, ErrorOrigin, ExecError, Executable, Ext, SeedOf, StorageKey,
|
||||
AccountIdOf, BlockNumberOf, ErrorOrigin, ExecError, Executable, Ext, FixSizedKey,
|
||||
SeedOf, VarSizedKey,
|
||||
},
|
||||
gas::GasMeter,
|
||||
storage::WriteOutcome,
|
||||
@@ -330,7 +331,7 @@ mod tests {
|
||||
}
|
||||
|
||||
pub struct MockExt {
|
||||
storage: HashMap<StorageKey, Vec<u8>>,
|
||||
storage: HashMap<Vec<u8>, Vec<u8>>,
|
||||
instantiates: Vec<InstantiateEntry>,
|
||||
terminations: Vec<TerminationEntry>,
|
||||
calls: Vec<CallEntry>,
|
||||
@@ -425,19 +426,45 @@ mod tests {
|
||||
self.terminations.push(TerminationEntry { beneficiary: beneficiary.clone() });
|
||||
Ok(())
|
||||
}
|
||||
fn get_storage(&mut self, key: &StorageKey) -> Option<Vec<u8>> {
|
||||
self.storage.get(key).cloned()
|
||||
fn get_storage(&mut self, key: &FixSizedKey) -> Option<Vec<u8>> {
|
||||
self.storage.get(&key.to_vec()).cloned()
|
||||
}
|
||||
fn get_storage_size(&mut self, key: &StorageKey) -> Option<u32> {
|
||||
self.storage.get(key).map(|val| val.len() as u32)
|
||||
fn get_storage_transparent(&mut self, key: &VarSizedKey<Self::T>) -> Option<Vec<u8>> {
|
||||
self.storage.get(&key.to_vec()).cloned()
|
||||
}
|
||||
fn get_storage_size(&mut self, key: &FixSizedKey) -> Option<u32> {
|
||||
self.storage.get(&key.to_vec()).map(|val| val.len() as u32)
|
||||
}
|
||||
fn get_storage_size_transparent(&mut self, key: &VarSizedKey<Self::T>) -> Option<u32> {
|
||||
self.storage.get(&key.to_vec()).map(|val| val.len() as u32)
|
||||
}
|
||||
fn set_storage(
|
||||
&mut self,
|
||||
key: StorageKey,
|
||||
key: &FixSizedKey,
|
||||
value: Option<Vec<u8>>,
|
||||
take_old: bool,
|
||||
) -> Result<WriteOutcome, DispatchError> {
|
||||
let entry = self.storage.entry(key);
|
||||
let key = key.to_vec();
|
||||
let entry = self.storage.entry(key.clone());
|
||||
let result = match (entry, take_old) {
|
||||
(Entry::Vacant(_), _) => WriteOutcome::New,
|
||||
(Entry::Occupied(entry), false) =>
|
||||
WriteOutcome::Overwritten(entry.remove().len() as u32),
|
||||
(Entry::Occupied(entry), true) => WriteOutcome::Taken(entry.remove()),
|
||||
};
|
||||
if let Some(value) = value {
|
||||
self.storage.insert(key, value);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
fn set_storage_transparent(
|
||||
&mut self,
|
||||
key: &VarSizedKey<Self::T>,
|
||||
value: Option<Vec<u8>>,
|
||||
take_old: bool,
|
||||
) -> Result<WriteOutcome, DispatchError> {
|
||||
let key = key.to_vec();
|
||||
let entry = self.storage.entry(key.clone());
|
||||
let result = match (entry, take_old) {
|
||||
(Entry::Vacant(_), _) => WriteOutcome::New,
|
||||
(Entry::Occupied(entry), false) =>
|
||||
@@ -836,6 +863,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "unstable-interface"))]
|
||||
fn contains_storage_works() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
@@ -844,11 +872,10 @@ mod tests {
|
||||
(import "seal0" "seal_contains_storage" (func $seal_contains_storage (param i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; [0, 4) size of input buffer (32 byte as we copy the key here)
|
||||
;; [0, 4) size of input buffer (32 bytes as we copy the key here)
|
||||
(data (i32.const 0) "\20")
|
||||
|
||||
;; [4, 36) input buffer
|
||||
|
||||
;; [36, inf) output buffer
|
||||
|
||||
(func (export "call")
|
||||
@@ -879,8 +906,8 @@ mod tests {
|
||||
|
||||
let mut ext = MockExt::default();
|
||||
|
||||
ext.storage.insert([1u8; 32], vec![42u8]);
|
||||
ext.storage.insert([2u8; 32], vec![]);
|
||||
ext.storage.insert(vec![1u8; 32], vec![42u8]);
|
||||
ext.storage.insert(vec![2u8; 32], vec![]);
|
||||
|
||||
// value does not exist -> sentinel value returned
|
||||
let result = execute(CODE, [3u8; 32].encode(), &mut ext).unwrap();
|
||||
@@ -895,6 +922,84 @@ mod tests {
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 0,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn contains_storage_works() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "__unstable__" "seal_contains_storage" (func $seal_contains_storage (param i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
|
||||
;; size of input buffer
|
||||
;; [0, 4) size of input buffer (128+32 = 160 bytes = 0xA0)
|
||||
(data (i32.const 0) "\A0")
|
||||
|
||||
;; [4, 164) input buffer
|
||||
|
||||
(func (export "call")
|
||||
;; Receive key
|
||||
(call $seal_input
|
||||
(i32.const 4) ;; Where we take input and store it
|
||||
(i32.const 0) ;; Where we take and store the length of the data
|
||||
)
|
||||
;; Call seal_clear_storage and save what it returns at 0
|
||||
(i32.store (i32.const 0)
|
||||
(call $seal_contains_storage
|
||||
(i32.const 8) ;; key_ptr
|
||||
(i32.load (i32.const 4)) ;; key_len
|
||||
)
|
||||
)
|
||||
(call $seal_return
|
||||
(i32.const 0) ;; flags
|
||||
(i32.const 0) ;; returned value
|
||||
(i32.const 4) ;; length of returned value
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
|
||||
let mut ext = MockExt::default();
|
||||
ext.set_storage_transparent(
|
||||
&VarSizedKey::<Test>::try_from([1u8; 64].to_vec()).unwrap(),
|
||||
Some(vec![42u8]),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
ext.set_storage_transparent(
|
||||
&VarSizedKey::<Test>::try_from([2u8; 19].to_vec()).unwrap(),
|
||||
Some(vec![]),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
//value does not exist (wrong key length)
|
||||
let input = (63, [1u8; 64]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
// sentinel returned
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), crate::SENTINEL);
|
||||
|
||||
// value exists
|
||||
let input = (64, [1u8; 64]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
// true as u32 returned
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 1);
|
||||
// getter does not remove the value from storage
|
||||
assert_eq!(ext.storage.get(&[1u8; 64].to_vec()).unwrap(), &[42u8]);
|
||||
|
||||
// value exists (test for 0 sized)
|
||||
let input = (19, [2u8; 19]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
// true as u32 returned
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 0);
|
||||
// getter does not remove the value from storage
|
||||
assert_eq!(ext.storage.get(&[2u8; 19].to_vec()).unwrap(), &([] as [u8; 0]));
|
||||
}
|
||||
|
||||
const CODE_INSTANTIATE: &str = r#"
|
||||
(module
|
||||
;; seal_instantiate(
|
||||
@@ -1204,7 +1309,7 @@ mod tests {
|
||||
#[test]
|
||||
fn get_storage_puts_data_into_buf() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
mock_ext.storage.insert([0x11; 32], [0x22; 32].to_vec());
|
||||
mock_ext.storage.insert([0x11; 32].to_vec(), [0x22; 32].to_vec());
|
||||
|
||||
let output = execute(CODE_GET_STORAGE, vec![], mock_ext).unwrap();
|
||||
|
||||
@@ -2176,6 +2281,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "unstable-interface"))]
|
||||
fn set_storage_works() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
@@ -2199,7 +2305,7 @@ mod tests {
|
||||
(call $seal_set_storage
|
||||
(i32.const 4) ;; key_ptr
|
||||
(i32.const 36) ;; value_ptr
|
||||
(i32.sub ;; value_len (input_size - key_size)
|
||||
(i32.sub ;; value_len (input_size - key_size)
|
||||
(i32.load (i32.const 0))
|
||||
(i32.const 32)
|
||||
)
|
||||
@@ -2207,7 +2313,7 @@ mod tests {
|
||||
)
|
||||
(call $seal_return
|
||||
(i32.const 0) ;; flags
|
||||
(i32.const 0) ;; returned value
|
||||
(i32.const 0) ;; pointer to returned value
|
||||
(i32.const 4) ;; length of returned value
|
||||
)
|
||||
)
|
||||
@@ -2222,19 +2328,184 @@ mod tests {
|
||||
let input = ([1u8; 32], [42u8, 48]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), crate::SENTINEL);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32]).unwrap(), &[42u8, 48]);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32].to_vec()).unwrap(), &[42u8, 48]);
|
||||
|
||||
// value do exist -> length of old value returned
|
||||
let input = ([1u8; 32], [0u8; 0]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 2);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32]).unwrap(), &[0u8; 0]);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32].to_vec()).unwrap(), &[0u8; 0]);
|
||||
|
||||
// value do exist -> length of old value returned (test for zero sized val)
|
||||
let input = ([1u8; 32], [99u8]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 0);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32]).unwrap(), &[99u8]);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32].to_vec()).unwrap(), &[99u8]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn set_storage_works() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "__unstable__" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; [0, 4) size of input buffer
|
||||
;; 4k in little endian
|
||||
(data (i32.const 0) "\00\10")
|
||||
|
||||
;; [4, 4100) input buffer
|
||||
|
||||
(func (export "call")
|
||||
;; Receive (key ++ value_to_write)
|
||||
(call $seal_input
|
||||
(i32.const 4) ;; Pointer to the input buffer
|
||||
(i32.const 0) ;; Size of the input buffer
|
||||
)
|
||||
;; Store the passed value to the passed key and store result to memory
|
||||
(i32.store (i32.const 168)
|
||||
(call $seal_set_storage
|
||||
(i32.const 8) ;; key_ptr
|
||||
(i32.load (i32.const 4)) ;; key_len
|
||||
(i32.add ;; value_ptr = 8 + key_len
|
||||
(i32.const 8)
|
||||
(i32.load (i32.const 4)))
|
||||
(i32.sub ;; value_len (input_size - (key_len + key_len_len))
|
||||
(i32.load (i32.const 0))
|
||||
(i32.add
|
||||
(i32.load (i32.const 4))
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(call $seal_return
|
||||
(i32.const 0) ;; flags
|
||||
(i32.const 168) ;; ptr to returned value
|
||||
(i32.const 4) ;; length of returned value
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
|
||||
let mut ext = MockExt::default();
|
||||
|
||||
// value did not exist before -> sentinel returned
|
||||
let input = (32, [1u8; 32], [42u8, 48]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), crate::SENTINEL);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32].to_vec()).unwrap(), &[42u8, 48]);
|
||||
|
||||
// value do exist -> length of old value returned
|
||||
let input = (32, [1u8; 32], [0u8; 0]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 2);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32].to_vec()).unwrap(), &[0u8; 0]);
|
||||
|
||||
// value do exist -> length of old value returned (test for zero sized val)
|
||||
let input = (32, [1u8; 32], [99u8]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 0);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32].to_vec()).unwrap(), &[99u8]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn get_storage_works() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "__unstable__" "seal_get_storage" (func $seal_get_storage (param i32 i32 i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; [0, 4) size of input buffer (160 bytes as we copy the key+len here)
|
||||
(data (i32.const 0) "\A0")
|
||||
|
||||
;; [4, 8) size of output buffer
|
||||
;; 4k in little endian
|
||||
(data (i32.const 4) "\00\10")
|
||||
|
||||
;; [8, 168) input buffer
|
||||
;; [168, 4264) output buffer
|
||||
|
||||
(func (export "call")
|
||||
;; Receive (key ++ value_to_write)
|
||||
(call $seal_input
|
||||
(i32.const 8) ;; Pointer to the input buffer
|
||||
(i32.const 0) ;; Size of the input buffer
|
||||
)
|
||||
;; Load a storage value and result of this call into the output buffer
|
||||
(i32.store (i32.const 168)
|
||||
(call $seal_get_storage
|
||||
(i32.const 12) ;; key_ptr
|
||||
(i32.load (i32.const 8)) ;; key_len
|
||||
(i32.const 172) ;; Pointer to the output buffer
|
||||
(i32.const 4) ;; Pointer to the size of the buffer
|
||||
)
|
||||
)
|
||||
(call $seal_return
|
||||
(i32.const 0) ;; flags
|
||||
(i32.const 168) ;; output buffer ptr
|
||||
(i32.add ;; length: output size + 4 (retval)
|
||||
(i32.load (i32.const 4))
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
|
||||
let mut ext = MockExt::default();
|
||||
|
||||
ext.set_storage_transparent(
|
||||
&VarSizedKey::<Test>::try_from([1u8; 64].to_vec()).unwrap(),
|
||||
Some(vec![42u8]),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
ext.set_storage_transparent(
|
||||
&VarSizedKey::<Test>::try_from([2u8; 19].to_vec()).unwrap(),
|
||||
Some(vec![]),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// value does not exist
|
||||
let input = (63, [1u8; 64]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(
|
||||
u32::from_le_bytes(result.data.0[0..4].try_into().unwrap()),
|
||||
ReturnCode::KeyNotFound as u32
|
||||
);
|
||||
|
||||
// value exists
|
||||
let input = (64, [1u8; 64]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(
|
||||
u32::from_le_bytes(result.data.0[0..4].try_into().unwrap()),
|
||||
ReturnCode::Success as u32
|
||||
);
|
||||
assert_eq!(ext.storage.get(&[1u8; 64].to_vec()).unwrap(), &[42u8]);
|
||||
assert_eq!(&result.data.0[4..], &[42u8]);
|
||||
|
||||
// value exists (test for 0 sized)
|
||||
let input = (19, [2u8; 19]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(
|
||||
u32::from_le_bytes(result.data.0[0..4].try_into().unwrap()),
|
||||
ReturnCode::Success as u32
|
||||
);
|
||||
assert_eq!(ext.storage.get(&[2u8; 19].to_vec()), Some(&vec![]));
|
||||
assert_eq!(&result.data.0[4..], &([] as [u8; 0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2244,23 +2515,26 @@ mod tests {
|
||||
(module
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "__unstable__" "seal_clear_storage" (func $seal_clear_storage (param i32) (result i32)))
|
||||
(import "__unstable__" "seal_clear_storage" (func $seal_clear_storage (param i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; 0x1000 = 4k in little endian
|
||||
;; size of input buffer
|
||||
(data (i32.const 0) "\00\10")
|
||||
;; [0, 4) size of input buffer (128+32 = 160 bytes = 0xA0)
|
||||
(data (i32.const 0) "\A0")
|
||||
|
||||
;; [4, 164) input buffer
|
||||
|
||||
(func (export "call")
|
||||
;; Receive key
|
||||
(call $seal_input
|
||||
(i32.const 4) ;; Pointer to the input buffer
|
||||
(i32.const 0) ;; Size of the length buffer
|
||||
(i32.const 4) ;; Where we take input and store it
|
||||
(i32.const 0) ;; Where we take and store the length of thedata
|
||||
)
|
||||
;; Store the passed value to the passed key and store result to memory
|
||||
;; Call seal_clear_storage and save what it returns at 0
|
||||
(i32.store (i32.const 0)
|
||||
(call $seal_clear_storage
|
||||
(i32.const 4) ;; key_ptr
|
||||
(i32.const 8) ;; key_ptr
|
||||
(i32.load (i32.const 4)) ;; key_len
|
||||
)
|
||||
)
|
||||
(call $seal_return
|
||||
@@ -2276,23 +2550,48 @@ mod tests {
|
||||
|
||||
let mut ext = MockExt::default();
|
||||
|
||||
ext.storage.insert([1u8; 32], vec![42u8]);
|
||||
ext.storage.insert([2u8; 32], vec![]);
|
||||
ext.set_storage_transparent(
|
||||
&VarSizedKey::<Test>::try_from([1u8; 64].to_vec()).unwrap(),
|
||||
Some(vec![42u8]),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
ext.set_storage_transparent(
|
||||
&VarSizedKey::<Test>::try_from([2u8; 19].to_vec()).unwrap(),
|
||||
Some(vec![]),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// value does not exist -> sentinel returned
|
||||
let result = execute(CODE, [3u8; 32].encode(), &mut ext).unwrap();
|
||||
// value did not exist
|
||||
let input = (32, [3u8; 32]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
// sentinel returned
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), crate::SENTINEL);
|
||||
assert_eq!(ext.storage.get(&[3u8; 32]), None);
|
||||
assert_eq!(ext.storage.get(&[3u8; 32].to_vec()), None);
|
||||
|
||||
// value did exist -> length returned
|
||||
let result = execute(CODE, [1u8; 32].encode(), &mut ext).unwrap();
|
||||
// value did exist
|
||||
let input = (64, [1u8; 64]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
// length returned
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 1);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32]), None);
|
||||
// value cleared
|
||||
assert_eq!(ext.storage.get(&[1u8; 64].to_vec()), None);
|
||||
|
||||
// value did exist -> length returned (test for 0 sized)
|
||||
let result = execute(CODE, [2u8; 32].encode(), &mut ext).unwrap();
|
||||
//value did not exist (wrong key length)
|
||||
let input = (63, [1u8; 64]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
// sentinel returned
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), crate::SENTINEL);
|
||||
assert_eq!(ext.storage.get(&[1u8; 64].to_vec()), None);
|
||||
|
||||
// value exists
|
||||
let input = (19, [2u8; 19]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
// length returned (test for 0 sized)
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 0);
|
||||
assert_eq!(ext.storage.get(&[2u8; 32]), None);
|
||||
// value cleared
|
||||
assert_eq!(ext.storage.get(&[2u8; 19].to_vec()), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2302,42 +2601,42 @@ mod tests {
|
||||
(module
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "__unstable__" "seal_take_storage" (func $seal_take_storage (param i32 i32 i32) (result i32)))
|
||||
(import "__unstable__" "seal_take_storage" (func $seal_take_storage (param i32 i32 i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; [0, 32) size of input buffer (32 byte as we copy the key here)
|
||||
(data (i32.const 0) "\20")
|
||||
;; [0, 4) size of input buffer (160 bytes as we copy the key+len here)
|
||||
(data (i32.const 0) "\A0")
|
||||
|
||||
;; [32, 64) size of output buffer
|
||||
;; [4, 8) size of output buffer
|
||||
;; 4k in little endian
|
||||
(data (i32.const 32) "\00\10")
|
||||
(data (i32.const 4) "\00\10")
|
||||
|
||||
;; [64, 96) input buffer
|
||||
|
||||
;; [96, inf) output buffer
|
||||
;; [8, 168) input buffer
|
||||
;; [168, 4264) output buffer
|
||||
|
||||
(func (export "call")
|
||||
;; Receive key
|
||||
(call $seal_input
|
||||
(i32.const 64) ;; Pointer to the input buffer
|
||||
(i32.const 8) ;; Pointer to the input buffer
|
||||
(i32.const 0) ;; Size of the length buffer
|
||||
)
|
||||
|
||||
;; Load a storage value and result of this call into the output buffer
|
||||
(i32.store (i32.const 96)
|
||||
(i32.store (i32.const 168)
|
||||
(call $seal_take_storage
|
||||
(i32.const 64) ;; The pointer to the storage key to fetch
|
||||
(i32.const 100) ;; Pointer to the output buffer
|
||||
(i32.const 32) ;; Pointer to the size of the buffer
|
||||
(i32.const 12) ;; key_ptr
|
||||
(i32.load (i32.const 8)) ;; key_len
|
||||
(i32.const 172) ;; Pointer to the output buffer
|
||||
(i32.const 4) ;; Pointer to the size of the buffer
|
||||
)
|
||||
)
|
||||
|
||||
;; Return the contents of the buffer
|
||||
(call $seal_return
|
||||
(i32.const 0) ;; flags
|
||||
(i32.const 96) ;; output buffer ptr
|
||||
(i32.add ;; length: storage size + 4 (retval)
|
||||
(i32.load (i32.const 32))
|
||||
(i32.const 0) ;; flags
|
||||
(i32.const 168) ;; output buffer ptr
|
||||
(i32.add ;; length: storage size + 4 (retval)
|
||||
(i32.load (i32.const 4))
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
@@ -2349,32 +2648,46 @@ mod tests {
|
||||
|
||||
let mut ext = MockExt::default();
|
||||
|
||||
ext.storage.insert([1u8; 32], vec![42u8]);
|
||||
ext.storage.insert([2u8; 32], vec![]);
|
||||
ext.set_storage_transparent(
|
||||
&VarSizedKey::<Test>::try_from([1u8; 64].to_vec()).unwrap(),
|
||||
Some(vec![42u8]),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
ext.set_storage_transparent(
|
||||
&VarSizedKey::<Test>::try_from([2u8; 19].to_vec()).unwrap(),
|
||||
Some(vec![]),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// value does not exist -> error returned
|
||||
let result = execute(CODE, [3u8; 32].encode(), &mut ext).unwrap();
|
||||
let input = (63, [1u8; 64]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(
|
||||
u32::from_le_bytes(result.data.0[0..4].try_into().unwrap()),
|
||||
ReturnCode::KeyNotFound as u32
|
||||
);
|
||||
|
||||
// value did exist -> value returned
|
||||
let result = execute(CODE, [1u8; 32].encode(), &mut ext).unwrap();
|
||||
let input = (64, [1u8; 64]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(
|
||||
u32::from_le_bytes(result.data.0[0..4].try_into().unwrap()),
|
||||
ReturnCode::Success as u32
|
||||
);
|
||||
assert_eq!(ext.storage.get(&[1u8; 32]), None);
|
||||
assert_eq!(ext.storage.get(&[1u8; 64].to_vec()), None);
|
||||
assert_eq!(&result.data.0[4..], &[42u8]);
|
||||
|
||||
// value did exist -> length returned (test for 0 sized)
|
||||
let result = execute(CODE, [2u8; 32].encode(), &mut ext).unwrap();
|
||||
let input = (19, [2u8; 19]).encode();
|
||||
let result = execute(CODE, input, &mut ext).unwrap();
|
||||
assert_eq!(
|
||||
u32::from_le_bytes(result.data.0[0..4].try_into().unwrap()),
|
||||
ReturnCode::Success as u32
|
||||
);
|
||||
assert_eq!(ext.storage.get(&[2u8; 32]), None);
|
||||
assert_eq!(ext.storage.get(&[2u8; 19].to_vec()), None);
|
||||
assert_eq!(&result.data.0[4..], &[0u8; 0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,15 +18,16 @@
|
||||
//! Environment definition of the wasm smart-contract runtime.
|
||||
|
||||
use crate::{
|
||||
exec::{ExecError, ExecResult, Ext, StorageKey, TopicOf},
|
||||
exec::{ExecError, ExecResult, Ext, FixSizedKey, TopicOf, VarSizedKey},
|
||||
gas::{ChargedAmount, Token},
|
||||
schedule::HostFnWeights,
|
||||
wasm::env_def::ConvertibleToWasm,
|
||||
BalanceOf, CodeHash, Config, Error, SENTINEL,
|
||||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use codec::{Decode, DecodeAll, Encode, MaxEncodedLen};
|
||||
use frame_support::{dispatch::DispatchError, ensure, weights::Weight};
|
||||
use frame_support::{dispatch::DispatchError, ensure, traits::Get, weights::Weight};
|
||||
use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags};
|
||||
use sp_core::{crypto::UncheckedFrom, Bytes};
|
||||
use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256};
|
||||
@@ -35,6 +36,28 @@ use sp_sandbox::SandboxMemory;
|
||||
use sp_std::prelude::*;
|
||||
use wasm_instrument::parity_wasm::elements::ValueType;
|
||||
|
||||
/// Type of a storage key.
|
||||
#[allow(dead_code)]
|
||||
enum KeyType {
|
||||
/// Deprecated fix sized key [0;32].
|
||||
Fix,
|
||||
/// Variable sized key used in transparent hashing,
|
||||
/// cannot be larger than MaxStorageKeyLen.
|
||||
Variable(u32),
|
||||
}
|
||||
|
||||
impl KeyType {
|
||||
fn len<T: Config>(&self) -> Result<u32, TrapReason> {
|
||||
match self {
|
||||
KeyType::Fix => Ok(32u32),
|
||||
KeyType::Variable(len) => {
|
||||
ensure!(len <= &<T>::MaxStorageKeyLen::get(), Error::<T>::DecodingFailed);
|
||||
Ok(*len)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Every error that can be returned to a contract when it calls any of the host functions.
|
||||
///
|
||||
/// # Note
|
||||
@@ -695,6 +718,7 @@ where
|
||||
|
||||
fn set_storage(
|
||||
&mut self,
|
||||
key_type: KeyType,
|
||||
key_ptr: u32,
|
||||
value_ptr: u32,
|
||||
value_len: u32,
|
||||
@@ -705,10 +729,21 @@ where
|
||||
if value_len > max_size {
|
||||
return Err(Error::<E::T>::ValueTooLarge.into())
|
||||
}
|
||||
let mut key: StorageKey = [0; 32];
|
||||
self.read_sandbox_memory_into_buf(key_ptr, &mut key)?;
|
||||
let key = self.read_sandbox_memory(key_ptr, key_type.len::<E::T>()?)?;
|
||||
let value = Some(self.read_sandbox_memory(value_ptr, value_len)?);
|
||||
let write_outcome = self.ext.set_storage(key, value, false)?;
|
||||
let write_outcome = match key_type {
|
||||
KeyType::Fix => self.ext.set_storage(
|
||||
&FixSizedKey::try_from(key).map_err(|_| Error::<E::T>::DecodingFailed)?,
|
||||
value,
|
||||
false,
|
||||
)?,
|
||||
KeyType::Variable(_) => self.ext.set_storage_transparent(
|
||||
&VarSizedKey::<E::T>::try_from(key).map_err(|_| Error::<E::T>::DecodingFailed)?,
|
||||
value,
|
||||
false,
|
||||
)?,
|
||||
};
|
||||
|
||||
self.adjust_gas(
|
||||
charged,
|
||||
RuntimeCosts::SetStorage { new_bytes: value_len, old_bytes: write_outcome.old_len() },
|
||||
@@ -716,15 +751,70 @@ where
|
||||
Ok(write_outcome.old_len_with_sentinel())
|
||||
}
|
||||
|
||||
fn clear_storage(&mut self, key_ptr: u32) -> Result<u32, TrapReason> {
|
||||
fn clear_storage(&mut self, key_type: KeyType, key_ptr: u32) -> Result<u32, TrapReason> {
|
||||
let charged = self.charge_gas(RuntimeCosts::ClearStorage(self.ext.max_value_size()))?;
|
||||
let mut key: StorageKey = [0; 32];
|
||||
self.read_sandbox_memory_into_buf(key_ptr, &mut key)?;
|
||||
let outcome = self.ext.set_storage(key, None, false)?;
|
||||
let key = self.read_sandbox_memory(key_ptr, key_type.len::<E::T>()?)?;
|
||||
let outcome = match key_type {
|
||||
KeyType::Fix => self.ext.set_storage(
|
||||
&FixSizedKey::try_from(key).map_err(|_| Error::<E::T>::DecodingFailed)?,
|
||||
None,
|
||||
false,
|
||||
)?,
|
||||
KeyType::Variable(_) => self.ext.set_storage_transparent(
|
||||
&VarSizedKey::<E::T>::try_from(key).map_err(|_| Error::<E::T>::DecodingFailed)?,
|
||||
None,
|
||||
false,
|
||||
)?,
|
||||
};
|
||||
|
||||
self.adjust_gas(charged, RuntimeCosts::ClearStorage(outcome.old_len()));
|
||||
Ok(outcome.old_len_with_sentinel())
|
||||
}
|
||||
|
||||
fn get_storage(
|
||||
&mut self,
|
||||
key_type: KeyType,
|
||||
key_ptr: u32,
|
||||
out_ptr: u32,
|
||||
out_len_ptr: u32,
|
||||
) -> Result<ReturnCode, TrapReason> {
|
||||
let charged = self.charge_gas(RuntimeCosts::GetStorage(self.ext.max_value_size()))?;
|
||||
let key = self.read_sandbox_memory(key_ptr, key_type.len::<E::T>()?)?;
|
||||
let outcome = match key_type {
|
||||
KeyType::Fix => self.ext.get_storage(
|
||||
&FixSizedKey::try_from(key).map_err(|_| Error::<E::T>::DecodingFailed)?,
|
||||
),
|
||||
KeyType::Variable(_) => self.ext.get_storage_transparent(
|
||||
&VarSizedKey::<E::T>::try_from(key).map_err(|_| Error::<E::T>::DecodingFailed)?,
|
||||
),
|
||||
};
|
||||
|
||||
if let Some(value) = outcome {
|
||||
self.adjust_gas(charged, RuntimeCosts::GetStorage(value.len() as u32));
|
||||
self.write_sandbox_output(out_ptr, out_len_ptr, &value, false, already_charged)?;
|
||||
Ok(ReturnCode::Success)
|
||||
} else {
|
||||
self.adjust_gas(charged, RuntimeCosts::GetStorage(0));
|
||||
Ok(ReturnCode::KeyNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_storage(&mut self, key_type: KeyType, key_ptr: u32) -> Result<u32, TrapReason> {
|
||||
let charged = self.charge_gas(RuntimeCosts::ContainsStorage(self.ext.max_value_size()))?;
|
||||
let key = self.read_sandbox_memory(key_ptr, key_type.len::<E::T>()?)?;
|
||||
let outcome = match key_type {
|
||||
KeyType::Fix => self.ext.get_storage_size(
|
||||
&FixSizedKey::try_from(key).map_err(|_| Error::<E::T>::DecodingFailed)?,
|
||||
),
|
||||
KeyType::Variable(_) => self.ext.get_storage_size_transparent(
|
||||
&VarSizedKey::<E::T>::try_from(key).map_err(|_| Error::<E::T>::DecodingFailed)?,
|
||||
),
|
||||
};
|
||||
|
||||
self.adjust_gas(charged, RuntimeCosts::ClearStorage(outcome.unwrap_or(0)));
|
||||
Ok(outcome.unwrap_or(SENTINEL))
|
||||
}
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
flags: CallFlags,
|
||||
@@ -863,11 +953,14 @@ define_env!(Env, <E: Ext>,
|
||||
// Equivalent to the newer version of `seal_set_storage` with the exception of the return
|
||||
// type. Still a valid thing to call when not interested in the return value.
|
||||
[seal0] seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) => {
|
||||
ctx.set_storage(key_ptr, value_ptr, value_len).map(|_| ())
|
||||
ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
|
||||
},
|
||||
|
||||
// Set the value at the given key in the contract storage.
|
||||
//
|
||||
// This version is to be used with a fixed sized storage key. For runtimes supporting transparent
|
||||
// hashing, please use the newer version of this function.
|
||||
//
|
||||
// The value length must not exceed the maximum defined by the contracts module parameters.
|
||||
// Specifying a `value_len` of zero will store an empty value.
|
||||
//
|
||||
@@ -882,7 +975,27 @@ define_env!(Env, <E: Ext>,
|
||||
// Returns the size of the pre-existing value at the specified key if any. Otherwise
|
||||
// `SENTINEL` is returned as a sentinel value.
|
||||
[seal1] seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) -> u32 => {
|
||||
ctx.set_storage(key_ptr, value_ptr, value_len)
|
||||
ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len)
|
||||
},
|
||||
|
||||
// Set the value at the given key in the contract storage.
|
||||
//
|
||||
// The key and value lengths must not exceed the maximums defined by the contracts module parameters.
|
||||
// Specifying a `value_len` of zero will store an empty value.
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// - `key_ptr`: pointer into the linear memory where the location to store the value is placed.
|
||||
// - `key_len`: the length of the key in bytes.
|
||||
// - `value_ptr`: pointer into the linear memory where the value to set is placed.
|
||||
// - `value_len`: the length of the value in bytes.
|
||||
//
|
||||
// # Return Value
|
||||
//
|
||||
// Returns the size of the pre-existing value at the specified key if any. Otherwise
|
||||
// `SENTINEL` is returned as a sentinel value.
|
||||
[__unstable__] seal_set_storage(ctx, key_ptr: u32, key_len: u32, value_ptr: u32, value_len: u32) -> u32 => {
|
||||
ctx.set_storage(KeyType::Variable(key_len), key_ptr, value_ptr, value_len)
|
||||
},
|
||||
|
||||
// Clear the value at the given key in the contract storage.
|
||||
@@ -890,25 +1003,29 @@ define_env!(Env, <E: Ext>,
|
||||
// Equivalent to the newer version of `seal_clear_storage` with the exception of the return
|
||||
// type. Still a valid thing to call when not interested in the return value.
|
||||
[seal0] seal_clear_storage(ctx, key_ptr: u32) => {
|
||||
ctx.clear_storage(key_ptr).map(|_| ()).map_err(Into::into)
|
||||
ctx.clear_storage(KeyType::Fix, key_ptr).map(|_| ())
|
||||
},
|
||||
|
||||
// Clear the value at the given key in the contract storage.
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// - `key_ptr`: pointer into the linear memory where the location to clear the value is placed.
|
||||
// - `key_ptr`: pointer into the linear memory where the key is placed.
|
||||
// - `key_len`: the length of the key in bytes.
|
||||
//
|
||||
// # Return Value
|
||||
//
|
||||
// Returns the size of the pre-existing value at the specified key if any. Otherwise
|
||||
// `SENTINEL` is returned as a sentinel value.
|
||||
[__unstable__] seal_clear_storage(ctx, key_ptr: u32) -> u32 => {
|
||||
ctx.clear_storage(key_ptr).map_err(Into::into)
|
||||
[__unstable__] seal_clear_storage(ctx, key_ptr: u32, key_len: u32) -> u32 => {
|
||||
ctx.clear_storage(KeyType::Variable(key_len), key_ptr)
|
||||
},
|
||||
|
||||
// Retrieve the value under the given key from storage.
|
||||
//
|
||||
// This version is to be used with a fixed sized storage key. For runtimes supporting transparent
|
||||
// hashing, please use the newer version of this function.
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// - `key_ptr`: pointer into the linear memory where the key of the requested value is placed.
|
||||
@@ -920,21 +1037,36 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// `ReturnCode::KeyNotFound`
|
||||
[seal0] seal_get_storage(ctx, key_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => {
|
||||
let charged = ctx.charge_gas(RuntimeCosts::GetStorage(ctx.ext.max_value_size()))?;
|
||||
let mut key: StorageKey = [0; 32];
|
||||
ctx.read_sandbox_memory_into_buf(key_ptr, &mut key)?;
|
||||
if let Some(value) = ctx.ext.get_storage(&key) {
|
||||
ctx.adjust_gas(charged, RuntimeCosts::GetStorage(value.len() as u32));
|
||||
ctx.write_sandbox_output(out_ptr, out_len_ptr, &value, false, already_charged)?;
|
||||
Ok(ReturnCode::Success)
|
||||
} else {
|
||||
ctx.adjust_gas(charged, RuntimeCosts::GetStorage(0));
|
||||
Ok(ReturnCode::KeyNotFound)
|
||||
}
|
||||
ctx.get_storage(KeyType::Fix, key_ptr, out_ptr, out_len_ptr)
|
||||
},
|
||||
|
||||
// Retrieve the value under the given key from storage.
|
||||
//
|
||||
// This version is to be used with a fixed sized storage key. For runtimes supporting transparent
|
||||
// hashing, please use the newer version of this function.
|
||||
//
|
||||
// The key length must not exceed the maximum defined by the contracts module parameter.
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// - `key_ptr`: pointer into the linear memory where the key of the requested value is placed.
|
||||
// - `key_len`: the length of the key in bytes.
|
||||
// - `out_ptr`: pointer to the linear memory where the value is written to.
|
||||
// - `out_len_ptr`: in-out pointer into linear memory where the buffer length
|
||||
// is read from and the value length is written to.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// `ReturnCode::KeyNotFound`
|
||||
[__unstable__] seal_get_storage(ctx, key_ptr: u32, key_len: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => {
|
||||
ctx.get_storage(KeyType::Variable(key_len), key_ptr, out_ptr, out_len_ptr)
|
||||
},
|
||||
|
||||
// Checks whether there is a value stored under the given key.
|
||||
//
|
||||
// This version is to be used with a fixed sized storage key. For runtimes supporting transparent
|
||||
// hashing, please use the newer version of this function.
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// - `key_ptr`: pointer into the linear memory where the key of the requested value is placed.
|
||||
@@ -944,16 +1076,24 @@ define_env!(Env, <E: Ext>,
|
||||
// Returns the size of the pre-existing value at the specified key if any. Otherwise
|
||||
// `SENTINEL` is returned as a sentinel value.
|
||||
[seal0] seal_contains_storage(ctx, key_ptr: u32) -> u32 => {
|
||||
let charged = ctx.charge_gas(RuntimeCosts::ContainsStorage(ctx.ext.max_value_size()))?;
|
||||
let mut key: StorageKey = [0; 32];
|
||||
ctx.read_sandbox_memory_into_buf(key_ptr, &mut key)?;
|
||||
if let Some(len) = ctx.ext.get_storage_size(&key) {
|
||||
ctx.adjust_gas(charged, RuntimeCosts::ContainsStorage(len));
|
||||
Ok(len)
|
||||
} else {
|
||||
ctx.adjust_gas(charged, RuntimeCosts::ContainsStorage(0));
|
||||
Ok(SENTINEL)
|
||||
}
|
||||
ctx.contains_storage(KeyType::Fix, key_ptr)
|
||||
},
|
||||
|
||||
// Checks whether there is a value stored under the given key.
|
||||
//
|
||||
// The key length must not exceed the maximum defined by the contracts module parameter.
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// - `key_ptr`: pointer into the linear memory where the key of the requested value is placed.
|
||||
// - `key_len`: the length of the key in bytes.
|
||||
//
|
||||
// # Return Value
|
||||
//
|
||||
// Returns the size of the pre-existing value at the specified key if any. Otherwise
|
||||
// `SENTINEL` is returned as a sentinel value.
|
||||
[__unstable__] seal_contains_storage(ctx, key_ptr: u32, key_len: u32) -> u32 => {
|
||||
ctx.contains_storage(KeyType::Variable(key_len), key_ptr)
|
||||
},
|
||||
|
||||
// Retrieve and remove the value under the given key from storage.
|
||||
@@ -961,6 +1101,7 @@ define_env!(Env, <E: Ext>,
|
||||
// # Parameters
|
||||
//
|
||||
// - `key_ptr`: pointer into the linear memory where the key of the requested value is placed.
|
||||
// - `key_len`: the length of the key in bytes.
|
||||
// - `out_ptr`: pointer to the linear memory where the value is written to.
|
||||
// - `out_len_ptr`: in-out pointer into linear memory where the buffer length
|
||||
// is read from and the value length is written to.
|
||||
@@ -968,11 +1109,10 @@ define_env!(Env, <E: Ext>,
|
||||
// # Errors
|
||||
//
|
||||
// `ReturnCode::KeyNotFound`
|
||||
[__unstable__] seal_take_storage(ctx, key_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => {
|
||||
[__unstable__] seal_take_storage(ctx, key_ptr: u32, key_len: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => {
|
||||
let charged = ctx.charge_gas(RuntimeCosts::TakeStorage(ctx.ext.max_value_size()))?;
|
||||
let mut key: StorageKey = [0; 32];
|
||||
ctx.read_sandbox_memory_into_buf(key_ptr, &mut key)?;
|
||||
if let crate::storage::WriteOutcome::Taken(value) = ctx.ext.set_storage(key, None, true)? {
|
||||
let key = ctx.read_sandbox_memory(key_ptr, key_len)?;
|
||||
if let crate::storage::WriteOutcome::Taken(value) = ctx.ext.set_storage_transparent(&VarSizedKey::<E::T>::try_from(key).map_err(|_| Error::<E::T>::DecodingFailed)?, None, true)? {
|
||||
ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(value.len() as u32));
|
||||
ctx.write_sandbox_output(out_ptr, out_len_ptr, &value, false, already_charged)?;
|
||||
Ok(ReturnCode::Success)
|
||||
|
||||
Reference in New Issue
Block a user