mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 14:31:02 +00:00
Some error improvements (#1956)
* Use `HeaderChainError` in parachains module * Use MessageProofError instead of 'static str in some places * Avoid implementing Into<'static str> for some errors We avoid deriving `Debug` for the structs that we use in the runtime and we derive `RuntimeDebug` instead in order to avoid bloating th eruntime with static strs. But implementing `Into<'static str>` does the same. So in some places it makes sense to replace `Into<'static str>` with `Debug`. * Move the messages error definition Move the messages error definition outside of `mod target`
This commit is contained in:
committed by
Bastian Köcher
parent
a4a6902bfb
commit
9b44db0fbe
@@ -100,6 +100,29 @@ pub type OriginOf<C> = <C as ThisChainWithMessages>::RuntimeOrigin;
|
|||||||
/// Type of call that is used on this chain.
|
/// Type of call that is used on this chain.
|
||||||
pub type CallOf<C> = <C as ThisChainWithMessages>::RuntimeCall;
|
pub type CallOf<C> = <C as ThisChainWithMessages>::RuntimeCall;
|
||||||
|
|
||||||
|
/// 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,8 +192,6 @@ pub mod source {
|
|||||||
/// The error message returned from `LaneMessageVerifier` when too many pending messages at the
|
/// The error message returned from `LaneMessageVerifier` when too many pending messages at the
|
||||||
/// lane.
|
/// lane.
|
||||||
pub const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane.";
|
pub const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane.";
|
||||||
/// The error message returned from `LaneMessageVerifier` when call origin is mismatch.
|
|
||||||
pub const BAD_ORIGIN: &str = "Unable to match the source origin to expected target origin.";
|
|
||||||
|
|
||||||
impl<B> LaneMessageVerifier<OriginOf<ThisChain<B>>, FromThisChainMessagePayload>
|
impl<B> LaneMessageVerifier<OriginOf<ThisChain<B>>, FromThisChainMessagePayload>
|
||||||
for FromThisChainMessageVerifier<B>
|
for FromThisChainMessageVerifier<B>
|
||||||
@@ -220,7 +241,7 @@ 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 = &'static str;
|
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<(), Self::Error> {
|
||||||
@@ -241,9 +262,9 @@ 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<(), &'static str> {
|
) -> Result<(), Error> {
|
||||||
if !BridgedChain::<B>::verify_dispatch_weight(payload) {
|
if !BridgedChain::<B>::verify_dispatch_weight(payload) {
|
||||||
return Err("Incorrect message weight declared")
|
return Err(Error::InvalidMessageWeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The maximal size of extrinsic at Substrate-based chain depends on the
|
// The maximal size of extrinsic at Substrate-based chain depends on the
|
||||||
@@ -257,7 +278,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("The message is too large to be sent over the lane")
|
return Err(Error::MessageTooLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -269,7 +290,7 @@ 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>, &'static str> {
|
) -> Result<ParsedMessagesDeliveryProofFromBridgedChain<B>, Error> {
|
||||||
let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } =
|
let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } =
|
||||||
proof;
|
proof;
|
||||||
B::BridgedHeaderChain::parse_finalized_storage_proof(
|
B::BridgedHeaderChain::parse_finalized_storage_proof(
|
||||||
@@ -283,22 +304,17 @@ pub mod source {
|
|||||||
B::BRIDGED_MESSAGES_PALLET_NAME,
|
B::BRIDGED_MESSAGES_PALLET_NAME,
|
||||||
&lane,
|
&lane,
|
||||||
);
|
);
|
||||||
let raw_inbound_lane_data = storage
|
let inbound_lane_data = storage
|
||||||
.read_value(storage_inbound_lane_data_key.0.as_ref())
|
.read_and_decode_mandatory_value(storage_inbound_lane_data_key.0.as_ref())
|
||||||
.map_err(|_| "Failed to read inbound lane state from storage proof")?
|
.map_err(Error::InboundLaneStorage)?;
|
||||||
.ok_or("Inbound lane state is missing from the messages proof")?;
|
|
||||||
let inbound_lane_data = InboundLaneData::decode(&mut &raw_inbound_lane_data[..])
|
|
||||||
.map_err(|_| "Failed to decode inbound lane state from the proof")?;
|
|
||||||
|
|
||||||
// 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
|
storage.ensure_no_unused_nodes().map_err(Error::StorageProof)?;
|
||||||
.ensure_no_unused_nodes()
|
|
||||||
.map_err(|_| "Messages delivery proof has unused trie nodes")?;
|
|
||||||
|
|
||||||
Ok((lane, inbound_lane_data))
|
Ok((lane, inbound_lane_data))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.map_err(<&'static str>::from)?
|
.map_err(Error::HeaderChain)?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// XCM bridge.
|
/// XCM bridge.
|
||||||
@@ -580,14 +596,14 @@ 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 = &'static str;
|
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>, Self::Error> {
|
||||||
verify_messages_proof::<B>(proof, messages_count).map_err(Into::into)
|
verify_messages_proof::<B>(proof, messages_count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,7 +618,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>, MessageProofError> {
|
) -> Result<ProvedMessages<Message>, Error> {
|
||||||
let FromBridgedChainMessagesProof {
|
let FromBridgedChainMessagesProof {
|
||||||
bridged_header_hash,
|
bridged_header_hash,
|
||||||
storage_proof,
|
storage_proof,
|
||||||
@@ -625,7 +641,7 @@ pub mod target {
|
|||||||
// (this bounds maximal capacity of messages vec below)
|
// (this bounds maximal capacity of messages vec below)
|
||||||
let messages_in_the_proof = nonces_difference.saturating_add(1);
|
let messages_in_the_proof = nonces_difference.saturating_add(1);
|
||||||
if messages_in_the_proof != MessageNonce::from(messages_count) {
|
if messages_in_the_proof != MessageNonce::from(messages_count) {
|
||||||
return Err(MessageProofError::MessagesCountMismatch)
|
return Err(Error::MessagesCountMismatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
messages_in_the_proof
|
messages_in_the_proof
|
||||||
@@ -640,37 +656,26 @@ pub mod target {
|
|||||||
let mut messages = Vec::with_capacity(messages_in_the_proof as _);
|
let mut messages = Vec::with_capacity(messages_in_the_proof as _);
|
||||||
for nonce in nonces_start..=nonces_end {
|
for nonce in nonces_start..=nonces_end {
|
||||||
let message_key = MessageKey { lane_id: lane, nonce };
|
let message_key = MessageKey { lane_id: lane, nonce };
|
||||||
let raw_message_data = parser
|
let message_payload = parser.read_and_decode_message_payload(&message_key)?;
|
||||||
.read_raw_message(&message_key)
|
messages.push(Message { key: message_key, payload: message_payload });
|
||||||
.ok_or(MessageProofError::MissingRequiredMessage)?;
|
|
||||||
let payload = MessagePayload::decode(&mut &raw_message_data[..])
|
|
||||||
.map_err(|_| MessageProofError::FailedToDecodeMessage)?;
|
|
||||||
messages.push(Message { key: message_key, payload });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now let's check if proof contains outbound lane state proof. It is optional, so
|
// Now let's check if proof contains outbound lane state proof. It is optional, so
|
||||||
// we simply ignore `read_value` errors and missing value.
|
// we simply ignore `read_value` errors and missing value.
|
||||||
let mut proved_lane_messages = ProvedLaneMessages { lane_state: None, messages };
|
let proved_lane_messages = ProvedLaneMessages {
|
||||||
let raw_outbound_lane_data = parser.read_raw_outbound_lane_data(&lane);
|
lane_state: parser.read_and_decode_outbound_lane_data(&lane)?,
|
||||||
if let Some(raw_outbound_lane_data) = raw_outbound_lane_data {
|
messages,
|
||||||
proved_lane_messages.lane_state = Some(
|
};
|
||||||
OutboundLaneData::decode(&mut &raw_outbound_lane_data[..])
|
|
||||||
.map_err(|_| MessageProofError::FailedToDecodeOutboundLaneState)?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we may actually check if the proof is empty or not.
|
// Now we may actually check if the proof is empty or not.
|
||||||
if proved_lane_messages.lane_state.is_none() &&
|
if proved_lane_messages.lane_state.is_none() &&
|
||||||
proved_lane_messages.messages.is_empty()
|
proved_lane_messages.messages.is_empty()
|
||||||
{
|
{
|
||||||
return Err(MessageProofError::Empty)
|
return Err(Error::EmptyMessageProof)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that the storage proof doesn't have any untouched trie nodes
|
// check that the storage proof doesn't have any untouched trie nodes
|
||||||
parser
|
parser.storage.ensure_no_unused_nodes().map_err(Error::StorageProof)?;
|
||||||
.storage
|
|
||||||
.ensure_no_unused_nodes()
|
|
||||||
.map_err(MessageProofError::StorageProof)?;
|
|
||||||
|
|
||||||
// We only support single lane messages in this generated_schema
|
// We only support single lane messages in this generated_schema
|
||||||
let mut proved_messages = ProvedMessages::new();
|
let mut proved_messages = ProvedMessages::new();
|
||||||
@@ -679,43 +684,7 @@ pub mod target {
|
|||||||
Ok(proved_messages)
|
Ok(proved_messages)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.map_err(MessageProofError::HeaderChain)?
|
.map_err(Error::HeaderChain)?
|
||||||
}
|
|
||||||
|
|
||||||
/// Error that happens during message proof verification.
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum MessageProofError {
|
|
||||||
/// Error returned by the bridged header chain.
|
|
||||||
HeaderChain(HeaderChainError),
|
|
||||||
/// The message proof is empty.
|
|
||||||
Empty,
|
|
||||||
/// Declared messages count doesn't match actual value.
|
|
||||||
MessagesCountMismatch,
|
|
||||||
/// Message is missing from the proof.
|
|
||||||
MissingRequiredMessage,
|
|
||||||
/// Failed to decode message from the proof.
|
|
||||||
FailedToDecodeMessage,
|
|
||||||
/// Failed to decode outbound lane data from the proof.
|
|
||||||
FailedToDecodeOutboundLaneState,
|
|
||||||
/// Storage proof related error.
|
|
||||||
StorageProof(StorageProofError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<MessageProofError> for &'static str {
|
|
||||||
fn from(err: MessageProofError) -> &'static str {
|
|
||||||
match err {
|
|
||||||
MessageProofError::HeaderChain(err) => err.into(),
|
|
||||||
MessageProofError::Empty => "Messages proof is empty",
|
|
||||||
MessageProofError::MessagesCountMismatch =>
|
|
||||||
"Declared messages count doesn't match actual value",
|
|
||||||
MessageProofError::MissingRequiredMessage => "Message is missing from the proof",
|
|
||||||
MessageProofError::FailedToDecodeMessage =>
|
|
||||||
"Failed to decode message from the proof",
|
|
||||||
MessageProofError::FailedToDecodeOutboundLaneState =>
|
|
||||||
"Failed to decode outbound lane data from the proof",
|
|
||||||
MessageProofError::StorageProof(_) => "Invalid storage proof",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StorageProofCheckerAdapter<H: Hasher, B> {
|
struct StorageProofCheckerAdapter<H: Hasher, B> {
|
||||||
@@ -724,21 +693,32 @@ pub mod target {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<H: Hasher, B: MessageBridge> StorageProofCheckerAdapter<H, B> {
|
impl<H: Hasher, B: MessageBridge> StorageProofCheckerAdapter<H, B> {
|
||||||
fn read_raw_outbound_lane_data(&mut self, lane_id: &LaneId) -> Option<Vec<u8>> {
|
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(
|
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,
|
||||||
);
|
);
|
||||||
self.storage.read_value(storage_outbound_lane_data_key.0.as_ref()).ok()?
|
|
||||||
|
self.storage
|
||||||
|
.read_and_decode_opt_value(storage_outbound_lane_data_key.0.as_ref())
|
||||||
|
.map_err(Error::OutboundLaneStorage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_raw_message(&mut self, message_key: &MessageKey) -> Option<Vec<u8>> {
|
fn read_and_decode_message_payload(
|
||||||
|
&mut self,
|
||||||
|
message_key: &MessageKey,
|
||||||
|
) -> Result<MessagePayload, Error> {
|
||||||
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,
|
||||||
message_key.nonce,
|
message_key.nonce,
|
||||||
);
|
);
|
||||||
self.storage.read_value(storage_message_key.0.as_ref()).ok()?
|
self.storage
|
||||||
|
.read_and_decode_mandatory_value(storage_message_key.0.as_ref())
|
||||||
|
.map_err(Error::MessageStorage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -896,7 +876,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(target::MessageProofError::MessagesCountMismatch),
|
Err(Error::MessagesCountMismatch),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,7 +886,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(target::MessageProofError::MessagesCountMismatch),
|
Err(Error::MessagesCountMismatch),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -919,7 +899,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(target::MessageProofError::HeaderChain(HeaderChainError::UnknownHeader)),
|
Err(Error::HeaderChain(HeaderChainError::UnknownHeader)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -942,7 +922,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
target::verify_messages_proof::<OnThisChainBridge>(proof, 10)
|
||||||
}),
|
}),
|
||||||
Err(target::MessageProofError::HeaderChain(HeaderChainError::StorageProof(
|
Err(Error::HeaderChain(HeaderChainError::StorageProof(
|
||||||
StorageProofError::StorageRootMismatch
|
StorageProofError::StorageRootMismatch
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
@@ -957,7 +937,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(target::MessageProofError::HeaderChain(HeaderChainError::StorageProof(
|
Err(Error::HeaderChain(HeaderChainError::StorageProof(
|
||||||
StorageProofError::DuplicateNodesInProof
|
StorageProofError::DuplicateNodesInProof
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
@@ -970,13 +950,13 @@ 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(target::MessageProofError::StorageProof(StorageProofError::UnusedNodesInTheProof)),
|
Err(Error::StorageProof(StorageProofError::UnusedNodesInTheProof)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn message_proof_is_rejected_if_required_message_is_missing() {
|
fn message_proof_is_rejected_if_required_message_is_missing() {
|
||||||
assert_eq!(
|
matches!(
|
||||||
using_messages_proof(
|
using_messages_proof(
|
||||||
10,
|
10,
|
||||||
None,
|
None,
|
||||||
@@ -984,13 +964,13 @@ 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(target::MessageProofError::MissingRequiredMessage),
|
Err(Error::MessageStorage(StorageProofError::StorageValueEmpty)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn message_proof_is_rejected_if_message_decode_fails() {
|
fn message_proof_is_rejected_if_message_decode_fails() {
|
||||||
assert_eq!(
|
matches!(
|
||||||
using_messages_proof(
|
using_messages_proof(
|
||||||
10,
|
10,
|
||||||
None,
|
None,
|
||||||
@@ -1004,13 +984,13 @@ 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(target::MessageProofError::FailedToDecodeMessage),
|
Err(Error::MessageStorage(StorageProofError::StorageValueDecodeFailed(_))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() {
|
fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() {
|
||||||
assert_eq!(
|
matches!(
|
||||||
using_messages_proof(
|
using_messages_proof(
|
||||||
10,
|
10,
|
||||||
Some(OutboundLaneData {
|
Some(OutboundLaneData {
|
||||||
@@ -1026,7 +1006,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
|
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
|
||||||
),
|
),
|
||||||
Err(target::MessageProofError::FailedToDecodeOutboundLaneState),
|
Err(Error::OutboundLaneStorage(StorageProofError::StorageValueDecodeFailed(_))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1036,7 +1016,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(target::MessageProofError::Empty),
|
Err(Error::EmptyMessageProof),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1110,7 +1090,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(target::MessageProofError::MessagesCountMismatch),
|
Err(Error::MessagesCountMismatch),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
pub use weights::WeightInfo;
|
pub use weights::WeightInfo;
|
||||||
pub use weights_ext::WeightInfoExt;
|
pub use weights_ext::WeightInfoExt;
|
||||||
|
|
||||||
use bp_header_chain::HeaderChain;
|
use bp_header_chain::{HeaderChain, HeaderChainError};
|
||||||
use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo, ParaStoredHeaderData};
|
use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo, ParaStoredHeaderData};
|
||||||
use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
|
use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
|
||||||
use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain, StorageProofError};
|
use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain, StorageProofError};
|
||||||
@@ -125,8 +125,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,
|
||||||
/// Invalid storage proof has been passed.
|
/// Error generated by a method defined in `bp-header-chain`.
|
||||||
InvalidStorageProof,
|
HeaderChain(HeaderChainError),
|
||||||
/// Given parachain head is unknown.
|
/// Given parachain head is unknown.
|
||||||
UnknownParaHead,
|
UnknownParaHead,
|
||||||
/// The storage proof doesn't contains storage root. So it is invalid for given header.
|
/// The storage proof doesn't contains storage root. So it is invalid for given header.
|
||||||
@@ -430,10 +430,10 @@ pub mod pallet {
|
|||||||
storage.ensure_no_unused_nodes()
|
storage.ensure_no_unused_nodes()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.and_then(|r| r.map_err(bp_header_chain::HeaderChainError::StorageProof))
|
.and_then(|r| r.map_err(HeaderChainError::StorageProof))
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::trace!(target: LOG_TARGET, "Parachain heads storage proof is invalid: {:?}", e);
|
log::trace!(target: LOG_TARGET, "Parachain heads storage proof is invalid: {:?}", e);
|
||||||
Error::<T, I>::InvalidStorageProof
|
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 })
|
||||||
@@ -1379,7 +1379,9 @@ 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>::InvalidStorageProof
|
Error::<TestRuntime>::HeaderChain(HeaderChainError::StorageProof(
|
||||||
|
StorageProofError::StorageRootMismatch
|
||||||
|
))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use bp_runtime::{
|
|||||||
};
|
};
|
||||||
use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen};
|
use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen};
|
||||||
use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug};
|
use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug};
|
||||||
|
use frame_support::PalletError;
|
||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -36,7 +37,7 @@ pub mod justification;
|
|||||||
pub mod storage_keys;
|
pub mod storage_keys;
|
||||||
|
|
||||||
/// Header chain error.
|
/// Header chain error.
|
||||||
#[derive(Clone, Eq, PartialEq, RuntimeDebug)]
|
#[derive(Clone, Decode, Encode, Eq, PartialEq, PalletError, Debug, TypeInfo)]
|
||||||
pub enum HeaderChainError {
|
pub enum HeaderChainError {
|
||||||
/// Header with given hash is missing from the chain.
|
/// Header with given hash is missing from the chain.
|
||||||
UnknownHeader,
|
UnknownHeader,
|
||||||
@@ -44,15 +45,6 @@ pub enum HeaderChainError {
|
|||||||
StorageProof(StorageProofError),
|
StorageProof(StorageProofError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<HeaderChainError> for &'static str {
|
|
||||||
fn from(err: HeaderChainError) -> &'static str {
|
|
||||||
match err {
|
|
||||||
HeaderChainError::UnknownHeader => "UnknownHeader",
|
|
||||||
HeaderChainError::StorageProof(e) => e.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Header data that we're storing on-chain.
|
/// Header data that we're storing on-chain.
|
||||||
///
|
///
|
||||||
/// Even though we may store full header, our applications (XCM) only use couple of header
|
/// Even though we may store full header, our applications (XCM) only use couple of header
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub type RelayersRewards<AccountId> = BTreeMap<AccountId, MessageNonce>;
|
|||||||
/// type used by the source chain.
|
/// type used by the source chain.
|
||||||
pub trait TargetHeaderChain<Payload, AccountId> {
|
pub trait TargetHeaderChain<Payload, AccountId> {
|
||||||
/// Error type.
|
/// Error type.
|
||||||
type Error: Debug + Into<&'static str>;
|
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;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ pub struct DispatchMessage<DispatchPayload> {
|
|||||||
/// 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.
|
/// Error type.
|
||||||
type Error: Debug + Into<&'static str>;
|
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.
|
||||||
|
|||||||
@@ -531,6 +531,37 @@ macro_rules! generate_static_str_provider {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, TypeInfo)]
|
||||||
|
#[scale_info(skip_type_params(T))]
|
||||||
|
pub struct StrippableError<T> {
|
||||||
|
_phantom_data: sp_std::marker::PhantomData<T>,
|
||||||
|
#[codec(skip)]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> From<T> for StrippableError<T> {
|
||||||
|
fn from(err: T) -> Self {
|
||||||
|
Self {
|
||||||
|
_phantom_data: Default::default(),
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
message: format!("{:?}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Debug for StrippableError<T> {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
|
||||||
|
f.write_str(&self.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
|
||||||
|
f.write_str("Stripped error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
//! Logic for checking Substrate storage proofs.
|
//! Logic for checking Substrate storage proofs.
|
||||||
|
|
||||||
use codec::Decode;
|
use crate::StrippableError;
|
||||||
|
use codec::{Decode, Encode};
|
||||||
|
use frame_support::PalletError;
|
||||||
use hash_db::{HashDB, Hasher, EMPTY_PREFIX};
|
use hash_db::{HashDB, Hasher, EMPTY_PREFIX};
|
||||||
use sp_runtime::RuntimeDebug;
|
use scale_info::TypeInfo;
|
||||||
use sp_std::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec};
|
use sp_std::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec};
|
||||||
use sp_trie::{
|
use sp_trie::{
|
||||||
read_trie_value, LayoutV1, MemoryDB, Recorder, StorageProof, Trie, TrieConfiguration,
|
read_trie_value, LayoutV1, MemoryDB, Recorder, StorageProof, Trie, TrieConfiguration,
|
||||||
@@ -116,14 +118,32 @@ where
|
|||||||
/// read, but decoding fails, this function returns an error.
|
/// read, but decoding fails, this function returns an error.
|
||||||
pub fn read_and_decode_value<T: Decode>(&mut self, key: &[u8]) -> Result<Option<T>, Error> {
|
pub fn read_and_decode_value<T: Decode>(&mut self, key: &[u8]) -> Result<Option<T>, Error> {
|
||||||
self.read_value(key).and_then(|v| {
|
self.read_value(key).and_then(|v| {
|
||||||
v.map(|v| T::decode(&mut &v[..]).map_err(Error::StorageValueDecodeFailed))
|
v.map(|v| T::decode(&mut &v[..]).map_err(|e| Error::StorageValueDecodeFailed(e.into())))
|
||||||
.transpose()
|
.transpose()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads and decodes a value from the available subset of storage. If the value cannot be read
|
||||||
|
/// due to an incomplete or otherwise invalid proof, or if the value is `None`, this function
|
||||||
|
/// returns an error. If value is read, but decoding fails, this function returns an error.
|
||||||
|
pub fn read_and_decode_mandatory_value<T: Decode>(&mut self, key: &[u8]) -> Result<T, Error> {
|
||||||
|
self.read_and_decode_value(key)?.ok_or(Error::StorageValueEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads and decodes a value from the available subset of storage. If the value cannot be read
|
||||||
|
/// due to an incomplete or otherwise invalid proof, this function returns `Ok(None)`.
|
||||||
|
/// If value is read, but decoding fails, this function returns an error.
|
||||||
|
pub fn read_and_decode_opt_value<T: Decode>(&mut self, key: &[u8]) -> Result<Option<T>, Error> {
|
||||||
|
match self.read_and_decode_value(key) {
|
||||||
|
Ok(outbound_lane_data) => Ok(outbound_lane_data),
|
||||||
|
Err(Error::StorageValueUnavailable) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Storage proof related errors.
|
/// Storage proof related errors.
|
||||||
#[derive(Clone, Eq, PartialEq, RuntimeDebug)]
|
#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, Debug, TypeInfo)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Duplicate trie nodes are found in the proof.
|
/// Duplicate trie nodes are found in the proof.
|
||||||
DuplicateNodesInProof,
|
DuplicateNodesInProof,
|
||||||
@@ -133,21 +153,10 @@ pub enum Error {
|
|||||||
StorageRootMismatch,
|
StorageRootMismatch,
|
||||||
/// Unable to reach expected storage value using provided trie nodes.
|
/// Unable to reach expected storage value using provided trie nodes.
|
||||||
StorageValueUnavailable,
|
StorageValueUnavailable,
|
||||||
|
/// The storage value is `None`.
|
||||||
|
StorageValueEmpty,
|
||||||
/// Failed to decode storage value.
|
/// Failed to decode storage value.
|
||||||
StorageValueDecodeFailed(codec::Error),
|
StorageValueDecodeFailed(StrippableError<codec::Error>),
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Error> for &'static str {
|
|
||||||
fn from(err: Error) -> &'static str {
|
|
||||||
match err {
|
|
||||||
Error::DuplicateNodesInProof => "Storage proof contains duplicate nodes",
|
|
||||||
Error::UnusedNodesInTheProof => "Storage proof contains unused nodes",
|
|
||||||
Error::StorageRootMismatch => "Storage root is missing from the storage proof",
|
|
||||||
Error::StorageValueUnavailable => "Storage value is missing from the storage proof",
|
|
||||||
Error::StorageValueDecodeFailed(_) =>
|
|
||||||
"Failed to decode storage value from the storage proof",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return valid storage proof and state root.
|
/// Return valid storage proof and state root.
|
||||||
@@ -155,7 +164,6 @@ impl From<Error> for &'static str {
|
|||||||
/// NOTE: This should only be used for **testing**.
|
/// NOTE: This should only be used for **testing**.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub fn craft_valid_storage_proof() -> (sp_core::H256, RawStorageProof) {
|
pub fn craft_valid_storage_proof() -> (sp_core::H256, RawStorageProof) {
|
||||||
use codec::Encode;
|
|
||||||
use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend};
|
use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend};
|
||||||
|
|
||||||
let state_version = sp_runtime::StateVersion::default();
|
let state_version = sp_runtime::StateVersion::default();
|
||||||
|
|||||||
Reference in New Issue
Block a user