fix: Complete snowbridge pezpallet rebrand and critical bug fixes
- snowbridge-pezpallet-* → pezsnowbridge-pezpallet-* (201 refs) - pallet/ directories → pezpallet/ (4 locations) - Fixed pezpallet.rs self-include recursion bug - Fixed sc-chain-spec hardcoded crate name in derive macro - Reverted .pezpallet_by_name() to .pallet_by_name() (subxt API) - Added BizinikiwiConfig type alias for zombienet tests - Deleted obsolete session state files Verified: pezsnowbridge-pezpallet-*, pezpallet-staking, pezpallet-staking-async, pezframe-benchmarking-cli all pass cargo check
This commit is contained in:
@@ -0,0 +1,446 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{ChainId, HeaderIdProvider};
|
||||
|
||||
use codec::{Codec, Decode, Encode, MaxEncodedLen};
|
||||
use pezframe_support::{weights::Weight, Parameter};
|
||||
use num_traits::{AsPrimitive, Bounded, CheckedSub, Saturating, SaturatingAdd, Zero};
|
||||
use pezsp_runtime::{
|
||||
traits::{
|
||||
AtLeast32Bit, AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay,
|
||||
MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps, Verify,
|
||||
},
|
||||
FixedPointOperand, StateVersion,
|
||||
};
|
||||
use pezsp_std::{fmt::Debug, hash::Hash, str::FromStr, vec, vec::Vec};
|
||||
|
||||
/// Chain call, that is either SCALE-encoded, or decoded.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum EncodedOrDecodedCall<ChainCall> {
|
||||
/// The call that is SCALE-encoded.
|
||||
///
|
||||
/// This variant is used when we the chain runtime is not bundled with the relay, but
|
||||
/// we still need the represent call in some RPC calls or transactions.
|
||||
Encoded(Vec<u8>),
|
||||
/// The decoded call.
|
||||
Decoded(ChainCall),
|
||||
}
|
||||
|
||||
impl<ChainCall: Clone + Codec> EncodedOrDecodedCall<ChainCall> {
|
||||
/// Returns decoded call.
|
||||
pub fn to_decoded(&self) -> Result<ChainCall, codec::Error> {
|
||||
match self {
|
||||
Self::Encoded(ref encoded_call) =>
|
||||
ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into),
|
||||
Self::Decoded(ref decoded_call) => Ok(decoded_call.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts self to decoded call.
|
||||
pub fn into_decoded(self) -> Result<ChainCall, codec::Error> {
|
||||
match self {
|
||||
Self::Encoded(encoded_call) =>
|
||||
ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into),
|
||||
Self::Decoded(decoded_call) => Ok(decoded_call),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts self to encoded call.
|
||||
pub fn into_encoded(self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Encoded(encoded_call) => encoded_call,
|
||||
Self::Decoded(decoded_call) => decoded_call.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<ChainCall> From<ChainCall> for EncodedOrDecodedCall<ChainCall> {
|
||||
fn from(call: ChainCall) -> EncodedOrDecodedCall<ChainCall> {
|
||||
EncodedOrDecodedCall::Decoded(call)
|
||||
}
|
||||
}
|
||||
|
||||
impl<ChainCall: Decode> Decode for EncodedOrDecodedCall<ChainCall> {
|
||||
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
|
||||
// having encoded version is better than decoded, because decoding isn't required
|
||||
// everywhere and for mocked calls it may lead to **unneeded** errors
|
||||
match input.remaining_len()? {
|
||||
Some(remaining_len) => {
|
||||
let mut encoded_call = vec![0u8; remaining_len];
|
||||
input.read(&mut encoded_call)?;
|
||||
Ok(EncodedOrDecodedCall::Encoded(encoded_call))
|
||||
},
|
||||
None => Ok(EncodedOrDecodedCall::Decoded(ChainCall::decode(input)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<ChainCall: Encode> Encode for EncodedOrDecodedCall<ChainCall> {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match *self {
|
||||
Self::Encoded(ref encoded_call) => encoded_call.clone(),
|
||||
Self::Decoded(ref decoded_call) => decoded_call.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimal Bizinikiwi-based chain representation that may be used from no_std environment.
|
||||
pub trait Chain: Send + Sync + 'static {
|
||||
/// Chain id.
|
||||
const ID: ChainId;
|
||||
|
||||
/// A type that fulfills the abstract idea of what a Bizinikiwi block number is.
|
||||
// Constraints come from the associated Number type of `pezsp_runtime::traits::Header`
|
||||
// See here for more info:
|
||||
// https://docs.rs/sp-runtime/latest/pezsp_runtime/traits/trait.Header.html#associatedtype.Number
|
||||
//
|
||||
// Note that the `AsPrimitive<usize>` trait is required by the GRANDPA justification
|
||||
// verifier, and is not usually part of a Bizinikiwi Header's Number type.
|
||||
type BlockNumber: Parameter
|
||||
+ Member
|
||||
+ MaybeSerializeDeserialize
|
||||
+ Hash
|
||||
+ Copy
|
||||
+ Default
|
||||
+ MaybeDisplay
|
||||
+ AtLeast32BitUnsigned
|
||||
+ FromStr
|
||||
+ AsPrimitive<usize>
|
||||
+ Default
|
||||
+ Saturating
|
||||
+ MaxEncodedLen;
|
||||
|
||||
/// A type that fulfills the abstract idea of what a Bizinikiwi hash is.
|
||||
// Constraints come from the associated Hash type of `pezsp_runtime::traits::Header`
|
||||
// See here for more info:
|
||||
// https://docs.rs/sp-runtime/latest/pezsp_runtime/traits/trait.Header.html#associatedtype.Hash
|
||||
type Hash: Parameter
|
||||
+ Member
|
||||
+ MaybeSerializeDeserialize
|
||||
+ Hash
|
||||
+ Ord
|
||||
+ Copy
|
||||
+ MaybeDisplay
|
||||
+ Default
|
||||
+ SimpleBitOps
|
||||
+ AsRef<[u8]>
|
||||
+ AsMut<[u8]>
|
||||
+ MaxEncodedLen;
|
||||
|
||||
/// A type that fulfills the abstract idea of what a Bizinikiwi hasher (a type
|
||||
/// that produces hashes) is.
|
||||
// Constraints come from the associated Hashing type of `pezsp_runtime::traits::Header`
|
||||
// See here for more info:
|
||||
// https://docs.rs/sp-runtime/latest/pezsp_runtime/traits/trait.Header.html#associatedtype.Hashing
|
||||
type Hasher: HashT<Output = Self::Hash>;
|
||||
|
||||
/// A type that fulfills the abstract idea of what a Bizinikiwi header is.
|
||||
// See here for more info:
|
||||
// https://docs.rs/sp-runtime/latest/pezsp_runtime/traits/trait.Header.html
|
||||
type Header: Parameter
|
||||
+ HeaderT<Number = Self::BlockNumber, Hash = Self::Hash>
|
||||
+ HeaderIdProvider<Self::Header>
|
||||
+ MaybeSerializeDeserialize;
|
||||
|
||||
/// The user account identifier type for the runtime.
|
||||
type AccountId: Parameter
|
||||
+ Member
|
||||
+ MaybeSerializeDeserialize
|
||||
+ Debug
|
||||
+ MaybeDisplay
|
||||
+ Ord
|
||||
+ MaxEncodedLen;
|
||||
/// Balance of an account in native tokens.
|
||||
///
|
||||
/// The chain may support multiple tokens, but this particular type is for token that is used
|
||||
/// to pay for transaction dispatch, to reward different relayers (headers, messages), etc.
|
||||
type Balance: AtLeast32BitUnsigned
|
||||
+ FixedPointOperand
|
||||
+ Parameter
|
||||
+ Member
|
||||
+ MaybeSerializeDeserialize
|
||||
+ Clone
|
||||
+ Copy
|
||||
+ Bounded
|
||||
+ CheckedSub
|
||||
+ PartialOrd
|
||||
+ SaturatingAdd
|
||||
+ Zero
|
||||
+ TryFrom<pezsp_core::U256>
|
||||
+ MaxEncodedLen;
|
||||
/// Nonce of a transaction used by the chain.
|
||||
type Nonce: Parameter
|
||||
+ Member
|
||||
+ MaybeSerialize
|
||||
+ Debug
|
||||
+ Default
|
||||
+ MaybeDisplay
|
||||
+ MaybeSerializeDeserialize
|
||||
+ AtLeast32Bit
|
||||
+ Copy
|
||||
+ MaxEncodedLen;
|
||||
/// Signature type, used on this chain.
|
||||
type Signature: Parameter + Verify;
|
||||
|
||||
/// Version of the state implementation used by this chain. This is directly related with the
|
||||
/// `TrieLayout` configuration used by the storage.
|
||||
const STATE_VERSION: StateVersion;
|
||||
|
||||
/// Get the maximum size (in bytes) of a Normal extrinsic at this chain.
|
||||
fn max_extrinsic_size() -> u32;
|
||||
/// Get the maximum weight (compute time) that a Normal extrinsic at this chain can use.
|
||||
fn max_extrinsic_weight() -> Weight;
|
||||
}
|
||||
|
||||
/// A trait that provides the type of the underlying chain.
|
||||
pub trait UnderlyingChainProvider: Send + Sync + 'static {
|
||||
/// Underlying chain type.
|
||||
type Chain: Chain;
|
||||
}
|
||||
|
||||
impl<T> Chain for T
|
||||
where
|
||||
T: Send + Sync + 'static + UnderlyingChainProvider,
|
||||
{
|
||||
const ID: ChainId = <T::Chain as Chain>::ID;
|
||||
|
||||
type BlockNumber = <T::Chain as Chain>::BlockNumber;
|
||||
type Hash = <T::Chain as Chain>::Hash;
|
||||
type Hasher = <T::Chain as Chain>::Hasher;
|
||||
type Header = <T::Chain as Chain>::Header;
|
||||
type AccountId = <T::Chain as Chain>::AccountId;
|
||||
type Balance = <T::Chain as Chain>::Balance;
|
||||
type Nonce = <T::Chain as Chain>::Nonce;
|
||||
type Signature = <T::Chain as Chain>::Signature;
|
||||
|
||||
const STATE_VERSION: StateVersion = <T::Chain as Chain>::STATE_VERSION;
|
||||
|
||||
fn max_extrinsic_size() -> u32 {
|
||||
<T::Chain as Chain>::max_extrinsic_size()
|
||||
}
|
||||
|
||||
fn max_extrinsic_weight() -> Weight {
|
||||
<T::Chain as Chain>::max_extrinsic_weight()
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimal teyrchain representation that may be used from no_std environment.
|
||||
pub trait Teyrchain: Chain {
|
||||
/// Teyrchain identifier.
|
||||
const TEYRCHAIN_ID: u32;
|
||||
/// Maximal size of the teyrchain header.
|
||||
///
|
||||
/// This isn't a strict limit. The relayer may submit larger headers and the
|
||||
/// pezpallet will accept the call. The limit is only used to compute whether
|
||||
/// the refund can be made.
|
||||
const MAX_HEADER_SIZE: u32;
|
||||
}
|
||||
|
||||
impl<T> Teyrchain for T
|
||||
where
|
||||
T: Chain + UnderlyingChainProvider,
|
||||
<T as UnderlyingChainProvider>::Chain: Teyrchain,
|
||||
{
|
||||
const TEYRCHAIN_ID: u32 = <<T as UnderlyingChainProvider>::Chain as Teyrchain>::TEYRCHAIN_ID;
|
||||
const MAX_HEADER_SIZE: u32 =
|
||||
<<T as UnderlyingChainProvider>::Chain as Teyrchain>::MAX_HEADER_SIZE;
|
||||
}
|
||||
|
||||
/// Adapter for `Get<u32>` to access `TEYRCHAIN_ID` from `trait Teyrchain`
|
||||
pub struct TeyrchainIdOf<Para>(pezsp_std::marker::PhantomData<Para>);
|
||||
impl<Para: Teyrchain> pezframe_support::traits::Get<u32> for TeyrchainIdOf<Para> {
|
||||
fn get() -> u32 {
|
||||
Para::TEYRCHAIN_ID
|
||||
}
|
||||
}
|
||||
|
||||
/// Underlying chain type.
|
||||
pub type UnderlyingChainOf<C> = <C as UnderlyingChainProvider>::Chain;
|
||||
|
||||
/// Block number used by the chain.
|
||||
pub type BlockNumberOf<C> = <C as Chain>::BlockNumber;
|
||||
|
||||
/// Hash type used by the chain.
|
||||
pub type HashOf<C> = <C as Chain>::Hash;
|
||||
|
||||
/// Hasher type used by the chain.
|
||||
pub type HasherOf<C> = <C as Chain>::Hasher;
|
||||
|
||||
/// Header type used by the chain.
|
||||
pub type HeaderOf<C> = <C as Chain>::Header;
|
||||
|
||||
/// Account id type used by the chain.
|
||||
pub type AccountIdOf<C> = <C as Chain>::AccountId;
|
||||
|
||||
/// Balance type used by the chain.
|
||||
pub type BalanceOf<C> = <C as Chain>::Balance;
|
||||
|
||||
/// Transaction nonce type used by the chain.
|
||||
pub type NonceOf<C> = <C as Chain>::Nonce;
|
||||
|
||||
/// Signature type used by the chain.
|
||||
pub type SignatureOf<C> = <C as Chain>::Signature;
|
||||
|
||||
/// Account public type used by the chain.
|
||||
pub type AccountPublicOf<C> = <SignatureOf<C> as Verify>::Signer;
|
||||
|
||||
/// Transaction era used by the chain.
|
||||
pub type TransactionEraOf<C> = crate::TransactionEra<BlockNumberOf<C>, HashOf<C>>;
|
||||
|
||||
/// Convenience macro that declares bridge finality runtime apis and related constants for a chain.
|
||||
/// This includes:
|
||||
/// - chain-specific bridge runtime APIs:
|
||||
/// - `<ThisChain>FinalityApi`
|
||||
/// - constants that are stringified names of runtime API methods:
|
||||
/// - `BEST_FINALIZED_<THIS_CHAIN>_HEADER_METHOD`
|
||||
/// - `<THIS_CHAIN>_ACCEPTED_<CONSENSUS>_FINALITY_PROOFS_METHOD`
|
||||
/// The name of the chain has to be specified in snake case (e.g. `bridge_hub_pezkuwi`).
|
||||
#[macro_export]
|
||||
macro_rules! decl_bridge_finality_runtime_apis {
|
||||
($chain: ident $(, $consensus: ident => $justification_type: ty)?) => {
|
||||
pezbp_runtime::paste::item! {
|
||||
mod [<$chain _finality_api>] {
|
||||
use super::*;
|
||||
|
||||
/// Name of the `<ThisChain>FinalityApi::best_finalized` runtime method.
|
||||
pub const [<BEST_FINALIZED_ $chain:upper _HEADER_METHOD>]: &str =
|
||||
stringify!([<$chain:camel FinalityApi_best_finalized>]);
|
||||
|
||||
/// Name of the `<ThisChain>FinalityApi::free_headers_interval` runtime method.
|
||||
pub const [<FREE_HEADERS_INTERVAL_FOR_ $chain:upper _METHOD>]: &str =
|
||||
stringify!([<$chain:camel FinalityApi_free_headers_interval>]);
|
||||
|
||||
|
||||
$(
|
||||
/// Name of the `<ThisChain>FinalityApi::accepted_<consensus>_finality_proofs`
|
||||
/// runtime method.
|
||||
pub const [<$chain:upper _SYNCED_HEADERS_ $consensus:upper _INFO_METHOD>]: &str =
|
||||
stringify!([<$chain:camel FinalityApi_synced_headers_ $consensus:lower _info>]);
|
||||
)?
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
/// API for querying information about the finalized chain headers.
|
||||
///
|
||||
/// This API is implemented by runtimes that are receiving messages from this chain, not by this
|
||||
/// chain's runtime itself.
|
||||
pub trait [<$chain:camel FinalityApi>] {
|
||||
/// Returns number and hash of the best finalized header known to the bridge module.
|
||||
fn best_finalized() -> Option<pezbp_runtime::HeaderId<Hash, BlockNumber>>;
|
||||
|
||||
/// Returns free headers interval, if it is configured in the runtime.
|
||||
/// The caller expects that if his transaction improves best known header
|
||||
/// at least by the free_headers_interval`, it will be fee-free.
|
||||
///
|
||||
/// See [`pezpallet_bridge_grandpa::Config::FreeHeadersInterval`] for details.
|
||||
fn free_headers_interval() -> Option<BlockNumber>;
|
||||
|
||||
$(
|
||||
/// Returns the justifications accepted in the current block.
|
||||
fn [<synced_headers_ $consensus:lower _info>](
|
||||
) -> $crate::private::Vec<$justification_type>;
|
||||
)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use [<$chain _finality_api>]::*;
|
||||
}
|
||||
};
|
||||
($chain: ident, grandpa) => {
|
||||
decl_bridge_finality_runtime_apis!($chain, grandpa => bp_header_pez_chain::StoredHeaderGrandpaInfo<Header>);
|
||||
};
|
||||
}
|
||||
|
||||
// Re-export to avoid include tuplex dependency everywhere.
|
||||
#[doc(hidden)]
|
||||
pub mod __private {
|
||||
pub use codec;
|
||||
}
|
||||
|
||||
/// Convenience macro that declares bridge messages runtime apis and related constants for a chain.
|
||||
/// This includes:
|
||||
/// - chain-specific bridge runtime APIs:
|
||||
/// - `To<ThisChain>OutboundLaneApi<LaneIdType>`
|
||||
/// - `From<ThisChain>InboundLaneApi<LaneIdType>`
|
||||
/// - constants that are stringified names of runtime API methods:
|
||||
/// - `FROM_<THIS_CHAIN>_MESSAGE_DETAILS_METHOD`,
|
||||
/// The name of the chain has to be specified in snake case (e.g. `bridge_hub_pezkuwi`).
|
||||
#[macro_export]
|
||||
macro_rules! decl_bridge_messages_runtime_apis {
|
||||
($chain: ident, $lane_id_type:ty) => {
|
||||
pezbp_runtime::paste::item! {
|
||||
mod [<$chain _messages_api>] {
|
||||
use super::*;
|
||||
|
||||
/// Name of the `To<ThisChain>OutboundLaneApi::message_details` runtime method.
|
||||
pub const [<TO_ $chain:upper _MESSAGE_DETAILS_METHOD>]: &str =
|
||||
stringify!([<To $chain:camel OutboundLaneApi_message_details>]);
|
||||
|
||||
/// Name of the `From<ThisChain>InboundLaneApi::message_details` runtime method.
|
||||
pub const [<FROM_ $chain:upper _MESSAGE_DETAILS_METHOD>]: &str =
|
||||
stringify!([<From $chain:camel InboundLaneApi_message_details>]);
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
/// Outbound message lane API for messages that are sent to this chain.
|
||||
///
|
||||
/// This API is implemented by runtimes that are receiving messages from this chain, not by this
|
||||
/// chain's runtime itself.
|
||||
pub trait [<To $chain:camel OutboundLaneApi>] {
|
||||
/// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all
|
||||
/// messages in given inclusive range.
|
||||
///
|
||||
/// If some (or all) messages are missing from the storage, they'll also will
|
||||
/// be missing from the resulting vector. The vector is ordered by the nonce.
|
||||
fn message_details(
|
||||
lane: $lane_id_type,
|
||||
begin: bp_messages::MessageNonce,
|
||||
end: bp_messages::MessageNonce,
|
||||
) -> $crate::private::Vec<bp_messages::OutboundMessageDetails>;
|
||||
}
|
||||
|
||||
/// Inbound message lane API for messages sent by this chain.
|
||||
///
|
||||
/// This API is implemented by runtimes that are receiving messages from this chain, not by this
|
||||
/// chain's runtime itself.
|
||||
///
|
||||
/// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the
|
||||
/// `messages` vector may (and need to) be read using `To<ThisChain>OutboundLaneApi::message_details`.
|
||||
pub trait [<From $chain:camel InboundLaneApi>] {
|
||||
/// Return details of given inbound messages.
|
||||
fn message_details(
|
||||
lane: $lane_id_type,
|
||||
messages: $crate::private::Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>,
|
||||
) -> $crate::private::Vec<bp_messages::InboundMessageDetails>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use [<$chain _messages_api>]::*;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Convenience macro that declares bridge finality runtime apis, bridge messages runtime apis
|
||||
/// and related constants for a chain.
|
||||
/// The name of the chain has to be specified in snake case (e.g. `bridge_hub_pezkuwi`).
|
||||
#[macro_export]
|
||||
macro_rules! decl_bridge_runtime_apis {
|
||||
($chain: ident $(, $consensus: ident, $lane_id_type:ident)?) => {
|
||||
pezbp_runtime::decl_bridge_finality_runtime_apis!($chain $(, $consensus)?);
|
||||
pezbp_runtime::decl_bridge_messages_runtime_apis!($chain, $lane_id_type);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Primitives that may be used for creating signed extensions for indirect runtimes.
|
||||
|
||||
use codec::{Compact, Decode, DecodeWithMemTracking, Encode};
|
||||
use impl_trait_for_tuples::impl_for_tuples;
|
||||
use scale_info::{StaticTypeInfo, TypeInfo};
|
||||
use pezsp_runtime::{
|
||||
impl_tx_ext_default,
|
||||
traits::{Dispatchable, TransactionExtension},
|
||||
transaction_validity::TransactionValidityError,
|
||||
};
|
||||
use pezsp_std::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
/// Trait that describes some properties of a `TransactionExtension` that are needed in order to
|
||||
/// send a transaction to the chain.
|
||||
pub trait TransactionExtensionSchema:
|
||||
Encode + Decode + DecodeWithMemTracking + Debug + Eq + Clone + StaticTypeInfo
|
||||
{
|
||||
/// A type of the data encoded as part of the transaction.
|
||||
type Payload: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo;
|
||||
/// Parameters which are part of the payload used to produce transaction signature,
|
||||
/// but don't end up in the transaction itself (i.e. inherent part of the runtime).
|
||||
type Implicit: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo;
|
||||
}
|
||||
|
||||
impl TransactionExtensionSchema for () {
|
||||
type Payload = ();
|
||||
type Implicit = ();
|
||||
}
|
||||
|
||||
/// An implementation of `TransactionExtensionSchema` using generic params.
|
||||
#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Debug, PartialEq, Eq, TypeInfo)]
|
||||
pub struct GenericTransactionExtensionSchema<P, S>(PhantomData<(P, S)>);
|
||||
|
||||
impl<P, S> TransactionExtensionSchema for GenericTransactionExtensionSchema<P, S>
|
||||
where
|
||||
P: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo,
|
||||
S: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo,
|
||||
{
|
||||
type Payload = P;
|
||||
type Implicit = S;
|
||||
}
|
||||
|
||||
/// The `TransactionExtensionSchema` for `pezframe_system::CheckNonZeroSender`.
|
||||
pub type CheckNonZeroSender = GenericTransactionExtensionSchema<(), ()>;
|
||||
|
||||
/// The `TransactionExtensionSchema` for `pezframe_system::CheckSpecVersion`.
|
||||
pub type CheckSpecVersion = GenericTransactionExtensionSchema<(), u32>;
|
||||
|
||||
/// The `TransactionExtensionSchema` for `pezframe_system::CheckTxVersion`.
|
||||
pub type CheckTxVersion = GenericTransactionExtensionSchema<(), u32>;
|
||||
|
||||
/// The `TransactionExtensionSchema` for `pezframe_system::CheckGenesis`.
|
||||
pub type CheckGenesis<Hash> = GenericTransactionExtensionSchema<(), Hash>;
|
||||
|
||||
/// The `TransactionExtensionSchema` for `pezframe_system::CheckEra`.
|
||||
pub type CheckEra<Hash> = GenericTransactionExtensionSchema<pezsp_runtime::generic::Era, Hash>;
|
||||
|
||||
/// The `TransactionExtensionSchema` for `pezframe_system::CheckNonce`.
|
||||
pub type CheckNonce<TxNonce> = GenericTransactionExtensionSchema<Compact<TxNonce>, ()>;
|
||||
|
||||
/// The `TransactionExtensionSchema` for `pezframe_system::CheckWeight`.
|
||||
pub type CheckWeight = GenericTransactionExtensionSchema<(), ()>;
|
||||
|
||||
/// The `TransactionExtensionSchema` for `pezpallet_transaction_payment::ChargeTransactionPayment`.
|
||||
pub type ChargeTransactionPayment<Balance> =
|
||||
GenericTransactionExtensionSchema<Compact<Balance>, ()>;
|
||||
|
||||
/// The `TransactionExtensionSchema` for `pezkuwi-runtime-common::PrevalidateAttests`.
|
||||
pub type PrevalidateAttests = GenericTransactionExtensionSchema<(), ()>;
|
||||
|
||||
/// The `TransactionExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`.
|
||||
pub type BridgeRejectObsoleteHeadersAndMessages = GenericTransactionExtensionSchema<(), ()>;
|
||||
|
||||
/// The `TransactionExtensionSchema` for `RefundBridgedTeyrchainMessages`.
|
||||
/// This schema is dedicated for `RefundBridgedTeyrchainMessages` signed extension as
|
||||
/// wildcard/placeholder, which relies on the scale encoding for `()` or `((), ())`, or `((), (),
|
||||
/// ())` is the same. So runtime can contains any kind of tuple:
|
||||
/// `(BridgeRefundBridgeHubPezkuwichainMessages)`
|
||||
/// `(BridgeRefundBridgeHubPezkuwichainMessages, BridgeRefundBridgeHubZagrosMessages)`
|
||||
/// `(BridgeRefundTeyrchainMessages1, ..., BridgeRefundTeyrchainMessagesN)`
|
||||
pub type RefundBridgedTeyrchainMessagesSchema = GenericTransactionExtensionSchema<(), ()>;
|
||||
|
||||
#[impl_for_tuples(1, 12)]
|
||||
impl TransactionExtensionSchema for Tuple {
|
||||
for_tuples!( type Payload = ( #( Tuple::Payload ),* ); );
|
||||
for_tuples!( type Implicit = ( #( Tuple::Implicit ),* ); );
|
||||
}
|
||||
|
||||
/// A simplified version of signed extensions meant for producing signed transactions
|
||||
/// and signed payloads in the client code.
|
||||
#[derive(Encode, Decode, DecodeWithMemTracking, Debug, PartialEq, Eq, Clone, TypeInfo)]
|
||||
pub struct GenericTransactionExtension<S: TransactionExtensionSchema> {
|
||||
/// A payload that is included in the transaction.
|
||||
pub payload: S::Payload,
|
||||
#[codec(skip)]
|
||||
// It may be set to `None` if extensions are decoded. We are never reconstructing transactions
|
||||
// (and it makes no sense to do that) => decoded version of `TransactionExtensions` is only
|
||||
// used to read fields of the `payload`. And when resigning transaction, we're reconstructing
|
||||
// `TransactionExtensions` from scratch.
|
||||
implicit: Option<S::Implicit>,
|
||||
}
|
||||
|
||||
impl<S: TransactionExtensionSchema> GenericTransactionExtension<S> {
|
||||
/// Create new `GenericTransactionExtension` object.
|
||||
pub fn new(payload: S::Payload, implicit: Option<S::Implicit>) -> Self {
|
||||
Self { payload, implicit }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, C> TransactionExtension<C> for GenericTransactionExtension<S>
|
||||
where
|
||||
C: Dispatchable,
|
||||
S: TransactionExtensionSchema,
|
||||
S::Payload: DecodeWithMemTracking + Send + Sync,
|
||||
S::Implicit: Send + Sync,
|
||||
{
|
||||
const IDENTIFIER: &'static str = "Not needed.";
|
||||
type Implicit = S::Implicit;
|
||||
|
||||
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
|
||||
// we shall not ever see this error in relay, because we are never signing decoded
|
||||
// transactions. Instead we're constructing and signing new transactions. So the error code
|
||||
// is kinda random here
|
||||
self.implicit
|
||||
.clone()
|
||||
.ok_or(pezframe_support::unsigned::TransactionValidityError::Unknown(
|
||||
pezframe_support::unsigned::UnknownTransaction::Custom(0xFF),
|
||||
))
|
||||
}
|
||||
type Pre = ();
|
||||
type Val = ();
|
||||
|
||||
impl_tx_ext_default!(C; weight validate prepare);
|
||||
}
|
||||
@@ -0,0 +1,542 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Primitives that may be used at (bridges) runtime level.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use codec::{Decode, DecodeWithMemTracking, Encode, FullCodec, MaxEncodedLen};
|
||||
use pezframe_support::{
|
||||
pezpallet_prelude::DispatchResult, weights::Weight, PalletError, StorageHasher, StorageValue,
|
||||
};
|
||||
use pezframe_system::RawOrigin;
|
||||
use scale_info::TypeInfo;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use pezsp_core::storage::StorageKey;
|
||||
use pezsp_runtime::{
|
||||
traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto},
|
||||
RuntimeDebug,
|
||||
};
|
||||
use pezsp_std::{fmt::Debug, ops::RangeInclusive, vec, vec::Vec};
|
||||
|
||||
pub use chain::{
|
||||
AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf,
|
||||
HasherOf, HeaderOf, NonceOf, SignatureOf, Teyrchain, TeyrchainIdOf, TransactionEraOf,
|
||||
UnderlyingChainOf, UnderlyingChainProvider, __private,
|
||||
};
|
||||
pub use pezframe_support::storage::storage_prefix as storage_value_final_key;
|
||||
use num_traits::{CheckedAdd, CheckedSub, One, SaturatingAdd, Zero};
|
||||
#[cfg(feature = "std")]
|
||||
pub use storage_proof::craft_valid_storage_proof;
|
||||
#[cfg(feature = "test-helpers")]
|
||||
pub use storage_proof::{
|
||||
grow_storage_proof, grow_storage_value, record_all_keys as record_all_trie_keys,
|
||||
UnverifiedStorageProofParams,
|
||||
};
|
||||
pub use storage_proof::{
|
||||
raw_storage_proof_size, RawStorageProof, StorageProofChecker, StorageProofError,
|
||||
};
|
||||
pub use storage_types::BoundedStorageValue;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod extensions;
|
||||
pub mod messages;
|
||||
|
||||
mod chain;
|
||||
mod storage_proof;
|
||||
mod storage_types;
|
||||
|
||||
// Re-export macro to avoid include paste dependency everywhere
|
||||
pub use pezsp_runtime::paste;
|
||||
|
||||
// Re-export for usage in macro.
|
||||
#[doc(hidden)]
|
||||
pub mod private {
|
||||
#[doc(hidden)]
|
||||
pub use alloc::vec::Vec;
|
||||
}
|
||||
|
||||
/// Use this when something must be shared among all instances.
|
||||
pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0];
|
||||
|
||||
/// Generic header Id.
|
||||
#[derive(
|
||||
RuntimeDebug,
|
||||
Default,
|
||||
Clone,
|
||||
Encode,
|
||||
Decode,
|
||||
Copy,
|
||||
Eq,
|
||||
Hash,
|
||||
MaxEncodedLen,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
TypeInfo,
|
||||
)]
|
||||
pub struct HeaderId<Hash, Number>(pub Number, pub Hash);
|
||||
|
||||
impl<Hash: Copy, Number: Copy> HeaderId<Hash, Number> {
|
||||
/// Return header number.
|
||||
pub fn number(&self) -> Number {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Return header hash.
|
||||
pub fn hash(&self) -> Hash {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
/// Header id used by the chain.
|
||||
pub type HeaderIdOf<C> = HeaderId<HashOf<C>, BlockNumberOf<C>>;
|
||||
|
||||
/// Generic header id provider.
|
||||
pub trait HeaderIdProvider<Header: HeaderT> {
|
||||
/// Get the header id.
|
||||
fn id(&self) -> HeaderId<Header::Hash, Header::Number>;
|
||||
|
||||
/// Get the header id for the parent block.
|
||||
fn parent_id(&self) -> Option<HeaderId<Header::Hash, Header::Number>>;
|
||||
}
|
||||
|
||||
impl<Header: HeaderT> HeaderIdProvider<Header> for Header {
|
||||
fn id(&self) -> HeaderId<Header::Hash, Header::Number> {
|
||||
HeaderId(*self.number(), self.hash())
|
||||
}
|
||||
|
||||
fn parent_id(&self) -> Option<HeaderId<Header::Hash, Header::Number>> {
|
||||
self.number()
|
||||
.checked_sub(&One::one())
|
||||
.map(|parent_number| HeaderId(parent_number, *self.parent_hash()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Unique identifier of the chain.
|
||||
///
|
||||
/// In addition to its main function (identifying the chain), this type may also be used to
|
||||
/// identify module instance. We have a bunch of pallets that may be used in different bridges. E.g.
|
||||
/// messages pezpallet may be deployed twice in the same runtime to bridge ThisChain with Chain1 and
|
||||
/// Chain2. Sometimes we need to be able to identify deployed instance dynamically. This type may be
|
||||
/// used for that.
|
||||
pub type ChainId = [u8; 4];
|
||||
|
||||
/// Anything that has size.
|
||||
pub trait Size {
|
||||
/// Return size of this object (in bytes).
|
||||
fn size(&self) -> u32;
|
||||
}
|
||||
|
||||
impl Size for () {
|
||||
fn size(&self) -> u32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl Size for Vec<u8> {
|
||||
fn size(&self) -> u32 {
|
||||
self.len() as _
|
||||
}
|
||||
}
|
||||
|
||||
/// Pre-computed size.
|
||||
pub struct PreComputedSize(pub usize);
|
||||
|
||||
impl Size for PreComputedSize {
|
||||
fn size(&self) -> u32 {
|
||||
u32::try_from(self.0).unwrap_or(u32::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
/// Era of specific transaction.
|
||||
#[derive(RuntimeDebug, Clone, Copy, PartialEq)]
|
||||
pub enum TransactionEra<BlockNumber, BlockHash> {
|
||||
/// Transaction is immortal.
|
||||
Immortal,
|
||||
/// Transaction is valid for a given number of blocks, starting from given block.
|
||||
Mortal(HeaderId<BlockHash, BlockNumber>, u32),
|
||||
}
|
||||
|
||||
impl<BlockNumber: Copy + UniqueSaturatedInto<u64>, BlockHash: Copy>
|
||||
TransactionEra<BlockNumber, BlockHash>
|
||||
{
|
||||
/// Prepare transaction era, based on mortality period and current best block number.
|
||||
pub fn new(
|
||||
best_block_id: HeaderId<BlockHash, BlockNumber>,
|
||||
mortality_period: Option<u32>,
|
||||
) -> Self {
|
||||
mortality_period
|
||||
.map(|mortality_period| TransactionEra::Mortal(best_block_id, mortality_period))
|
||||
.unwrap_or(TransactionEra::Immortal)
|
||||
}
|
||||
|
||||
/// Create new immortal transaction era.
|
||||
pub fn immortal() -> Self {
|
||||
TransactionEra::Immortal
|
||||
}
|
||||
|
||||
/// Returns mortality period if transaction is mortal.
|
||||
pub fn mortality_period(&self) -> Option<u32> {
|
||||
match *self {
|
||||
TransactionEra::Immortal => None,
|
||||
TransactionEra::Mortal(_, period) => Some(period),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns era that is used by FRAME-based runtimes.
|
||||
pub fn frame_era(&self) -> pezsp_runtime::generic::Era {
|
||||
match *self {
|
||||
TransactionEra::Immortal => pezsp_runtime::generic::Era::immortal(),
|
||||
// `unique_saturated_into` is fine here - mortality `u64::MAX` is not something we
|
||||
// expect to see on any chain
|
||||
TransactionEra::Mortal(header_id, period) =>
|
||||
pezsp_runtime::generic::Era::mortal(period as _, header_id.0.unique_saturated_into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns header hash that needs to be included in the signature payload.
|
||||
pub fn signed_payload(&self, genesis_hash: BlockHash) -> BlockHash {
|
||||
match *self {
|
||||
TransactionEra::Immortal => genesis_hash,
|
||||
TransactionEra::Mortal(header_id, _) => header_id.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a copy of the
|
||||
/// `pezframe_support::storage::generator::StorageMap::storage_map_final_key` for maps based
|
||||
/// on selected hasher.
|
||||
///
|
||||
/// We're using it because to call `storage_map_final_key` directly, we need access to the runtime
|
||||
/// and pezpallet instance, which (sometimes) is impossible.
|
||||
pub fn storage_map_final_key<H: StorageHasher>(
|
||||
pezpallet_prefix: &str,
|
||||
map_name: &str,
|
||||
key: &[u8],
|
||||
) -> StorageKey {
|
||||
let key_hashed = H::hash(key);
|
||||
let pezpallet_prefix_hashed = pezframe_support::Twox128::hash(pezpallet_prefix.as_bytes());
|
||||
let storage_prefix_hashed = pezframe_support::Twox128::hash(map_name.as_bytes());
|
||||
|
||||
let mut final_key = Vec::with_capacity(
|
||||
pezpallet_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len(),
|
||||
);
|
||||
|
||||
final_key.extend_from_slice(&pezpallet_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(key_hashed.as_ref());
|
||||
|
||||
StorageKey(final_key)
|
||||
}
|
||||
|
||||
/// This is how a storage key of storage value is computed.
|
||||
///
|
||||
/// Copied from `pezframe_support::storage::storage_prefix`.
|
||||
pub fn storage_value_key(pezpallet_prefix: &str, value_name: &str) -> StorageKey {
|
||||
let pezpallet_hash = pezsp_io::hashing::twox_128(pezpallet_prefix.as_bytes());
|
||||
let storage_hash = pezsp_io::hashing::twox_128(value_name.as_bytes());
|
||||
|
||||
let mut final_key = vec![0u8; 32];
|
||||
final_key[..16].copy_from_slice(&pezpallet_hash);
|
||||
final_key[16..].copy_from_slice(&storage_hash);
|
||||
|
||||
StorageKey(final_key)
|
||||
}
|
||||
|
||||
/// Can be use to access the runtime storage key of a `StorageMap`.
|
||||
pub trait StorageMapKeyProvider {
|
||||
/// The name of the variable that holds the `StorageMap`.
|
||||
const MAP_NAME: &'static str;
|
||||
|
||||
/// The same as `StorageMap::Hasher1`.
|
||||
type Hasher: StorageHasher;
|
||||
/// The same as `StorageMap::Key1`.
|
||||
type Key: FullCodec + Send + Sync;
|
||||
/// The same as `StorageMap::Value`.
|
||||
type Value: 'static + FullCodec;
|
||||
|
||||
/// This is a copy of the
|
||||
/// `pezframe_support::storage::generator::StorageMap::storage_map_final_key`.
|
||||
///
|
||||
/// We're using it because to call `storage_map_final_key` directly, we need access
|
||||
/// to the runtime and pezpallet instance, which (sometimes) is impossible.
|
||||
fn final_key(pezpallet_prefix: &str, key: &Self::Key) -> StorageKey {
|
||||
storage_map_final_key::<Self::Hasher>(pezpallet_prefix, Self::MAP_NAME, &key.encode())
|
||||
}
|
||||
}
|
||||
|
||||
/// Can be used to access the runtime storage key of a `StorageDoubleMap`.
|
||||
pub trait StorageDoubleMapKeyProvider {
|
||||
/// The name of the variable that holds the `StorageDoubleMap`.
|
||||
const MAP_NAME: &'static str;
|
||||
|
||||
/// The same as `StorageDoubleMap::Hasher1`.
|
||||
type Hasher1: StorageHasher;
|
||||
/// The same as `StorageDoubleMap::Key1`.
|
||||
type Key1: FullCodec + Send + Sync;
|
||||
/// The same as `StorageDoubleMap::Hasher2`.
|
||||
type Hasher2: StorageHasher;
|
||||
/// The same as `StorageDoubleMap::Key2`.
|
||||
type Key2: FullCodec + Send + Sync;
|
||||
/// The same as `StorageDoubleMap::Value`.
|
||||
type Value: 'static + FullCodec;
|
||||
|
||||
/// This is a copy of the
|
||||
/// `pezframe_support::storage::generator::StorageDoubleMap::storage_double_map_final_key`.
|
||||
///
|
||||
/// We're using it because to call `storage_double_map_final_key` directly, we need access
|
||||
/// to the runtime and pezpallet instance, which (sometimes) is impossible.
|
||||
fn final_key(pezpallet_prefix: &str, key1: &Self::Key1, key2: &Self::Key2) -> StorageKey {
|
||||
let key1_hashed = Self::Hasher1::hash(&key1.encode());
|
||||
let key2_hashed = Self::Hasher2::hash(&key2.encode());
|
||||
let pezpallet_prefix_hashed = pezframe_support::Twox128::hash(pezpallet_prefix.as_bytes());
|
||||
let storage_prefix_hashed = pezframe_support::Twox128::hash(Self::MAP_NAME.as_bytes());
|
||||
|
||||
let mut final_key = Vec::with_capacity(
|
||||
pezpallet_prefix_hashed.len() +
|
||||
storage_prefix_hashed.len() +
|
||||
key1_hashed.as_ref().len() +
|
||||
key2_hashed.as_ref().len(),
|
||||
);
|
||||
|
||||
final_key.extend_from_slice(&pezpallet_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(key1_hashed.as_ref());
|
||||
final_key.extend_from_slice(key2_hashed.as_ref());
|
||||
|
||||
StorageKey(final_key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error generated by the `OwnedBridgeModule` trait.
|
||||
#[derive(Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo, PalletError)]
|
||||
pub enum OwnedBridgeModuleError {
|
||||
/// All pezpallet operations are halted.
|
||||
Halted,
|
||||
}
|
||||
|
||||
/// Operating mode for a bridge module.
|
||||
pub trait OperatingMode: Send + Copy + Debug + FullCodec {
|
||||
/// Returns true if the bridge module is halted.
|
||||
fn is_halted(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Basic operating modes for a bridges module (Normal/Halted).
|
||||
#[derive(
|
||||
Encode,
|
||||
Decode,
|
||||
DecodeWithMemTracking,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
RuntimeDebug,
|
||||
TypeInfo,
|
||||
MaxEncodedLen,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
pub enum BasicOperatingMode {
|
||||
/// Normal mode, when all operations are allowed.
|
||||
Normal,
|
||||
/// The pezpallet is halted. All operations (except operating mode change) are prohibited.
|
||||
Halted,
|
||||
}
|
||||
|
||||
impl Default for BasicOperatingMode {
|
||||
fn default() -> Self {
|
||||
Self::Normal
|
||||
}
|
||||
}
|
||||
|
||||
impl OperatingMode for BasicOperatingMode {
|
||||
fn is_halted(&self) -> bool {
|
||||
*self == BasicOperatingMode::Halted
|
||||
}
|
||||
}
|
||||
|
||||
const COMMON_LOG_TARGET: &'static str = "runtime::bridge-module";
|
||||
|
||||
/// Bridge module that has owner and operating mode
|
||||
pub trait OwnedBridgeModule<T: pezframe_system::Config> {
|
||||
/// The target that will be used when publishing logs related to this module.
|
||||
const LOG_TARGET: &'static str;
|
||||
|
||||
/// A storage entry that holds the module `Owner` account.
|
||||
type OwnerStorage: StorageValue<T::AccountId, Query = Option<T::AccountId>>;
|
||||
/// Operating mode type of the pezpallet.
|
||||
type OperatingMode: OperatingMode;
|
||||
/// A storage value that holds the pezpallet operating mode.
|
||||
type OperatingModeStorage: StorageValue<Self::OperatingMode, Query = Self::OperatingMode>;
|
||||
|
||||
/// Check if the module is halted.
|
||||
fn is_halted() -> bool {
|
||||
Self::OperatingModeStorage::get().is_halted()
|
||||
}
|
||||
|
||||
/// Ensure that the origin is either root, or `PalletOwner`.
|
||||
fn ensure_owner_or_root(origin: T::RuntimeOrigin) -> Result<(), BadOrigin> {
|
||||
match origin.into() {
|
||||
Ok(RawOrigin::Root) => Ok(()),
|
||||
Ok(RawOrigin::Signed(ref signer))
|
||||
if Self::OwnerStorage::get().as_ref() == Some(signer) =>
|
||||
Ok(()),
|
||||
_ => Err(BadOrigin),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the module is not halted.
|
||||
fn ensure_not_halted() -> Result<(), OwnedBridgeModuleError> {
|
||||
match Self::is_halted() {
|
||||
true => Err(OwnedBridgeModuleError::Halted),
|
||||
false => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the owner of the module.
|
||||
fn set_owner(origin: T::RuntimeOrigin, maybe_owner: Option<T::AccountId>) -> DispatchResult {
|
||||
Self::ensure_owner_or_root(origin)?;
|
||||
match maybe_owner {
|
||||
Some(owner) => {
|
||||
Self::OwnerStorage::put(&owner);
|
||||
tracing::info!(target: COMMON_LOG_TARGET, module=%Self::LOG_TARGET, ?owner, "Setting pezpallet.");
|
||||
},
|
||||
None => {
|
||||
Self::OwnerStorage::kill();
|
||||
tracing::info!(target: COMMON_LOG_TARGET, module=%Self::LOG_TARGET, "Removed Owner of pezpallet.");
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Halt or resume all/some module operations.
|
||||
fn set_operating_mode(
|
||||
origin: T::RuntimeOrigin,
|
||||
operating_mode: Self::OperatingMode,
|
||||
) -> DispatchResult {
|
||||
Self::ensure_owner_or_root(origin)?;
|
||||
Self::OperatingModeStorage::put(operating_mode);
|
||||
tracing::info!(target: COMMON_LOG_TARGET, module=%Self::LOG_TARGET, ?operating_mode, "Setting operating mode.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pezpallet owner has a right to halt all module operations and then resume it. If it is `None`,
|
||||
/// then there are no direct ways to halt/resume module operations, but other runtime methods
|
||||
/// may still be used to do that (i.e. democracy::referendum to update halt flag directly
|
||||
/// or call the `set_operating_mode`).
|
||||
fn module_owner() -> Option<T::AccountId> {
|
||||
Self::OwnerStorage::get()
|
||||
}
|
||||
|
||||
/// The current operating mode of the module.
|
||||
/// Depending on the mode either all, some, or no transactions will be allowed.
|
||||
fn operating_mode() -> Self::OperatingMode {
|
||||
Self::OperatingModeStorage::get()
|
||||
}
|
||||
}
|
||||
|
||||
/// All extra operations with weights that we need in bridges.
|
||||
pub trait WeightExtraOps {
|
||||
/// Checked division of individual components of two weights.
|
||||
///
|
||||
/// Divides components and returns minimal division result. Returns `None` if one
|
||||
/// of `other` weight components is zero.
|
||||
fn min_components_checked_div(&self, other: Weight) -> Option<u64>;
|
||||
}
|
||||
|
||||
impl WeightExtraOps for Weight {
|
||||
fn min_components_checked_div(&self, other: Weight) -> Option<u64> {
|
||||
Some(pezsp_std::cmp::min(
|
||||
self.ref_time().checked_div(other.ref_time())?,
|
||||
self.proof_size().checked_div(other.proof_size())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that provides a static `str`.
|
||||
pub trait StaticStrProvider {
|
||||
/// Static string.
|
||||
const STR: &'static str;
|
||||
}
|
||||
|
||||
/// A macro that generates `StaticStrProvider` with the string set to its stringified argument.
|
||||
#[macro_export]
|
||||
macro_rules! generate_static_str_provider {
|
||||
($str:expr) => {
|
||||
$crate::paste::item! {
|
||||
pub struct [<Str $str>];
|
||||
|
||||
impl $crate::StaticStrProvider for [<Str $str>] {
|
||||
const STR: &'static str = stringify!($str);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A trait defining helper methods for `RangeInclusive` (start..=end)
|
||||
pub trait RangeInclusiveExt<Idx> {
|
||||
/// Computes the length of the `RangeInclusive`, checking for underflow and overflow.
|
||||
fn checked_len(&self) -> Option<Idx>;
|
||||
/// Computes the length of the `RangeInclusive`, saturating in case of underflow or overflow.
|
||||
fn saturating_len(&self) -> Idx;
|
||||
}
|
||||
|
||||
impl<Idx> RangeInclusiveExt<Idx> for RangeInclusive<Idx>
|
||||
where
|
||||
Idx: CheckedSub + CheckedAdd + SaturatingAdd + One + Zero,
|
||||
{
|
||||
fn checked_len(&self) -> Option<Idx> {
|
||||
self.end()
|
||||
.checked_sub(self.start())
|
||||
.and_then(|len| len.checked_add(&Idx::one()))
|
||||
}
|
||||
|
||||
fn saturating_len(&self) -> Idx {
|
||||
let len = match self.end().checked_sub(self.start()) {
|
||||
Some(len) => len,
|
||||
None => return Idx::zero(),
|
||||
};
|
||||
len.saturating_add(&Idx::one())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn storage_value_key_works() {
|
||||
assert_eq!(
|
||||
storage_value_key("PalletTransactionPayment", "NextFeeMultiplier"),
|
||||
StorageKey(
|
||||
hex_literal::hex!(
|
||||
"f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc"
|
||||
)
|
||||
.to_vec()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_static_str_provider_works() {
|
||||
generate_static_str_provider!(Test);
|
||||
assert_eq!(StrTest::STR, "Test");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Primitives that may be used by different message delivery and dispatch mechanisms.
|
||||
|
||||
use codec::{Decode, DecodeWithMemTracking, Encode};
|
||||
use pezframe_support::weights::Weight;
|
||||
use scale_info::TypeInfo;
|
||||
use pezsp_runtime::RuntimeDebug;
|
||||
|
||||
/// Message dispatch result.
|
||||
#[derive(Encode, Decode, DecodeWithMemTracking, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)]
|
||||
pub struct MessageDispatchResult<DispatchLevelResult> {
|
||||
/// Unspent dispatch weight. This weight that will be deducted from total delivery transaction
|
||||
/// weight, thus reducing the transaction cost. This shall not be zero in (at least) two cases:
|
||||
///
|
||||
/// 1) if message has been dispatched successfully, but post-dispatch weight is less than the
|
||||
/// weight, declared by the message sender;
|
||||
/// 2) if message has not been dispatched at all.
|
||||
pub unspent_weight: Weight,
|
||||
/// Fine-grained result of single message dispatch (for better diagnostic purposes)
|
||||
pub dispatch_level_result: DispatchLevelResult,
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Logic for working with storage proofs.
|
||||
|
||||
use pezframe_support::PalletError;
|
||||
use pezsp_core::RuntimeDebug;
|
||||
use pezsp_std::vec::Vec;
|
||||
use pezsp_trie::{
|
||||
accessed_nodes_tracker::AccessedNodesTracker, read_trie_value, LayoutV1, MemoryDB, StorageProof,
|
||||
};
|
||||
|
||||
use codec::{Decode, DecodeWithMemTracking, Encode};
|
||||
use hash_db::{HashDB, Hasher, EMPTY_PREFIX};
|
||||
use scale_info::TypeInfo;
|
||||
#[cfg(feature = "test-helpers")]
|
||||
use pezsp_trie::{recorder_ext::RecorderExt, Recorder, TrieDBBuilder, TrieError, TrieHash};
|
||||
#[cfg(feature = "test-helpers")]
|
||||
use trie_db::{Trie, TrieConfiguration, TrieDBMut};
|
||||
|
||||
/// Errors that can occur when interacting with `UnverifiedStorageProof` and `VerifiedStorageProof`.
|
||||
#[derive(
|
||||
Clone, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo,
|
||||
)]
|
||||
pub enum StorageProofError {
|
||||
/// Call to `generate_trie_proof()` failed.
|
||||
UnableToGenerateTrieProof,
|
||||
/// Call to `verify_trie_proof()` failed.
|
||||
InvalidProof,
|
||||
/// The `Vec` entries weren't sorted as expected.
|
||||
UnsortedEntries,
|
||||
/// The provided key wasn't found.
|
||||
UnavailableKey,
|
||||
/// The value associated to the provided key is `None`.
|
||||
EmptyVal,
|
||||
/// Error decoding value associated to a provided key.
|
||||
DecodeError,
|
||||
/// At least one key or node wasn't read.
|
||||
UnusedKey,
|
||||
|
||||
/// Expected storage root is missing from the proof. (for non-compact proofs)
|
||||
StorageRootMismatch,
|
||||
/// Unable to reach expected storage value using provided trie nodes. (for non-compact proofs)
|
||||
StorageValueUnavailable,
|
||||
/// The proof contains duplicate nodes. (for non-compact proofs)
|
||||
DuplicateNodes,
|
||||
}
|
||||
|
||||
impl From<pezsp_trie::StorageProofError> for StorageProofError {
|
||||
fn from(e: pezsp_trie::StorageProofError) -> Self {
|
||||
match e {
|
||||
pezsp_trie::StorageProofError::DuplicateNodes => StorageProofError::DuplicateNodes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<pezsp_trie::accessed_nodes_tracker::Error> for StorageProofError {
|
||||
fn from(e: pezsp_trie::accessed_nodes_tracker::Error) -> Self {
|
||||
match e {
|
||||
pezsp_trie::accessed_nodes_tracker::Error::UnusedNodes => StorageProofError::UnusedKey,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw storage proof type (just raw trie nodes).
|
||||
pub type RawStorageProof = pezsp_trie::RawStorageProof;
|
||||
|
||||
/// Calculates size for `RawStorageProof`.
|
||||
pub fn raw_storage_proof_size(raw_storage_proof: &RawStorageProof) -> usize {
|
||||
raw_storage_proof
|
||||
.iter()
|
||||
.fold(0usize, |sum, node| sum.saturating_add(node.len()))
|
||||
}
|
||||
|
||||
/// Storage values size requirements.
|
||||
///
|
||||
/// This is currently used by benchmarks when generating storage proofs.
|
||||
#[cfg(feature = "test-helpers")]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct UnverifiedStorageProofParams {
|
||||
/// Expected storage proof size in bytes.
|
||||
pub db_size: Option<u32>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-helpers")]
|
||||
impl UnverifiedStorageProofParams {
|
||||
/// Make storage proof parameters that require proof of at least `db_size` bytes.
|
||||
pub fn from_db_size(db_size: u32) -> Self {
|
||||
Self { db_size: Some(db_size) }
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct is used to read storage values from a subset of a Merklized database. The "proof"
|
||||
/// is a subset of the nodes in the Merkle structure of the database, so that it provides
|
||||
/// authentication against a known Merkle root as well as the values in the
|
||||
/// database themselves.
|
||||
pub struct StorageProofChecker<H>
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
root: H::Out,
|
||||
db: MemoryDB<H>,
|
||||
accessed_nodes_tracker: AccessedNodesTracker<H::Out>,
|
||||
}
|
||||
|
||||
impl<H> StorageProofChecker<H>
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
/// Constructs a new storage proof checker.
|
||||
///
|
||||
/// This returns an error if the given proof is invalid with respect to the given root.
|
||||
pub fn new(root: H::Out, proof: RawStorageProof) -> Result<Self, StorageProofError> {
|
||||
let proof = StorageProof::new_with_duplicate_nodes_check(proof)?;
|
||||
|
||||
let recorder = AccessedNodesTracker::new(proof.len());
|
||||
|
||||
let db = proof.into_memory_db();
|
||||
if !db.contains(&root, EMPTY_PREFIX) {
|
||||
return Err(StorageProofError::StorageRootMismatch);
|
||||
}
|
||||
|
||||
Ok(StorageProofChecker { root, db, accessed_nodes_tracker: recorder })
|
||||
}
|
||||
|
||||
/// Returns error if the proof has some nodes that are left intact by previous `read_value`
|
||||
/// calls.
|
||||
pub fn ensure_no_unused_nodes(self) -> Result<(), StorageProofError> {
|
||||
self.accessed_nodes_tracker.ensure_no_unused_nodes().map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Reads a value from the available subset of storage. If the value cannot be read due to an
|
||||
/// incomplete or otherwise invalid proof, this function returns an error.
|
||||
pub fn read_value(&mut self, key: &[u8]) -> Result<Option<Vec<u8>>, StorageProofError> {
|
||||
// LayoutV1 or LayoutV0 is identical for proof that only read values.
|
||||
read_trie_value::<LayoutV1<H>, _>(
|
||||
&self.db,
|
||||
&self.root,
|
||||
key,
|
||||
Some(&mut self.accessed_nodes_tracker),
|
||||
None,
|
||||
)
|
||||
.map_err(|_| StorageProofError::StorageValueUnavailable)
|
||||
}
|
||||
|
||||
/// Reads and decodes a value from the available subset of storage. If the value cannot be read
|
||||
/// due to an incomplete or otherwise invalid proof, this function returns an error. If value is
|
||||
/// read, but decoding fails, this function returns an error.
|
||||
pub fn read_and_decode_value<T: Decode>(
|
||||
&mut self,
|
||||
key: &[u8],
|
||||
) -> Result<Option<T>, StorageProofError> {
|
||||
self.read_value(key).and_then(|v| {
|
||||
v.map(|v| {
|
||||
T::decode(&mut &v[..]).map_err(|e| {
|
||||
tracing::warn!(target: "bridge-storage-proofs", error=?e, "read_and_decode_value");
|
||||
StorageProofError::DecodeError
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads and decodes a value from the available subset of storage. If the value cannot be read
|
||||
/// due to an incomplete or otherwise invalid proof, or if the value is `None`, this function
|
||||
/// returns an error. If value is read, but decoding fails, this function returns an error.
|
||||
pub fn read_and_decode_mandatory_value<T: Decode>(
|
||||
&mut self,
|
||||
key: &[u8],
|
||||
) -> Result<T, StorageProofError> {
|
||||
self.read_and_decode_value(key)?.ok_or(StorageProofError::EmptyVal)
|
||||
}
|
||||
|
||||
/// Reads and decodes a value from the available subset of storage. If the value cannot be read
|
||||
/// due to an incomplete or otherwise invalid proof, this function returns `Ok(None)`.
|
||||
/// If value is read, but decoding fails, this function returns an error.
|
||||
pub fn read_and_decode_opt_value<T: Decode>(
|
||||
&mut self,
|
||||
key: &[u8],
|
||||
) -> Result<Option<T>, StorageProofError> {
|
||||
match self.read_and_decode_value(key) {
|
||||
Ok(outbound_lane_data) => Ok(outbound_lane_data),
|
||||
Err(StorageProofError::StorageValueUnavailable) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add extra data to the storage value so that it'll be of given size.
|
||||
#[cfg(feature = "test-helpers")]
|
||||
pub fn grow_storage_value(mut value: Vec<u8>, params: &UnverifiedStorageProofParams) -> Vec<u8> {
|
||||
if let Some(db_size) = params.db_size {
|
||||
if db_size as usize > value.len() {
|
||||
value.extend(pezsp_std::iter::repeat(42u8).take(db_size as usize - value.len()));
|
||||
}
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
/// Insert values in the provided trie at common-prefix keys in order to inflate the resulting
|
||||
/// storage proof.
|
||||
///
|
||||
/// This function can add at most 15 common-prefix keys per prefix nibble (4 bits).
|
||||
/// Each such key adds about 33 bytes (a node) to the proof.
|
||||
#[cfg(feature = "test-helpers")]
|
||||
pub fn grow_storage_proof<L: TrieConfiguration>(
|
||||
trie: &mut TrieDBMut<L>,
|
||||
prefix: Vec<u8>,
|
||||
num_extra_nodes: usize,
|
||||
) {
|
||||
use pezsp_trie::TrieMut;
|
||||
|
||||
let mut added_nodes = 0;
|
||||
for i in 0..prefix.len() {
|
||||
let mut prefix = prefix[0..=i].to_vec();
|
||||
// 1 byte has 2 nibbles (4 bits each)
|
||||
let first_nibble = (prefix[i] & 0xf0) >> 4;
|
||||
let second_nibble = prefix[i] & 0x0f;
|
||||
|
||||
// create branches at the 1st nibble
|
||||
for branch in 1..=15 {
|
||||
if added_nodes >= num_extra_nodes {
|
||||
return;
|
||||
}
|
||||
|
||||
// create branches at the 1st nibble
|
||||
prefix[i] = (first_nibble.wrapping_add(branch) % 16) << 4;
|
||||
trie.insert(&prefix, &[0; 32])
|
||||
.map_err(|_| "TrieMut::insert has failed")
|
||||
.expect("TrieMut::insert should not fail in benchmarks");
|
||||
added_nodes += 1;
|
||||
}
|
||||
|
||||
// create branches at the 2nd nibble
|
||||
for branch in 1..=15 {
|
||||
if added_nodes >= num_extra_nodes {
|
||||
return;
|
||||
}
|
||||
|
||||
prefix[i] = (first_nibble << 4) | (second_nibble.wrapping_add(branch) % 16);
|
||||
trie.insert(&prefix, &[0; 32])
|
||||
.map_err(|_| "TrieMut::insert has failed")
|
||||
.expect("TrieMut::insert should not fail in benchmarks");
|
||||
added_nodes += 1;
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(added_nodes, num_extra_nodes)
|
||||
}
|
||||
|
||||
/// Record all keys for a given root.
|
||||
#[cfg(feature = "test-helpers")]
|
||||
pub fn record_all_keys<L: TrieConfiguration, DB>(
|
||||
db: &DB,
|
||||
root: &TrieHash<L>,
|
||||
) -> Result<RawStorageProof, pezsp_std::boxed::Box<TrieError<L>>>
|
||||
where
|
||||
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
|
||||
{
|
||||
let mut recorder = Recorder::<L>::new();
|
||||
let trie = TrieDBBuilder::<L>::new(db, root).with_recorder(&mut recorder).build();
|
||||
for x in trie.iter()? {
|
||||
let (key, _) = x?;
|
||||
trie.get(&key)?;
|
||||
}
|
||||
|
||||
Ok(recorder.into_raw_storage_proof())
|
||||
}
|
||||
|
||||
/// Return valid storage proof and state root.
|
||||
///
|
||||
/// Note: This should only be used for **testing**.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn craft_valid_storage_proof() -> (pezsp_core::H256, RawStorageProof) {
|
||||
use pezsp_state_machine::{backend::Backend, prove_read, InMemoryBackend};
|
||||
|
||||
let state_version = pezsp_runtime::StateVersion::default();
|
||||
|
||||
// construct storage proof
|
||||
let backend = <InMemoryBackend<pezsp_core::Blake2Hasher>>::from((
|
||||
pezsp_std::vec![
|
||||
(None, vec![(b"key1".to_vec(), Some(b"value1".to_vec()))]),
|
||||
(None, vec![(b"key2".to_vec(), Some(b"value2".to_vec()))]),
|
||||
(None, vec![(b"key3".to_vec(), Some(b"value3".to_vec()))]),
|
||||
(None, vec![(b"key4".to_vec(), Some((42u64, 42u32, 42u16, 42u8).encode()))]),
|
||||
// Value is too big to fit in a branch node
|
||||
(None, vec![(b"key11".to_vec(), Some(vec![0u8; 32]))]),
|
||||
],
|
||||
state_version,
|
||||
));
|
||||
let root = backend.storage_root(pezsp_std::iter::empty(), state_version).0;
|
||||
let proof =
|
||||
prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key4"[..], &b"key22"[..]]).unwrap();
|
||||
|
||||
(root, proof.into_nodes().into_iter().collect())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests_for_storage_proof_checker {
|
||||
use super::*;
|
||||
use codec::Encode;
|
||||
|
||||
#[test]
|
||||
fn storage_proof_check() {
|
||||
let (root, proof) = craft_valid_storage_proof();
|
||||
|
||||
// check proof in runtime
|
||||
let mut checker =
|
||||
<StorageProofChecker<pezsp_core::Blake2Hasher>>::new(root, proof.clone()).unwrap();
|
||||
assert_eq!(checker.read_value(b"key1"), Ok(Some(b"value1".to_vec())));
|
||||
assert_eq!(checker.read_value(b"key2"), Ok(Some(b"value2".to_vec())));
|
||||
assert_eq!(checker.read_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8).encode())));
|
||||
assert_eq!(
|
||||
checker.read_value(b"key11111"),
|
||||
Err(StorageProofError::StorageValueUnavailable)
|
||||
);
|
||||
assert_eq!(checker.read_value(b"key22"), Ok(None));
|
||||
assert_eq!(checker.read_and_decode_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8))),);
|
||||
assert!(matches!(
|
||||
checker.read_and_decode_value::<[u8; 64]>(b"key4"),
|
||||
Err(StorageProofError::DecodeError),
|
||||
));
|
||||
|
||||
// checking proof against invalid commitment fails
|
||||
assert_eq!(
|
||||
<StorageProofChecker<pezsp_core::Blake2Hasher>>::new(pezsp_core::H256::random(), proof).err(),
|
||||
Some(StorageProofError::StorageRootMismatch)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proof_with_unused_items_is_rejected() {
|
||||
let (root, proof) = craft_valid_storage_proof();
|
||||
|
||||
let mut checker =
|
||||
StorageProofChecker::<pezsp_core::Blake2Hasher>::new(root, proof.clone()).unwrap();
|
||||
checker.read_value(b"key1").unwrap().unwrap();
|
||||
checker.read_value(b"key2").unwrap();
|
||||
checker.read_value(b"key4").unwrap();
|
||||
checker.read_value(b"key22").unwrap();
|
||||
assert_eq!(checker.ensure_no_unused_nodes(), Ok(()));
|
||||
|
||||
let checker = StorageProofChecker::<pezsp_core::Blake2Hasher>::new(root, proof).unwrap();
|
||||
assert_eq!(checker.ensure_no_unused_nodes(), Err(StorageProofError::UnusedKey));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Wrapper for a runtime storage value that checks if value exceeds given maximum
|
||||
//! during conversion.
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use pezframe_support::traits::Get;
|
||||
use scale_info::{Type, TypeInfo};
|
||||
use pezsp_runtime::RuntimeDebug;
|
||||
use pezsp_std::{marker::PhantomData, ops::Deref};
|
||||
|
||||
/// Error that is returned when the value size exceeds maximal configured size.
|
||||
#[derive(RuntimeDebug)]
|
||||
pub struct MaximalSizeExceededError {
|
||||
/// Size of the value.
|
||||
pub value_size: usize,
|
||||
/// Maximal configured size.
|
||||
pub maximal_size: usize,
|
||||
}
|
||||
|
||||
/// A bounded runtime storage value.
|
||||
#[derive(Clone, Decode, Encode, Eq, PartialEq)]
|
||||
pub struct BoundedStorageValue<B, V> {
|
||||
value: V,
|
||||
_phantom: PhantomData<B>,
|
||||
}
|
||||
|
||||
impl<B, V: pezsp_std::fmt::Debug> pezsp_std::fmt::Debug for BoundedStorageValue<B, V> {
|
||||
fn fmt(&self, fmt: &mut pezsp_std::fmt::Formatter) -> pezsp_std::fmt::Result {
|
||||
self.value.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Get<u32>, V: Encode> BoundedStorageValue<B, V> {
|
||||
/// Construct `BoundedStorageValue` from the underlying `value` with all required checks.
|
||||
///
|
||||
/// Returns error if value size exceeds given bounds.
|
||||
pub fn try_from_inner(value: V) -> Result<Self, MaximalSizeExceededError> {
|
||||
// this conversion is heavy (since we do encoding here), so we may want to optimize it later
|
||||
// (e.g. by introducing custom Encode implementation, and turning `BoundedStorageValue` into
|
||||
// `enum BoundedStorageValue { Decoded(V), Encoded(Vec<u8>) }`)
|
||||
let value_size = value.encoded_size();
|
||||
let maximal_size = B::get() as usize;
|
||||
if value_size > maximal_size {
|
||||
Err(MaximalSizeExceededError { value_size, maximal_size })
|
||||
} else {
|
||||
Ok(BoundedStorageValue { value, _phantom: Default::default() })
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert into the inner type
|
||||
pub fn into_inner(self) -> V {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, V> Deref for BoundedStorageValue<B, V> {
|
||||
type Target = V;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: 'static, V: TypeInfo + 'static> TypeInfo for BoundedStorageValue<B, V> {
|
||||
type Identity = Self;
|
||||
|
||||
fn type_info() -> Type {
|
||||
V::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Get<u32>, V: Encode> MaxEncodedLen for BoundedStorageValue<B, V> {
|
||||
fn max_encoded_len() -> usize {
|
||||
B::get() as usize
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user