mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 05:11:02 +00:00
Propagate message verification errors (#2114)
* Propagate message verification errors * Replace parse_finalized_storage_proof() with storage_proof_checker() * small fixes * fix comment
This commit is contained in:
committed by
Bastian Köcher
parent
c490222fc6
commit
56d4013878
@@ -22,18 +22,19 @@
|
|||||||
|
|
||||||
pub use bp_runtime::{RangeInclusiveExt, UnderlyingChainOf, UnderlyingChainProvider};
|
pub use bp_runtime::{RangeInclusiveExt, UnderlyingChainOf, UnderlyingChainProvider};
|
||||||
|
|
||||||
use bp_header_chain::{HeaderChain, HeaderChainError};
|
use bp_header_chain::HeaderChain;
|
||||||
use bp_messages::{
|
use bp_messages::{
|
||||||
source_chain::{LaneMessageVerifier, TargetHeaderChain},
|
source_chain::{LaneMessageVerifier, TargetHeaderChain},
|
||||||
target_chain::{ProvedLaneMessages, ProvedMessages, SourceHeaderChain},
|
target_chain::{ProvedLaneMessages, ProvedMessages, SourceHeaderChain},
|
||||||
InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData,
|
InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData,
|
||||||
|
VerificationError,
|
||||||
};
|
};
|
||||||
use bp_runtime::{Chain, RawStorageProof, Size, StorageProofChecker, StorageProofError};
|
use bp_runtime::{Chain, RawStorageProof, Size, StorageProofChecker};
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use frame_support::{traits::Get, weights::Weight, RuntimeDebug};
|
use frame_support::{traits::Get, weights::Weight, RuntimeDebug};
|
||||||
use hash_db::Hasher;
|
use hash_db::Hasher;
|
||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
use sp_std::{convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec};
|
use sp_std::{convert::TryFrom, marker::PhantomData, vec::Vec};
|
||||||
|
|
||||||
/// Bidirectional message bridge.
|
/// Bidirectional message bridge.
|
||||||
pub trait MessageBridge {
|
pub trait MessageBridge {
|
||||||
@@ -74,29 +75,6 @@ pub type BalanceOf<C> = bp_runtime::BalanceOf<UnderlyingChainOf<C>>;
|
|||||||
/// Type of origin that is used on the chain.
|
/// Type of origin that is used on the chain.
|
||||||
pub type OriginOf<C> = <C as ThisChainWithMessages>::RuntimeOrigin;
|
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.
|
/// Sub-module that is declaring types required for processing This -> Bridged chain messages.
|
||||||
pub mod source {
|
pub mod source {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -169,14 +147,12 @@ pub mod source {
|
|||||||
+ Into<Result<frame_system::RawOrigin<AccountIdOf<ThisChain<B>>>, OriginOf<ThisChain<B>>>>,
|
+ Into<Result<frame_system::RawOrigin<AccountIdOf<ThisChain<B>>>, OriginOf<ThisChain<B>>>>,
|
||||||
AccountIdOf<ThisChain<B>>: PartialEq + Clone,
|
AccountIdOf<ThisChain<B>>: PartialEq + Clone,
|
||||||
{
|
{
|
||||||
type Error = &'static str;
|
|
||||||
|
|
||||||
fn verify_message(
|
fn verify_message(
|
||||||
_submitter: &OriginOf<ThisChain<B>>,
|
_submitter: &OriginOf<ThisChain<B>>,
|
||||||
_lane: &LaneId,
|
_lane: &LaneId,
|
||||||
_lane_outbound_data: &OutboundLaneData,
|
_lane_outbound_data: &OutboundLaneData,
|
||||||
_payload: &FromThisChainMessagePayload,
|
_payload: &FromThisChainMessagePayload,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), VerificationError> {
|
||||||
// IMPORTANT: any error that is returned here is fatal for the bridge, because
|
// 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
|
// 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
|
// at some sibling parachain. So we are failing **after** the message has been
|
||||||
@@ -200,16 +176,15 @@ pub mod source {
|
|||||||
impl<B: MessageBridge> TargetHeaderChain<FromThisChainMessagePayload, AccountIdOf<ThisChain<B>>>
|
impl<B: MessageBridge> TargetHeaderChain<FromThisChainMessagePayload, AccountIdOf<ThisChain<B>>>
|
||||||
for TargetHeaderChainAdapter<B>
|
for TargetHeaderChainAdapter<B>
|
||||||
{
|
{
|
||||||
type Error = Error;
|
|
||||||
type MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>;
|
type MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>;
|
||||||
|
|
||||||
fn verify_message(payload: &FromThisChainMessagePayload) -> Result<(), Self::Error> {
|
fn verify_message(payload: &FromThisChainMessagePayload) -> Result<(), VerificationError> {
|
||||||
verify_chain_message::<B>(payload)
|
verify_chain_message::<B>(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_messages_delivery_proof(
|
fn verify_messages_delivery_proof(
|
||||||
proof: Self::MessagesDeliveryProof,
|
proof: Self::MessagesDeliveryProof,
|
||||||
) -> Result<(LaneId, InboundLaneData<AccountIdOf<ThisChain<B>>>), Self::Error> {
|
) -> Result<(LaneId, InboundLaneData<AccountIdOf<ThisChain<B>>>), VerificationError> {
|
||||||
verify_messages_delivery_proof::<B>(proof)
|
verify_messages_delivery_proof::<B>(proof)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,7 +196,7 @@ pub mod source {
|
|||||||
/// check) that would reject message (see `FromThisChainMessageVerifier`).
|
/// check) that would reject message (see `FromThisChainMessageVerifier`).
|
||||||
pub fn verify_chain_message<B: MessageBridge>(
|
pub fn verify_chain_message<B: MessageBridge>(
|
||||||
payload: &FromThisChainMessagePayload,
|
payload: &FromThisChainMessagePayload,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), VerificationError> {
|
||||||
// IMPORTANT: any error that is returned here is fatal for the bridge, because
|
// 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
|
// 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
|
// at some sibling parachain. So we are failing **after** the message has been
|
||||||
@@ -243,7 +218,7 @@ pub mod source {
|
|||||||
// transaction also contains signatures and signed extensions. Because of this, we reserve
|
// transaction also contains signatures and signed extensions. Because of this, we reserve
|
||||||
// 1/3 of the the maximal extrinsic weight for this data.
|
// 1/3 of the the maximal extrinsic weight for this data.
|
||||||
if payload.len() > maximal_message_size::<B>() as usize {
|
if payload.len() > maximal_message_size::<B>() as usize {
|
||||||
return Err(Error::MessageTooLarge)
|
return Err(VerificationError::MessageTooLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -255,31 +230,26 @@ pub mod source {
|
|||||||
/// parachains, please use the `verify_messages_delivery_proof_from_parachain`.
|
/// parachains, please use the `verify_messages_delivery_proof_from_parachain`.
|
||||||
pub fn verify_messages_delivery_proof<B: MessageBridge>(
|
pub fn verify_messages_delivery_proof<B: MessageBridge>(
|
||||||
proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>,
|
proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>,
|
||||||
) -> Result<ParsedMessagesDeliveryProofFromBridgedChain<B>, Error> {
|
) -> Result<ParsedMessagesDeliveryProofFromBridgedChain<B>, VerificationError> {
|
||||||
let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } =
|
let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } =
|
||||||
proof;
|
proof;
|
||||||
B::BridgedHeaderChain::parse_finalized_storage_proof(
|
let mut storage =
|
||||||
bridged_header_hash,
|
B::BridgedHeaderChain::storage_proof_checker(bridged_header_hash, storage_proof)
|
||||||
storage_proof,
|
.map_err(VerificationError::HeaderChain)?;
|
||||||
|mut storage| {
|
// Messages delivery proof is just proof of single storage key read => any error
|
||||||
// Messages delivery proof is just proof of single storage key read => any error
|
// is fatal.
|
||||||
// is fatal.
|
let storage_inbound_lane_data_key = bp_messages::storage_keys::inbound_lane_data_key(
|
||||||
let storage_inbound_lane_data_key =
|
B::BRIDGED_MESSAGES_PALLET_NAME,
|
||||||
bp_messages::storage_keys::inbound_lane_data_key(
|
&lane,
|
||||||
B::BRIDGED_MESSAGES_PALLET_NAME,
|
);
|
||||||
&lane,
|
let inbound_lane_data = storage
|
||||||
);
|
.read_and_decode_mandatory_value(storage_inbound_lane_data_key.0.as_ref())
|
||||||
let inbound_lane_data = storage
|
.map_err(VerificationError::InboundLaneStorage)?;
|
||||||
.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
|
// check that the storage proof doesn't have any untouched trie nodes
|
||||||
storage.ensure_no_unused_nodes().map_err(Error::StorageProof)?;
|
storage.ensure_no_unused_nodes().map_err(VerificationError::StorageProof)?;
|
||||||
|
|
||||||
Ok((lane, inbound_lane_data))
|
Ok((lane, inbound_lane_data))
|
||||||
},
|
|
||||||
)
|
|
||||||
.map_err(Error::HeaderChain)?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,13 +305,12 @@ pub mod target {
|
|||||||
pub struct SourceHeaderChainAdapter<B>(PhantomData<B>);
|
pub struct SourceHeaderChainAdapter<B>(PhantomData<B>);
|
||||||
|
|
||||||
impl<B: MessageBridge> SourceHeaderChain for SourceHeaderChainAdapter<B> {
|
impl<B: MessageBridge> SourceHeaderChain for SourceHeaderChainAdapter<B> {
|
||||||
type Error = Error;
|
|
||||||
type MessagesProof = FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>;
|
type MessagesProof = FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>;
|
||||||
|
|
||||||
fn verify_messages_proof(
|
fn verify_messages_proof(
|
||||||
proof: Self::MessagesProof,
|
proof: Self::MessagesProof,
|
||||||
messages_count: u32,
|
messages_count: u32,
|
||||||
) -> Result<ProvedMessages<Message>, Self::Error> {
|
) -> Result<ProvedMessages<Message>, VerificationError> {
|
||||||
verify_messages_proof::<B>(proof, messages_count)
|
verify_messages_proof::<B>(proof, messages_count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,7 +326,7 @@ pub mod target {
|
|||||||
pub fn verify_messages_proof<B: MessageBridge>(
|
pub fn verify_messages_proof<B: MessageBridge>(
|
||||||
proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>,
|
proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>,
|
||||||
messages_count: u32,
|
messages_count: u32,
|
||||||
) -> Result<ProvedMessages<Message>, Error> {
|
) -> Result<ProvedMessages<Message>, VerificationError> {
|
||||||
let FromBridgedChainMessagesProof {
|
let FromBridgedChainMessagesProof {
|
||||||
bridged_header_hash,
|
bridged_header_hash,
|
||||||
storage_proof,
|
storage_proof,
|
||||||
@@ -365,57 +334,52 @@ pub mod target {
|
|||||||
nonces_start,
|
nonces_start,
|
||||||
nonces_end,
|
nonces_end,
|
||||||
} = proof;
|
} = proof;
|
||||||
|
let storage =
|
||||||
|
B::BridgedHeaderChain::storage_proof_checker(bridged_header_hash, storage_proof)
|
||||||
|
.map_err(VerificationError::HeaderChain)?;
|
||||||
|
let mut parser = StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() };
|
||||||
let nonces_range = nonces_start..=nonces_end;
|
let nonces_range = nonces_start..=nonces_end;
|
||||||
|
|
||||||
B::BridgedHeaderChain::parse_finalized_storage_proof(
|
// receiving proofs where end < begin is ok (if proof includes outbound lane state)
|
||||||
bridged_header_hash,
|
let messages_in_the_proof = nonces_range.checked_len().unwrap_or(0);
|
||||||
storage_proof,
|
if messages_in_the_proof != MessageNonce::from(messages_count) {
|
||||||
|storage| {
|
return Err(VerificationError::MessagesCountMismatch)
|
||||||
let mut parser =
|
}
|
||||||
StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() };
|
|
||||||
|
|
||||||
// receiving proofs where end < begin is ok (if proof includes outbound lane state)
|
// Read messages first. All messages that are claimed to be in the proof must
|
||||||
let messages_in_the_proof = nonces_range.checked_len().unwrap_or(0);
|
// be in the proof. So any error in `read_value`, or even missing value is fatal.
|
||||||
if messages_in_the_proof != MessageNonce::from(messages_count) {
|
//
|
||||||
return Err(Error::MessagesCountMismatch)
|
// 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 });
|
||||||
|
}
|
||||||
|
|
||||||
// Read messages first. All messages that are claimed to be in the proof must
|
// Now let's check if proof contains outbound lane state proof. It is optional, so
|
||||||
// be in the proof. So any error in `read_value`, or even missing value is fatal.
|
// we simply ignore `read_value` errors and missing value.
|
||||||
//
|
let proved_lane_messages = ProvedLaneMessages {
|
||||||
// Mind that we allow proofs with no messages if outbound lane state is proved.
|
lane_state: parser.read_and_decode_outbound_lane_data(&lane)?,
|
||||||
let mut messages = Vec::with_capacity(messages_in_the_proof as _);
|
messages,
|
||||||
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
|
// Now we may actually check if the proof is empty or not.
|
||||||
// we simply ignore `read_value` errors and missing value.
|
if proved_lane_messages.lane_state.is_none() && proved_lane_messages.messages.is_empty() {
|
||||||
let proved_lane_messages = ProvedLaneMessages {
|
return Err(VerificationError::EmptyMessageProof)
|
||||||
lane_state: parser.read_and_decode_outbound_lane_data(&lane)?,
|
}
|
||||||
messages,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now we may actually check if the proof is empty or not.
|
// check that the storage proof doesn't have any untouched trie nodes
|
||||||
if proved_lane_messages.lane_state.is_none() &&
|
parser
|
||||||
proved_lane_messages.messages.is_empty()
|
.storage
|
||||||
{
|
.ensure_no_unused_nodes()
|
||||||
return Err(Error::EmptyMessageProof)
|
.map_err(VerificationError::StorageProof)?;
|
||||||
}
|
|
||||||
|
|
||||||
// check that the storage proof doesn't have any untouched trie nodes
|
// We only support single lane messages in this generated_schema
|
||||||
parser.storage.ensure_no_unused_nodes().map_err(Error::StorageProof)?;
|
let mut proved_messages = ProvedMessages::new();
|
||||||
|
proved_messages.insert(lane, proved_lane_messages);
|
||||||
|
|
||||||
// We only support single lane messages in this generated_schema
|
Ok(proved_messages)
|
||||||
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> {
|
struct StorageProofCheckerAdapter<H: Hasher, B> {
|
||||||
@@ -427,7 +391,7 @@ pub mod target {
|
|||||||
fn read_and_decode_outbound_lane_data(
|
fn read_and_decode_outbound_lane_data(
|
||||||
&mut self,
|
&mut self,
|
||||||
lane_id: &LaneId,
|
lane_id: &LaneId,
|
||||||
) -> Result<Option<OutboundLaneData>, Error> {
|
) -> Result<Option<OutboundLaneData>, VerificationError> {
|
||||||
let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key(
|
let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key(
|
||||||
B::BRIDGED_MESSAGES_PALLET_NAME,
|
B::BRIDGED_MESSAGES_PALLET_NAME,
|
||||||
lane_id,
|
lane_id,
|
||||||
@@ -435,13 +399,13 @@ pub mod target {
|
|||||||
|
|
||||||
self.storage
|
self.storage
|
||||||
.read_and_decode_opt_value(storage_outbound_lane_data_key.0.as_ref())
|
.read_and_decode_opt_value(storage_outbound_lane_data_key.0.as_ref())
|
||||||
.map_err(Error::OutboundLaneStorage)
|
.map_err(VerificationError::OutboundLaneStorage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_and_decode_message_payload(
|
fn read_and_decode_message_payload(
|
||||||
&mut self,
|
&mut self,
|
||||||
message_key: &MessageKey,
|
message_key: &MessageKey,
|
||||||
) -> Result<MessagePayload, Error> {
|
) -> Result<MessagePayload, VerificationError> {
|
||||||
let storage_message_key = bp_messages::storage_keys::message_key(
|
let storage_message_key = bp_messages::storage_keys::message_key(
|
||||||
B::BRIDGED_MESSAGES_PALLET_NAME,
|
B::BRIDGED_MESSAGES_PALLET_NAME,
|
||||||
&message_key.lane_id,
|
&message_key.lane_id,
|
||||||
@@ -449,7 +413,7 @@ pub mod target {
|
|||||||
);
|
);
|
||||||
self.storage
|
self.storage
|
||||||
.read_and_decode_mandatory_value(storage_message_key.0.as_ref())
|
.read_and_decode_mandatory_value(storage_message_key.0.as_ref())
|
||||||
.map_err(Error::MessageStorage)
|
.map_err(VerificationError::MessageStorage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -470,8 +434,8 @@ mod tests {
|
|||||||
},
|
},
|
||||||
mock::*,
|
mock::*,
|
||||||
};
|
};
|
||||||
use bp_header_chain::StoredHeaderDataBuilder;
|
use bp_header_chain::{HeaderChainError, StoredHeaderDataBuilder};
|
||||||
use bp_runtime::HeaderId;
|
use bp_runtime::{HeaderId, StorageProofError};
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use sp_core::H256;
|
use sp_core::H256;
|
||||||
use sp_runtime::traits::Header as _;
|
use sp_runtime::traits::Header as _;
|
||||||
@@ -559,7 +523,7 @@ mod tests {
|
|||||||
using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
|
using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
|
||||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 5)
|
target::verify_messages_proof::<OnThisChainBridge>(proof, 5)
|
||||||
}),
|
}),
|
||||||
Err(Error::MessagesCountMismatch),
|
Err(VerificationError::MessagesCountMismatch),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,7 +533,7 @@ mod tests {
|
|||||||
using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
|
using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| {
|
||||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 15)
|
target::verify_messages_proof::<OnThisChainBridge>(proof, 15)
|
||||||
}),
|
}),
|
||||||
Err(Error::MessagesCountMismatch),
|
Err(VerificationError::MessagesCountMismatch),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,7 +546,7 @@ mod tests {
|
|||||||
pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::remove(bridged_header_hash);
|
pallet_bridge_grandpa::ImportedHeaders::<TestRuntime>::remove(bridged_header_hash);
|
||||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||||
}),
|
}),
|
||||||
Err(Error::HeaderChain(HeaderChainError::UnknownHeader)),
|
Err(VerificationError::HeaderChain(HeaderChainError::UnknownHeader)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +569,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||||
}),
|
}),
|
||||||
Err(Error::HeaderChain(HeaderChainError::StorageProof(
|
Err(VerificationError::HeaderChain(HeaderChainError::StorageProof(
|
||||||
StorageProofError::StorageRootMismatch
|
StorageProofError::StorageRootMismatch
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
@@ -620,7 +584,7 @@ mod tests {
|
|||||||
proof.storage_proof.push(node);
|
proof.storage_proof.push(node);
|
||||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||||
},),
|
},),
|
||||||
Err(Error::HeaderChain(HeaderChainError::StorageProof(
|
Err(VerificationError::HeaderChain(HeaderChainError::StorageProof(
|
||||||
StorageProofError::DuplicateNodesInProof
|
StorageProofError::DuplicateNodesInProof
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
@@ -633,7 +597,7 @@ mod tests {
|
|||||||
proof.storage_proof.push(vec![42]);
|
proof.storage_proof.push(vec![42]);
|
||||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||||
},),
|
},),
|
||||||
Err(Error::StorageProof(StorageProofError::UnusedNodesInTheProof)),
|
Err(VerificationError::StorageProof(StorageProofError::UnusedNodesInTheProof)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,7 +611,7 @@ mod tests {
|
|||||||
encode_lane_data,
|
encode_lane_data,
|
||||||
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||||
),
|
),
|
||||||
Err(Error::MessageStorage(StorageProofError::StorageValueEmpty)),
|
Err(VerificationError::MessageStorage(StorageProofError::StorageValueEmpty)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -667,7 +631,7 @@ mod tests {
|
|||||||
encode_lane_data,
|
encode_lane_data,
|
||||||
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
|
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
|
||||||
),
|
),
|
||||||
Err(Error::MessageStorage(StorageProofError::StorageValueDecodeFailed(_))),
|
Err(VerificationError::MessageStorage(StorageProofError::StorageValueDecodeFailed(_))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,7 +653,9 @@ mod tests {
|
|||||||
},
|
},
|
||||||
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
|
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
|
||||||
),
|
),
|
||||||
Err(Error::OutboundLaneStorage(StorageProofError::StorageValueDecodeFailed(_))),
|
Err(VerificationError::OutboundLaneStorage(
|
||||||
|
StorageProofError::StorageValueDecodeFailed(_)
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,7 +665,7 @@ mod tests {
|
|||||||
using_messages_proof(0, None, encode_all_messages, encode_lane_data, |proof| {
|
using_messages_proof(0, None, encode_all_messages, encode_lane_data, |proof| {
|
||||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 0)
|
target::verify_messages_proof::<OnThisChainBridge>(proof, 0)
|
||||||
},),
|
},),
|
||||||
Err(Error::EmptyMessageProof),
|
Err(VerificationError::EmptyMessageProof),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,7 +739,7 @@ mod tests {
|
|||||||
proof.nonces_end = u64::MAX;
|
proof.nonces_end = u64::MAX;
|
||||||
target::verify_messages_proof::<OnThisChainBridge>(proof, u32::MAX)
|
target::verify_messages_proof::<OnThisChainBridge>(proof, u32::MAX)
|
||||||
},),
|
},),
|
||||||
Err(Error::MessagesCountMismatch),
|
Err(VerificationError::MessagesCountMismatch),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1212,11 +1212,8 @@ mod tests {
|
|||||||
fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() {
|
fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() {
|
||||||
run_test(|| {
|
run_test(|| {
|
||||||
assert_noop!(
|
assert_noop!(
|
||||||
Pallet::<TestRuntime>::parse_finalized_storage_proof(
|
Pallet::<TestRuntime>::storage_proof_checker(Default::default(), vec![],)
|
||||||
Default::default(),
|
.map(|_| ()),
|
||||||
vec![],
|
|
||||||
|_| (),
|
|
||||||
),
|
|
||||||
bp_header_chain::HeaderChainError::UnknownHeader,
|
bp_header_chain::HeaderChainError::UnknownHeader,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -1235,8 +1232,7 @@ mod tests {
|
|||||||
<ImportedHeaders<TestRuntime>>::insert(hash, header.build());
|
<ImportedHeaders<TestRuntime>>::insert(hash, header.build());
|
||||||
|
|
||||||
assert_ok!(
|
assert_ok!(
|
||||||
Pallet::<TestRuntime>::parse_finalized_storage_proof(hash, storage_proof, |_| (),),
|
Pallet::<TestRuntime>::storage_proof_checker(hash, storage_proof).map(|_| ())
|
||||||
(),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ use bp_messages::{
|
|||||||
},
|
},
|
||||||
total_unrewarded_messages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId,
|
total_unrewarded_messages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId,
|
||||||
MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData,
|
MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData,
|
||||||
OutboundMessageDetails, UnrewardedRelayersState,
|
OutboundMessageDetails, UnrewardedRelayersState, VerificationError,
|
||||||
};
|
};
|
||||||
use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, PreComputedSize, Size};
|
use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, PreComputedSize, Size};
|
||||||
use codec::{Decode, Encode, MaxEncodedLen};
|
use codec::{Decode, Encode, MaxEncodedLen};
|
||||||
@@ -557,9 +557,9 @@ pub mod pallet {
|
|||||||
/// The message is too large to be sent over the bridge.
|
/// The message is too large to be sent over the bridge.
|
||||||
MessageIsTooLarge,
|
MessageIsTooLarge,
|
||||||
/// Message has been treated as invalid by chain verifier.
|
/// Message has been treated as invalid by chain verifier.
|
||||||
MessageRejectedByChainVerifier,
|
MessageRejectedByChainVerifier(VerificationError),
|
||||||
/// Message has been treated as invalid by lane verifier.
|
/// Message has been treated as invalid by lane verifier.
|
||||||
MessageRejectedByLaneVerifier,
|
MessageRejectedByLaneVerifier(VerificationError),
|
||||||
/// Submitter has failed to pay fee for delivering and dispatching messages.
|
/// Submitter has failed to pay fee for delivering and dispatching messages.
|
||||||
FailedToWithdrawMessageFee,
|
FailedToWithdrawMessageFee,
|
||||||
/// The transaction brings too many messages.
|
/// The transaction brings too many messages.
|
||||||
@@ -732,7 +732,7 @@ fn send_message<T: Config<I>, I: 'static>(
|
|||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
|
|
||||||
Error::<T, I>::MessageRejectedByChainVerifier
|
Error::<T, I>::MessageRejectedByChainVerifier(err)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// now let's enforce any additional lane rules
|
// now let's enforce any additional lane rules
|
||||||
@@ -746,7 +746,7 @@ fn send_message<T: Config<I>, I: 'static>(
|
|||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
|
|
||||||
Error::<T, I>::MessageRejectedByLaneVerifier
|
Error::<T, I>::MessageRejectedByLaneVerifier(err)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -918,7 +918,7 @@ impl<T: Config<I>, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorag
|
|||||||
fn verify_and_decode_messages_proof<Chain: SourceHeaderChain, DispatchPayload: Decode>(
|
fn verify_and_decode_messages_proof<Chain: SourceHeaderChain, DispatchPayload: Decode>(
|
||||||
proof: Chain::MessagesProof,
|
proof: Chain::MessagesProof,
|
||||||
messages_count: u32,
|
messages_count: u32,
|
||||||
) -> Result<ProvedMessages<DispatchMessage<DispatchPayload>>, Chain::Error> {
|
) -> Result<ProvedMessages<DispatchMessage<DispatchPayload>>, VerificationError> {
|
||||||
// `receive_messages_proof` weight formula and `MaxUnconfirmedMessagesAtInboundLane` check
|
// `receive_messages_proof` weight formula and `MaxUnconfirmedMessagesAtInboundLane` check
|
||||||
// guarantees that the `message_count` is sane and Vec<Message> may be allocated.
|
// guarantees that the `message_count` is sane and Vec<Message> may be allocated.
|
||||||
// (tx with too many messages will either be rejected from the pool, or will fail earlier)
|
// (tx with too many messages will either be rejected from the pool, or will fail earlier)
|
||||||
@@ -1177,7 +1177,9 @@ mod tests {
|
|||||||
TEST_LANE_ID,
|
TEST_LANE_ID,
|
||||||
PAYLOAD_REJECTED_BY_TARGET_CHAIN,
|
PAYLOAD_REJECTED_BY_TARGET_CHAIN,
|
||||||
),
|
),
|
||||||
Error::<TestRuntime, ()>::MessageRejectedByChainVerifier,
|
Error::<TestRuntime, ()>::MessageRejectedByChainVerifier(VerificationError::Other(
|
||||||
|
mock::TEST_ERROR
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1190,7 +1192,9 @@ mod tests {
|
|||||||
message.reject_by_lane_verifier = true;
|
message.reject_by_lane_verifier = true;
|
||||||
assert_noop!(
|
assert_noop!(
|
||||||
send_message::<TestRuntime, ()>(RuntimeOrigin::signed(1), TEST_LANE_ID, message,),
|
send_message::<TestRuntime, ()>(RuntimeOrigin::signed(1), TEST_LANE_ID, message,),
|
||||||
Error::<TestRuntime, ()>::MessageRejectedByLaneVerifier,
|
Error::<TestRuntime, ()>::MessageRejectedByLaneVerifier(VerificationError::Other(
|
||||||
|
mock::TEST_ERROR
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ use bp_messages::{
|
|||||||
ProvedLaneMessages, ProvedMessages, SourceHeaderChain,
|
ProvedLaneMessages, ProvedMessages, SourceHeaderChain,
|
||||||
},
|
},
|
||||||
DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload,
|
DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload,
|
||||||
OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState,
|
OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, VerificationError,
|
||||||
};
|
};
|
||||||
use bp_runtime::{messages::MessageDispatchResult, Size};
|
use bp_runtime::{messages::MessageDispatchResult, Size};
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
@@ -295,13 +295,11 @@ impl Size for TestMessagesDeliveryProof {
|
|||||||
pub struct TestTargetHeaderChain;
|
pub struct TestTargetHeaderChain;
|
||||||
|
|
||||||
impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain {
|
impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain {
|
||||||
type Error = &'static str;
|
|
||||||
|
|
||||||
type MessagesDeliveryProof = TestMessagesDeliveryProof;
|
type MessagesDeliveryProof = TestMessagesDeliveryProof;
|
||||||
|
|
||||||
fn verify_message(payload: &TestPayload) -> Result<(), Self::Error> {
|
fn verify_message(payload: &TestPayload) -> Result<(), VerificationError> {
|
||||||
if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN {
|
if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN {
|
||||||
Err(TEST_ERROR)
|
Err(VerificationError::Other(TEST_ERROR))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -309,8 +307,8 @@ impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain {
|
|||||||
|
|
||||||
fn verify_messages_delivery_proof(
|
fn verify_messages_delivery_proof(
|
||||||
proof: Self::MessagesDeliveryProof,
|
proof: Self::MessagesDeliveryProof,
|
||||||
) -> Result<(LaneId, InboundLaneData<TestRelayer>), Self::Error> {
|
) -> Result<(LaneId, InboundLaneData<TestRelayer>), VerificationError> {
|
||||||
proof.0.map_err(|_| TEST_ERROR)
|
proof.0.map_err(|_| VerificationError::Other(TEST_ERROR))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,18 +317,16 @@ impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain {
|
|||||||
pub struct TestLaneMessageVerifier;
|
pub struct TestLaneMessageVerifier;
|
||||||
|
|
||||||
impl LaneMessageVerifier<RuntimeOrigin, TestPayload> for TestLaneMessageVerifier {
|
impl LaneMessageVerifier<RuntimeOrigin, TestPayload> for TestLaneMessageVerifier {
|
||||||
type Error = &'static str;
|
|
||||||
|
|
||||||
fn verify_message(
|
fn verify_message(
|
||||||
_submitter: &RuntimeOrigin,
|
_submitter: &RuntimeOrigin,
|
||||||
_lane: &LaneId,
|
_lane: &LaneId,
|
||||||
_lane_outbound_data: &OutboundLaneData,
|
_lane_outbound_data: &OutboundLaneData,
|
||||||
payload: &TestPayload,
|
payload: &TestPayload,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), VerificationError> {
|
||||||
if !payload.reject_by_lane_verifier {
|
if !payload.reject_by_lane_verifier {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(TEST_ERROR)
|
Err(VerificationError::Other(TEST_ERROR))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -400,15 +396,16 @@ impl DeliveryConfirmationPayments<AccountId> for TestDeliveryConfirmationPayment
|
|||||||
pub struct TestSourceHeaderChain;
|
pub struct TestSourceHeaderChain;
|
||||||
|
|
||||||
impl SourceHeaderChain for TestSourceHeaderChain {
|
impl SourceHeaderChain for TestSourceHeaderChain {
|
||||||
type Error = &'static str;
|
|
||||||
|
|
||||||
type MessagesProof = TestMessagesProof;
|
type MessagesProof = TestMessagesProof;
|
||||||
|
|
||||||
fn verify_messages_proof(
|
fn verify_messages_proof(
|
||||||
proof: Self::MessagesProof,
|
proof: Self::MessagesProof,
|
||||||
_messages_count: u32,
|
_messages_count: u32,
|
||||||
) -> Result<ProvedMessages<Message>, Self::Error> {
|
) -> Result<ProvedMessages<Message>, VerificationError> {
|
||||||
proof.result.map(|proof| proof.into_iter().collect()).map_err(|_| TEST_ERROR)
|
proof
|
||||||
|
.result
|
||||||
|
.map(|proof| proof.into_iter().collect())
|
||||||
|
.map_err(|_| VerificationError::Other(TEST_ERROR))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ pub mod pallet {
|
|||||||
BoundedStorageValue<<T as Config<I>>::MaxParaHeadDataSize, ParaStoredHeaderData>;
|
BoundedStorageValue<<T as Config<I>>::MaxParaHeadDataSize, ParaStoredHeaderData>;
|
||||||
/// Weight info of the given parachains pallet.
|
/// Weight info of the given parachains pallet.
|
||||||
pub type WeightInfoOf<T, I> = <T as Config<I>>::WeightInfo;
|
pub type WeightInfoOf<T, I> = <T as Config<I>>::WeightInfo;
|
||||||
|
type GrandpaPalletOf<T, I> =
|
||||||
|
pallet_bridge_grandpa::Pallet<T, <T as Config<I>>::BridgesGrandpaPalletInstance>;
|
||||||
|
|
||||||
#[pallet::event]
|
#[pallet::event]
|
||||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||||
@@ -125,14 +127,8 @@ pub mod pallet {
|
|||||||
UnknownRelayChainBlock,
|
UnknownRelayChainBlock,
|
||||||
/// The number of stored relay block is different from what the relayer has provided.
|
/// The number of stored relay block is different from what the relayer has provided.
|
||||||
InvalidRelayChainBlockNumber,
|
InvalidRelayChainBlockNumber,
|
||||||
/// Error generated by a method defined in `bp-header-chain`.
|
/// Parachain heads storage proof is invalid.
|
||||||
HeaderChain(HeaderChainError),
|
HeaderChainStorageProof(HeaderChainError),
|
||||||
/// Given parachain head is unknown.
|
|
||||||
UnknownParaHead,
|
|
||||||
/// The storage proof doesn't contains storage root. So it is invalid for given header.
|
|
||||||
StorageRootMismatch,
|
|
||||||
/// Failed to extract state root from given parachain head.
|
|
||||||
FailedToExtractStateRoot,
|
|
||||||
/// Error generated by the `OwnedBridgeModule` trait.
|
/// Error generated by the `OwnedBridgeModule` trait.
|
||||||
BridgeModule(bp_runtime::OwnedBridgeModuleError),
|
BridgeModule(bp_runtime::OwnedBridgeModuleError),
|
||||||
}
|
}
|
||||||
@@ -337,112 +333,113 @@ pub mod pallet {
|
|||||||
parachains.len() as _,
|
parachains.len() as _,
|
||||||
);
|
);
|
||||||
|
|
||||||
pallet_bridge_grandpa::Pallet::<T, T::BridgesGrandpaPalletInstance>::parse_finalized_storage_proof(
|
let mut storage = GrandpaPalletOf::<T, I>::storage_proof_checker(
|
||||||
relay_block_hash,
|
relay_block_hash,
|
||||||
parachain_heads_proof.0,
|
parachain_heads_proof.0,
|
||||||
move |mut storage| {
|
)
|
||||||
for (parachain, parachain_head_hash) in parachains {
|
.map_err(Error::<T, I>::HeaderChainStorageProof)?;
|
||||||
let parachain_head = match Pallet::<T, I>::read_parachain_head(&mut storage, parachain) {
|
|
||||||
Ok(Some(parachain_head)) => parachain_head,
|
|
||||||
Ok(None) => {
|
|
||||||
log::trace!(
|
|
||||||
target: LOG_TARGET,
|
|
||||||
"The head of parachain {:?} is None. {}",
|
|
||||||
parachain,
|
|
||||||
if ParasInfo::<T, I>::contains_key(parachain) {
|
|
||||||
"Looks like it is not yet registered at the source relay chain"
|
|
||||||
} else {
|
|
||||||
"Looks like it has been deregistered from the source relay chain"
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Self::deposit_event(Event::MissingParachainHead { parachain });
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
log::trace!(
|
|
||||||
target: LOG_TARGET,
|
|
||||||
"The read of head of parachain {:?} has failed: {:?}",
|
|
||||||
parachain,
|
|
||||||
e,
|
|
||||||
);
|
|
||||||
Self::deposit_event(Event::MissingParachainHead { parachain });
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// if relayer has specified invalid parachain head hash, ignore the head
|
for (parachain, parachain_head_hash) in parachains {
|
||||||
// (this isn't strictly necessary, but better safe than sorry)
|
let parachain_head = match Self::read_parachain_head(&mut storage, parachain) {
|
||||||
let actual_parachain_head_hash = parachain_head.hash();
|
Ok(Some(parachain_head)) => parachain_head,
|
||||||
if parachain_head_hash != actual_parachain_head_hash {
|
Ok(None) => {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: LOG_TARGET,
|
target: LOG_TARGET,
|
||||||
"The submitter has specified invalid parachain {:?} head hash: {:?} vs {:?}",
|
"The head of parachain {:?} is None. {}",
|
||||||
parachain,
|
|
||||||
parachain_head_hash,
|
|
||||||
actual_parachain_head_hash,
|
|
||||||
);
|
|
||||||
Self::deposit_event(Event::IncorrectParachainHeadHash {
|
|
||||||
parachain,
|
|
||||||
parachain_head_hash,
|
|
||||||
actual_parachain_head_hash,
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert from parachain head into stored parachain head data
|
|
||||||
let parachain_head_data = match T::ParaStoredHeaderDataBuilder::try_build(
|
|
||||||
parachain,
|
parachain,
|
||||||
¶chain_head,
|
if ParasInfo::<T, I>::contains_key(parachain) {
|
||||||
) {
|
"Looks like it is not yet registered at the source relay chain"
|
||||||
Some(parachain_head_data) => parachain_head_data,
|
} else {
|
||||||
None => {
|
"Looks like it has been deregistered from the source relay chain"
|
||||||
log::trace!(
|
},
|
||||||
|
);
|
||||||
|
Self::deposit_event(Event::MissingParachainHead { parachain });
|
||||||
|
continue
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::trace!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
"The read of head of parachain {:?} has failed: {:?}",
|
||||||
|
parachain,
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
Self::deposit_event(Event::MissingParachainHead { parachain });
|
||||||
|
continue
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// if relayer has specified invalid parachain head hash, ignore the head
|
||||||
|
// (this isn't strictly necessary, but better safe than sorry)
|
||||||
|
let actual_parachain_head_hash = parachain_head.hash();
|
||||||
|
if parachain_head_hash != actual_parachain_head_hash {
|
||||||
|
log::trace!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
"The submitter has specified invalid parachain {:?} head hash: \
|
||||||
|
{:?} vs {:?}",
|
||||||
|
parachain,
|
||||||
|
parachain_head_hash,
|
||||||
|
actual_parachain_head_hash,
|
||||||
|
);
|
||||||
|
Self::deposit_event(Event::IncorrectParachainHeadHash {
|
||||||
|
parachain,
|
||||||
|
parachain_head_hash,
|
||||||
|
actual_parachain_head_hash,
|
||||||
|
});
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert from parachain head into stored parachain head data
|
||||||
|
let parachain_head_data =
|
||||||
|
match T::ParaStoredHeaderDataBuilder::try_build(parachain, ¶chain_head) {
|
||||||
|
Some(parachain_head_data) => parachain_head_data,
|
||||||
|
None => {
|
||||||
|
log::trace!(
|
||||||
target: LOG_TARGET,
|
target: LOG_TARGET,
|
||||||
"The head of parachain {:?} has been provided, but it is not tracked by the pallet",
|
"The head of parachain {:?} has been provided, but it is not tracked by the pallet",
|
||||||
parachain,
|
parachain,
|
||||||
);
|
);
|
||||||
Self::deposit_event(Event::UntrackedParachainRejected { parachain });
|
Self::deposit_event(Event::UntrackedParachainRejected { parachain });
|
||||||
continue;
|
continue
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let update_result: Result<_, ()> = ParasInfo::<T, I>::try_mutate(parachain, |stored_best_head| {
|
let update_result: Result<_, ()> =
|
||||||
let artifacts = Pallet::<T, I>::update_parachain_head(
|
ParasInfo::<T, I>::try_mutate(parachain, |stored_best_head| {
|
||||||
parachain,
|
let artifacts = Pallet::<T, I>::update_parachain_head(
|
||||||
stored_best_head.take(),
|
parachain,
|
||||||
relay_block_number,
|
stored_best_head.take(),
|
||||||
parachain_head_data,
|
relay_block_number,
|
||||||
parachain_head_hash,
|
parachain_head_data,
|
||||||
)?;
|
parachain_head_hash,
|
||||||
*stored_best_head = Some(artifacts.best_head);
|
)?;
|
||||||
Ok(artifacts.prune_happened)
|
*stored_best_head = Some(artifacts.best_head);
|
||||||
});
|
Ok(artifacts.prune_happened)
|
||||||
|
});
|
||||||
|
|
||||||
// we're refunding weight if update has not happened and if pruning has not happened
|
// we're refunding weight if update has not happened and if pruning has not happened
|
||||||
let is_update_happened = matches!(update_result, Ok(_));
|
let is_update_happened = matches!(update_result, Ok(_));
|
||||||
if !is_update_happened {
|
if !is_update_happened {
|
||||||
actual_weight = actual_weight
|
actual_weight = actual_weight.saturating_sub(
|
||||||
.saturating_sub(WeightInfoOf::<T, I>::parachain_head_storage_write_weight(T::DbWeight::get()));
|
WeightInfoOf::<T, I>::parachain_head_storage_write_weight(
|
||||||
}
|
T::DbWeight::get(),
|
||||||
let is_prune_happened = matches!(update_result, Ok(true));
|
),
|
||||||
if !is_prune_happened {
|
);
|
||||||
actual_weight = actual_weight
|
}
|
||||||
.saturating_sub(WeightInfoOf::<T, I>::parachain_head_pruning_weight(T::DbWeight::get()));
|
let is_prune_happened = matches!(update_result, Ok(true));
|
||||||
}
|
if !is_prune_happened {
|
||||||
}
|
actual_weight = actual_weight.saturating_sub(
|
||||||
|
WeightInfoOf::<T, I>::parachain_head_pruning_weight(T::DbWeight::get()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// even though we may have accepted some parachain heads, we can't allow relayers to submit
|
// even though we may have accepted some parachain heads, we can't allow relayers to
|
||||||
// proof with unused trie nodes
|
// submit proof with unused trie nodes
|
||||||
// => treat this as an error
|
// => treat this as an error
|
||||||
//
|
//
|
||||||
// (we can throw error here, because now all our calls are transactional)
|
// (we can throw error here, because now all our calls are transactional)
|
||||||
storage.ensure_no_unused_nodes()
|
storage.ensure_no_unused_nodes().map_err(|e| {
|
||||||
},
|
Error::<T, I>::HeaderChainStorageProof(HeaderChainError::StorageProof(e))
|
||||||
)
|
|
||||||
.and_then(|r| r.map_err(HeaderChainError::StorageProof))
|
|
||||||
.map_err(|e| {
|
|
||||||
log::trace!(target: LOG_TARGET, "Parachain heads storage proof is invalid: {:?}", e);
|
|
||||||
Error::<T, I>::HeaderChain(e)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes })
|
Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes })
|
||||||
@@ -1438,7 +1435,7 @@ pub(crate) mod tests {
|
|||||||
// try to import head#5 of parachain#1 at relay chain block #0
|
// try to import head#5 of parachain#1 at relay chain block #0
|
||||||
assert_noop!(
|
assert_noop!(
|
||||||
import_parachain_1_head(0, Default::default(), parachains, proof),
|
import_parachain_1_head(0, Default::default(), parachains, proof),
|
||||||
Error::<TestRuntime>::HeaderChain(HeaderChainError::StorageProof(
|
Error::<TestRuntime>::HeaderChainStorageProof(HeaderChainError::StorageProof(
|
||||||
StorageProofError::StorageRootMismatch
|
StorageProofError::StorageRootMismatch
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -73,18 +73,14 @@ impl<H: HeaderT> StoredHeaderDataBuilder<H::Number, H::Hash> for H {
|
|||||||
pub trait HeaderChain<C: Chain> {
|
pub trait HeaderChain<C: Chain> {
|
||||||
/// Returns state (storage) root of given finalized header.
|
/// Returns state (storage) root of given finalized header.
|
||||||
fn finalized_header_state_root(header_hash: HashOf<C>) -> Option<HashOf<C>>;
|
fn finalized_header_state_root(header_hash: HashOf<C>) -> Option<HashOf<C>>;
|
||||||
/// Parse storage proof using finalized header.
|
/// Get storage proof checker using finalized header.
|
||||||
fn parse_finalized_storage_proof<R>(
|
fn storage_proof_checker(
|
||||||
header_hash: HashOf<C>,
|
header_hash: HashOf<C>,
|
||||||
storage_proof: RawStorageProof,
|
storage_proof: RawStorageProof,
|
||||||
parse: impl FnOnce(StorageProofChecker<HasherOf<C>>) -> R,
|
) -> Result<StorageProofChecker<HasherOf<C>>, HeaderChainError> {
|
||||||
) -> Result<R, HeaderChainError> {
|
|
||||||
let state_root = Self::finalized_header_state_root(header_hash)
|
let state_root = Self::finalized_header_state_root(header_hash)
|
||||||
.ok_or(HeaderChainError::UnknownHeader)?;
|
.ok_or(HeaderChainError::UnknownHeader)?;
|
||||||
let storage_proof_checker = bp_runtime::StorageProofChecker::new(state_root, storage_proof)
|
StorageProofChecker::new(state_root, storage_proof).map_err(HeaderChainError::StorageProof)
|
||||||
.map_err(HeaderChainError::StorageProof)?;
|
|
||||||
|
|
||||||
Ok(parse(storage_proof_checker))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ serde = { version = "1.0", optional = true, features = ["derive"] }
|
|||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
|
|
||||||
bp-runtime = { path = "../runtime", default-features = false }
|
bp-runtime = { path = "../runtime", default-features = false }
|
||||||
|
bp-header-chain = { path = "../header-chain", default-features = false }
|
||||||
|
|
||||||
# Substrate Dependencies
|
# Substrate Dependencies
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ hex-literal = "0.4"
|
|||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = [
|
std = [
|
||||||
"bp-runtime/std",
|
"bp-runtime/std",
|
||||||
|
"bp-header-chain/std",
|
||||||
"codec/std",
|
"codec/std",
|
||||||
"frame-support/std",
|
"frame-support/std",
|
||||||
"scale-info/std",
|
"scale-info/std",
|
||||||
|
|||||||
@@ -20,9 +20,15 @@
|
|||||||
// RuntimeApi generated functions
|
// RuntimeApi generated functions
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
|
|
||||||
use bp_runtime::{BasicOperatingMode, OperatingMode, RangeInclusiveExt};
|
use bp_header_chain::HeaderChainError;
|
||||||
|
use bp_runtime::{
|
||||||
|
messages::MessageDispatchResult, BasicOperatingMode, OperatingMode, RangeInclusiveExt,
|
||||||
|
StorageProofError,
|
||||||
|
};
|
||||||
use codec::{Decode, Encode, MaxEncodedLen};
|
use codec::{Decode, Encode, MaxEncodedLen};
|
||||||
use frame_support::RuntimeDebug;
|
use frame_support::{PalletError, RuntimeDebug};
|
||||||
|
// Weight is reexported to avoid additional frame-support dependencies in related crates.
|
||||||
|
pub use frame_support::weights::Weight;
|
||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
use source_chain::RelayersRewards;
|
use source_chain::RelayersRewards;
|
||||||
use sp_core::TypeId;
|
use sp_core::TypeId;
|
||||||
@@ -32,10 +38,6 @@ pub mod source_chain;
|
|||||||
pub mod storage_keys;
|
pub mod storage_keys;
|
||||||
pub mod target_chain;
|
pub mod target_chain;
|
||||||
|
|
||||||
use bp_runtime::messages::MessageDispatchResult;
|
|
||||||
// Weight is reexported to avoid additional frame-support dependencies in related crates.
|
|
||||||
pub use frame_support::weights::Weight;
|
|
||||||
|
|
||||||
/// Messages pallet operating mode.
|
/// Messages pallet operating mode.
|
||||||
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
|
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
|
||||||
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
|
||||||
@@ -414,6 +416,31 @@ pub enum BridgeMessagesCall<AccountId, MessagesProof, MessagesDeliveryProof> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error that happens during message verification.
|
||||||
|
#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)]
|
||||||
|
pub enum VerificationError {
|
||||||
|
/// 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),
|
||||||
|
/// Custom error
|
||||||
|
Other(#[codec(skip)] &'static str),
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! Primitives of messages module, that are used on the source chain.
|
//! Primitives of messages module, that are used on the source chain.
|
||||||
|
|
||||||
use crate::{InboundLaneData, LaneId, MessageNonce, OutboundLaneData};
|
use crate::{InboundLaneData, LaneId, MessageNonce, OutboundLaneData, VerificationError};
|
||||||
|
|
||||||
use crate::UnrewardedRelayer;
|
use crate::UnrewardedRelayer;
|
||||||
use bp_runtime::Size;
|
use bp_runtime::Size;
|
||||||
@@ -40,9 +40,6 @@ pub type RelayersRewards<AccountId> = BTreeMap<AccountId, MessageNonce>;
|
|||||||
/// source chain to the target chain. The `AccountId` type here means the account
|
/// source chain to the target chain. The `AccountId` type here means the account
|
||||||
/// type used by the source chain.
|
/// type used by the source chain.
|
||||||
pub trait TargetHeaderChain<Payload, AccountId> {
|
pub trait TargetHeaderChain<Payload, AccountId> {
|
||||||
/// Error type.
|
|
||||||
type Error: Debug;
|
|
||||||
|
|
||||||
/// Proof that messages have been received by target chain.
|
/// Proof that messages have been received by target chain.
|
||||||
type MessagesDeliveryProof: Parameter + Size;
|
type MessagesDeliveryProof: Parameter + Size;
|
||||||
|
|
||||||
@@ -58,12 +55,12 @@ pub trait TargetHeaderChain<Payload, AccountId> {
|
|||||||
/// 1MB. BTC nodes aren't accepting transactions that are larger than 1MB, so relayer
|
/// 1MB. BTC nodes aren't accepting transactions that are larger than 1MB, so relayer
|
||||||
/// will be unable to craft valid transaction => this (and all subsequent) messages will
|
/// will be unable to craft valid transaction => this (and all subsequent) messages will
|
||||||
/// never be delivered.
|
/// never be delivered.
|
||||||
fn verify_message(payload: &Payload) -> Result<(), Self::Error>;
|
fn verify_message(payload: &Payload) -> Result<(), VerificationError>;
|
||||||
|
|
||||||
/// Verify messages delivery proof and return lane && nonce of the latest received message.
|
/// Verify messages delivery proof and return lane && nonce of the latest received message.
|
||||||
fn verify_messages_delivery_proof(
|
fn verify_messages_delivery_proof(
|
||||||
proof: Self::MessagesDeliveryProof,
|
proof: Self::MessagesDeliveryProof,
|
||||||
) -> Result<(LaneId, InboundLaneData<AccountId>), Self::Error>;
|
) -> Result<(LaneId, InboundLaneData<AccountId>), VerificationError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lane message verifier.
|
/// Lane message verifier.
|
||||||
@@ -75,9 +72,6 @@ pub trait TargetHeaderChain<Payload, AccountId> {
|
|||||||
///
|
///
|
||||||
/// Any fee requirements should also be enforced here.
|
/// Any fee requirements should also be enforced here.
|
||||||
pub trait LaneMessageVerifier<SenderOrigin, Payload> {
|
pub trait LaneMessageVerifier<SenderOrigin, Payload> {
|
||||||
/// Error type.
|
|
||||||
type Error: Debug + Into<&'static str>;
|
|
||||||
|
|
||||||
/// Verify message payload and return Ok(()) if message is valid and allowed to be sent over the
|
/// Verify message payload and return Ok(()) if message is valid and allowed to be sent over the
|
||||||
/// lane.
|
/// lane.
|
||||||
fn verify_message(
|
fn verify_message(
|
||||||
@@ -85,7 +79,7 @@ pub trait LaneMessageVerifier<SenderOrigin, Payload> {
|
|||||||
lane: &LaneId,
|
lane: &LaneId,
|
||||||
outbound_data: &OutboundLaneData,
|
outbound_data: &OutboundLaneData,
|
||||||
payload: &Payload,
|
payload: &Payload,
|
||||||
) -> Result<(), Self::Error>;
|
) -> Result<(), VerificationError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manages payments that are happening at the source chain during delivery confirmation
|
/// Manages payments that are happening at the source chain during delivery confirmation
|
||||||
@@ -169,31 +163,27 @@ const ALL_OUTBOUND_MESSAGES_REJECTED: &str =
|
|||||||
"This chain is configured to reject all outbound messages";
|
"This chain is configured to reject all outbound messages";
|
||||||
|
|
||||||
impl<Payload, AccountId> TargetHeaderChain<Payload, AccountId> for ForbidOutboundMessages {
|
impl<Payload, AccountId> TargetHeaderChain<Payload, AccountId> for ForbidOutboundMessages {
|
||||||
type Error = &'static str;
|
|
||||||
|
|
||||||
type MessagesDeliveryProof = ();
|
type MessagesDeliveryProof = ();
|
||||||
|
|
||||||
fn verify_message(_payload: &Payload) -> Result<(), Self::Error> {
|
fn verify_message(_payload: &Payload) -> Result<(), VerificationError> {
|
||||||
Err(ALL_OUTBOUND_MESSAGES_REJECTED)
|
Err(VerificationError::Other(ALL_OUTBOUND_MESSAGES_REJECTED))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_messages_delivery_proof(
|
fn verify_messages_delivery_proof(
|
||||||
_proof: Self::MessagesDeliveryProof,
|
_proof: Self::MessagesDeliveryProof,
|
||||||
) -> Result<(LaneId, InboundLaneData<AccountId>), Self::Error> {
|
) -> Result<(LaneId, InboundLaneData<AccountId>), VerificationError> {
|
||||||
Err(ALL_OUTBOUND_MESSAGES_REJECTED)
|
Err(VerificationError::Other(ALL_OUTBOUND_MESSAGES_REJECTED))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SenderOrigin, Payload> LaneMessageVerifier<SenderOrigin, Payload> for ForbidOutboundMessages {
|
impl<SenderOrigin, Payload> LaneMessageVerifier<SenderOrigin, Payload> for ForbidOutboundMessages {
|
||||||
type Error = &'static str;
|
|
||||||
|
|
||||||
fn verify_message(
|
fn verify_message(
|
||||||
_submitter: &SenderOrigin,
|
_submitter: &SenderOrigin,
|
||||||
_lane: &LaneId,
|
_lane: &LaneId,
|
||||||
_outbound_data: &OutboundLaneData,
|
_outbound_data: &OutboundLaneData,
|
||||||
_payload: &Payload,
|
_payload: &Payload,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), VerificationError> {
|
||||||
Err(ALL_OUTBOUND_MESSAGES_REJECTED)
|
Err(VerificationError::Other(ALL_OUTBOUND_MESSAGES_REJECTED))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
//! Primitives of messages module, that are used on the target chain.
|
//! Primitives of messages module, that are used on the target chain.
|
||||||
|
|
||||||
use crate::{LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData};
|
use crate::{
|
||||||
|
LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, VerificationError,
|
||||||
|
};
|
||||||
|
|
||||||
use bp_runtime::{messages::MessageDispatchResult, Size};
|
use bp_runtime::{messages::MessageDispatchResult, Size};
|
||||||
use codec::{Decode, Encode, Error as CodecError};
|
use codec::{Decode, Encode, Error as CodecError};
|
||||||
@@ -58,9 +60,6 @@ pub struct DispatchMessage<DispatchPayload> {
|
|||||||
/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane
|
/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane
|
||||||
/// that's stuck) and/or processing messages without paying fees.
|
/// that's stuck) and/or processing messages without paying fees.
|
||||||
pub trait SourceHeaderChain {
|
pub trait SourceHeaderChain {
|
||||||
/// Error type.
|
|
||||||
type Error: Debug;
|
|
||||||
|
|
||||||
/// Proof that messages are sent from source chain. This may also include proof
|
/// Proof that messages are sent from source chain. This may also include proof
|
||||||
/// of corresponding outbound lane states.
|
/// of corresponding outbound lane states.
|
||||||
type MessagesProof: Parameter + Size;
|
type MessagesProof: Parameter + Size;
|
||||||
@@ -79,7 +78,7 @@ pub trait SourceHeaderChain {
|
|||||||
fn verify_messages_proof(
|
fn verify_messages_proof(
|
||||||
proof: Self::MessagesProof,
|
proof: Self::MessagesProof,
|
||||||
messages_count: u32,
|
messages_count: u32,
|
||||||
) -> Result<ProvedMessages<Message>, Self::Error>;
|
) -> Result<ProvedMessages<Message>, VerificationError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when inbound message is received.
|
/// Called when inbound message is received.
|
||||||
@@ -164,21 +163,20 @@ pub struct ForbidInboundMessages<MessagesProof, DispatchPayload>(
|
|||||||
PhantomData<(MessagesProof, DispatchPayload)>,
|
PhantomData<(MessagesProof, DispatchPayload)>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Error message that is used in `ForbidOutboundMessages` implementation.
|
/// Error message that is used in `ForbidInboundMessages` implementation.
|
||||||
const ALL_INBOUND_MESSAGES_REJECTED: &str =
|
const ALL_INBOUND_MESSAGES_REJECTED: &str =
|
||||||
"This chain is configured to reject all inbound messages";
|
"This chain is configured to reject all inbound messages";
|
||||||
|
|
||||||
impl<MessagesProof: Parameter + Size, DispatchPayload> SourceHeaderChain
|
impl<MessagesProof: Parameter + Size, DispatchPayload> SourceHeaderChain
|
||||||
for ForbidInboundMessages<MessagesProof, DispatchPayload>
|
for ForbidInboundMessages<MessagesProof, DispatchPayload>
|
||||||
{
|
{
|
||||||
type Error = &'static str;
|
|
||||||
type MessagesProof = MessagesProof;
|
type MessagesProof = MessagesProof;
|
||||||
|
|
||||||
fn verify_messages_proof(
|
fn verify_messages_proof(
|
||||||
_proof: Self::MessagesProof,
|
_proof: Self::MessagesProof,
|
||||||
_messages_count: u32,
|
_messages_count: u32,
|
||||||
) -> Result<ProvedMessages<Message>, Self::Error> {
|
) -> Result<ProvedMessages<Message>, VerificationError> {
|
||||||
Err(ALL_INBOUND_MESSAGES_REJECTED)
|
Err(VerificationError::Other(ALL_INBOUND_MESSAGES_REJECTED))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user