Bound uses of Call (#11649)

* Introduce preimages module in traits

* Multisize Preimages

* Len not actually necessary

* Tweaks to the preimage API

* Fixes

* Get Scheduler building with new API

* Scheduler tests pass

* Bounded Scheduler 🎉

* Use Agenda holes and introduce IncompleteSince to avoid need to reschedule

* Tests pass with new weight system

* New benchmarks

* Add missing file

* Drop preimage when permenantly overeight

* Drop preimage when permenantly overeight

* Referenda uses latest preimage API

* Testing ok

* Adding tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fmt

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add preimage migration

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Docs

* Remove dbg

* Refactor Democracy

* Refactor Democracy

* Add final MEL

* Remove silly maps

* Fixes

* Minor refactor

* Formatting

* Fixes

* Fixes

* Fixes

* Update frame/preimage/src/lib.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Add migrations to Democracy

* WIP

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Resolve conflicts

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Revert "Resolve conflicts"

This reverts commit 734d66d69e54553471ffa54fa52e3e304dc8f106.

* Undo wrong resolves...

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* WIP

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Make compile

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* massage clippy

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* More clippy

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* clippy annoyance

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* clippy annoyance

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix benchmarks

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* add missing file

* Test <Preimage as QueryPreimage>

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* More tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Clippy harassment

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add test

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* clippy

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fixup tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove old stuff

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fmt

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Test <Scheduler as Anon> trait functions

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update pallet-ui tests

Why is this needed? Should not be the case unless master is broken...

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* More scheduler trait test

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* More tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Apply review suggestion

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Beauty fixes

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add Scheduler test migration_v3_to_v4_works

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Merge fixup

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Keep referenda benchmarks instantiatable

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update weights

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use new scheduler weight functions

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use new democracy weight functions

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use weight compare functions

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update pallet-ui tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* More renaming…

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* More renaming…

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add comment

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Implement OnRuntimeUpgrade for scheduler::v3_to_v4 migration

Put the migration into a proper `MigrateToV4` struct and implement
the OnRuntimeUpgrade hooks for it. Also move the test to use that
instead.

This should make it easier for adding it to Polkadot.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Clippy

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Handle undecodable Agendas

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove trash

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix test

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use new OnRuntimeUpgrade functions

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fix test

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix BoundedSlice::truncate_from

Co-authored-by: jakoblell

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix pre_upgrade hook return values

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add more error logging

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Find too large preimages in the pre_upgrade hook

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Test that too large Calls in agendas are ignored

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use new OnRuntimeUpgrade hooks

Why did the CI not catch this?!

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* works fine - just more logs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix staking migration

Causing issues on Kusama...

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix UI tests

No idea why this is needed. This is actually undoing an earlier change.
Maybe the CI has different rustc versions!?

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove multisig's Calls (#12072)

* Remove multisig's Calls

* Multisig: Fix tests and re-introduce reserve logic (#12241)

* Fix tests and re-introduce reserve logic

* fix benches

* add todo

* remove irrelevant bench

* [Feature] Add a migration that drains and refunds stored calls (#12313)

* [Feature] Add a migration that drains and refunds stored calls

* migration fixes

* fixes

* address review comments

* consume the whole block weight

* fix assertions

* license header

* fix interface

Co-authored-by: parity-processbot <>

Co-authored-by: parity-processbot <>
Co-authored-by: Roman Useinov <roman.useinov@gmail.com>

* Fix test

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix multisig benchmarks

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* ".git/.scripts/bench-bot.sh" pallet dev pallet_democracy

* ".git/.scripts/bench-bot.sh" pallet dev pallet_scheduler

* ".git/.scripts/bench-bot.sh" pallet dev pallet_preimage

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: parity-processbot <>
Co-authored-by: Roman Useinov <roman.useinov@gmail.com>
This commit is contained in:
Gavin Wood
2022-10-05 19:21:37 +01:00
committed by GitHub
parent 24f0c3601c
commit 93e8ffed55
67 changed files with 5089 additions and 3488 deletions
+16 -10
View File
@@ -35,7 +35,7 @@ fn funded_account<T: Config>(name: &'static str, index: u32) -> T::AccountId {
}
fn preimage_and_hash<T: Config>() -> (Vec<u8>, T::Hash) {
sized_preimage_and_hash::<T>(T::MaxSize::get())
sized_preimage_and_hash::<T>(MAX_SIZE)
}
fn sized_preimage_and_hash<T: Config>(size: u32) -> (Vec<u8>, T::Hash) {
@@ -48,7 +48,7 @@ fn sized_preimage_and_hash<T: Config>(size: u32) -> (Vec<u8>, T::Hash) {
benchmarks! {
// Expensive note - will reserve.
note_preimage {
let s in 0 .. T::MaxSize::get();
let s in 0 .. MAX_SIZE;
let caller = funded_account::<T>("caller", 0);
whitelist_account!(caller);
let (preimage, hash) = sized_preimage_and_hash::<T>(s);
@@ -58,7 +58,7 @@ benchmarks! {
}
// Cheap note - will not reserve since it was requested.
note_requested_preimage {
let s in 0 .. T::MaxSize::get();
let s in 0 .. MAX_SIZE;
let caller = funded_account::<T>("caller", 0);
whitelist_account!(caller);
let (preimage, hash) = sized_preimage_and_hash::<T>(s);
@@ -69,7 +69,7 @@ benchmarks! {
}
// Cheap note - will not reserve since it's the manager.
note_no_deposit_preimage {
let s in 0 .. T::MaxSize::get();
let s in 0 .. MAX_SIZE;
let (preimage, hash) = sized_preimage_and_hash::<T>(s);
assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash));
}: note_preimage<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), preimage)
@@ -101,10 +101,12 @@ benchmarks! {
let (preimage, hash) = preimage_and_hash::<T>();
let noter = funded_account::<T>("noter", 0);
whitelist_account!(noter);
assert_ok!(Preimage::<T>::note_preimage(RawOrigin::Signed(noter).into(), preimage));
assert_ok!(Preimage::<T>::note_preimage(RawOrigin::Signed(noter.clone()).into(), preimage));
}: _<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), hash)
verify {
assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1)));
let deposit = T::BaseDeposit::get() + T::ByteDeposit::get() * MAX_SIZE.into();
let s = RequestStatus::Requested { deposit: Some((noter, deposit)), count: 1, len: Some(MAX_SIZE) };
assert_eq!(StatusFor::<T>::get(&hash), Some(s));
}
// Cheap request - would unreserve the deposit but none was held.
request_no_deposit_preimage {
@@ -112,14 +114,16 @@ benchmarks! {
assert_ok!(Preimage::<T>::note_preimage(T::ManagerOrigin::successful_origin(), preimage));
}: request_preimage<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), hash)
verify {
assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1)));
let s = RequestStatus::Requested { deposit: None, count: 2, len: Some(MAX_SIZE) };
assert_eq!(StatusFor::<T>::get(&hash), Some(s));
}
// Cheap request - the preimage is not yet noted, so deposit to unreserve.
request_unnoted_preimage {
let (_, hash) = preimage_and_hash::<T>();
}: request_preimage<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), hash)
verify {
assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1)));
let s = RequestStatus::Requested { deposit: None, count: 1, len: None };
assert_eq!(StatusFor::<T>::get(&hash), Some(s));
}
// Cheap request - the preimage is already requested, so just a counter bump.
request_requested_preimage {
@@ -127,7 +131,8 @@ benchmarks! {
assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash));
}: request_preimage<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), hash)
verify {
assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(2)));
let s = RequestStatus::Requested { deposit: None, count: 2, len: None };
assert_eq!(StatusFor::<T>::get(&hash), Some(s));
}
// Expensive unrequest - last reference and it's noted, so will destroy the preimage.
@@ -154,7 +159,8 @@ benchmarks! {
assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash));
}: unrequest_preimage<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), hash)
verify {
assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1)));
let s = RequestStatus::Requested { deposit: None, count: 1, len: None };
assert_eq!(StatusFor::<T>::get(&hash), Some(s));
}
impl_benchmark_test_suite!(Preimage, crate::mock::new_test_ext(), crate::mock::Test);
+173 -64
View File
@@ -30,6 +30,7 @@
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod migration;
#[cfg(test)]
mod mock;
#[cfg(test)]
@@ -37,15 +38,18 @@ mod tests;
pub mod weights;
use sp_runtime::traits::{BadOrigin, Hash, Saturating};
use sp_std::prelude::*;
use sp_std::{borrow::Cow, prelude::*};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
dispatch::Pays,
ensure,
pallet_prelude::Get,
traits::{Currency, PreimageProvider, PreimageRecipient, ReservableCurrency},
BoundedVec,
traits::{
Currency, Defensive, FetchResult, Hash as PreimageHash, PreimageProvider,
PreimageRecipient, QueryPreimage, ReservableCurrency, StorePreimage,
},
BoundedSlice, BoundedVec,
};
use scale_info::TypeInfo;
pub use weights::WeightInfo;
@@ -59,20 +63,27 @@ pub use pallet::*;
#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)]
pub enum RequestStatus<AccountId, Balance> {
/// The associated preimage has not yet been requested by the system. The given deposit (if
/// some) is being held until either it becomes requested or the user retracts the primage.
Unrequested(Option<(AccountId, Balance)>),
/// some) is being held until either it becomes requested or the user retracts the preimage.
Unrequested { deposit: (AccountId, Balance), len: u32 },
/// There are a non-zero number of outstanding requests for this hash by this chain. If there
/// is a preimage registered, then it may be removed iff this counter becomes zero.
Requested(u32),
/// is a preimage registered, then `len` is `Some` and it may be removed iff this counter
/// becomes zero.
Requested { deposit: Option<(AccountId, Balance)>, count: u32, len: Option<u32> },
}
type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
/// Maximum size of preimage we can store is 4mb.
const MAX_SIZE: u32 = 4 * 1024 * 1024;
#[frame_support::pallet]
pub mod pallet {
use super::*;
/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
@@ -88,9 +99,6 @@ pub mod pallet {
/// manage existing preimages.
type ManagerOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// Max size allowed for a preimage.
type MaxSize: Get<u32>;
/// The base deposit for placing a preimage on chain.
type BaseDeposit: Get<BalanceOf<Self>>;
@@ -100,6 +108,7 @@ pub mod pallet {
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::event]
@@ -116,7 +125,7 @@ pub mod pallet {
#[pallet::error]
pub enum Error<T> {
/// Preimage is too large to store on-chain.
TooLarge,
TooBig,
/// Preimage has already been noted on-chain.
AlreadyNoted,
/// The user is not authorized to perform this action.
@@ -134,10 +143,9 @@ pub mod pallet {
pub(super) type StatusFor<T: Config> =
StorageMap<_, Identity, T::Hash, RequestStatus<T::AccountId, BalanceOf<T>>>;
/// The preimages stored by this pallet.
#[pallet::storage]
pub(super) type PreimageFor<T: Config> =
StorageMap<_, Identity, T::Hash, BoundedVec<u8, T::MaxSize>>;
StorageMap<_, Identity, (T::Hash, u32), BoundedVec<u8, ConstU32<MAX_SIZE>>>;
#[pallet::call]
impl<T: Config> Pallet<T> {
@@ -150,9 +158,7 @@ pub mod pallet {
// We accept a signed origin which will pay a deposit, or a root origin where a deposit
// is not taken.
let maybe_sender = Self::ensure_signed_or_manager(origin)?;
let bounded_vec =
BoundedVec::<u8, T::MaxSize>::try_from(bytes).map_err(|()| Error::<T>::TooLarge)?;
let system_requested = Self::note_bytes(bounded_vec, maybe_sender.as_ref())?;
let (system_requested, _) = Self::note_bytes(bytes.into(), maybe_sender.as_ref())?;
if system_requested || maybe_sender.is_none() {
Ok(Pays::No.into())
} else {
@@ -161,6 +167,11 @@ pub mod pallet {
}
/// Clear an unrequested preimage from the runtime storage.
///
/// If `len` is provided, then it will be a much cheaper operation.
///
/// - `hash`: The hash of the preimage to be removed from the store.
/// - `len`: The length of the preimage of `hash`.
#[pallet::weight(T::WeightInfo::unnote_preimage())]
pub fn unnote_preimage(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult {
let maybe_sender = Self::ensure_signed_or_manager(origin)?;
@@ -203,41 +214,46 @@ impl<T: Config> Pallet<T> {
/// Store some preimage on chain.
///
/// If `maybe_depositor` is `None` then it is also requested. If `Some`, then it is not.
///
/// We verify that the preimage is within the bounds of what the pallet supports.
///
/// If the preimage was requested to be uploaded, then the user pays no deposits or tx fees.
fn note_bytes(
preimage: BoundedVec<u8, T::MaxSize>,
preimage: Cow<[u8]>,
maybe_depositor: Option<&T::AccountId>,
) -> Result<bool, DispatchError> {
) -> Result<(bool, T::Hash), DispatchError> {
let hash = T::Hashing::hash(&preimage);
ensure!(!PreimageFor::<T>::contains_key(hash), Error::<T>::AlreadyNoted);
let len = preimage.len() as u32;
ensure!(len <= MAX_SIZE, Error::<T>::TooBig);
// We take a deposit only if there is a provided depositor, and the preimage was not
// We take a deposit only if there is a provided depositor and the preimage was not
// previously requested. This also allows the tx to pay no fee.
let was_requested = match (StatusFor::<T>::get(hash), maybe_depositor) {
(Some(RequestStatus::Requested(..)), _) => true,
(Some(RequestStatus::Unrequested(..)), _) =>
let status = match (StatusFor::<T>::get(hash), maybe_depositor) {
(Some(RequestStatus::Requested { count, deposit, .. }), _) =>
RequestStatus::Requested { count, deposit, len: Some(len) },
(Some(RequestStatus::Unrequested { .. }), Some(_)) =>
return Err(Error::<T>::AlreadyNoted.into()),
(None, None) => {
StatusFor::<T>::insert(hash, RequestStatus::Unrequested(None));
false
},
(Some(RequestStatus::Unrequested { len, deposit }), None) =>
RequestStatus::Requested { deposit: Some(deposit), count: 1, len: Some(len) },
(None, None) => RequestStatus::Requested { count: 1, len: Some(len), deposit: None },
(None, Some(depositor)) => {
let length = preimage.len() as u32;
let deposit = T::BaseDeposit::get()
.saturating_add(T::ByteDeposit::get().saturating_mul(length.into()));
T::Currency::reserve(depositor, deposit)?;
let status = RequestStatus::Unrequested(Some((depositor.clone(), deposit)));
StatusFor::<T>::insert(hash, status);
false
RequestStatus::Unrequested { deposit: (depositor.clone(), deposit), len }
},
};
let was_requested = matches!(status, RequestStatus::Requested { .. });
StatusFor::<T>::insert(hash, status);
let _ = Self::insert(&hash, preimage)
.defensive_proof("Unable to insert. Logic error in `note_bytes`?");
PreimageFor::<T>::insert(hash, preimage);
Self::deposit_event(Event::Noted { hash });
Ok(was_requested)
Ok((was_requested, hash))
}
// This function will add a hash to the list of requested preimages.
@@ -245,19 +261,15 @@ impl<T: Config> Pallet<T> {
// If the preimage already exists before the request is made, the deposit for the preimage is
// returned to the user, and removed from their management.
fn do_request_preimage(hash: &T::Hash) {
let count = StatusFor::<T>::get(hash).map_or(1, |x| match x {
RequestStatus::Requested(mut count) => {
count.saturating_inc();
count
},
RequestStatus::Unrequested(None) => 1,
RequestStatus::Unrequested(Some((owner, deposit))) => {
// Return the deposit - the preimage now has outstanding requests.
T::Currency::unreserve(&owner, deposit);
1
},
});
StatusFor::<T>::insert(hash, RequestStatus::Requested(count));
let (count, len, deposit) =
StatusFor::<T>::get(hash).map_or((1, None, None), |x| match x {
RequestStatus::Requested { mut count, len, deposit } => {
count.saturating_inc();
(count, len, deposit)
},
RequestStatus::Unrequested { deposit, len } => (1, Some(len), Some(deposit)),
});
StatusFor::<T>::insert(hash, RequestStatus::Requested { count, len, deposit });
if count == 1 {
Self::deposit_event(Event::Requested { hash: *hash });
}
@@ -265,6 +277,8 @@ impl<T: Config> Pallet<T> {
// Clear a preimage from the storage of the chain, returning any deposit that may be reserved.
//
// If `len` is provided, it will be a much cheaper operation.
//
// If `maybe_owner` is provided, we verify that it is the correct owner before clearing the
// data.
fn do_unnote_preimage(
@@ -272,51 +286,101 @@ impl<T: Config> Pallet<T> {
maybe_check_owner: Option<T::AccountId>,
) -> DispatchResult {
match StatusFor::<T>::get(hash).ok_or(Error::<T>::NotNoted)? {
RequestStatus::Unrequested(Some((owner, deposit))) => {
RequestStatus::Requested { deposit: Some((owner, deposit)), count, len } => {
ensure!(maybe_check_owner.map_or(true, |c| c == owner), Error::<T>::NotAuthorized);
T::Currency::unreserve(&owner, deposit);
StatusFor::<T>::insert(
hash,
RequestStatus::Requested { deposit: None, count, len },
);
Ok(())
},
RequestStatus::Unrequested(None) => {
RequestStatus::Requested { deposit: None, .. } => {
ensure!(maybe_check_owner.is_none(), Error::<T>::NotAuthorized);
Self::do_unrequest_preimage(hash)
},
RequestStatus::Unrequested { deposit: (owner, deposit), len } => {
ensure!(maybe_check_owner.map_or(true, |c| c == owner), Error::<T>::NotAuthorized);
T::Currency::unreserve(&owner, deposit);
StatusFor::<T>::remove(hash);
Self::remove(hash, len);
Self::deposit_event(Event::Cleared { hash: *hash });
Ok(())
},
RequestStatus::Requested(_) => return Err(Error::<T>::Requested.into()),
}
StatusFor::<T>::remove(hash);
PreimageFor::<T>::remove(hash);
Self::deposit_event(Event::Cleared { hash: *hash });
Ok(())
}
/// Clear a preimage request.
fn do_unrequest_preimage(hash: &T::Hash) -> DispatchResult {
match StatusFor::<T>::get(hash).ok_or(Error::<T>::NotRequested)? {
RequestStatus::Requested(mut count) if count > 1 => {
RequestStatus::Requested { mut count, len, deposit } if count > 1 => {
count.saturating_dec();
StatusFor::<T>::insert(hash, RequestStatus::Requested(count));
StatusFor::<T>::insert(hash, RequestStatus::Requested { count, len, deposit });
},
RequestStatus::Requested(count) => {
RequestStatus::Requested { count, len, deposit } => {
debug_assert!(count == 1, "preimage request counter at zero?");
PreimageFor::<T>::remove(hash);
StatusFor::<T>::remove(hash);
Self::deposit_event(Event::Cleared { hash: *hash });
match (len, deposit) {
// Preimage was never noted.
(None, _) => StatusFor::<T>::remove(hash),
// Preimage was noted without owner - just remove it.
(Some(len), None) => {
Self::remove(hash, len);
StatusFor::<T>::remove(hash);
Self::deposit_event(Event::Cleared { hash: *hash });
},
// Preimage was noted with owner - move to unrequested so they can get refund.
(Some(len), Some(deposit)) => {
StatusFor::<T>::insert(hash, RequestStatus::Unrequested { deposit, len });
},
}
},
RequestStatus::Unrequested(_) => return Err(Error::<T>::NotRequested.into()),
RequestStatus::Unrequested { .. } => return Err(Error::<T>::NotRequested.into()),
}
Ok(())
}
fn insert(hash: &T::Hash, preimage: Cow<[u8]>) -> Result<(), ()> {
BoundedSlice::<u8, ConstU32<MAX_SIZE>>::try_from(preimage.as_ref())
.map(|s| PreimageFor::<T>::insert((hash, s.len() as u32), s))
}
fn remove(hash: &T::Hash, len: u32) {
PreimageFor::<T>::remove((hash, len))
}
fn have(hash: &T::Hash) -> bool {
Self::len(hash).is_some()
}
fn len(hash: &T::Hash) -> Option<u32> {
use RequestStatus::*;
match StatusFor::<T>::get(hash) {
Some(Requested { len: Some(len), .. }) | Some(Unrequested { len, .. }) => Some(len),
_ => None,
}
}
fn fetch(hash: &T::Hash, len: Option<u32>) -> FetchResult {
let len = len.or_else(|| Self::len(hash)).ok_or(DispatchError::Unavailable)?;
PreimageFor::<T>::get((hash, len))
.map(|p| p.into_inner())
.map(Into::into)
.ok_or(DispatchError::Unavailable)
}
}
impl<T: Config> PreimageProvider<T::Hash> for Pallet<T> {
fn have_preimage(hash: &T::Hash) -> bool {
PreimageFor::<T>::contains_key(hash)
Self::have(hash)
}
fn preimage_requested(hash: &T::Hash) -> bool {
matches!(StatusFor::<T>::get(hash), Some(RequestStatus::Requested(..)))
matches!(StatusFor::<T>::get(hash), Some(RequestStatus::Requested { .. }))
}
fn get_preimage(hash: &T::Hash) -> Option<Vec<u8>> {
PreimageFor::<T>::get(hash).map(|preimage| preimage.to_vec())
Self::fetch(hash, None).ok().map(Cow::into_owned)
}
fn request_preimage(hash: &T::Hash) {
@@ -330,15 +394,60 @@ impl<T: Config> PreimageProvider<T::Hash> for Pallet<T> {
}
impl<T: Config> PreimageRecipient<T::Hash> for Pallet<T> {
type MaxSize = T::MaxSize;
type MaxSize = ConstU32<MAX_SIZE>; // 2**22
fn note_preimage(bytes: BoundedVec<u8, Self::MaxSize>) {
// We don't really care if this fails, since that's only the case if someone else has
// already noted it.
let _ = Self::note_bytes(bytes, None);
let _ = Self::note_bytes(bytes.into_inner().into(), None);
}
fn unnote_preimage(hash: &T::Hash) {
// Should never fail if authorization check is skipped.
let res = Self::do_unrequest_preimage(hash);
debug_assert!(res.is_ok(), "unnote_preimage failed - request outstanding?");
}
}
impl<T: Config<Hash = PreimageHash>> QueryPreimage for Pallet<T> {
fn len(hash: &T::Hash) -> Option<u32> {
Pallet::<T>::len(hash)
}
fn fetch(hash: &T::Hash, len: Option<u32>) -> FetchResult {
Pallet::<T>::fetch(hash, len)
}
fn is_requested(hash: &T::Hash) -> bool {
matches!(StatusFor::<T>::get(hash), Some(RequestStatus::Requested { .. }))
}
fn request(hash: &T::Hash) {
Self::do_request_preimage(hash)
}
fn unrequest(hash: &T::Hash) {
let res = Self::do_unrequest_preimage(hash);
debug_assert!(res.is_ok(), "do_unrequest_preimage failed - counter underflow?");
}
}
impl<T: Config<Hash = PreimageHash>> StorePreimage for Pallet<T> {
const MAX_LENGTH: usize = MAX_SIZE as usize;
fn note(bytes: Cow<[u8]>) -> Result<T::Hash, DispatchError> {
// We don't really care if this fails, since that's only the case if someone else has
// already noted it.
let maybe_hash = Self::note_bytes(bytes, None).map(|(_, h)| h);
// Map to the correct trait error.
if maybe_hash == Err(DispatchError::from(Error::<T>::TooBig)) {
Err(DispatchError::Exhausted)
} else {
maybe_hash
}
}
fn unnote(hash: &T::Hash) {
// Should never fail if authorization check is skipped.
let res = Self::do_unnote_preimage(hash, None);
debug_assert!(res.is_ok(), "unnote_preimage failed - request outstanding?");
+263
View File
@@ -0,0 +1,263 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Storage migrations for the preimage pallet.
use super::*;
use frame_support::{
storage_alias,
traits::{ConstU32, OnRuntimeUpgrade},
};
use sp_std::collections::btree_map::BTreeMap;
/// The log target.
const TARGET: &'static str = "runtime::preimage::migration::v1";
/// The original data layout of the preimage pallet without a specific version number.
mod v0 {
use super::*;
#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)]
pub enum RequestStatus<AccountId, Balance> {
Unrequested(Option<(AccountId, Balance)>),
Requested(u32),
}
#[storage_alias]
pub type PreimageFor<T: Config> = StorageMap<
Pallet<T>,
Identity,
<T as frame_system::Config>::Hash,
BoundedVec<u8, ConstU32<MAX_SIZE>>,
>;
#[storage_alias]
pub type StatusFor<T: Config> = StorageMap<
Pallet<T>,
Identity,
<T as frame_system::Config>::Hash,
RequestStatus<<T as frame_system::Config>::AccountId, BalanceOf<T>>,
>;
/// Returns the number of images or `None` if the storage is corrupted.
#[cfg(feature = "try-runtime")]
pub fn image_count<T: Config>() -> Option<u32> {
let images = v0::PreimageFor::<T>::iter_values().count() as u32;
let status = v0::StatusFor::<T>::iter_values().count() as u32;
if images == status {
Some(images)
} else {
None
}
}
}
pub mod v1 {
use super::*;
/// Migration for moving preimage from V0 to V1 storage.
///
/// Note: This needs to be run with the same hashing algorithm as before
/// since it is not re-hashing the preimages.
pub struct Migration<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for Migration<T> {
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> {
assert_eq!(StorageVersion::get::<Pallet<T>>(), 0, "can only upgrade from version 0");
let images = v0::image_count::<T>().expect("v0 storage corrupted");
log::info!(target: TARGET, "Migrating {} images", &images);
Ok((images as u32).encode())
}
fn on_runtime_upgrade() -> Weight {
let mut weight = T::DbWeight::get().reads(1);
if StorageVersion::get::<Pallet<T>>() != 0 {
log::warn!(
target: TARGET,
"skipping MovePreimagesIntoBuckets: executed on wrong storage version.\
Expected version 0"
);
return weight
}
let status = v0::StatusFor::<T>::drain().collect::<Vec<_>>();
weight.saturating_accrue(T::DbWeight::get().reads(status.len() as u64));
let preimages = v0::PreimageFor::<T>::drain().collect::<BTreeMap<_, _>>();
weight.saturating_accrue(T::DbWeight::get().reads(preimages.len() as u64));
for (hash, status) in status.into_iter() {
let preimage = if let Some(preimage) = preimages.get(&hash) {
preimage
} else {
log::error!(target: TARGET, "preimage not found for hash {:?}", &hash);
continue
};
let len = preimage.len() as u32;
if len > MAX_SIZE {
log::error!(
target: TARGET,
"preimage too large for hash {:?}, len: {}",
&hash,
len
);
continue
}
let status = match status {
v0::RequestStatus::Unrequested(deposit) => match deposit {
Some(deposit) => RequestStatus::Unrequested { deposit, len },
// `None` depositor becomes system-requested.
None =>
RequestStatus::Requested { deposit: None, count: 1, len: Some(len) },
},
v0::RequestStatus::Requested(count) if count == 0 => {
log::error!(target: TARGET, "preimage has counter of zero: {:?}", hash);
continue
},
v0::RequestStatus::Requested(count) =>
RequestStatus::Requested { deposit: None, count, len: Some(len) },
};
log::trace!(target: TARGET, "Moving preimage {:?} with len {}", hash, len);
crate::StatusFor::<T>::insert(hash, status);
crate::PreimageFor::<T>::insert(&(hash, len), preimage);
weight.saturating_accrue(T::DbWeight::get().writes(2));
}
StorageVersion::new(1).put::<Pallet<T>>();
weight.saturating_add(T::DbWeight::get().writes(1))
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> {
let old_images: u32 =
Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
let new_images = image_count::<T>().expect("V1 storage corrupted");
if new_images != old_images {
log::error!(
target: TARGET,
"migrated {} images, expected {}",
new_images,
old_images
);
}
assert_eq!(StorageVersion::get::<Pallet<T>>(), 1, "must upgrade");
Ok(())
}
}
/// Returns the number of images or `None` if the storage is corrupted.
#[cfg(feature = "try-runtime")]
pub fn image_count<T: Config>() -> Option<u32> {
// Use iter_values() to ensure that the values are decodable.
let images = crate::PreimageFor::<T>::iter_values().count() as u32;
let status = crate::StatusFor::<T>::iter_values().count() as u32;
if images == status {
Some(images)
} else {
None
}
}
}
#[cfg(test)]
#[cfg(feature = "try-runtime")]
mod test {
use super::*;
use crate::mock::{Test as T, *};
use frame_support::bounded_vec;
#[test]
fn migration_works() {
new_test_ext().execute_with(|| {
assert_eq!(StorageVersion::get::<Pallet<T>>(), 0);
// Insert some preimages into the v0 storage:
// Case 1: Unrequested without deposit
let (p, h) = preimage::<T>(128);
v0::PreimageFor::<T>::insert(h, p);
v0::StatusFor::<T>::insert(h, v0::RequestStatus::Unrequested(None));
// Case 2: Unrequested with deposit
let (p, h) = preimage::<T>(1024);
v0::PreimageFor::<T>::insert(h, p);
v0::StatusFor::<T>::insert(h, v0::RequestStatus::Unrequested(Some((1, 1))));
// Case 3: Requested by 0 (invalid)
let (p, h) = preimage::<T>(8192);
v0::PreimageFor::<T>::insert(h, p);
v0::StatusFor::<T>::insert(h, v0::RequestStatus::Requested(0));
// Case 4: Requested by 10
let (p, h) = preimage::<T>(65536);
v0::PreimageFor::<T>::insert(h, p);
v0::StatusFor::<T>::insert(h, v0::RequestStatus::Requested(10));
assert_eq!(v0::image_count::<T>(), Some(4));
assert_eq!(v1::image_count::<T>(), None, "V1 storage should be corrupted");
let state = v1::Migration::<T>::pre_upgrade().unwrap();
let _w = v1::Migration::<T>::on_runtime_upgrade();
v1::Migration::<T>::post_upgrade(state).unwrap();
// V0 and V1 share the same prefix, so `iter_values` still counts the same.
assert_eq!(v0::image_count::<T>(), Some(3));
assert_eq!(v1::image_count::<T>(), Some(3)); // One gets skipped therefore 3.
assert_eq!(StorageVersion::get::<Pallet<T>>(), 1);
// Case 1: Unrequested without deposit becomes system-requested
let (p, h) = preimage::<T>(128);
assert_eq!(crate::PreimageFor::<T>::get(&(h, 128)), Some(p));
assert_eq!(
crate::StatusFor::<T>::get(h),
Some(RequestStatus::Requested { deposit: None, count: 1, len: Some(128) })
);
// Case 2: Unrequested with deposit becomes unrequested
let (p, h) = preimage::<T>(1024);
assert_eq!(crate::PreimageFor::<T>::get(&(h, 1024)), Some(p));
assert_eq!(
crate::StatusFor::<T>::get(h),
Some(RequestStatus::Unrequested { deposit: (1, 1), len: 1024 })
);
// Case 3: Requested by 0 should be skipped
let (_, h) = preimage::<T>(8192);
assert_eq!(crate::PreimageFor::<T>::get(&(h, 8192)), None);
assert_eq!(crate::StatusFor::<T>::get(h), None);
// Case 4: Requested by 10 becomes requested by 10
let (p, h) = preimage::<T>(65536);
assert_eq!(crate::PreimageFor::<T>::get(&(h, 65536)), Some(p));
assert_eq!(
crate::StatusFor::<T>::get(h),
Some(RequestStatus::Requested { deposit: None, count: 10, len: Some(65536) })
);
});
}
/// Returns a preimage with a given size and its hash.
fn preimage<T: Config>(
len: usize,
) -> (BoundedVec<u8, ConstU32<MAX_SIZE>>, <T as frame_system::Config>::Hash) {
let p = bounded_vec![1; len];
let h = <T as frame_system::Config>::Hashing::hash_of(&p);
(p, h)
}
}
-1
View File
@@ -105,7 +105,6 @@ impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type ManagerOrigin = EnsureSignedBy<One, u64>;
type MaxSize = ConstU32<1024>;
type BaseDeposit = ConstU64<2>;
type ByteDeposit = ConstU64<1>;
}
+268 -11
View File
@@ -17,11 +17,35 @@
//! # Scheduler tests.
#![cfg(test)]
use super::*;
use crate::mock::*;
use frame_support::{assert_noop, assert_ok};
use frame_support::{
assert_err, assert_noop, assert_ok, assert_storage_noop, bounded_vec,
traits::{Bounded, BoundedInline, Hash as PreimageHash},
StorageNoopGuard,
};
use pallet_balances::Error as BalancesError;
use sp_core::{blake2_256, H256};
/// Returns one `Inline`, `Lookup` and `Legacy` item each with different data and hash.
pub fn make_bounded_values() -> (Bounded<Vec<u8>>, Bounded<Vec<u8>>, Bounded<Vec<u8>>) {
let data: BoundedInline = bounded_vec![1];
let inline = Bounded::<Vec<u8>>::Inline(data);
let data = vec![1, 2];
let hash: H256 = blake2_256(&data[..]).into();
let len = data.len() as u32;
let lookup = Bounded::<Vec<u8>>::unrequested(hash, len);
let data = vec![1, 2, 3];
let hash: H256 = blake2_256(&data[..]).into();
let legacy = Bounded::<Vec<u8>>::Legacy { hash, dummy: Default::default() };
(inline, lookup, legacy)
}
#[test]
fn user_note_preimage_works() {
@@ -56,10 +80,7 @@ fn manager_note_preimage_works() {
assert!(Preimage::have_preimage(&h));
assert_eq!(Preimage::get_preimage(&h), Some(vec![1]));
assert_noop!(
Preimage::note_preimage(RuntimeOrigin::signed(1), vec![1]),
Error::<Test>::AlreadyNoted
);
assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![1]));
});
}
@@ -130,14 +151,16 @@ fn requested_then_noted_preimage_cannot_be_unnoted() {
new_test_ext().execute_with(|| {
assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![1]));
assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1])));
assert_noop!(
Preimage::unnote_preimage(RuntimeOrigin::signed(1), hashed([1])),
Error::<Test>::Requested
);
assert_ok!(Preimage::unnote_preimage(RuntimeOrigin::signed(1), hashed([1])));
// it's still here.
let h = hashed([1]);
assert!(Preimage::have_preimage(&h));
assert_eq!(Preimage::get_preimage(&h), Some(vec![1]));
// now it's gone
assert_ok!(Preimage::unrequest_preimage(RuntimeOrigin::signed(1), hashed([1])));
assert!(!Preimage::have_preimage(&hashed([1])));
});
}
@@ -145,15 +168,16 @@ fn requested_then_noted_preimage_cannot_be_unnoted() {
fn request_note_order_makes_no_difference() {
let one_way = new_test_ext().execute_with(|| {
assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1])));
assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![1]));
assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1]));
(
StatusFor::<Test>::iter().collect::<Vec<_>>(),
PreimageFor::<Test>::iter().collect::<Vec<_>>(),
)
});
new_test_ext().execute_with(|| {
assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![1]));
assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1]));
assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1])));
assert_ok!(Preimage::unnote_preimage(RuntimeOrigin::signed(2), hashed([1])));
let other_way = (
StatusFor::<Test>::iter().collect::<Vec<_>>(),
PreimageFor::<Test>::iter().collect::<Vec<_>>(),
@@ -189,6 +213,7 @@ fn request_user_note_order_makes_no_difference() {
new_test_ext().execute_with(|| {
assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1]));
assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1])));
assert_ok!(Preimage::unnote_preimage(RuntimeOrigin::signed(2), hashed([1])));
let other_way = (
StatusFor::<Test>::iter().collect::<Vec<_>>(),
PreimageFor::<Test>::iter().collect::<Vec<_>>(),
@@ -226,8 +251,240 @@ fn user_noted_then_requested_preimage_is_refunded_once_only() {
assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1]));
assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1])));
assert_ok!(Preimage::unrequest_preimage(RuntimeOrigin::signed(1), hashed([1])));
assert_ok!(Preimage::unnote_preimage(RuntimeOrigin::signed(2), hashed([1])));
// Still have reserve from `vec[1; 3]`.
assert_eq!(Balances::reserved_balance(2), 5);
assert_eq!(Balances::free_balance(2), 95);
});
}
#[test]
fn noted_preimage_use_correct_map() {
new_test_ext().execute_with(|| {
// Add one preimage per bucket...
for i in 0..7 {
assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![0; 128 << (i * 2)]));
}
assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![0; MAX_SIZE as usize]));
assert_eq!(PreimageFor::<Test>::iter().count(), 8);
// All are present
assert_eq!(StatusFor::<Test>::iter().count(), 8);
// Now start removing them again...
for i in 0..7 {
assert_ok!(Preimage::unnote_preimage(
RuntimeOrigin::signed(1),
hashed(vec![0; 128 << (i * 2)])
));
}
assert_eq!(PreimageFor::<Test>::iter().count(), 1);
assert_ok!(Preimage::unnote_preimage(
RuntimeOrigin::signed(1),
hashed(vec![0; MAX_SIZE as usize])
));
assert_eq!(PreimageFor::<Test>::iter().count(), 0);
// All are gone
assert_eq!(StatusFor::<Test>::iter().count(), 0);
});
}
/// The `StorePreimage` and `QueryPreimage` traits work together.
#[test]
fn query_and_store_preimage_workflow() {
new_test_ext().execute_with(|| {
let _guard = StorageNoopGuard::default();
let data: Vec<u8> = vec![1; 512];
let encoded = data.encode();
// Bound an unbound value.
let bound = Preimage::bound(data.clone()).unwrap();
let (len, hash) = (bound.len().unwrap(), bound.hash());
assert_eq!(hash, blake2_256(&encoded).into());
assert_eq!(bound.len(), Some(len));
assert!(bound.lookup_needed(), "Should not be Inlined");
assert_eq!(bound.lookup_len(), Some(len));
// The value is requested and available.
assert!(Preimage::is_requested(&hash));
assert!(<Preimage as QueryPreimage>::have(&bound));
assert_eq!(Preimage::len(&hash), Some(len));
// It can be fetched with length.
assert_eq!(Preimage::fetch(&hash, Some(len)).unwrap(), encoded);
// ... and without length.
assert_eq!(Preimage::fetch(&hash, None).unwrap(), encoded);
// ... but not with wrong length.
assert_err!(Preimage::fetch(&hash, Some(0)), DispatchError::Unavailable);
// It can be peeked and decoded correctly.
assert_eq!(Preimage::peek::<Vec<u8>>(&bound).unwrap(), (data.clone(), Some(len)));
// Request it two more times.
assert_eq!(Preimage::pick::<Vec<u8>>(hash, len), bound);
Preimage::request(&hash);
// It is requested thrice.
assert!(matches!(
StatusFor::<Test>::get(&hash).unwrap(),
RequestStatus::Requested { count: 3, .. }
));
// It can be realized and decoded correctly.
assert_eq!(Preimage::realize::<Vec<u8>>(&bound).unwrap(), (data.clone(), Some(len)));
assert!(matches!(
StatusFor::<Test>::get(&hash).unwrap(),
RequestStatus::Requested { count: 2, .. }
));
// Dropping should unrequest.
Preimage::drop(&bound);
assert!(matches!(
StatusFor::<Test>::get(&hash).unwrap(),
RequestStatus::Requested { count: 1, .. }
));
// Is still available.
assert!(<Preimage as QueryPreimage>::have(&bound));
// Manually unnote it.
Preimage::unnote(&hash);
// Is not available anymore.
assert!(!<Preimage as QueryPreimage>::have(&bound));
assert_err!(Preimage::fetch(&hash, Some(len)), DispatchError::Unavailable);
// And not requested since the traits assume permissioned origin.
assert!(!Preimage::is_requested(&hash));
// No storage changes remain. Checked by `StorageNoopGuard`.
});
}
/// The request function behaves as expected.
#[test]
fn query_preimage_request_works() {
new_test_ext().execute_with(|| {
let _guard = StorageNoopGuard::default();
let data: Vec<u8> = vec![1; 10];
let hash: PreimageHash = blake2_256(&data[..]).into();
// Request the preimage.
<Preimage as QueryPreimage>::request(&hash);
// The preimage is requested with unknown length and cannot be fetched.
assert!(<Preimage as QueryPreimage>::is_requested(&hash));
assert!(<Preimage as QueryPreimage>::len(&hash).is_none());
assert_noop!(<Preimage as QueryPreimage>::fetch(&hash, None), DispatchError::Unavailable);
// Request again.
<Preimage as QueryPreimage>::request(&hash);
// The preimage is still requested.
assert!(<Preimage as QueryPreimage>::is_requested(&hash));
assert!(<Preimage as QueryPreimage>::len(&hash).is_none());
assert_noop!(<Preimage as QueryPreimage>::fetch(&hash, None), DispatchError::Unavailable);
// But there is only one entry in the map.
assert_eq!(StatusFor::<Test>::iter().count(), 1);
// Un-request the preimage.
<Preimage as QueryPreimage>::unrequest(&hash);
// It is still requested.
assert!(<Preimage as QueryPreimage>::is_requested(&hash));
// Un-request twice.
<Preimage as QueryPreimage>::unrequest(&hash);
// It is not requested anymore.
assert!(!<Preimage as QueryPreimage>::is_requested(&hash));
// And there is no entry in the map.
assert_eq!(StatusFor::<Test>::iter().count(), 0);
});
}
/// The `QueryPreimage` functions can be used together with `Bounded` values.
#[test]
fn query_preimage_hold_and_drop_work() {
new_test_ext().execute_with(|| {
let _guard = StorageNoopGuard::default();
let (inline, lookup, legacy) = make_bounded_values();
// `hold` does nothing for `Inline` values.
assert_storage_noop!(<Preimage as QueryPreimage>::hold(&inline));
// `hold` requests `Lookup` values.
<Preimage as QueryPreimage>::hold(&lookup);
assert!(<Preimage as QueryPreimage>::is_requested(&lookup.hash()));
// `hold` requests `Legacy` values.
<Preimage as QueryPreimage>::hold(&legacy);
assert!(<Preimage as QueryPreimage>::is_requested(&legacy.hash()));
// There are two values requested in total.
assert_eq!(StatusFor::<Test>::iter().count(), 2);
// Cleanup by dropping both.
<Preimage as QueryPreimage>::drop(&lookup);
assert!(!<Preimage as QueryPreimage>::is_requested(&lookup.hash()));
<Preimage as QueryPreimage>::drop(&legacy);
assert!(!<Preimage as QueryPreimage>::is_requested(&legacy.hash()));
// There are no values requested anymore.
assert_eq!(StatusFor::<Test>::iter().count(), 0);
});
}
/// The `StorePreimage` trait works as expected.
#[test]
fn store_preimage_basic_works() {
new_test_ext().execute_with(|| {
let _guard = StorageNoopGuard::default();
let data: Vec<u8> = vec![1; 512]; // Too large to inline.
let encoded = Cow::from(data.encode());
// Bound the data.
let bound = <Preimage as StorePreimage>::bound(data.clone()).unwrap();
// The preimage can be peeked.
assert_ok!(<Preimage as QueryPreimage>::peek(&bound));
// Un-note the preimage.
<Preimage as StorePreimage>::unnote(&bound.hash());
// The preimage cannot be peeked anymore.
assert_err!(<Preimage as QueryPreimage>::peek(&bound), DispatchError::Unavailable);
// Noting the wrong pre-image does not make it peek-able.
assert_ok!(<Preimage as StorePreimage>::note(Cow::Borrowed(&data)));
assert_err!(<Preimage as QueryPreimage>::peek(&bound), DispatchError::Unavailable);
// Manually note the preimage makes it peek-able again.
assert_ok!(<Preimage as StorePreimage>::note(encoded.clone()));
// Noting again works.
assert_ok!(<Preimage as StorePreimage>::note(encoded));
assert_ok!(<Preimage as QueryPreimage>::peek(&bound));
// Cleanup.
<Preimage as StorePreimage>::unnote(&bound.hash());
let data_hash = blake2_256(&data);
<Preimage as StorePreimage>::unnote(&data_hash.into());
// No storage changes remain. Checked by `StorageNoopGuard`.
});
}
#[test]
fn store_preimage_note_too_large_errors() {
new_test_ext().execute_with(|| {
// Works with `MAX_LENGTH`.
let len = <Preimage as StorePreimage>::MAX_LENGTH;
let data = vec![0u8; len];
assert_ok!(<Preimage as StorePreimage>::note(data.into()));
// Errors with `MAX_LENGTH+1`.
let data = vec![0u8; len + 1];
assert_err!(<Preimage as StorePreimage>::note(data.into()), DispatchError::Exhausted);
});
}
#[test]
fn store_preimage_bound_too_large_errors() {
new_test_ext().execute_with(|| {
// Using `MAX_LENGTH` number of bytes in a vector does not work
// since SCALE prepends the length.
let len = <Preimage as StorePreimage>::MAX_LENGTH;
let data: Vec<u8> = vec![0; len];
assert_err!(<Preimage as StorePreimage>::bound(data.clone()), DispatchError::Exhausted);
// Works with `MAX_LENGTH-4`.
let data: Vec<u8> = vec![0; len - 4];
assert_ok!(<Preimage as StorePreimage>::bound(data.clone()));
});
}
+65 -59
View File
@@ -18,22 +18,24 @@
//! Autogenerated weights for pallet_preimage
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-05-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2022-10-03, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// ./target/production/substrate
// /home/benchbot/cargo_target_dir/production/substrate
// benchmark
// pallet
// --chain=dev
// --steps=50
// --repeat=20
// --pallet=pallet_preimage
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --template=./.maintain/frame-weight-template.hbs
// --heap-pages=4096
// --pallet=pallet_preimage
// --chain=dev
// --output=./frame/preimage/src/weights.rs
// --template=./.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
@@ -61,88 +63,90 @@ pub trait WeightInfo {
/// Weights for pallet_preimage using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Storage: Preimage PreimageFor (r:1 w:1)
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
/// The range of component `s` is `[0, 4194304]`.
fn note_preimage(s: u32, ) -> Weight {
Weight::from_ref_time(0 as u64)
Weight::from_ref_time(32_591_000 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64))
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(Weight::from_ref_time(1_680 as u64).saturating_mul(s as u64))
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
// Storage: Preimage PreimageFor (r:1 w:1)
// Storage: Preimage StatusFor (r:1 w:0)
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
/// The range of component `s` is `[0, 4194304]`.
fn note_requested_preimage(s: u32, ) -> Weight {
Weight::from_ref_time(0 as u64)
Weight::from_ref_time(23_350_000 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64))
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
.saturating_add(Weight::from_ref_time(1_681 as u64).saturating_mul(s as u64))
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
// Storage: Preimage PreimageFor (r:1 w:1)
// Storage: Preimage StatusFor (r:1 w:0)
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
/// The range of component `s` is `[0, 4194304]`.
fn note_no_deposit_preimage(s: u32, ) -> Weight {
Weight::from_ref_time(0 as u64)
Weight::from_ref_time(21_436_000 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64))
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
.saturating_add(Weight::from_ref_time(1_680 as u64).saturating_mul(s as u64))
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
fn unnote_preimage() -> Weight {
Weight::from_ref_time(44_380_000 as u64)
Weight::from_ref_time(44_567_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
fn unnote_no_deposit_preimage() -> Weight {
Weight::from_ref_time(30_280_000 as u64)
Weight::from_ref_time(30_065_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
fn request_preimage() -> Weight {
Weight::from_ref_time(42_809_000 as u64)
Weight::from_ref_time(28_470_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
fn request_no_deposit_preimage() -> Weight {
Weight::from_ref_time(28_964_000 as u64)
Weight::from_ref_time(14_601_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
fn request_unnoted_preimage() -> Weight {
Weight::from_ref_time(17_555_000 as u64)
Weight::from_ref_time(20_121_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
fn request_requested_preimage() -> Weight {
Weight::from_ref_time(7_745_000 as u64)
Weight::from_ref_time(9_440_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
fn unrequest_preimage() -> Weight {
Weight::from_ref_time(29_758_000 as u64)
Weight::from_ref_time(29_013_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
fn unrequest_unnoted_preimage() -> Weight {
Weight::from_ref_time(18_360_000 as u64)
Weight::from_ref_time(9_223_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
fn unrequest_multi_referenced_preimage() -> Weight {
Weight::from_ref_time(7_439_000 as u64)
Weight::from_ref_time(9_252_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
@@ -150,88 +154,90 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// For backwards compatibility and tests
impl WeightInfo for () {
// Storage: Preimage PreimageFor (r:1 w:1)
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
/// The range of component `s` is `[0, 4194304]`.
fn note_preimage(s: u32, ) -> Weight {
Weight::from_ref_time(0 as u64)
Weight::from_ref_time(32_591_000 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64))
.saturating_add(RocksDbWeight::get().reads(2 as u64))
.saturating_add(Weight::from_ref_time(1_680 as u64).saturating_mul(s as u64))
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
// Storage: Preimage PreimageFor (r:1 w:1)
// Storage: Preimage StatusFor (r:1 w:0)
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
/// The range of component `s` is `[0, 4194304]`.
fn note_requested_preimage(s: u32, ) -> Weight {
Weight::from_ref_time(0 as u64)
Weight::from_ref_time(23_350_000 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64))
.saturating_add(RocksDbWeight::get().reads(2 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
.saturating_add(Weight::from_ref_time(1_681 as u64).saturating_mul(s as u64))
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
// Storage: Preimage PreimageFor (r:1 w:1)
// Storage: Preimage StatusFor (r:1 w:0)
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
/// The range of component `s` is `[0, 4194304]`.
fn note_no_deposit_preimage(s: u32, ) -> Weight {
Weight::from_ref_time(0 as u64)
Weight::from_ref_time(21_436_000 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64))
.saturating_add(RocksDbWeight::get().reads(2 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
.saturating_add(Weight::from_ref_time(1_680 as u64).saturating_mul(s as u64))
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
fn unnote_preimage() -> Weight {
Weight::from_ref_time(44_380_000 as u64)
Weight::from_ref_time(44_567_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
fn unnote_no_deposit_preimage() -> Weight {
Weight::from_ref_time(30_280_000 as u64)
Weight::from_ref_time(30_065_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
fn request_preimage() -> Weight {
Weight::from_ref_time(42_809_000 as u64)
Weight::from_ref_time(28_470_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
fn request_no_deposit_preimage() -> Weight {
Weight::from_ref_time(28_964_000 as u64)
Weight::from_ref_time(14_601_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
fn request_unnoted_preimage() -> Weight {
Weight::from_ref_time(17_555_000 as u64)
Weight::from_ref_time(20_121_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
fn request_requested_preimage() -> Weight {
Weight::from_ref_time(7_745_000 as u64)
Weight::from_ref_time(9_440_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
fn unrequest_preimage() -> Weight {
Weight::from_ref_time(29_758_000 as u64)
Weight::from_ref_time(29_013_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
// Storage: Preimage PreimageFor (r:0 w:1)
fn unrequest_unnoted_preimage() -> Weight {
Weight::from_ref_time(18_360_000 as u64)
Weight::from_ref_time(9_223_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
// Storage: Preimage StatusFor (r:1 w:1)
fn unrequest_multi_referenced_preimage() -> Weight {
Weight::from_ref_time(7_439_000 as u64)
Weight::from_ref_time(9_252_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}