contracts: Refactor trait Ext::*_storage_transparent functions (#13600)

* Refactor _transparent methods

rewrote commits, stashed the typo changes to remove some diff noise
fixed my unverified email commit

* remove type alias

* Get rid of From<Fix/VarSizedKey> impl blocks

* Get rid of KeyType impl block

* remove unnecessary Key export

* Update frame/contracts/src/exec.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* PR review comment

---------

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
This commit is contained in:
PG Herveou
2023-03-17 23:17:21 +01:00
committed by GitHub
parent 6c3747ba34
commit 8fcd235e38
8 changed files with 199 additions and 295 deletions
+112 -153
View File
@@ -35,7 +35,7 @@ use smallvec::{Array, SmallVec};
use sp_core::ecdsa::Public as ECDSAPublic;
use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256};
use sp_runtime::traits::{Convert, Hash};
use sp_std::{marker::PhantomData, mem, prelude::*};
use sp_std::{marker::PhantomData, mem, prelude::*, vec::Vec};
pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
pub type MomentOf<T> = <<T as Config>::Time as Time>::Moment;
@@ -46,32 +46,39 @@ pub type ExecResult = Result<ExecReturnValue, ExecError>;
/// A type that represents a topic of an event. At the moment a hash is used.
pub type TopicOf<T> = <T as frame_system::Config>::Hash;
/// Type for fix sized storage key.
pub type FixSizedKey = [u8; 32];
/// Type for variable sized storage key. Used for transparent hashing.
pub type VarSizedKey<T> = BoundedVec<u8, <T as Config>::MaxStorageKeyLen>;
type VarSizedKey<T> = BoundedVec<u8, <T as Config>::MaxStorageKeyLen>;
/// Trait for hashing storage keys.
pub trait StorageKey<T>
where
T: Config,
{
fn hash(&self) -> Vec<u8>;
/// Combined key type for both fixed and variable sized storage keys.
pub enum Key<T: Config> {
/// Variant for fixed sized keys.
Fix([u8; 32]),
/// Variant for variable sized keys.
Var(VarSizedKey<T>),
}
impl<T: Config> StorageKey<T> for FixSizedKey {
fn hash(&self) -> Vec<u8> {
blake2_256(self.as_slice()).to_vec()
impl<T: Config> Key<T> {
/// Copies self into a new vec.
pub fn to_vec(&self) -> Vec<u8> {
match self {
Key::Fix(v) => v.to_vec(),
Key::Var(v) => v.to_vec(),
}
}
}
impl<T> StorageKey<T> for VarSizedKey<T>
where
T: Config,
{
fn hash(&self) -> Vec<u8> {
Blake2_128Concat::hash(self.as_slice())
pub fn hash(&self) -> Vec<u8> {
match self {
Key::Fix(v) => blake2_256(v.as_slice()).to_vec(),
Key::Var(v) => Blake2_128Concat::hash(v.as_slice()),
}
}
pub fn try_from_fix(v: Vec<u8>) -> Result<Self, Vec<u8>> {
<[u8; 32]>::try_from(v).map(Self::Fix)
}
pub fn try_from_var(v: Vec<u8>) -> Result<Self, Vec<u8>> {
VarSizedKey::<T>::try_from(v).map(Self::Var)
}
}
@@ -169,44 +176,19 @@ pub trait Ext: sealing::Sealed {
///
/// Returns `None` if the `key` wasn't previously set by `set_storage` or
/// was deleted.
fn get_storage(&mut self, key: &FixSizedKey) -> Option<Vec<u8>>;
/// This is a variation of `get_storage()` to be used with transparent hashing.
/// These two will be merged into a single function after some refactoring is done.
/// Returns the storage entry of the executing account by the given `key`.
///
/// Returns `None` if the `key` wasn't previously set by `set_storage` or
/// was deleted.
fn get_storage_transparent(&mut self, key: &VarSizedKey<Self::T>) -> Option<Vec<u8>>;
fn get_storage(&mut self, key: &Key<Self::T>) -> Option<Vec<u8>>;
/// Returns `Some(len)` (in bytes) if a storage item exists at `key`.
///
/// Returns `None` if the `key` wasn't previously set by `set_storage` or
/// was deleted.
fn get_storage_size(&mut self, key: &FixSizedKey) -> Option<u32>;
/// This is the variation of `get_storage_size()` to be used with transparent hashing.
/// These two will be merged into a single function after some refactoring is done.
/// Returns `Some(len)` (in bytes) if a storage item exists at `key`.
///
/// Returns `None` if the `key` wasn't previously set by `set_storage` or
/// was deleted.
fn get_storage_size_transparent(&mut self, key: &VarSizedKey<Self::T>) -> Option<u32>;
fn get_storage_size(&mut self, key: &Key<Self::T>) -> Option<u32>;
/// Sets the storage entry by the given key to the specified value. If `value` is `None` then
/// the storage entry is deleted.
fn set_storage(
&mut self,
key: &FixSizedKey,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError>;
/// This is the variation of `set_storage()` to be used with transparent hashing.
/// These two will be merged into a single function after some refactoring is done.
fn set_storage_transparent(
&mut self,
key: &VarSizedKey<Self::T>,
key: &Key<Self::T>,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError>;
@@ -1236,46 +1218,23 @@ where
Self::transfer(ExistenceRequirement::KeepAlive, &self.top_frame().account_id, to, value)
}
fn get_storage(&mut self, key: &FixSizedKey) -> Option<Vec<u8>> {
fn get_storage(&mut self, key: &Key<T>) -> Option<Vec<u8>> {
self.top_frame_mut().contract_info().read(key)
}
fn get_storage_transparent(&mut self, key: &VarSizedKey<T>) -> Option<Vec<u8>> {
self.top_frame_mut().contract_info().read(key)
}
fn get_storage_size(&mut self, key: &FixSizedKey) -> Option<u32> {
self.top_frame_mut().contract_info().size(key)
}
fn get_storage_size_transparent(&mut self, key: &VarSizedKey<T>) -> Option<u32> {
self.top_frame_mut().contract_info().size(key)
fn get_storage_size(&mut self, key: &Key<T>) -> Option<u32> {
self.top_frame_mut().contract_info().size(key.into())
}
fn set_storage(
&mut self,
key: &FixSizedKey,
key: &Key<T>,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError> {
let frame = self.top_frame_mut();
frame.contract_info.get(&frame.account_id).write(
key,
value,
Some(&mut frame.nested_storage),
take_old,
)
}
fn set_storage_transparent(
&mut self,
key: &VarSizedKey<T>,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError> {
let frame = self.top_frame_mut();
frame.contract_info.get(&frame.account_id).write(
key,
key.into(),
value,
Some(&mut frame.nested_storage),
take_old,
@@ -1688,7 +1647,7 @@ mod tests {
place_contract(&dest, success_ch);
set_balance(&origin, 100);
let balance = get_balance(&dest);
let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 55).unwrap();
let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), value).unwrap();
let _ = MockStack::run_call(
origin.clone(),
@@ -2986,35 +2945,41 @@ mod tests {
let code_hash = MockLoader::insert(Call, |ctx, _| {
// Write
assert_eq!(
ctx.ext.set_storage(&[1; 32], Some(vec![1, 2, 3]), false),
ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage(&[2; 32], Some(vec![4, 5, 6]), true),
ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![4, 5, 6]), true),
Ok(WriteOutcome::New)
);
assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New));
assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New));
assert_eq!(
ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true),
Ok(WriteOutcome::New)
);
assert_eq!(ctx.ext.set_storage(&[3; 32], None, false), Ok(WriteOutcome::New));
assert_eq!(ctx.ext.set_storage(&[4; 32], None, true), Ok(WriteOutcome::New));
assert_eq!(ctx.ext.set_storage(&[5; 32], Some(vec![]), false), Ok(WriteOutcome::New));
assert_eq!(ctx.ext.set_storage(&[6; 32], Some(vec![]), true), Ok(WriteOutcome::New));
// Overwrite
assert_eq!(
ctx.ext.set_storage(&[1; 32], Some(vec![42]), false),
ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![42]), false),
Ok(WriteOutcome::Overwritten(3))
);
assert_eq!(
ctx.ext.set_storage(&[2; 32], Some(vec![48]), true),
ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![48]), true),
Ok(WriteOutcome::Taken(vec![4, 5, 6]))
);
assert_eq!(ctx.ext.set_storage(&[3; 32], None, false), Ok(WriteOutcome::New));
assert_eq!(ctx.ext.set_storage(&[4; 32], None, true), Ok(WriteOutcome::New));
assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New));
assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New));
assert_eq!(
ctx.ext.set_storage(&[5; 32], Some(vec![]), false),
ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false),
Ok(WriteOutcome::Overwritten(0))
);
assert_eq!(
ctx.ext.set_storage(&[6; 32], Some(vec![]), true),
ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true),
Ok(WriteOutcome::Taken(vec![]))
);
@@ -3043,52 +3008,52 @@ mod tests {
}
#[test]
fn set_storage_transparent_works() {
fn set_storage_varsized_key_works() {
let code_hash = MockLoader::insert(Call, |ctx, _| {
// Write
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([1; 64].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([1; 64].to_vec()).unwrap(),
Some(vec![1, 2, 3]),
false
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([2; 19].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([2; 19].to_vec()).unwrap(),
Some(vec![4, 5, 6]),
true
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([3; 19].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([3; 19].to_vec()).unwrap(),
None,
false
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([4; 64].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([4; 64].to_vec()).unwrap(),
None,
true
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([5; 30].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([5; 30].to_vec()).unwrap(),
Some(vec![]),
false
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([6; 128].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([6; 128].to_vec()).unwrap(),
Some(vec![]),
true
),
@@ -3097,48 +3062,48 @@ mod tests {
// Overwrite
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([1; 64].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([1; 64].to_vec()).unwrap(),
Some(vec![42, 43, 44]),
false
),
Ok(WriteOutcome::Overwritten(3))
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([2; 19].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([2; 19].to_vec()).unwrap(),
Some(vec![48]),
true
),
Ok(WriteOutcome::Taken(vec![4, 5, 6]))
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([3; 19].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([3; 19].to_vec()).unwrap(),
None,
false
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([4; 64].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([4; 64].to_vec()).unwrap(),
None,
true
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([5; 30].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([5; 30].to_vec()).unwrap(),
Some(vec![]),
false
),
Ok(WriteOutcome::Overwritten(0))
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([6; 128].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([6; 128].to_vec()).unwrap(),
Some(vec![]),
true
),
@@ -3173,13 +3138,16 @@ mod tests {
fn get_storage_works() {
let code_hash = MockLoader::insert(Call, |ctx, _| {
assert_eq!(
ctx.ext.set_storage(&[1; 32], Some(vec![1, 2, 3]), false),
ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
Ok(WriteOutcome::New)
);
assert_eq!(ctx.ext.set_storage(&[2; 32], Some(vec![]), false), Ok(WriteOutcome::New));
assert_eq!(ctx.ext.get_storage(&[1; 32]), Some(vec![1, 2, 3]));
assert_eq!(ctx.ext.get_storage(&[2; 32]), Some(vec![]));
assert_eq!(ctx.ext.get_storage(&[3; 32]), None);
assert_eq!(
ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false),
Ok(WriteOutcome::New)
);
assert_eq!(ctx.ext.get_storage(&Key::Fix([1; 32])), Some(vec![1, 2, 3]));
assert_eq!(ctx.ext.get_storage(&Key::Fix([2; 32])), Some(vec![]));
assert_eq!(ctx.ext.get_storage(&Key::Fix([3; 32])), None);
exec_success()
});
@@ -3209,13 +3177,16 @@ mod tests {
fn get_storage_size_works() {
let code_hash = MockLoader::insert(Call, |ctx, _| {
assert_eq!(
ctx.ext.set_storage(&[1; 32], Some(vec![1, 2, 3]), false),
ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
Ok(WriteOutcome::New)
);
assert_eq!(ctx.ext.set_storage(&[2; 32], Some(vec![]), false), Ok(WriteOutcome::New));
assert_eq!(ctx.ext.get_storage_size(&[1; 32]), Some(3));
assert_eq!(ctx.ext.get_storage_size(&[2; 32]), Some(0));
assert_eq!(ctx.ext.get_storage_size(&[3; 32]), None);
assert_eq!(
ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false),
Ok(WriteOutcome::New)
);
assert_eq!(ctx.ext.get_storage_size(&Key::Fix([1; 32])), Some(3));
assert_eq!(ctx.ext.get_storage_size(&Key::Fix([2; 32])), Some(0));
assert_eq!(ctx.ext.get_storage_size(&Key::Fix([3; 32])), None);
exec_success()
});
@@ -3242,40 +3213,34 @@ mod tests {
}
#[test]
fn get_storage_transparent_works() {
fn get_storage_varsized_key_works() {
let code_hash = MockLoader::insert(Call, |ctx, _| {
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([1; 19].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([1; 19].to_vec()).unwrap(),
Some(vec![1, 2, 3]),
false
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([2; 16].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([2; 16].to_vec()).unwrap(),
Some(vec![]),
false
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.get_storage_transparent(
&VarSizedKey::<Test>::try_from([1; 19].to_vec()).unwrap()
),
ctx.ext.get_storage(&Key::<Test>::try_from_var([1; 19].to_vec()).unwrap()),
Some(vec![1, 2, 3])
);
assert_eq!(
ctx.ext.get_storage_transparent(
&VarSizedKey::<Test>::try_from([2; 16].to_vec()).unwrap()
),
ctx.ext.get_storage(&Key::<Test>::try_from_var([2; 16].to_vec()).unwrap()),
Some(vec![])
);
assert_eq!(
ctx.ext.get_storage_transparent(
&VarSizedKey::<Test>::try_from([3; 8].to_vec()).unwrap()
),
ctx.ext.get_storage(&Key::<Test>::try_from_var([3; 8].to_vec()).unwrap()),
None
);
@@ -3304,40 +3269,34 @@ mod tests {
}
#[test]
fn get_storage_size_transparent_works() {
fn get_storage_size_varsized_key_works() {
let code_hash = MockLoader::insert(Call, |ctx, _| {
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([1; 19].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([1; 19].to_vec()).unwrap(),
Some(vec![1, 2, 3]),
false
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.set_storage_transparent(
&VarSizedKey::<Test>::try_from([2; 16].to_vec()).unwrap(),
ctx.ext.set_storage(
&Key::<Test>::try_from_var([2; 16].to_vec()).unwrap(),
Some(vec![]),
false
),
Ok(WriteOutcome::New)
);
assert_eq!(
ctx.ext.get_storage_size_transparent(
&VarSizedKey::<Test>::try_from([1; 19].to_vec()).unwrap()
),
ctx.ext.get_storage_size(&Key::<Test>::try_from_var([1; 19].to_vec()).unwrap()),
Some(3)
);
assert_eq!(
ctx.ext.get_storage_size_transparent(
&VarSizedKey::<Test>::try_from([2; 16].to_vec()).unwrap()
),
ctx.ext.get_storage_size(&Key::<Test>::try_from_var([2; 16].to_vec()).unwrap()),
Some(0)
);
assert_eq!(
ctx.ext.get_storage_size_transparent(
&VarSizedKey::<Test>::try_from([3; 8].to_vec()).unwrap()
),
ctx.ext.get_storage_size(&Key::<Test>::try_from_var([3; 8].to_vec()).unwrap()),
None
);