Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

[contracts] Implement transparent hashing for contract storage #11501

Merged
merged 78 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
9a0da02
save
agryaznov May 19, 2022
da608cb
builds and old tests pass
agryaznov May 21, 2022
e15f881
type names enhanced
agryaznov May 22, 2022
78fa5d5
Merge branch 'master' into ag-transparent-hashing
agryaznov May 22, 2022
ceb1588
VarSizedKey bounded to new Config param
agryaznov May 22, 2022
ea8c5be
improved wasm runtime updated funcs
agryaznov May 22, 2022
7d5a613
unstable-interface tests fixed
agryaznov May 22, 2022
0d8206c
benchmarks fixed
agryaznov May 22, 2022
e1a735f
Apply suggestions from code review
agryaznov May 24, 2022
e195d5c
fixes on feedback
agryaznov May 24, 2022
1534c24
Merge branch 'ag-transparent-hashing' of github.com:paritytech/substr…
agryaznov May 24, 2022
b6c2915
Merge branch 'master' into ag-transparent-hashing
agryaznov May 24, 2022
cb01583
fixes on feedback applied + make it build
agryaznov May 25, 2022
75fadc8
benchmarks build but fail (old)
agryaznov May 25, 2022
7a8ea0f
"Original code too large"
agryaznov May 25, 2022
4d548c8
seal_clear_storage bench fixed (code size workaround hack removal tbd)
agryaznov May 25, 2022
ed3539c
bench_seal_clear_storage pass
agryaznov May 27, 2022
86b9275
bench_seal_take_storage ... ok
agryaznov May 27, 2022
a0ffe4b
added new seal_set_storage + updated benchmarks
agryaznov May 27, 2022
01db020
added new seal_get_storage + updated benchmarks
agryaznov May 27, 2022
67ee754
added new seal_contains_storage + updated benchmarks
agryaznov May 27, 2022
25f4e85
added tests for _transparent exec functions
agryaznov May 27, 2022
036c4a0
Merge branch 'master' into ag-transparent-hashing
agryaznov May 27, 2022
970f8b7
wasm test for clear_storage
agryaznov May 30, 2022
d9206b4
Merge branch 'master' into ag-transparent-hashing
agryaznov May 30, 2022
840488a
wasm test for take_storage
agryaznov May 30, 2022
d1068d8
wasm test for new set_storage
agryaznov May 30, 2022
c7663fc
wasm test for new get_storage
agryaznov May 31, 2022
a38bda6
wasm test for new contains_storage
agryaznov May 31, 2022
1e47811
Merge branch 'master' into ag-transparent-hashing
agryaznov May 31, 2022
a2f5143
CI fix
agryaznov May 31, 2022
76fbbb0
ci fix
agryaznov May 31, 2022
7b76ea9
ci fix
agryaznov May 31, 2022
d5d4bae
ci fix
agryaznov May 31, 2022
f79d9d7
Merge branch 'master' of https://github.com/paritytech/substrate into…
Jun 7, 2022
f35dccd
cargo run --quiet --profile=production --features=runtime-benchmarks…
Jun 7, 2022
954c185
fixes according to the review feedback
agryaznov Jun 9, 2022
513c256
Merge branch 'master' into ag-transparent-hashing
agryaznov Jun 9, 2022
c429e99
tests & benchmarks fixed
agryaznov Jun 9, 2022
abc1a22
Merge branch 'ag-transparent-hashing' of github.com:paritytech/substr…
agryaznov Jun 9, 2022
bfb1550
Merge branch 'master' of https://github.com/paritytech/substrate into…
Jun 10, 2022
f6336eb
cargo run --quiet --profile=production --features=runtime-benchmarks…
Jun 10, 2022
7b3ddf8
refactoring
agryaznov Jun 11, 2022
7b99cb4
Merge branch 'master' into ag-transparent-hashing
agryaznov Jun 11, 2022
62735c2
fix to runtime api
agryaznov Jun 11, 2022
d5f240c
ci fix
agryaznov Jun 11, 2022
929ec62
ctx.get_storage() factored out
agryaznov Jun 14, 2022
e81d2c2
ctx.contains_storage() factored out
agryaznov Jun 14, 2022
52a6982
number of batches reduced for transparent hashing storage benchmarks
agryaznov Jun 14, 2022
8a5ce46
Merge branch 'master' into ag-transparent-hashing
agryaznov Jun 14, 2022
456f4c9
contracts RPC & pallet::get_storage to use transparent hashing
agryaznov Jun 14, 2022
e99e7a9
Merge branch 'master' into ag-transparent-hashing
agryaznov Jun 21, 2022
2b764ff
node and rpc updated to use get_storage with VarSizedKey
agryaznov Jun 21, 2022
4652e6f
refactored (more concize)
agryaznov Jun 21, 2022
4ff9bf3
refactored contains_storage (DRYed)
agryaznov Jun 21, 2022
5f28ab8
refactored contains_storage (DRYed)
agryaznov Jun 21, 2022
744bf2e
Merge branch 'master' into ag-transparent-hashing
agryaznov Jun 21, 2022
2e59a8e
fix rpc
agryaznov Jun 21, 2022
c582cff
fmt fix
agryaznov Jun 21, 2022
a5c2659
more fixes in rpc
agryaznov Jun 21, 2022
205d428
rollback `Pallet:get_storage` to Vec<u8> and rpc and node parts relat…
agryaznov Jun 21, 2022
cb6945b
added `KeyDecodingFailed` error
agryaznov Jun 21, 2022
72682de
Revert weird "fmt fix"
agryaznov Jun 21, 2022
23d2209
node-executor basic test update
agryaznov Jun 21, 2022
a7c58b4
fix node-executor basic test
agryaznov Jun 21, 2022
0497517
benchmarks fix
agryaznov Jun 22, 2022
13777b3
more benchmarks fix
agryaznov Jun 22, 2022
12c246e
FixedSizedKey is hidden from pub, VarSizedKey is exported as StorageKey
agryaznov Jun 22, 2022
b91af13
ci fix
agryaznov Jun 22, 2022
591b4b7
set_storage benchmark fix
agryaznov Jun 22, 2022
5802637
ci fix
agryaznov Jun 22, 2022
d95fe82
ci fix
agryaznov Jun 22, 2022
d659578
comments improved
agryaznov Jun 22, 2022
ada2139
new error code to rpc: KEY_DECODING_FAILED
agryaznov Jun 22, 2022
3f578bd
Put `rusty-cachier` before PR merge into `master` for `cargo-check-be…
rcny Jun 22, 2022
363ac54
Merge branch 'vi-fix-cargo-check-benches-ordering' into ag-transparen…
agryaznov Jun 22, 2022
b19f503
cargo run --quiet --profile=production --features=runtime-benchmarks…
Jun 22, 2022
f34676c
minor optimization
agryaznov Jun 23, 2022
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
1 change: 1 addition & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,7 @@ impl pallet_contracts::Config for Runtime {
type ContractAccessWeight = pallet_contracts::DefaultContractAccessWeight<RuntimeBlockWeights>;
type MaxCodeLen = ConstU32<{ 128 * 1024 }>;
type RelaxedMaxCodeLen = ConstU32<{ 256 * 1024 }>;
type MaxStorageKeyLen = ConstU32<128>;
}

impl pallet_sudo::Config for Runtime {
Expand Down
36 changes: 21 additions & 15 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use self::{
sandbox::Sandbox,
};
use crate::{
exec::{AccountIdOf, StorageKey},
exec::{AccountIdOf, FixSizedKey},
schedule::{API_BENCHMARK_BATCH_SIZE, INSTR_BENCHMARK_BATCH_SIZE},
storage::Storage,
wasm::CallFlags,
Expand Down Expand Up @@ -132,11 +132,17 @@ where
}

/// Store the supplied storage items into this contracts storage.
fn store(&self, items: &Vec<(StorageKey, Vec<u8>)>) -> Result<(), &'static str> {
fn store(&self, items: &Vec<(FixSizedKey, Vec<u8>)>) -> Result<(), &'static str> {
let info = self.info()?;
for item in items {
Storage::<T>::write(&info.trie_id, &item.0, Some(item.1.clone()), None, false)
.map_err(|_| "Failed to write storage to restoration dest")?;
Storage::<T>::write(
&info.trie_id,
item.0 as FixSizedKey,
Some(item.1.clone()),
None,
false,
)
.map_err(|_| "Failed to write storage to restoration dest")?;
}
<ContractInfoOf<T>>::insert(&self.account_id, info);
Ok(())
Expand Down Expand Up @@ -935,7 +941,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![]),
None,
false,
Expand Down Expand Up @@ -981,7 +987,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![]),
None,
false,
Expand Down Expand Up @@ -1027,7 +1033,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![42u8; (n * 1024) as usize]),
None,
false,
Expand Down Expand Up @@ -1074,7 +1080,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![]),
None,
false,
Expand Down Expand Up @@ -1119,7 +1125,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![42u8; (n * 1024) as usize]),
None,
false,
Expand Down Expand Up @@ -1171,7 +1177,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![]),
None,
false,
Expand Down Expand Up @@ -1223,7 +1229,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![42u8; (n * 1024) as usize]),
None,
false,
Expand Down Expand Up @@ -1270,7 +1276,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![]),
None,
false,
Expand Down Expand Up @@ -1315,7 +1321,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![42u8; (n * 1024) as usize]),
None,
false,
Expand Down Expand Up @@ -1367,7 +1373,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![]),
None,
false,
Expand Down Expand Up @@ -1419,7 +1425,7 @@ benchmarks! {
for key in keys {
Storage::<T>::write(
&info.trie_id,
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
FixSizedKey::try_from(key).map_err(|e| "Key has wrong length")?,
Some(vec![42u8; (n * 1024) as usize]),
None,
false,
Expand Down
82 changes: 70 additions & 12 deletions frame/contracts/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,48 @@ use crate::{
use frame_support::{
crypto::ecdsa::ECDSAExt,
dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable},
pallet_prelude::ConstU32,
storage::{with_transaction, TransactionOutcome},
traits::{Contains, Currency, ExistenceRequirement, OriginTrait, Randomness, Time},
weights::Weight,
Blake2_128Concat, BoundedVec, StorageHasher,
};
use frame_system::RawOrigin;
use pallet_contracts_primitives::ExecReturnValue;
use smallvec::{Array, SmallVec};
use sp_core::{crypto::UncheckedFrom, ecdsa::Public as ECDSAPublic};
use sp_io::crypto::secp256k1_ecdsa_recover_compressed;
use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256};
use sp_runtime::traits::Convert;
use sp_std::{marker::PhantomData, mem, prelude::*};

pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
pub type MomentOf<T> = <<T as Config>::Time as Time>::Moment;
pub type SeedOf<T> = <T as frame_system::Config>::Hash;
pub type BlockNumberOf<T> = <T as frame_system::Config>::BlockNumber;
pub type StorageKey = [u8; 32];
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;

pub type FixSizedKey = [u8; 32];
pub type VarSizedKey = BoundedVec<u8, ConstU32<128>>; // TODO: length to Config Get param
agryaznov marked this conversation as resolved.
Show resolved Hide resolved

pub trait StorageHash {
fn hash(self) -> Vec<u8>;
}

impl StorageHash for FixSizedKey {
fn hash(self) -> Vec<u8> {
blake2_256(self.as_slice()).to_vec()
}
}

impl StorageHash for VarSizedKey {
fn hash(self) -> Vec<u8> {
Blake2_128Concat::hash(self.as_slice())
}
}

/// Origin of the error.
///
/// Call or instantiate both called into other contracts and pass through errors happening
Expand Down Expand Up @@ -140,19 +160,33 @@ 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: &StorageKey) -> Option<Vec<u8>>;
fn get_storage(&mut self, key: FixSizedKey) -> Option<Vec<u8>>;
athei marked this conversation as resolved.
Show resolved Hide resolved

///
fn get_storage_transparent(&mut self, key: VarSizedKey) -> 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: &StorageKey) -> Option<u32>;
fn get_storage_size(&mut self, key: FixSizedKey) -> Option<u32>;

///
fn get_storage_size_transparent(&mut self, key: VarSizedKey) -> 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: StorageKey,
key: FixSizedKey,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError>;

///
fn set_storage_transparent(
&mut self,
key: VarSizedKey,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError>;
athei marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -1085,24 +1119,48 @@ where
Self::transfer(ExistenceRequirement::KeepAlive, &self.top_frame().account_id, to, value)
}

fn get_storage(&mut self, key: &StorageKey) -> Option<Vec<u8>> {
fn get_storage(&mut self, key: FixSizedKey) -> Option<Vec<u8>> {
Storage::<T>::read(&self.top_frame_mut().contract_info().trie_id, key)
}

fn get_storage_size(&mut self, key: &StorageKey) -> Option<u32> {
fn get_storage_transparent(&mut self, key: VarSizedKey) -> Option<Vec<u8>> {
Storage::<T>::read(&self.top_frame_mut().contract_info().trie_id, key)
}

fn get_storage_size(&mut self, key: FixSizedKey) -> Option<u32> {
Storage::<T>::size(&self.top_frame_mut().contract_info().trie_id, key)
}
athei marked this conversation as resolved.
Show resolved Hide resolved

fn get_storage_size_transparent(&mut self, key: VarSizedKey) -> Option<u32> {
Storage::<T>::size(&self.top_frame_mut().contract_info().trie_id, key)
}

fn set_storage(
&mut self,
key: StorageKey,
key: FixSizedKey,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError> {
let frame = self.top_frame_mut();
Storage::<T>::write(
&frame.contract_info.get(&frame.account_id).trie_id,
key,
value,
Some(&mut frame.nested_storage),
take_old,
)
}

fn set_storage_transparent(
&mut self,
athei marked this conversation as resolved.
Show resolved Hide resolved
key: VarSizedKey,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError> {
let frame = self.top_frame_mut();
Storage::<T>::write(
&frame.contract_info.get(&frame.account_id).trie_id,
&key,
key,
value,
Some(&mut frame.nested_storage),
take_old,
Expand Down Expand Up @@ -2698,9 +2756,9 @@ mod tests {
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.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);

exec_success()
});
Expand Down
9 changes: 7 additions & 2 deletions frame/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ use sp_core::{crypto::UncheckedFrom, Bytes};
use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup};
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};

use crate::exec::StorageHash;
athei marked this conversation as resolved.
Show resolved Hide resolved

type CodeHash<T> = <T as frame_system::Config>::Hash;
type TrieId = BoundedVec<u8, ConstU32<128>>;
type BalanceOf<T> =
Expand Down Expand Up @@ -372,6 +374,9 @@ pub mod pallet {
/// new instrumentation increases the size beyond the limit it would make that contract
/// inaccessible until rectified by another runtime upgrade.
type RelaxedMaxCodeLen: Get<u32>;

/// The maximum length of veriable sized key used on storage with transparent hasing.
agryaznov marked this conversation as resolved.
Show resolved Hide resolved
type MaxStorageKeyLen: Get<u32>;
}

#[pallet::hooks]
Expand Down Expand Up @@ -929,11 +934,11 @@ where
}

/// Query storage of a specified contract under a specified key.
pub fn get_storage(address: T::AccountId, key: [u8; 32]) -> GetStorageResult {
pub fn get_storage<K: StorageHash>(address: T::AccountId, key: K) -> GetStorageResult {
let contract_info =
ContractInfoOf::<T>::get(&address).ok_or(ContractAccessError::DoesntExist)?;

let maybe_value = Storage::<T>::read(&contract_info.trie_id, &key);
let maybe_value = Storage::<T>::read(&contract_info.trie_id, key);
Ok(maybe_value)
}

Expand Down
17 changes: 8 additions & 9 deletions frame/contracts/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
pub mod meter;

use crate::{
exec::{AccountIdOf, StorageKey},
exec::{AccountIdOf, StorageHash},
weights::WeightInfo,
BalanceOf, CodeHash, Config, ContractInfoOf, DeletionQueue, Error, TrieId, SENTINEL,
};
Expand All @@ -32,7 +32,6 @@ use frame_support::{
};
use scale_info::TypeInfo;
use sp_core::crypto::UncheckedFrom;
use sp_io::hashing::blake2_256;
use sp_runtime::{
traits::{Hash, Zero},
RuntimeDebug,
Expand Down Expand Up @@ -124,16 +123,16 @@ where
///
/// The read is performed from the `trie_id` only. The `address` is not necessary. If the
/// contract doesn't store under the given `key` `None` is returned.
pub fn read(trie_id: &TrieId, key: &StorageKey) -> Option<Vec<u8>> {
child::get_raw(&child_trie_info(trie_id), &blake2_256(key))
pub fn read<K: StorageHash>(trie_id: &TrieId, key: K) -> Option<Vec<u8>> {
child::get_raw(&child_trie_info(trie_id), key.hash().as_slice())
}

/// 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.
pub fn size(trie_id: &TrieId, key: &StorageKey) -> Option<u32> {
child::len(&child_trie_info(trie_id), &blake2_256(key))
pub fn size<K: StorageHash>(trie_id: &TrieId, key: K) -> Option<u32> {
child::len(&child_trie_info(trie_id), key.hash().as_slice())
}

/// Update a storage entry into a contract's kv storage.
Expand All @@ -143,15 +142,15 @@ where
///
/// This function also records how much storage was created or removed if a `storage_meter`
/// is supplied. It should only be absent for testing or benchmarking code.
pub fn write(
pub fn write<K: StorageHash>(
trie_id: &TrieId,
key: &StorageKey,
key: K,
new_value: Option<Vec<u8>>,
storage_meter: Option<&mut meter::NestedMeter<T>>,
take: bool,
) -> Result<WriteOutcome, DispatchError> {
let hashed_key = blake2_256(key);
let child_trie_info = &child_trie_info(trie_id);
let hashed_key = key.hash();
let (old_len, old_value) = if take {
let val = child::get_raw(child_trie_info, &hashed_key);
(val.as_ref().map(|v| v.len() as u32), val)
Expand Down
Loading