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:
Serban Iorga
2023-03-09 12:56:07 +02:00
committed by Bastian Köcher
parent a4a6902bfb
commit 9b44db0fbe
7 changed files with 145 additions and 132 deletions
+75 -95
View File
@@ -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),
); );
} }
} }
+8 -6
View File
@@ -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
))
); );
}); });
} }
+2 -10
View File
@@ -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.
+31
View File
@@ -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::*;
+27 -19
View File
@@ -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();