mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-18 23:25:40 +00:00
Initial version of bridging pallets as git subtree (#2458)
* Initial version of bridges pallet as subtree of https://github.com/paritytech/parity-bridges-common Added `Bridges subtree files` pr review rule * Squashed 'bridges/' content from commit d30927c08 git-subtree-dir: bridges git-subtree-split: d30927c089bd9e73092d1ec1a62895603cb277a3 * Updated REAMDE.md and BRIDGES.md (inspired by original https://github.com/paritytech/polkadot/blob/d22eb62fe40e55e15eb91d375f48cc540d83a47e/BRIDGES.md) * Squashed 'bridges/' changes from d30927c08..d3970944b d3970944b Small simplifications (#2050) git-subtree-dir: bridges git-subtree-split: d3970944b0cfc4ea5226225e1ca07dab234c3556 * Squashed 'bridges/' changes from d3970944b..2180797fb 2180797fb Removed CODEOWNERS (#2051) git-subtree-dir: bridges git-subtree-split: 2180797fbf8a990490c67853dcffd81bc8dd083c * Squashed 'bridges/' changes from 2180797fbf..4850aac8ce 4850aac8ce Removed relayer_account: &AccountId from MessageDispatch (#2080) 8c8adafd54 Revert "Fix max-size messages at test chains (#2064)" (#2077) c01a63efd8 Fixed off-by-one when confirming rewards in messages pallet (#2075) a298be96aa Update subxt dependencies (#2072) c0eef51eab Fix max-size messages at test chains (#2064) 3a658e3697 Messages relay fixes (#2073) 0022b5ab22 Slash relayers for invalid transactions (#2025) 198104007f Bump enumflags2 from 0.7.5 to 0.7.7 9229b257e5 [ci] Fix rules for docker build (#2069) 660d791390 [ci] Update buildah command and version (#2058) e4535c0ca4 fix the way latest_confirmed_nonce_at_source is "calculated" (#2067) dbc2d37590 select nothing if we have already selected nonces to submit or have submitted something (#2065) a7eedd21fe [relay-substrate-client] Bump jsonrpsee (#2066) 8875d5aeae Bump clap from 4.2.2 to 4.2.4 25f9cf55e2 Another use of RangeInclusiveExt::checked_len() (#2060) 4942c12a5f submit lane unblock transactions from relay (#2030) c0325d3c9c Test deployments fixes (#2057) fc7b9b7ed7 Use the new matrix server (#2056) 63bcb5c10b Fixed delivery alert rule (#2052) git-subtree-dir: bridges git-subtree-split: 4850aac8ce6c34e5ca6246b88cd14c873a879cba * Squashed 'bridges/' changes from 4850aac8ce..66aaf0dd23 66aaf0dd23 Nits (#2083) git-subtree-dir: bridges git-subtree-split: 66aaf0dd239dde40b64264061a77c921e2c82568 * Squashed 'bridges/' changes from 66aaf0dd23..557ecbcecc 557ecbcecc Fix sized messages (Follow-up on #2064) (#2103) 54f587a066 Add weight of refund extension post_dispatch to the weights of messages pallet (#2089) 5b1626f8c4 fix pallet param for nightly benchmarks check (#2099) ae44c6b7a1 Add millau specific messages weights (#2097) 6ad0bd1f1e Add integrity tests to rialto parachain runtiime (#2096) 6919556de5 Bump tokio from 1.27.0 to 1.28.0 58795fcb75 Bump clap from 4.2.4 to 4.2.5 01bf31085b Bump scale-info from 2.5.0 to 2.6.0 8fe383240d Bump anyhow from 1.0.70 to 1.0.71 8d94e82ad5 deployments: add new BEEFY metrics and alarms (#2090) e9a4749e7e Bump wasmtime from 6.0.1 to 6.0.2 9d9936c0d9 Bump wasmtime from 6.0.1 to 6.0.2 in /tools/runtime-codegen 5d77cd7bee Add more logs to relayer and message pallets (#2082) 75fbb9d3ef Update comment (#2081) 9904d09cf6 Benchmarks for new relayers pallet calls (#2040) git-subtree-dir: bridges git-subtree-split: 557ecbcecc585547b744a5ac9fb8d7f3b9de4521 * fmt * Squashed 'bridges/' changes from 557ecbcecc..04b3dda6aa 04b3dda6aa Remove from subtree (#2111) f8ff15e7e7 Add `MessagesPalletInstance` for integrity tests (#2107) 92ccef58e6 Use generated runtimes for BHR/BHW (#2106) b33e0a585b Fix comment (#2105) git-subtree-dir: bridges git-subtree-split: 04b3dda6aa38599e612ff637710b6d2cff275ef3 * ".git/.scripts/commands/fmt/fmt.sh" --------- Co-authored-by: parity-processbot <>
This commit is contained in:
@@ -0,0 +1,779 @@
|
||||
// Copyright 2019-2021 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/>.
|
||||
|
||||
//! Types that allow runtime to act as a source/target endpoint of message lanes.
|
||||
//!
|
||||
//! Messages are assumed to be encoded `Call`s of the target chain. Call-dispatch
|
||||
//! pallet is used to dispatch incoming messages. Message identified by a tuple
|
||||
//! of to elements - message lane id and message nonce.
|
||||
|
||||
pub use bp_runtime::{RangeInclusiveExt, UnderlyingChainOf, UnderlyingChainProvider};
|
||||
|
||||
use bp_header_chain::{HeaderChain, HeaderChainError};
|
||||
use bp_messages::{
|
||||
source_chain::{LaneMessageVerifier, TargetHeaderChain},
|
||||
target_chain::{ProvedLaneMessages, ProvedMessages, SourceHeaderChain},
|
||||
InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData,
|
||||
};
|
||||
use bp_runtime::{Chain, RawStorageProof, Size, StorageProofChecker, StorageProofError};
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::{traits::Get, weights::Weight, RuntimeDebug};
|
||||
use hash_db::Hasher;
|
||||
use scale_info::TypeInfo;
|
||||
use sp_std::{convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec};
|
||||
|
||||
/// Bidirectional message bridge.
|
||||
pub trait MessageBridge {
|
||||
/// Name of the paired messages pallet instance at the Bridged chain.
|
||||
///
|
||||
/// Should be the name that is used in the `construct_runtime!()` macro.
|
||||
const BRIDGED_MESSAGES_PALLET_NAME: &'static str;
|
||||
|
||||
/// This chain in context of message bridge.
|
||||
type ThisChain: ThisChainWithMessages;
|
||||
/// Bridged chain in context of message bridge.
|
||||
type BridgedChain: BridgedChainWithMessages;
|
||||
/// Bridged header chain.
|
||||
type BridgedHeaderChain: HeaderChain<UnderlyingChainOf<Self::BridgedChain>>;
|
||||
}
|
||||
|
||||
/// This chain that has `pallet-bridge-messages` module.
|
||||
pub trait ThisChainWithMessages: UnderlyingChainProvider {
|
||||
/// Call origin on the chain.
|
||||
type RuntimeOrigin;
|
||||
}
|
||||
|
||||
/// Bridged chain that has `pallet-bridge-messages` module.
|
||||
pub trait BridgedChainWithMessages: UnderlyingChainProvider {}
|
||||
|
||||
/// This chain in context of message bridge.
|
||||
pub type ThisChain<B> = <B as MessageBridge>::ThisChain;
|
||||
/// Bridged chain in context of message bridge.
|
||||
pub type BridgedChain<B> = <B as MessageBridge>::BridgedChain;
|
||||
/// Hash used on the chain.
|
||||
pub type HashOf<C> = bp_runtime::HashOf<<C as UnderlyingChainProvider>::Chain>;
|
||||
/// Hasher used on the chain.
|
||||
pub type HasherOf<C> = bp_runtime::HasherOf<UnderlyingChainOf<C>>;
|
||||
/// Account id used on the chain.
|
||||
pub type AccountIdOf<C> = bp_runtime::AccountIdOf<UnderlyingChainOf<C>>;
|
||||
/// Type of balances that is used on the chain.
|
||||
pub type BalanceOf<C> = bp_runtime::BalanceOf<UnderlyingChainOf<C>>;
|
||||
/// Type of origin that is used on the chain.
|
||||
pub type OriginOf<C> = <C as ThisChainWithMessages>::RuntimeOrigin;
|
||||
|
||||
/// Error that happens during message verification.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// The message proof is empty.
|
||||
EmptyMessageProof,
|
||||
/// Error returned by the bridged header chain.
|
||||
HeaderChain(HeaderChainError),
|
||||
/// Error returned while reading/decoding inbound lane data from the storage proof.
|
||||
InboundLaneStorage(StorageProofError),
|
||||
/// The declared message weight is incorrect.
|
||||
InvalidMessageWeight,
|
||||
/// Declared messages count doesn't match actual value.
|
||||
MessagesCountMismatch,
|
||||
/// Error returned while reading/decoding message data from the storage proof.
|
||||
MessageStorage(StorageProofError),
|
||||
/// The message is too large.
|
||||
MessageTooLarge,
|
||||
/// Error returned while reading/decoding outbound lane data from the storage proof.
|
||||
OutboundLaneStorage(StorageProofError),
|
||||
/// Storage proof related error.
|
||||
StorageProof(StorageProofError),
|
||||
}
|
||||
|
||||
/// Sub-module that is declaring types required for processing This -> Bridged chain messages.
|
||||
pub mod source {
|
||||
use super::*;
|
||||
|
||||
/// Message payload for This -> Bridged chain messages.
|
||||
pub type FromThisChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload;
|
||||
|
||||
/// Maximal size of outbound message payload.
|
||||
pub struct FromThisChainMaximalOutboundPayloadSize<B>(PhantomData<B>);
|
||||
|
||||
impl<B: MessageBridge> Get<u32> for FromThisChainMaximalOutboundPayloadSize<B> {
|
||||
fn get() -> u32 {
|
||||
maximal_message_size::<B>()
|
||||
}
|
||||
}
|
||||
|
||||
/// Messages delivery proof from bridged chain:
|
||||
///
|
||||
/// - hash of finalized header;
|
||||
/// - storage proof of inbound lane state;
|
||||
/// - lane id.
|
||||
#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
|
||||
pub struct FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
|
||||
/// Hash of the bridge header the proof is for.
|
||||
pub bridged_header_hash: BridgedHeaderHash,
|
||||
/// Storage trie proof generated for [`Self::bridged_header_hash`].
|
||||
pub storage_proof: RawStorageProof,
|
||||
/// Lane id of which messages were delivered and the proof is for.
|
||||
pub lane: LaneId,
|
||||
}
|
||||
|
||||
impl<BridgedHeaderHash> Size for FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
|
||||
fn size(&self) -> u32 {
|
||||
u32::try_from(
|
||||
self.storage_proof
|
||||
.iter()
|
||||
.fold(0usize, |sum, node| sum.saturating_add(node.len())),
|
||||
)
|
||||
.unwrap_or(u32::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
/// 'Parsed' message delivery proof - inbound lane id and its state.
|
||||
pub type ParsedMessagesDeliveryProofFromBridgedChain<B> =
|
||||
(LaneId, InboundLaneData<AccountIdOf<ThisChain<B>>>);
|
||||
|
||||
/// Message verifier that is doing all basic checks.
|
||||
///
|
||||
/// This verifier assumes following:
|
||||
///
|
||||
/// - all message lanes are equivalent, so all checks are the same;
|
||||
///
|
||||
/// Following checks are made:
|
||||
///
|
||||
/// - message is rejected if its lane is currently blocked;
|
||||
/// - message is rejected if there are too many pending (undelivered) messages at the outbound
|
||||
/// lane;
|
||||
/// - check that the sender has rights to dispatch the call on target chain using provided
|
||||
/// dispatch origin;
|
||||
/// - check that the sender has paid enough funds for both message delivery and dispatch.
|
||||
#[derive(RuntimeDebug)]
|
||||
pub struct FromThisChainMessageVerifier<B>(PhantomData<B>);
|
||||
|
||||
impl<B> LaneMessageVerifier<OriginOf<ThisChain<B>>, FromThisChainMessagePayload>
|
||||
for FromThisChainMessageVerifier<B>
|
||||
where
|
||||
B: MessageBridge,
|
||||
// matches requirements from the `frame_system::Config::Origin`
|
||||
OriginOf<ThisChain<B>>: Clone
|
||||
+ Into<Result<frame_system::RawOrigin<AccountIdOf<ThisChain<B>>>, OriginOf<ThisChain<B>>>>,
|
||||
AccountIdOf<ThisChain<B>>: PartialEq + Clone,
|
||||
{
|
||||
type Error = &'static str;
|
||||
|
||||
fn verify_message(
|
||||
_submitter: &OriginOf<ThisChain<B>>,
|
||||
_lane: &LaneId,
|
||||
_lane_outbound_data: &OutboundLaneData,
|
||||
_payload: &FromThisChainMessagePayload,
|
||||
) -> Result<(), Self::Error> {
|
||||
// IMPORTANT: any error that is returned here is fatal for the bridge, because
|
||||
// this code is executed at the bridge hub and message sender actually lives
|
||||
// at some sibling parachain. So we are failing **after** the message has been
|
||||
// sent and we can't report it back to sender (unless error report mechanism is
|
||||
// embedded into message and its dispatcher).
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Return maximal message size of This -> Bridged chain message.
|
||||
pub fn maximal_message_size<B: MessageBridge>() -> u32 {
|
||||
super::target::maximal_incoming_message_size(
|
||||
UnderlyingChainOf::<BridgedChain<B>>::max_extrinsic_size(),
|
||||
)
|
||||
}
|
||||
|
||||
/// `TargetHeaderChain` implementation that is using default types and perform default checks.
|
||||
pub struct TargetHeaderChainAdapter<B>(PhantomData<B>);
|
||||
|
||||
impl<B: MessageBridge> TargetHeaderChain<FromThisChainMessagePayload, AccountIdOf<ThisChain<B>>>
|
||||
for TargetHeaderChainAdapter<B>
|
||||
{
|
||||
type Error = Error;
|
||||
type MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>;
|
||||
|
||||
fn verify_message(payload: &FromThisChainMessagePayload) -> Result<(), Self::Error> {
|
||||
verify_chain_message::<B>(payload)
|
||||
}
|
||||
|
||||
fn verify_messages_delivery_proof(
|
||||
proof: Self::MessagesDeliveryProof,
|
||||
) -> Result<(LaneId, InboundLaneData<AccountIdOf<ThisChain<B>>>), Self::Error> {
|
||||
verify_messages_delivery_proof::<B>(proof)
|
||||
}
|
||||
}
|
||||
|
||||
/// Do basic Bridged-chain specific verification of This -> Bridged chain message.
|
||||
///
|
||||
/// Ok result from this function means that the delivery transaction with this message
|
||||
/// may be 'mined' by the target chain. But the lane may have its own checks (e.g. fee
|
||||
/// check) that would reject message (see `FromThisChainMessageVerifier`).
|
||||
pub fn verify_chain_message<B: MessageBridge>(
|
||||
payload: &FromThisChainMessagePayload,
|
||||
) -> Result<(), Error> {
|
||||
// IMPORTANT: any error that is returned here is fatal for the bridge, because
|
||||
// this code is executed at the bridge hub and message sender actually lives
|
||||
// at some sibling parachain. So we are failing **after** the message has been
|
||||
// sent and we can't report it back to sender (unless error report mechanism is
|
||||
// embedded into message and its dispatcher).
|
||||
|
||||
// apart from maximal message size check (see below), we should also check the message
|
||||
// dispatch weight here. But we assume that the bridged chain will just push the message
|
||||
// to some queue (XCMP, UMP, DMP), so the weight is constant and fits the block.
|
||||
|
||||
// The maximal size of extrinsic at Substrate-based chain depends on the
|
||||
// `frame_system::Config::MaximumBlockLength` and
|
||||
// `frame_system::Config::AvailableBlockRatio` constants. This check is here to be sure that
|
||||
// the lane won't stuck because message is too large to fit into delivery transaction.
|
||||
//
|
||||
// **IMPORTANT NOTE**: the delivery transaction contains storage proof of the message, not
|
||||
// the message itself. The proof is always larger than the message. But unless chain state
|
||||
// is enormously large, it should be several dozens/hundreds of bytes. The delivery
|
||||
// transaction also contains signatures and signed extensions. Because of this, we reserve
|
||||
// 1/3 of the the maximal extrinsic weight for this data.
|
||||
if payload.len() > maximal_message_size::<B>() as usize {
|
||||
return Err(Error::MessageTooLarge)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify proof of This -> Bridged chain messages delivery.
|
||||
///
|
||||
/// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged
|
||||
/// parachains, please use the `verify_messages_delivery_proof_from_parachain`.
|
||||
pub fn verify_messages_delivery_proof<B: MessageBridge>(
|
||||
proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>,
|
||||
) -> Result<ParsedMessagesDeliveryProofFromBridgedChain<B>, Error> {
|
||||
let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } =
|
||||
proof;
|
||||
B::BridgedHeaderChain::parse_finalized_storage_proof(
|
||||
bridged_header_hash,
|
||||
storage_proof,
|
||||
|mut storage| {
|
||||
// Messages delivery proof is just proof of single storage key read => any error
|
||||
// is fatal.
|
||||
let storage_inbound_lane_data_key =
|
||||
bp_messages::storage_keys::inbound_lane_data_key(
|
||||
B::BRIDGED_MESSAGES_PALLET_NAME,
|
||||
&lane,
|
||||
);
|
||||
let inbound_lane_data = storage
|
||||
.read_and_decode_mandatory_value(storage_inbound_lane_data_key.0.as_ref())
|
||||
.map_err(Error::InboundLaneStorage)?;
|
||||
|
||||
// check that the storage proof doesn't have any untouched trie nodes
|
||||
storage.ensure_no_unused_nodes().map_err(Error::StorageProof)?;
|
||||
|
||||
Ok((lane, inbound_lane_data))
|
||||
},
|
||||
)
|
||||
.map_err(Error::HeaderChain)?
|
||||
}
|
||||
}
|
||||
|
||||
/// Sub-module that is declaring types required for processing Bridged -> This chain messages.
|
||||
pub mod target {
|
||||
use super::*;
|
||||
|
||||
/// Decoded Bridged -> This message payload.
|
||||
pub type FromBridgedChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload;
|
||||
|
||||
/// Messages proof from bridged chain:
|
||||
///
|
||||
/// - hash of finalized header;
|
||||
/// - storage proof of messages and (optionally) outbound lane state;
|
||||
/// - lane id;
|
||||
/// - nonces (inclusive range) of messages which are included in this proof.
|
||||
#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
|
||||
pub struct FromBridgedChainMessagesProof<BridgedHeaderHash> {
|
||||
/// Hash of the finalized bridged header the proof is for.
|
||||
pub bridged_header_hash: BridgedHeaderHash,
|
||||
/// A storage trie proof of messages being delivered.
|
||||
pub storage_proof: RawStorageProof,
|
||||
/// Messages in this proof are sent over this lane.
|
||||
pub lane: LaneId,
|
||||
/// Nonce of the first message being delivered.
|
||||
pub nonces_start: MessageNonce,
|
||||
/// Nonce of the last message being delivered.
|
||||
pub nonces_end: MessageNonce,
|
||||
}
|
||||
|
||||
impl<BridgedHeaderHash> Size for FromBridgedChainMessagesProof<BridgedHeaderHash> {
|
||||
fn size(&self) -> u32 {
|
||||
u32::try_from(
|
||||
self.storage_proof
|
||||
.iter()
|
||||
.fold(0usize, |sum, node| sum.saturating_add(node.len())),
|
||||
)
|
||||
.unwrap_or(u32::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return maximal dispatch weight of the message we're able to receive.
|
||||
pub fn maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight {
|
||||
maximal_extrinsic_weight / 2
|
||||
}
|
||||
|
||||
/// Return maximal message size given maximal extrinsic size.
|
||||
pub fn maximal_incoming_message_size(maximal_extrinsic_size: u32) -> u32 {
|
||||
maximal_extrinsic_size / 3 * 2
|
||||
}
|
||||
|
||||
/// `SourceHeaderChain` implementation that is using default types and perform default checks.
|
||||
pub struct SourceHeaderChainAdapter<B>(PhantomData<B>);
|
||||
|
||||
impl<B: MessageBridge> SourceHeaderChain for SourceHeaderChainAdapter<B> {
|
||||
type Error = Error;
|
||||
type MessagesProof = FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>;
|
||||
|
||||
fn verify_messages_proof(
|
||||
proof: Self::MessagesProof,
|
||||
messages_count: u32,
|
||||
) -> Result<ProvedMessages<Message>, Self::Error> {
|
||||
verify_messages_proof::<B>(proof, messages_count)
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify proof of Bridged -> This chain messages.
|
||||
///
|
||||
/// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged
|
||||
/// parachains, please use the `verify_messages_proof_from_parachain`.
|
||||
///
|
||||
/// The `messages_count` argument verification (sane limits) is supposed to be made
|
||||
/// outside of this function. This function only verifies that the proof declares exactly
|
||||
/// `messages_count` messages.
|
||||
pub fn verify_messages_proof<B: MessageBridge>(
|
||||
proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>,
|
||||
messages_count: u32,
|
||||
) -> Result<ProvedMessages<Message>, Error> {
|
||||
let FromBridgedChainMessagesProof {
|
||||
bridged_header_hash,
|
||||
storage_proof,
|
||||
lane,
|
||||
nonces_start,
|
||||
nonces_end,
|
||||
} = proof;
|
||||
let nonces_range = nonces_start..=nonces_end;
|
||||
|
||||
B::BridgedHeaderChain::parse_finalized_storage_proof(
|
||||
bridged_header_hash,
|
||||
storage_proof,
|
||||
|storage| {
|
||||
let mut parser =
|
||||
StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() };
|
||||
|
||||
// receiving proofs where end < begin is ok (if proof includes outbound lane state)
|
||||
let messages_in_the_proof = nonces_range.checked_len().unwrap_or(0);
|
||||
if messages_in_the_proof != MessageNonce::from(messages_count) {
|
||||
return Err(Error::MessagesCountMismatch)
|
||||
}
|
||||
|
||||
// Read messages first. All messages that are claimed to be in the proof must
|
||||
// be in the proof. So any error in `read_value`, or even missing value is fatal.
|
||||
//
|
||||
// Mind that we allow proofs with no messages if outbound lane state is proved.
|
||||
let mut messages = Vec::with_capacity(messages_in_the_proof as _);
|
||||
for nonce in nonces_range {
|
||||
let message_key = MessageKey { lane_id: lane, nonce };
|
||||
let message_payload = parser.read_and_decode_message_payload(&message_key)?;
|
||||
messages.push(Message { key: message_key, payload: message_payload });
|
||||
}
|
||||
|
||||
// Now let's check if proof contains outbound lane state proof. It is optional, so
|
||||
// we simply ignore `read_value` errors and missing value.
|
||||
let proved_lane_messages = ProvedLaneMessages {
|
||||
lane_state: parser.read_and_decode_outbound_lane_data(&lane)?,
|
||||
messages,
|
||||
};
|
||||
|
||||
// Now we may actually check if the proof is empty or not.
|
||||
if proved_lane_messages.lane_state.is_none() &&
|
||||
proved_lane_messages.messages.is_empty()
|
||||
{
|
||||
return Err(Error::EmptyMessageProof)
|
||||
}
|
||||
|
||||
// check that the storage proof doesn't have any untouched trie nodes
|
||||
parser.storage.ensure_no_unused_nodes().map_err(Error::StorageProof)?;
|
||||
|
||||
// We only support single lane messages in this generated_schema
|
||||
let mut proved_messages = ProvedMessages::new();
|
||||
proved_messages.insert(lane, proved_lane_messages);
|
||||
|
||||
Ok(proved_messages)
|
||||
},
|
||||
)
|
||||
.map_err(Error::HeaderChain)?
|
||||
}
|
||||
|
||||
struct StorageProofCheckerAdapter<H: Hasher, B> {
|
||||
storage: StorageProofChecker<H>,
|
||||
_dummy: sp_std::marker::PhantomData<B>,
|
||||
}
|
||||
|
||||
impl<H: Hasher, B: MessageBridge> StorageProofCheckerAdapter<H, B> {
|
||||
fn read_and_decode_outbound_lane_data(
|
||||
&mut self,
|
||||
lane_id: &LaneId,
|
||||
) -> Result<Option<OutboundLaneData>, Error> {
|
||||
let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key(
|
||||
B::BRIDGED_MESSAGES_PALLET_NAME,
|
||||
lane_id,
|
||||
);
|
||||
|
||||
self.storage
|
||||
.read_and_decode_opt_value(storage_outbound_lane_data_key.0.as_ref())
|
||||
.map_err(Error::OutboundLaneStorage)
|
||||
}
|
||||
|
||||
fn read_and_decode_message_payload(
|
||||
&mut self,
|
||||
message_key: &MessageKey,
|
||||
) -> Result<MessagePayload, Error> {
|
||||
let storage_message_key = bp_messages::storage_keys::message_key(
|
||||
B::BRIDGED_MESSAGES_PALLET_NAME,
|
||||
&message_key.lane_id,
|
||||
message_key.nonce,
|
||||
);
|
||||
self.storage
|
||||
.read_and_decode_mandatory_value(storage_message_key.0.as_ref())
|
||||
.map_err(Error::MessageStorage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The `BridgeMessagesCall` used by a chain.
|
||||
pub type BridgeMessagesCallOf<C> = bp_messages::BridgeMessagesCall<
|
||||
bp_runtime::AccountIdOf<C>,
|
||||
target::FromBridgedChainMessagesProof<bp_runtime::HashOf<C>>,
|
||||
source::FromBridgedChainMessagesDeliveryProof<bp_runtime::HashOf<C>>,
|
||||
>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
messages_generation::{
|
||||
encode_all_messages, encode_lane_data, prepare_messages_storage_proof,
|
||||
},
|
||||
mock::*,
|
||||
};
|
||||
use bp_header_chain::StoredHeaderDataBuilder;
|
||||
use bp_runtime::HeaderId;
|
||||
use codec::Encode;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::traits::Header as _;
|
||||
|
||||
#[test]
|
||||
fn verify_chain_message_rejects_message_with_too_large_declared_weight() {
|
||||
assert!(source::verify_chain_message::<OnThisChainBridge>(&vec![
|
||||
42;
|
||||
BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT -
|
||||
1
|
||||
])
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_chain_message_rejects_message_too_large_message() {
|
||||
assert!(source::verify_chain_message::<OnThisChainBridge>(&vec![
|
||||
0;
|
||||
source::maximal_message_size::<OnThisChainBridge>()
|
||||
as usize + 1
|
||||
],)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_chain_message_accepts_maximal_message() {
|
||||
assert_eq!(
|
||||
source::verify_chain_message::<OnThisChainBridge>(&vec![
|
||||
0;
|
||||
source::maximal_message_size::<OnThisChainBridge>()
|
||||
as _
|
||||
],),
|
||||
Ok(()),
|
||||
);
|
||||
}
|
||||
|
||||
fn using_messages_proof<R>(
|
||||
nonces_end: MessageNonce,
|
||||
outbound_lane_data: Option<OutboundLaneData>,
|
||||
encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option<Vec<u8>>,
|
||||
encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec<u8>,
|
||||
test: impl Fn(target::FromBridgedChainMessagesProof<H256>) -> R,
|
||||
) -> R {
|
||||
let (state_root, storage_proof) = prepare_messages_storage_proof::<OnThisChainBridge>(
|
||||
TEST_LANE_ID,
|
||||
1..=nonces_end,
|
||||
outbound_lane_data,
|
||||
bp_runtime::StorageProofSize::Minimal(0),
|
||||
vec![42],
|
||||
encode_message,
|
||||
encode_outbound_lane_data,
|
||||
);
|
||||
|
||||
sp_io::TestExternalities::new(Default::default()).execute_with(move || {
|
||||
let bridged_header = BridgedChainHeader::new(
|
||||
0,
|
||||
Default::default(),
|
||||
state_root,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let bridged_header_hash = bridged_header.hash();
|
||||
|
||||
pallet_bridge_grandpa::BestFinalized::<TestRuntime>::put(HeaderId(
|
||||
0,
|
||||
bridged_header_hash,
|
||||
));
|
||||
pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
|
||||
bridged_header_hash,
|
||||
bridged_header.build(),
|
||||
);
|
||||
test(target::FromBridgedChainMessagesProof {
|
||||
bridged_header_hash,
|
||||
storage_proof,
|
||||
lane: TEST_LANE_ID,
|
||||
nonces_start: 1,
|
||||
nonces_end,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn messages_proof_is_rejected_if_declared_less_than_actual_number_of_messages() {
|
||||
assert_eq!(
|
||||
using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
|
||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 5)
|
||||
}),
|
||||
Err(Error::MessagesCountMismatch),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn messages_proof_is_rejected_if_declared_more_than_actual_number_of_messages() {
|
||||
assert_eq!(
|
||||
using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
|
||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 15)
|
||||
}),
|
||||
Err(Error::MessagesCountMismatch),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_proof_is_rejected_if_header_is_missing_from_the_chain() {
|
||||
assert_eq!(
|
||||
using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
|
||||
let bridged_header_hash =
|
||||
pallet_bridge_grandpa::BestFinalized::<TestRuntime>::get().unwrap().1;
|
||||
pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::remove(bridged_header_hash);
|
||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||
}),
|
||||
Err(Error::HeaderChain(HeaderChainError::UnknownHeader)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_proof_is_rejected_if_header_state_root_mismatches() {
|
||||
assert_eq!(
|
||||
using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
|
||||
let bridged_header_hash =
|
||||
pallet_bridge_grandpa::BestFinalized::<TestRuntime>::get().unwrap().1;
|
||||
pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::insert(
|
||||
bridged_header_hash,
|
||||
BridgedChainHeader::new(
|
||||
0,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
)
|
||||
.build(),
|
||||
);
|
||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||
}),
|
||||
Err(Error::HeaderChain(HeaderChainError::StorageProof(
|
||||
StorageProofError::StorageRootMismatch
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_proof_is_rejected_if_it_has_duplicate_trie_nodes() {
|
||||
assert_eq!(
|
||||
using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| {
|
||||
let node = proof.storage_proof.pop().unwrap();
|
||||
proof.storage_proof.push(node.clone());
|
||||
proof.storage_proof.push(node);
|
||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||
},),
|
||||
Err(Error::HeaderChain(HeaderChainError::StorageProof(
|
||||
StorageProofError::DuplicateNodesInProof
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_proof_is_rejected_if_it_has_unused_trie_nodes() {
|
||||
assert_eq!(
|
||||
using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| {
|
||||
proof.storage_proof.push(vec![42]);
|
||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||
},),
|
||||
Err(Error::StorageProof(StorageProofError::UnusedNodesInTheProof)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_proof_is_rejected_if_required_message_is_missing() {
|
||||
matches!(
|
||||
using_messages_proof(
|
||||
10,
|
||||
None,
|
||||
|n, m| if n != 5 { Some(m.encode()) } else { None },
|
||||
encode_lane_data,
|
||||
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||
),
|
||||
Err(Error::MessageStorage(StorageProofError::StorageValueEmpty)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_proof_is_rejected_if_message_decode_fails() {
|
||||
matches!(
|
||||
using_messages_proof(
|
||||
10,
|
||||
None,
|
||||
|n, m| {
|
||||
let mut m = m.encode();
|
||||
if n == 5 {
|
||||
m = vec![42]
|
||||
}
|
||||
Some(m)
|
||||
},
|
||||
encode_lane_data,
|
||||
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
|
||||
),
|
||||
Err(Error::MessageStorage(StorageProofError::StorageValueDecodeFailed(_))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() {
|
||||
matches!(
|
||||
using_messages_proof(
|
||||
10,
|
||||
Some(OutboundLaneData {
|
||||
oldest_unpruned_nonce: 1,
|
||||
latest_received_nonce: 1,
|
||||
latest_generated_nonce: 1,
|
||||
}),
|
||||
encode_all_messages,
|
||||
|d| {
|
||||
let mut d = d.encode();
|
||||
d.truncate(1);
|
||||
d
|
||||
},
|
||||
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
|
||||
),
|
||||
Err(Error::OutboundLaneStorage(StorageProofError::StorageValueDecodeFailed(_))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_proof_is_rejected_if_it_is_empty() {
|
||||
assert_eq!(
|
||||
using_messages_proof(0, None, encode_all_messages, encode_lane_data, |proof| {
|
||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 0)
|
||||
},),
|
||||
Err(Error::EmptyMessageProof),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_empty_message_proof_without_messages_is_accepted() {
|
||||
assert_eq!(
|
||||
using_messages_proof(
|
||||
0,
|
||||
Some(OutboundLaneData {
|
||||
oldest_unpruned_nonce: 1,
|
||||
latest_received_nonce: 1,
|
||||
latest_generated_nonce: 1,
|
||||
}),
|
||||
encode_all_messages,
|
||||
encode_lane_data,
|
||||
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 0),
|
||||
),
|
||||
Ok(vec![(
|
||||
TEST_LANE_ID,
|
||||
ProvedLaneMessages {
|
||||
lane_state: Some(OutboundLaneData {
|
||||
oldest_unpruned_nonce: 1,
|
||||
latest_received_nonce: 1,
|
||||
latest_generated_nonce: 1,
|
||||
}),
|
||||
messages: Vec::new(),
|
||||
},
|
||||
)]
|
||||
.into_iter()
|
||||
.collect()),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_empty_message_proof_is_accepted() {
|
||||
assert_eq!(
|
||||
using_messages_proof(
|
||||
1,
|
||||
Some(OutboundLaneData {
|
||||
oldest_unpruned_nonce: 1,
|
||||
latest_received_nonce: 1,
|
||||
latest_generated_nonce: 1,
|
||||
}),
|
||||
encode_all_messages,
|
||||
encode_lane_data,
|
||||
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 1),
|
||||
),
|
||||
Ok(vec![(
|
||||
TEST_LANE_ID,
|
||||
ProvedLaneMessages {
|
||||
lane_state: Some(OutboundLaneData {
|
||||
oldest_unpruned_nonce: 1,
|
||||
latest_received_nonce: 1,
|
||||
latest_generated_nonce: 1,
|
||||
}),
|
||||
messages: vec![Message {
|
||||
key: MessageKey { lane_id: TEST_LANE_ID, nonce: 1 },
|
||||
payload: vec![42],
|
||||
}],
|
||||
},
|
||||
)]
|
||||
.into_iter()
|
||||
.collect()),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_messages_proof_does_not_panic_if_messages_count_mismatches() {
|
||||
assert_eq!(
|
||||
using_messages_proof(1, None, encode_all_messages, encode_lane_data, |mut proof| {
|
||||
proof.nonces_end = u64::MAX;
|
||||
target::verify_messages_proof::<OnThisChainBridge>(proof, u32::MAX)
|
||||
},),
|
||||
Err(Error::MessagesCountMismatch),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user