mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-15 03:21:06 +00:00
Fees, weights, message delivery and dispatch (#339)
* introduce BridgedHeaderChain trait * LaneMessageVerifier + tests * fixed tests * do not expose intenal functions * cargo fmt --all + fix no_std compilation * ByWeightDispatcher * process queued messages from message-lane::on_initialize * scheduled_messages_are_processed_from_on_initialize * flush * deal with fees + weights * drop heavy messages on dispatch * cargo fmt * clippy * fix comment * Update primitives/message-lane/src/source_chain.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * removed messages_processed * Update primitives/message-lane/src/source_chain.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * Update modules/message-lane/src/lib.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * remove queueing from message-lane * also remove queueing from RPCs * remove by-weight traces * dispatch fee * receiving -> delivery * receival -> delivery * remove extra line * Update primitives/message-lane/src/source_chain.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * cargo fmt --all * clippy * let dispatch_weight to be larger than actual_dispatch_weight * post-merge fix Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
This commit is contained in:
committed by
Bastian Köcher
parent
44beb30836
commit
3cd8937b38
@@ -46,10 +46,6 @@ use sp_std::{marker::PhantomData, prelude::*};
|
|||||||
/// Spec version type.
|
/// Spec version type.
|
||||||
pub type SpecVersion = u32;
|
pub type SpecVersion = u32;
|
||||||
|
|
||||||
// TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78)
|
|
||||||
/// Weight of single deposit_event() call.
|
|
||||||
const DEPOSIT_EVENT_WEIGHT: Weight = 0;
|
|
||||||
|
|
||||||
/// Origin of the call on the target chain.
|
/// Origin of the call on the target chain.
|
||||||
#[derive(RuntimeDebug, Encode, Decode, Clone)]
|
#[derive(RuntimeDebug, Encode, Decode, Clone)]
|
||||||
pub enum CallOrigin<SourceChainAccountPublic, TargetChainAccountPublic, TargetChainSignature> {
|
pub enum CallOrigin<SourceChainAccountPublic, TargetChainAccountPublic, TargetChainSignature> {
|
||||||
@@ -149,7 +145,11 @@ impl<T: Trait<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
|
|||||||
<T as Trait<I>>::Call,
|
<T as Trait<I>>::Call,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
fn dispatch(bridge: InstanceId, id: T::MessageId, message: Self::Message) -> Weight {
|
fn dispatch_weight(message: &Self::Message) -> Weight {
|
||||||
|
message.weight
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch(bridge: InstanceId, id: T::MessageId, message: Self::Message) {
|
||||||
// verify spec version
|
// verify spec version
|
||||||
// (we want it to be the same, because otherwise we may decode Call improperly)
|
// (we want it to be the same, because otherwise we may decode Call improperly)
|
||||||
let expected_version = <T as frame_system::Trait>::Version::get().spec_version;
|
let expected_version = <T as frame_system::Trait>::Version::get().spec_version;
|
||||||
@@ -167,7 +167,7 @@ impl<T: Trait<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
|
|||||||
expected_version,
|
expected_version,
|
||||||
message.spec_version,
|
message.spec_version,
|
||||||
));
|
));
|
||||||
return DEPOSIT_EVENT_WEIGHT;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify weight
|
// verify weight
|
||||||
@@ -189,7 +189,7 @@ impl<T: Trait<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
|
|||||||
expected_weight,
|
expected_weight,
|
||||||
message.weight,
|
message.weight,
|
||||||
));
|
));
|
||||||
return DEPOSIT_EVENT_WEIGHT;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare dispatch origin
|
// prepare dispatch origin
|
||||||
@@ -210,7 +210,7 @@ impl<T: Trait<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
|
|||||||
target_signature,
|
target_signature,
|
||||||
);
|
);
|
||||||
Self::deposit_event(RawEvent::MessageSignatureMismatch(bridge, id));
|
Self::deposit_event(RawEvent::MessageSignatureMismatch(bridge, id));
|
||||||
return DEPOSIT_EVENT_WEIGHT;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
target_account
|
target_account
|
||||||
@@ -222,9 +222,11 @@ impl<T: Trait<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
|
|||||||
let dispatch_result = message.call.dispatch(origin);
|
let dispatch_result = message.call.dispatch(origin);
|
||||||
let actual_call_weight = extract_actual_weight(&dispatch_result, &dispatch_info);
|
let actual_call_weight = extract_actual_weight(&dispatch_result, &dispatch_info);
|
||||||
frame_support::debug::trace!(
|
frame_support::debug::trace!(
|
||||||
"Message {:?}/{:?} has been dispatched. Result: {:?}",
|
"Message {:?}/{:?} has been dispatched. Weight: {} of {}. Result: {:?}",
|
||||||
bridge,
|
bridge,
|
||||||
id,
|
id,
|
||||||
|
actual_call_weight,
|
||||||
|
message.weight,
|
||||||
dispatch_result,
|
dispatch_result,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -233,8 +235,6 @@ impl<T: Trait<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
|
|||||||
id,
|
id,
|
||||||
dispatch_result.map(drop).map_err(|e| e.error),
|
dispatch_result.map(drop).map_err(|e| e.error),
|
||||||
));
|
));
|
||||||
|
|
||||||
actual_call_weight + DEPOSIT_EVENT_WEIGHT
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ edition = "2018"
|
|||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false }
|
||||||
bp-message-lane = { path = "../../primitives/message-lane", default-features = false }
|
bp-message-lane = { path = "../../primitives/message-lane", default-features = false }
|
||||||
|
bp-runtime = { path = "../../primitives/runtime", default-features = false }
|
||||||
codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false }
|
codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false }
|
||||||
|
|
||||||
# Substrate Based Dependencies
|
# Substrate Based Dependencies
|
||||||
@@ -48,9 +50,11 @@ git = "https://github.com/paritytech/substrate/"
|
|||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = [
|
std = [
|
||||||
|
"bp-message-dispatch/std",
|
||||||
"bp-message-lane/std",
|
"bp-message-lane/std",
|
||||||
|
"bp-runtime/std",
|
||||||
"codec/std",
|
"codec/std",
|
||||||
"frame-support/std",
|
"frame-support/std",
|
||||||
"frame-system/std",
|
"frame-system/std",
|
||||||
"sp-std/std"
|
"sp-std/std",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub type MessagesProof = Bytes;
|
|||||||
|
|
||||||
/// Trie-based storage proof that the message(s) with given key(s) have been received by the bridged chain.
|
/// Trie-based storage proof that the message(s) with given key(s) have been received by the bridged chain.
|
||||||
/// SCALE-encoded trie nodes array `Vec<Vec<u8>>`.
|
/// SCALE-encoded trie nodes array `Vec<Vec<u8>>`.
|
||||||
pub type MessagesReceivingProof = Bytes;
|
pub type MessagesDeliveryProof = Bytes;
|
||||||
|
|
||||||
/// Runtime adapter.
|
/// Runtime adapter.
|
||||||
pub trait Runtime: Send + Sync + 'static {
|
pub trait Runtime: Send + Sync + 'static {
|
||||||
@@ -63,14 +63,14 @@ pub trait MessageLaneApi<BlockHash> {
|
|||||||
block: Option<BlockHash>,
|
block: Option<BlockHash>,
|
||||||
) -> FutureResult<MessagesProof>;
|
) -> FutureResult<MessagesProof>;
|
||||||
|
|
||||||
/// Returns proof-of-message(s) receiving.
|
/// Returns proof-of-message(s) delivery.
|
||||||
#[rpc(name = "messageLane_proveMessagesReceiving")]
|
#[rpc(name = "messageLane_proveMessagesDelivery")]
|
||||||
fn prove_messages_receiving(
|
fn prove_messages_delivery(
|
||||||
&self,
|
&self,
|
||||||
instance: InstanceId,
|
instance: InstanceId,
|
||||||
lane: LaneId,
|
lane: LaneId,
|
||||||
block: Option<BlockHash>,
|
block: Option<BlockHash>,
|
||||||
) -> FutureResult<MessagesReceivingProof>;
|
) -> FutureResult<MessagesDeliveryProof>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements the MessageLaneApi trait for interacting with message lanes.
|
/// Implements the MessageLaneApi trait for interacting with message lanes.
|
||||||
@@ -119,12 +119,12 @@ where
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prove_messages_receiving(
|
fn prove_messages_delivery(
|
||||||
&self,
|
&self,
|
||||||
instance: InstanceId,
|
instance: InstanceId,
|
||||||
lane: LaneId,
|
lane: LaneId,
|
||||||
block: Option<Block::Hash>,
|
block: Option<Block::Hash>,
|
||||||
) -> FutureResult<MessagesReceivingProof> {
|
) -> FutureResult<MessagesDeliveryProof> {
|
||||||
Box::new(
|
Box::new(
|
||||||
prove_keys_read(
|
prove_keys_read(
|
||||||
self.backend.clone(),
|
self.backend.clone(),
|
||||||
|
|||||||
@@ -16,12 +16,16 @@
|
|||||||
|
|
||||||
//! Everything about incoming messages receival.
|
//! Everything about incoming messages receival.
|
||||||
|
|
||||||
use bp_message_lane::{InboundLaneData, LaneId, Message, MessageKey, MessageNonce, OnMessageReceived};
|
use bp_message_lane::{
|
||||||
|
target_chain::MessageDispatch, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce,
|
||||||
|
};
|
||||||
|
|
||||||
/// Inbound lane storage.
|
/// Inbound lane storage.
|
||||||
pub trait InboundLaneStorage {
|
pub trait InboundLaneStorage {
|
||||||
/// Message payload.
|
/// Message payload.
|
||||||
type Payload;
|
type Payload;
|
||||||
|
/// Delivery and dispatch fee type on source chain.
|
||||||
|
type MessageFee;
|
||||||
|
|
||||||
/// Lane id.
|
/// Lane id.
|
||||||
fn id(&self) -> LaneId;
|
fn id(&self) -> LaneId;
|
||||||
@@ -43,10 +47,10 @@ impl<S: InboundLaneStorage> InboundLane<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Receive new message.
|
/// Receive new message.
|
||||||
pub fn receive_message<P: OnMessageReceived<S::Payload>>(
|
pub fn receive_message<P: MessageDispatch<S::Payload, S::MessageFee>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
nonce: MessageNonce,
|
nonce: MessageNonce,
|
||||||
payload: S::Payload,
|
message_data: MessageData<S::Payload, S::MessageFee>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut data = self.storage.data();
|
let mut data = self.storage.data();
|
||||||
let is_correct_message = nonce == data.latest_received_nonce + 1;
|
let is_correct_message = nonce == data.latest_received_nonce + 1;
|
||||||
@@ -57,12 +61,12 @@ impl<S: InboundLaneStorage> InboundLane<S> {
|
|||||||
data.latest_received_nonce = nonce;
|
data.latest_received_nonce = nonce;
|
||||||
self.storage.set_data(data);
|
self.storage.set_data(data);
|
||||||
|
|
||||||
P::on_message_received(Message {
|
P::dispatch(Message {
|
||||||
key: MessageKey {
|
key: MessageKey {
|
||||||
lane_id: self.storage.id(),
|
lane_id: self.storage.id(),
|
||||||
nonce,
|
nonce,
|
||||||
},
|
},
|
||||||
payload,
|
data: message_data,
|
||||||
});
|
});
|
||||||
|
|
||||||
true
|
true
|
||||||
@@ -74,14 +78,14 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
inbound_lane,
|
inbound_lane,
|
||||||
mock::{run_test, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID},
|
mock::{message_data, run_test, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fails_to_receive_message_with_incorrect_nonce() {
|
fn fails_to_receive_message_with_incorrect_nonce() {
|
||||||
run_test(|| {
|
run_test(|| {
|
||||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||||
assert!(!lane.receive_message::<()>(10, REGULAR_PAYLOAD));
|
assert!(!lane.receive_message::<TestMessageDispatch>(10, message_data(REGULAR_PAYLOAD)));
|
||||||
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -90,7 +94,7 @@ mod tests {
|
|||||||
fn correct_message_is_processed_instantly() {
|
fn correct_message_is_processed_instantly() {
|
||||||
run_test(|| {
|
run_test(|| {
|
||||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||||
assert!(lane.receive_message::<()>(1, REGULAR_PAYLOAD));
|
assert!(lane.receive_message::<TestMessageDispatch>(1, message_data(REGULAR_PAYLOAD)));
|
||||||
assert_eq!(lane.storage.data().latest_received_nonce, 1);
|
assert_eq!(lane.storage.data().latest_received_nonce, 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,11 @@
|
|||||||
//! 3) the messages are stored in the storage;
|
//! 3) the messages are stored in the storage;
|
||||||
//! 4) external component (relay) delivers messages to bridged chain;
|
//! 4) external component (relay) delivers messages to bridged chain;
|
||||||
//! 5) messages are processed in order (ordered by assigned nonce);
|
//! 5) messages are processed in order (ordered by assigned nonce);
|
||||||
//! 6) relay may send proof-of-receiving and proof-of-processing back to this chain.
|
//! 6) relay may send proof-of-delivery back to this chain.
|
||||||
//!
|
//!
|
||||||
//! Once message is sent, its progress can be tracked by looking at module events.
|
//! Once message is sent, its progress can be tracked by looking at module events.
|
||||||
//! The assigned nonce is reported using `MessageAccepted` event. When message is
|
//! The assigned nonce is reported using `MessageAccepted` event. When message is
|
||||||
//! accepted by the bridged chain, `MessagesDelivered` is fired. When message is
|
//! delivered to the the bridged chain, it is reported using `MessagesDelivered` event.
|
||||||
//! processedby the bridged chain, `MessagesProcessed` by the bridged chain.
|
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
@@ -34,9 +33,14 @@ use crate::inbound_lane::{InboundLane, InboundLaneStorage};
|
|||||||
use crate::outbound_lane::{OutboundLane, OutboundLaneStorage};
|
use crate::outbound_lane::{OutboundLane, OutboundLaneStorage};
|
||||||
|
|
||||||
use bp_message_lane::{
|
use bp_message_lane::{
|
||||||
InboundLaneData, LaneId, Message, MessageKey, MessageNonce, OnMessageReceived, OutboundLaneData,
|
source_chain::{LaneMessageVerifier, MessageDeliveryAndDispatchPayment, TargetHeaderChain},
|
||||||
|
target_chain::{MessageDispatch, SourceHeaderChain},
|
||||||
|
InboundLaneData, LaneId, MessageData, MessageKey, MessageNonce, OutboundLaneData,
|
||||||
|
};
|
||||||
|
use frame_support::{
|
||||||
|
decl_error, decl_event, decl_module, decl_storage, sp_runtime::DispatchResult, traits::Get, weights::Weight,
|
||||||
|
Parameter, StorageMap,
|
||||||
};
|
};
|
||||||
use frame_support::{decl_event, decl_module, decl_storage, traits::Get, Parameter, StorageMap};
|
|
||||||
use frame_system::ensure_signed;
|
use frame_system::ensure_signed;
|
||||||
use sp_std::{marker::PhantomData, prelude::*};
|
use sp_std::{marker::PhantomData, prelude::*};
|
||||||
|
|
||||||
@@ -46,8 +50,14 @@ mod outbound_lane;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod mock;
|
mod mock;
|
||||||
|
|
||||||
|
// TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78)
|
||||||
|
/// Upper bound of delivery transaction weight.
|
||||||
|
const DELIVERY_BASE_WEIGHT: Weight = 0;
|
||||||
|
|
||||||
/// The module configuration trait
|
/// The module configuration trait
|
||||||
pub trait Trait<I = DefaultInstance>: frame_system::Trait {
|
pub trait Trait<I = DefaultInstance>: frame_system::Trait {
|
||||||
|
// General types
|
||||||
|
|
||||||
/// They overarching event type.
|
/// They overarching event type.
|
||||||
type Event: From<Event<Self, I>> + Into<<Self as frame_system::Trait>::Event>;
|
type Event: From<Event<Self, I>> + Into<<Self as frame_system::Trait>::Event>;
|
||||||
/// Message payload.
|
/// Message payload.
|
||||||
@@ -57,8 +67,50 @@ pub trait Trait<I = DefaultInstance>: frame_system::Trait {
|
|||||||
/// confirmed. The reason is that if you want to use lane, you should be ready to pay
|
/// confirmed. The reason is that if you want to use lane, you should be ready to pay
|
||||||
/// for it.
|
/// for it.
|
||||||
type MaxMessagesToPruneAtOnce: Get<MessageNonce>;
|
type MaxMessagesToPruneAtOnce: Get<MessageNonce>;
|
||||||
/// Called when message has been received.
|
|
||||||
type OnMessageReceived: OnMessageReceived<Self::Payload>;
|
// Types that are used by outbound_lane (on source chain).
|
||||||
|
|
||||||
|
/// Type of delivery_and_dispatch_fee on source chain.
|
||||||
|
type MessageFee: Parameter;
|
||||||
|
/// Target header chain.
|
||||||
|
type TargetHeaderChain: TargetHeaderChain<Self::Payload>;
|
||||||
|
/// Message payload verifier.
|
||||||
|
type LaneMessageVerifier: LaneMessageVerifier<Self::AccountId, Self::Payload, Self::MessageFee>;
|
||||||
|
/// Message delivery payment.
|
||||||
|
type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment<Self::AccountId, Self::MessageFee>;
|
||||||
|
|
||||||
|
// Types that are used by inbound_lane (on target chain).
|
||||||
|
|
||||||
|
/// Source header chain, as it is represented on target chain.
|
||||||
|
type SourceHeaderChain: SourceHeaderChain<Self::Payload, Self::MessageFee>;
|
||||||
|
/// Message dispatch.
|
||||||
|
type MessageDispatch: MessageDispatch<Self::Payload, Self::MessageFee>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to messages proof type for Trait.
|
||||||
|
type MessagesProofOf<T, I> = <<T as Trait<I>>::SourceHeaderChain as SourceHeaderChain<
|
||||||
|
<T as Trait<I>>::Payload,
|
||||||
|
<T as Trait<I>>::MessageFee,
|
||||||
|
>>::MessagesProof;
|
||||||
|
/// Shortcut to messages delivery proof type for Trait.
|
||||||
|
type MessagesDeliveryProofOf<T, I> =
|
||||||
|
<<T as Trait<I>>::TargetHeaderChain as TargetHeaderChain<<T as Trait<I>>::Payload>>::MessagesDeliveryProof;
|
||||||
|
|
||||||
|
decl_error! {
|
||||||
|
pub enum Error for Module<T: Trait<I>, I: Instance> {
|
||||||
|
/// Message has been treated as invalid by chain verifier.
|
||||||
|
MessageRejectedByChainVerifier,
|
||||||
|
/// Message has been treated as invalid by lane verifier.
|
||||||
|
MessageRejectedByLaneVerifier,
|
||||||
|
/// Submitter has failed to pay fee for delivering and dispatching messages.
|
||||||
|
FailedToWithdrawMessageFee,
|
||||||
|
/// Invalid messages has been submitted.
|
||||||
|
InvalidMessagesProof,
|
||||||
|
/// Invalid messages dispatch weight has been declared by the relayer.
|
||||||
|
InvalidMessagesDispatchWeight,
|
||||||
|
/// Invalid messages delivery proof has been submitted.
|
||||||
|
InvalidMessagesDeliveryProof,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decl_storage! {
|
decl_storage! {
|
||||||
@@ -68,7 +120,7 @@ decl_storage! {
|
|||||||
/// Map of lane id => outbound lane data.
|
/// Map of lane id => outbound lane data.
|
||||||
OutboundLanes: map hasher(blake2_128_concat) LaneId => OutboundLaneData;
|
OutboundLanes: map hasher(blake2_128_concat) LaneId => OutboundLaneData;
|
||||||
/// All queued outbound messages.
|
/// All queued outbound messages.
|
||||||
OutboundMessages: map hasher(blake2_128_concat) MessageKey => Option<T::Payload>;
|
OutboundMessages: map hasher(blake2_128_concat) MessageKey => Option<MessageData<T::Payload, T::MessageFee>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,59 +148,160 @@ decl_module! {
|
|||||||
origin,
|
origin,
|
||||||
lane_id: LaneId,
|
lane_id: LaneId,
|
||||||
payload: T::Payload,
|
payload: T::Payload,
|
||||||
) {
|
delivery_and_dispatch_fee: T::MessageFee,
|
||||||
let _ = ensure_signed(origin)?;
|
) -> DispatchResult {
|
||||||
|
let submitter = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
// let's first check if message can be delivered to target chain
|
||||||
|
T::TargetHeaderChain::verify_message(&payload).map_err(|err| {
|
||||||
|
frame_support::debug::trace!(
|
||||||
|
target: "runtime",
|
||||||
|
"Message to lane {:?} is rejected by target chain: {:?}",
|
||||||
|
lane_id,
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
|
||||||
|
Error::<T, I>::MessageRejectedByChainVerifier
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// now let's enforce any additional lane rules
|
||||||
|
T::LaneMessageVerifier::verify_message(
|
||||||
|
&submitter,
|
||||||
|
&delivery_and_dispatch_fee,
|
||||||
|
&lane_id,
|
||||||
|
&payload,
|
||||||
|
).map_err(|err| {
|
||||||
|
frame_support::debug::trace!(
|
||||||
|
target: "runtime",
|
||||||
|
"Message to lane {:?} is rejected by lane verifier: {:?}",
|
||||||
|
lane_id,
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
|
||||||
|
Error::<T, I>::MessageRejectedByLaneVerifier
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// let's withdraw delivery and dispatch fee from submitter
|
||||||
|
T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
|
||||||
|
&submitter,
|
||||||
|
&delivery_and_dispatch_fee,
|
||||||
|
).map_err(|err| {
|
||||||
|
frame_support::debug::trace!(
|
||||||
|
target: "runtime",
|
||||||
|
"Message to lane {:?} is rejected because submitter {:?} is unable to pay fee {:?}: {:?}",
|
||||||
|
lane_id,
|
||||||
|
submitter,
|
||||||
|
delivery_and_dispatch_fee,
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
|
||||||
|
Error::<T, I>::FailedToWithdrawMessageFee
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// finally, save message in outbound storage and emit event
|
||||||
let mut lane = outbound_lane::<T, I>(lane_id);
|
let mut lane = outbound_lane::<T, I>(lane_id);
|
||||||
let nonce = lane.send_message(payload);
|
let nonce = lane.send_message(MessageData {
|
||||||
|
payload,
|
||||||
|
fee: delivery_and_dispatch_fee,
|
||||||
|
});
|
||||||
lane.prune_messages(T::MaxMessagesToPruneAtOnce::get());
|
lane.prune_messages(T::MaxMessagesToPruneAtOnce::get());
|
||||||
|
|
||||||
|
frame_support::debug::trace!(
|
||||||
|
target: "runtime",
|
||||||
|
"Accepted message {} to lane {:?}",
|
||||||
|
nonce,
|
||||||
|
lane_id,
|
||||||
|
);
|
||||||
|
|
||||||
Self::deposit_event(RawEvent::MessageAccepted(lane_id, nonce));
|
Self::deposit_event(RawEvent::MessageAccepted(lane_id, nonce));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Trait<I>, I: Instance> Module<T, I> {
|
/// Receive messages proof from bridged chain.
|
||||||
// =========================================================================================
|
#[weight = DELIVERY_BASE_WEIGHT + dispatch_weight]
|
||||||
// === Exposed mutables ====================================================================
|
pub fn receive_messages_proof(
|
||||||
// =========================================================================================
|
origin,
|
||||||
|
proof: MessagesProofOf<T, I>,
|
||||||
|
dispatch_weight: Weight,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let _ = ensure_signed(origin)?;
|
||||||
|
|
||||||
/// Receive new TRUSTED lane messages.
|
// verify messages proof && convert proof into messages
|
||||||
///
|
let messages = T::SourceHeaderChain::verify_messages_proof(proof).map_err(|err| {
|
||||||
/// Trusted here means that the function itself doesn't check whether message has actually
|
frame_support::debug::trace!(
|
||||||
/// been sent through the other end of the channel. We only check that we are receiving
|
target: "runtime",
|
||||||
/// and processing messages in order here.
|
"Rejecting invalid messages proof: {:?}",
|
||||||
///
|
err,
|
||||||
/// Messages vector is required to be sorted by nonce within each lane. Otherise messages
|
);
|
||||||
/// will be rejected.
|
|
||||||
pub fn receive_messages(messages: Vec<Message<T::Payload>>) -> MessageNonce {
|
Error::<T, I>::InvalidMessagesProof
|
||||||
let mut correct_messages = 0;
|
})?;
|
||||||
for message in messages {
|
|
||||||
let mut lane = inbound_lane::<T, I>(message.key.lane_id);
|
// verify that relayer is paying actual dispatch weight
|
||||||
if lane.receive_message::<T::OnMessageReceived>(message.key.nonce, message.payload) {
|
let actual_dispatch_weight: Weight = messages
|
||||||
correct_messages += 1;
|
.iter()
|
||||||
|
.map(T::MessageDispatch::dispatch_weight)
|
||||||
|
.sum();
|
||||||
|
if dispatch_weight < actual_dispatch_weight {
|
||||||
|
frame_support::debug::trace!(
|
||||||
|
target: "runtime",
|
||||||
|
"Rejecting messages proof because of dispatch weight mismatch: declared={}, expected={}",
|
||||||
|
dispatch_weight,
|
||||||
|
actual_dispatch_weight,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(Error::<T, I>::InvalidMessagesDispatchWeight.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dispatch messages
|
||||||
|
let total_messages = messages.len();
|
||||||
|
let mut valid_messages = 0;
|
||||||
|
for message in messages {
|
||||||
|
let mut lane = inbound_lane::<T, I>(message.key.lane_id);
|
||||||
|
if lane.receive_message::<T::MessageDispatch>(message.key.nonce, message.data) {
|
||||||
|
valid_messages += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_support::debug::trace!(
|
||||||
|
target: "runtime",
|
||||||
|
"Received messages: total={}, valid={}",
|
||||||
|
total_messages,
|
||||||
|
valid_messages,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
correct_messages
|
/// Receive messages delivery proof from bridged chain.
|
||||||
}
|
#[weight = 0] // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78)
|
||||||
|
pub fn receive_messages_delivery_proof(origin, proof: MessagesDeliveryProofOf<T, I>) -> DispatchResult {
|
||||||
|
let _ = ensure_signed(origin)?;
|
||||||
|
let (lane_id, nonce) = T::TargetHeaderChain::verify_messages_delivery_proof(proof).map_err(|err| {
|
||||||
|
frame_support::debug::trace!(
|
||||||
|
target: "runtime",
|
||||||
|
"Rejecting invalid messages delivery proof: {:?}",
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
|
||||||
/// Receive TRUSTED proof of message receival.
|
Error::<T, I>::InvalidMessagesDeliveryProof
|
||||||
///
|
})?;
|
||||||
/// Trusted here means that the function itself doesn't check whether the bridged chain has
|
|
||||||
/// actually received these messages.
|
|
||||||
///
|
|
||||||
/// The caller may break the channel by providing `latest_received_nonce` that is larger
|
|
||||||
/// than actual one. Not-yet-sent messages may be pruned in this case.
|
|
||||||
pub fn confirm_receival(lane_id: &LaneId, latest_received_nonce: MessageNonce) {
|
|
||||||
let mut lane = outbound_lane::<T, I>(*lane_id);
|
|
||||||
let received_range = lane.confirm_receival(latest_received_nonce);
|
|
||||||
|
|
||||||
if let Some(received_range) = received_range {
|
let mut lane = outbound_lane::<T, I>(lane_id);
|
||||||
Self::deposit_event(RawEvent::MessagesDelivered(
|
let received_range = lane.confirm_delivery(nonce);
|
||||||
*lane_id,
|
if let Some(received_range) = received_range {
|
||||||
received_range.0,
|
Self::deposit_event(RawEvent::MessagesDelivered(lane_id, received_range.0, received_range.1));
|
||||||
received_range.1,
|
}
|
||||||
));
|
|
||||||
|
frame_support::debug::trace!(
|
||||||
|
target: "runtime",
|
||||||
|
"Received messages delivery proof up to (and including) {} at lane {:?}",
|
||||||
|
nonce,
|
||||||
|
lane_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,6 +330,7 @@ struct RuntimeInboundLaneStorage<T, I = DefaultInstance> {
|
|||||||
|
|
||||||
impl<T: Trait<I>, I: Instance> InboundLaneStorage for RuntimeInboundLaneStorage<T, I> {
|
impl<T: Trait<I>, I: Instance> InboundLaneStorage for RuntimeInboundLaneStorage<T, I> {
|
||||||
type Payload = T::Payload;
|
type Payload = T::Payload;
|
||||||
|
type MessageFee = T::MessageFee;
|
||||||
|
|
||||||
fn id(&self) -> LaneId {
|
fn id(&self) -> LaneId {
|
||||||
self.lane_id
|
self.lane_id
|
||||||
@@ -199,6 +353,7 @@ struct RuntimeOutboundLaneStorage<T, I = DefaultInstance> {
|
|||||||
|
|
||||||
impl<T: Trait<I>, I: Instance> OutboundLaneStorage for RuntimeOutboundLaneStorage<T, I> {
|
impl<T: Trait<I>, I: Instance> OutboundLaneStorage for RuntimeOutboundLaneStorage<T, I> {
|
||||||
type Payload = T::Payload;
|
type Payload = T::Payload;
|
||||||
|
type MessageFee = T::MessageFee;
|
||||||
|
|
||||||
fn id(&self) -> LaneId {
|
fn id(&self) -> LaneId {
|
||||||
self.lane_id
|
self.lane_id
|
||||||
@@ -213,20 +368,20 @@ impl<T: Trait<I>, I: Instance> OutboundLaneStorage for RuntimeOutboundLaneStorag
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn message(&self, nonce: &MessageNonce) -> Option<Self::Payload> {
|
fn message(&self, nonce: &MessageNonce) -> Option<MessageData<T::Payload, T::MessageFee>> {
|
||||||
OutboundMessages::<T, I>::get(MessageKey {
|
OutboundMessages::<T, I>::get(MessageKey {
|
||||||
lane_id: self.lane_id,
|
lane_id: self.lane_id,
|
||||||
nonce: *nonce,
|
nonce: *nonce,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_message(&mut self, nonce: MessageNonce, payload: T::Payload) {
|
fn save_message(&mut self, nonce: MessageNonce, mesage_data: MessageData<T::Payload, T::MessageFee>) {
|
||||||
OutboundMessages::<T, I>::insert(
|
OutboundMessages::<T, I>::insert(
|
||||||
MessageKey {
|
MessageKey {
|
||||||
lane_id: self.lane_id,
|
lane_id: self.lane_id,
|
||||||
nonce,
|
nonce,
|
||||||
},
|
},
|
||||||
payload,
|
mesage_data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,3 +392,190 @@ impl<T: Trait<I>, I: Instance> OutboundLaneStorage for RuntimeOutboundLaneStorag
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::mock::{
|
||||||
|
run_test, Origin, TestEvent, TestMessageDeliveryAndDispatchPayment, TestRuntime,
|
||||||
|
PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID,
|
||||||
|
};
|
||||||
|
use bp_message_lane::Message;
|
||||||
|
use frame_support::{assert_noop, assert_ok};
|
||||||
|
use frame_system::{EventRecord, Module as System, Phase};
|
||||||
|
|
||||||
|
fn send_regular_message() {
|
||||||
|
System::<TestRuntime>::set_block_number(1);
|
||||||
|
System::<TestRuntime>::reset_events();
|
||||||
|
|
||||||
|
assert_ok!(Module::<TestRuntime>::send_message(
|
||||||
|
Origin::signed(1),
|
||||||
|
TEST_LANE_ID,
|
||||||
|
REGULAR_PAYLOAD,
|
||||||
|
REGULAR_PAYLOAD.1,
|
||||||
|
));
|
||||||
|
|
||||||
|
// check event with assigned nonce
|
||||||
|
assert_eq!(
|
||||||
|
System::<TestRuntime>::events(),
|
||||||
|
vec![EventRecord {
|
||||||
|
phase: Phase::Initialization,
|
||||||
|
event: TestEvent::message_lane(RawEvent::MessageAccepted(TEST_LANE_ID, 1)),
|
||||||
|
topics: vec![],
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
|
||||||
|
// check that fee has been withdrawn from submitter
|
||||||
|
assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(1, REGULAR_PAYLOAD.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_messages_delivery_proof() {
|
||||||
|
System::<TestRuntime>::set_block_number(1);
|
||||||
|
System::<TestRuntime>::reset_events();
|
||||||
|
|
||||||
|
assert_ok!(Module::<TestRuntime>::receive_messages_delivery_proof(
|
||||||
|
Origin::signed(1),
|
||||||
|
Ok((TEST_LANE_ID, 1)),
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
System::<TestRuntime>::events(),
|
||||||
|
vec![EventRecord {
|
||||||
|
phase: Phase::Initialization,
|
||||||
|
event: TestEvent::message_lane(RawEvent::MessagesDelivered(TEST_LANE_ID, 1, 1)),
|
||||||
|
topics: vec![],
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn send_message_works() {
|
||||||
|
run_test(|| {
|
||||||
|
send_regular_message();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn chain_verifier_rejects_invalid_message_in_send_message() {
|
||||||
|
run_test(|| {
|
||||||
|
// messages with this payload are rejected by target chain verifier
|
||||||
|
assert_noop!(
|
||||||
|
Module::<TestRuntime>::send_message(
|
||||||
|
Origin::signed(1),
|
||||||
|
TEST_LANE_ID,
|
||||||
|
PAYLOAD_REJECTED_BY_TARGET_CHAIN,
|
||||||
|
PAYLOAD_REJECTED_BY_TARGET_CHAIN.1
|
||||||
|
),
|
||||||
|
Error::<TestRuntime, DefaultInstance>::MessageRejectedByChainVerifier,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lane_verifier_rejects_invalid_message_in_send_message() {
|
||||||
|
run_test(|| {
|
||||||
|
// messages with zero fee are rejected by lane verifier
|
||||||
|
assert_noop!(
|
||||||
|
Module::<TestRuntime>::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 0),
|
||||||
|
Error::<TestRuntime, DefaultInstance>::MessageRejectedByLaneVerifier,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn message_send_fails_if_submitter_cant_pay_message_fee() {
|
||||||
|
run_test(|| {
|
||||||
|
TestMessageDeliveryAndDispatchPayment::reject_payments();
|
||||||
|
assert_noop!(
|
||||||
|
Module::<TestRuntime>::send_message(
|
||||||
|
Origin::signed(1),
|
||||||
|
TEST_LANE_ID,
|
||||||
|
REGULAR_PAYLOAD,
|
||||||
|
REGULAR_PAYLOAD.1
|
||||||
|
),
|
||||||
|
Error::<TestRuntime, DefaultInstance>::FailedToWithdrawMessageFee,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn receive_messages_proof_works() {
|
||||||
|
run_test(|| {
|
||||||
|
assert_ok!(Module::<TestRuntime>::receive_messages_proof(
|
||||||
|
Origin::signed(1),
|
||||||
|
Ok(vec![Message {
|
||||||
|
key: MessageKey {
|
||||||
|
lane_id: TEST_LANE_ID,
|
||||||
|
nonce: 1,
|
||||||
|
},
|
||||||
|
data: MessageData {
|
||||||
|
payload: REGULAR_PAYLOAD,
|
||||||
|
fee: 0,
|
||||||
|
},
|
||||||
|
}]),
|
||||||
|
REGULAR_PAYLOAD.1,
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
InboundLanes::<DefaultInstance>::get(TEST_LANE_ID).latest_received_nonce,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn receive_messages_proof_rejects_invalid_dispatch_weight() {
|
||||||
|
run_test(|| {
|
||||||
|
assert_noop!(
|
||||||
|
Module::<TestRuntime>::receive_messages_proof(
|
||||||
|
Origin::signed(1),
|
||||||
|
Ok(vec![Message {
|
||||||
|
key: MessageKey {
|
||||||
|
lane_id: TEST_LANE_ID,
|
||||||
|
nonce: 1,
|
||||||
|
},
|
||||||
|
data: MessageData {
|
||||||
|
payload: REGULAR_PAYLOAD,
|
||||||
|
fee: 0,
|
||||||
|
},
|
||||||
|
}]),
|
||||||
|
REGULAR_PAYLOAD.1 - 1,
|
||||||
|
),
|
||||||
|
Error::<TestRuntime, DefaultInstance>::InvalidMessagesDispatchWeight,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn receive_messages_proof_rejects_invalid_proof() {
|
||||||
|
run_test(|| {
|
||||||
|
assert_noop!(
|
||||||
|
Module::<TestRuntime, DefaultInstance>::receive_messages_proof(Origin::signed(1), Err(()), 0),
|
||||||
|
Error::<TestRuntime, DefaultInstance>::InvalidMessagesProof,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn receive_messages_delivery_proof_works() {
|
||||||
|
run_test(|| {
|
||||||
|
send_regular_message();
|
||||||
|
receive_messages_delivery_proof();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
OutboundLanes::<DefaultInstance>::get(&TEST_LANE_ID).latest_received_nonce,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn receive_messages_delivery_proof_rejects_invalid_proof() {
|
||||||
|
run_test(|| {
|
||||||
|
assert_noop!(
|
||||||
|
Module::<TestRuntime>::receive_messages_delivery_proof(Origin::signed(1), Err(()),),
|
||||||
|
Error::<TestRuntime, DefaultInstance>::InvalidMessagesDeliveryProof,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,13 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use bp_message_lane::LaneId;
|
use crate::Trait;
|
||||||
|
|
||||||
|
use bp_message_lane::{
|
||||||
|
source_chain::{LaneMessageVerifier, MessageDeliveryAndDispatchPayment, TargetHeaderChain},
|
||||||
|
target_chain::{MessageDispatch, SourceHeaderChain},
|
||||||
|
LaneId, Message, MessageData, MessageNonce,
|
||||||
|
};
|
||||||
use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight};
|
use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight};
|
||||||
use sp_core::H256;
|
use sp_core::H256;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
@@ -23,10 +29,9 @@ use sp_runtime::{
|
|||||||
Perbill,
|
Perbill,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Trait;
|
|
||||||
|
|
||||||
pub type AccountId = u64;
|
pub type AccountId = u64;
|
||||||
pub type TestPayload = u64;
|
pub type TestPayload = (u64, Weight);
|
||||||
|
pub type TestMessageFee = u64;
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
pub struct TestRuntime;
|
pub struct TestRuntime;
|
||||||
@@ -69,7 +74,7 @@ impl frame_system::Trait for TestRuntime {
|
|||||||
type DbWeight = ();
|
type DbWeight = ();
|
||||||
type BlockExecutionWeight = ();
|
type BlockExecutionWeight = ();
|
||||||
type ExtrinsicBaseWeight = ();
|
type ExtrinsicBaseWeight = ();
|
||||||
type MaximumExtrinsicWeight = ();
|
type MaximumExtrinsicWeight = MaximumBlockWeight;
|
||||||
type AvailableBlockRatio = AvailableBlockRatio;
|
type AvailableBlockRatio = AvailableBlockRatio;
|
||||||
type MaximumBlockLength = MaximumBlockLength;
|
type MaximumBlockLength = MaximumBlockLength;
|
||||||
type Version = ();
|
type Version = ();
|
||||||
@@ -89,16 +94,140 @@ impl Trait for TestRuntime {
|
|||||||
type Event = TestEvent;
|
type Event = TestEvent;
|
||||||
type Payload = TestPayload;
|
type Payload = TestPayload;
|
||||||
type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce;
|
type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce;
|
||||||
type OnMessageReceived = ();
|
|
||||||
|
type MessageFee = TestMessageFee;
|
||||||
|
type TargetHeaderChain = TestTargetHeaderChain;
|
||||||
|
type LaneMessageVerifier = TestLaneMessageVerifier;
|
||||||
|
type MessageDeliveryAndDispatchPayment = TestMessageDeliveryAndDispatchPayment;
|
||||||
|
|
||||||
|
type SourceHeaderChain = TestSourceHeaderChain;
|
||||||
|
type MessageDispatch = TestMessageDispatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error that is returned by all test implementations.
|
||||||
|
pub const TEST_ERROR: &str = "Test error";
|
||||||
|
|
||||||
/// Lane that we're using in tests.
|
/// Lane that we're using in tests.
|
||||||
pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1];
|
pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1];
|
||||||
|
|
||||||
/// Regular message payload.
|
/// Regular message payload.
|
||||||
pub const REGULAR_PAYLOAD: TestPayload = 0;
|
pub const REGULAR_PAYLOAD: TestPayload = (0, 50);
|
||||||
|
|
||||||
|
/// Payload that is rejected by `TestTargetHeaderChain`.
|
||||||
|
pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = (1, 50);
|
||||||
|
|
||||||
|
/// Target header chain that is used in tests.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TestTargetHeaderChain;
|
||||||
|
|
||||||
|
impl TargetHeaderChain<TestPayload> for TestTargetHeaderChain {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
type MessagesDeliveryProof = Result<(LaneId, MessageNonce), ()>;
|
||||||
|
|
||||||
|
fn verify_message(payload: &TestPayload) -> Result<(), Self::Error> {
|
||||||
|
if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN {
|
||||||
|
Err(TEST_ERROR)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_messages_delivery_proof(
|
||||||
|
proof: Self::MessagesDeliveryProof,
|
||||||
|
) -> Result<(LaneId, MessageNonce), Self::Error> {
|
||||||
|
proof.map_err(|_| TEST_ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lane message verifier that is used in tests.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TestLaneMessageVerifier;
|
||||||
|
|
||||||
|
impl LaneMessageVerifier<AccountId, TestPayload, TestMessageFee> for TestLaneMessageVerifier {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn verify_message(
|
||||||
|
_submitter: &AccountId,
|
||||||
|
delivery_and_dispatch_fee: &TestMessageFee,
|
||||||
|
_lane: &LaneId,
|
||||||
|
_payload: &TestPayload,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
if *delivery_and_dispatch_fee != 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(TEST_ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message fee payment system that is used in tests.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TestMessageDeliveryAndDispatchPayment;
|
||||||
|
|
||||||
|
impl TestMessageDeliveryAndDispatchPayment {
|
||||||
|
/// Reject all payments.
|
||||||
|
pub fn reject_payments() {
|
||||||
|
frame_support::storage::unhashed::put(b":reject-message-fee:", &true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if given fee has been paid by given relayer.
|
||||||
|
pub fn is_fee_paid(submitter: AccountId, fee: TestMessageFee) -> bool {
|
||||||
|
frame_support::storage::unhashed::get(b":message-fee:") == Some((submitter, fee))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageDeliveryAndDispatchPayment<AccountId, TestMessageFee> for TestMessageDeliveryAndDispatchPayment {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn pay_delivery_and_dispatch_fee(submitter: &AccountId, fee: &TestMessageFee) -> Result<(), Self::Error> {
|
||||||
|
if frame_support::storage::unhashed::get(b":reject-message-fee:") == Some(true) {
|
||||||
|
return Err(TEST_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_support::storage::unhashed::put(b":message-fee:", &(submitter, fee));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Source header chain that is used in tests.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TestSourceHeaderChain;
|
||||||
|
|
||||||
|
impl SourceHeaderChain<TestPayload, TestMessageFee> for TestSourceHeaderChain {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
type MessagesProof = Result<Vec<Message<TestPayload, TestMessageFee>>, ()>;
|
||||||
|
|
||||||
|
fn verify_messages_proof(
|
||||||
|
proof: Self::MessagesProof,
|
||||||
|
) -> Result<Vec<Message<TestPayload, TestMessageFee>>, Self::Error> {
|
||||||
|
proof.map_err(|_| TEST_ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Source header chain that is used in tests.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TestMessageDispatch;
|
||||||
|
|
||||||
|
impl MessageDispatch<TestPayload, TestMessageFee> for TestMessageDispatch {
|
||||||
|
fn dispatch_weight(message: &Message<TestPayload, TestMessageFee>) -> Weight {
|
||||||
|
message.data.payload.1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch(_message: Message<TestPayload, TestMessageFee>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return message data with valid fee for given payload.
|
||||||
|
pub fn message_data(payload: TestPayload) -> MessageData<TestPayload, TestMessageFee> {
|
||||||
|
MessageData { payload, fee: 1 }
|
||||||
|
}
|
||||||
|
|
||||||
/// Run message lane test.
|
/// Run message lane test.
|
||||||
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
|
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
|
||||||
sp_io::TestExternalities::new(Default::default()).execute_with(test)
|
let t = frame_system::GenesisConfig::default()
|
||||||
|
.build_storage::<TestRuntime>()
|
||||||
|
.unwrap();
|
||||||
|
let mut ext = sp_io::TestExternalities::new(t);
|
||||||
|
ext.execute_with(test)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
//! Everything about outgoing messages sending.
|
//! Everything about outgoing messages sending.
|
||||||
|
|
||||||
use bp_message_lane::{LaneId, MessageNonce, OutboundLaneData};
|
use bp_message_lane::{LaneId, MessageData, MessageNonce, OutboundLaneData};
|
||||||
|
|
||||||
/// Outbound lane storage.
|
/// Outbound lane storage.
|
||||||
pub trait OutboundLaneStorage {
|
pub trait OutboundLaneStorage {
|
||||||
/// Message payload.
|
/// Message payload.
|
||||||
type Payload;
|
type Payload;
|
||||||
|
/// Delivery and dispatch fee type on source chain.
|
||||||
|
type MessageFee;
|
||||||
|
|
||||||
/// Lane id.
|
/// Lane id.
|
||||||
fn id(&self) -> LaneId;
|
fn id(&self) -> LaneId;
|
||||||
@@ -31,9 +33,9 @@ pub trait OutboundLaneStorage {
|
|||||||
fn set_data(&mut self, data: OutboundLaneData);
|
fn set_data(&mut self, data: OutboundLaneData);
|
||||||
/// Returns saved outbound message payload.
|
/// Returns saved outbound message payload.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn message(&self, nonce: &MessageNonce) -> Option<Self::Payload>;
|
fn message(&self, nonce: &MessageNonce) -> Option<MessageData<Self::Payload, Self::MessageFee>>;
|
||||||
/// Save outbound message in the storage.
|
/// Save outbound message in the storage.
|
||||||
fn save_message(&mut self, nonce: MessageNonce, payload: Self::Payload);
|
fn save_message(&mut self, nonce: MessageNonce, message_data: MessageData<Self::Payload, Self::MessageFee>);
|
||||||
/// Remove outbound message from the storage.
|
/// Remove outbound message from the storage.
|
||||||
fn remove_message(&mut self, nonce: &MessageNonce);
|
fn remove_message(&mut self, nonce: &MessageNonce);
|
||||||
}
|
}
|
||||||
@@ -52,22 +54,22 @@ impl<S: OutboundLaneStorage> OutboundLane<S> {
|
|||||||
/// Send message over lane.
|
/// Send message over lane.
|
||||||
///
|
///
|
||||||
/// Returns new message nonce.
|
/// Returns new message nonce.
|
||||||
pub fn send_message(&mut self, payload: S::Payload) -> MessageNonce {
|
pub fn send_message(&mut self, message_data: MessageData<S::Payload, S::MessageFee>) -> MessageNonce {
|
||||||
let mut data = self.storage.data();
|
let mut data = self.storage.data();
|
||||||
let nonce = data.latest_generated_nonce + 1;
|
let nonce = data.latest_generated_nonce + 1;
|
||||||
data.latest_generated_nonce = nonce;
|
data.latest_generated_nonce = nonce;
|
||||||
|
|
||||||
self.storage.save_message(nonce, payload);
|
self.storage.save_message(nonce, message_data);
|
||||||
self.storage.set_data(data);
|
self.storage.set_data(data);
|
||||||
|
|
||||||
nonce
|
nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Confirm message receival.
|
/// Confirm messages delivery.
|
||||||
///
|
///
|
||||||
/// Returns `None` if confirmation is wrong/duplicate.
|
/// Returns `None` if confirmation is wrong/duplicate.
|
||||||
/// Returns `Some` with inclusive ranges of message nonces that have been received.
|
/// Returns `Some` with inclusive ranges of message nonces that have been received.
|
||||||
pub fn confirm_receival(&mut self, latest_received_nonce: MessageNonce) -> Option<(MessageNonce, MessageNonce)> {
|
pub fn confirm_delivery(&mut self, latest_received_nonce: MessageNonce) -> Option<(MessageNonce, MessageNonce)> {
|
||||||
let mut data = self.storage.data();
|
let mut data = self.storage.data();
|
||||||
if latest_received_nonce <= data.latest_received_nonce || latest_received_nonce > data.latest_generated_nonce {
|
if latest_received_nonce <= data.latest_received_nonce || latest_received_nonce > data.latest_generated_nonce {
|
||||||
return None;
|
return None;
|
||||||
@@ -107,7 +109,7 @@ impl<S: OutboundLaneStorage> OutboundLane<S> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
mock::{run_test, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID},
|
mock::{message_data, run_test, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID},
|
||||||
outbound_lane,
|
outbound_lane,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -116,57 +118,57 @@ mod tests {
|
|||||||
run_test(|| {
|
run_test(|| {
|
||||||
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||||
assert_eq!(lane.storage.data().latest_generated_nonce, 0);
|
assert_eq!(lane.storage.data().latest_generated_nonce, 0);
|
||||||
assert_eq!(lane.send_message(REGULAR_PAYLOAD), 1);
|
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 1);
|
||||||
assert!(lane.storage.message(&1).is_some());
|
assert!(lane.storage.message(&1).is_some());
|
||||||
assert_eq!(lane.storage.data().latest_generated_nonce, 1);
|
assert_eq!(lane.storage.data().latest_generated_nonce, 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn confirm_receival_works() {
|
fn confirm_delivery_works() {
|
||||||
run_test(|| {
|
run_test(|| {
|
||||||
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||||
assert_eq!(lane.send_message(REGULAR_PAYLOAD), 1);
|
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 1);
|
||||||
assert_eq!(lane.send_message(REGULAR_PAYLOAD), 2);
|
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 2);
|
||||||
assert_eq!(lane.send_message(REGULAR_PAYLOAD), 3);
|
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 3);
|
||||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||||
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
||||||
assert_eq!(lane.confirm_receival(3), Some((1, 3)));
|
assert_eq!(lane.confirm_delivery(3), Some((1, 3)));
|
||||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||||
assert_eq!(lane.storage.data().latest_received_nonce, 3);
|
assert_eq!(lane.storage.data().latest_received_nonce, 3);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn confirm_receival_rejects_nonce_lesser_than_latest_received() {
|
fn confirm_delivery_rejects_nonce_lesser_than_latest_received() {
|
||||||
run_test(|| {
|
run_test(|| {
|
||||||
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||||
lane.send_message(REGULAR_PAYLOAD);
|
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||||
lane.send_message(REGULAR_PAYLOAD);
|
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||||
lane.send_message(REGULAR_PAYLOAD);
|
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||||
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
||||||
assert_eq!(lane.confirm_receival(3), Some((1, 3)));
|
assert_eq!(lane.confirm_delivery(3), Some((1, 3)));
|
||||||
assert_eq!(lane.confirm_receival(3), None);
|
assert_eq!(lane.confirm_delivery(3), None);
|
||||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||||
assert_eq!(lane.storage.data().latest_received_nonce, 3);
|
assert_eq!(lane.storage.data().latest_received_nonce, 3);
|
||||||
|
|
||||||
assert_eq!(lane.confirm_receival(2), None);
|
assert_eq!(lane.confirm_delivery(2), None);
|
||||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||||
assert_eq!(lane.storage.data().latest_received_nonce, 3);
|
assert_eq!(lane.storage.data().latest_received_nonce, 3);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn confirm_receival_rejects_nonce_larger_than_last_generated() {
|
fn confirm_delivery_rejects_nonce_larger_than_last_generated() {
|
||||||
run_test(|| {
|
run_test(|| {
|
||||||
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||||
lane.send_message(REGULAR_PAYLOAD);
|
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||||
lane.send_message(REGULAR_PAYLOAD);
|
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||||
lane.send_message(REGULAR_PAYLOAD);
|
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||||
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
||||||
assert_eq!(lane.confirm_receival(10), None);
|
assert_eq!(lane.confirm_delivery(10), None);
|
||||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||||
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
||||||
});
|
});
|
||||||
@@ -180,17 +182,17 @@ mod tests {
|
|||||||
assert_eq!(lane.prune_messages(100), 0);
|
assert_eq!(lane.prune_messages(100), 0);
|
||||||
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
|
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
|
||||||
// when nothing is confirmed, nothing is pruned
|
// when nothing is confirmed, nothing is pruned
|
||||||
lane.send_message(REGULAR_PAYLOAD);
|
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||||
lane.send_message(REGULAR_PAYLOAD);
|
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||||
lane.send_message(REGULAR_PAYLOAD);
|
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||||
assert_eq!(lane.prune_messages(100), 0);
|
assert_eq!(lane.prune_messages(100), 0);
|
||||||
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
|
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
|
||||||
// after confirmation, some messages are received
|
// after confirmation, some messages are received
|
||||||
assert_eq!(lane.confirm_receival(2), Some((1, 2)));
|
assert_eq!(lane.confirm_delivery(2), Some((1, 2)));
|
||||||
assert_eq!(lane.prune_messages(100), 2);
|
assert_eq!(lane.prune_messages(100), 2);
|
||||||
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3);
|
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3);
|
||||||
// after last message is confirmed, everything is pruned
|
// after last message is confirmed, everything is pruned
|
||||||
assert_eq!(lane.confirm_receival(3), Some((3, 3)));
|
assert_eq!(lane.confirm_delivery(3), Some((3, 3)));
|
||||||
assert_eq!(lane.prune_messages(100), 1);
|
assert_eq!(lane.prune_messages(100), 1);
|
||||||
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4);
|
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ pub trait MessageDispatch<MessageId> {
|
|||||||
/// A type of the message to be dispatched.
|
/// A type of the message to be dispatched.
|
||||||
type Message: codec::Decode;
|
type Message: codec::Decode;
|
||||||
|
|
||||||
|
/// Estimate dispatch weight.
|
||||||
|
///
|
||||||
|
/// This function must: (1) be instant and (2) return correct upper bound
|
||||||
|
/// of dispatch weight.
|
||||||
|
fn dispatch_weight(message: &Self::Message) -> Weight;
|
||||||
|
|
||||||
/// Dispatches the message internally.
|
/// Dispatches the message internally.
|
||||||
///
|
///
|
||||||
/// `bridge` indicates instance of deployed bridge where the message came from.
|
/// `bridge` indicates instance of deployed bridge where the message came from.
|
||||||
@@ -36,5 +42,5 @@ pub trait MessageDispatch<MessageId> {
|
|||||||
/// `id` is a short unique if of the message.
|
/// `id` is a short unique if of the message.
|
||||||
///
|
///
|
||||||
/// Returns post-dispatch (actual) message weight.
|
/// Returns post-dispatch (actual) message weight.
|
||||||
fn dispatch(bridge: InstanceId, id: MessageId, message: Self::Message) -> Weight;
|
fn dispatch(bridge: InstanceId, id: MessageId, message: Self::Message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,15 +11,29 @@ codec = { package = "parity-scale-codec", version = "1.3.1", default-features =
|
|||||||
|
|
||||||
# Substrate Based Dependencies
|
# Substrate Based Dependencies
|
||||||
|
|
||||||
|
[dependencies.frame-support]
|
||||||
|
version = "2.0.0-rc6"
|
||||||
|
tag = 'v2.0.0-rc6'
|
||||||
|
default-features = false
|
||||||
|
git = "https://github.com/paritytech/substrate.git"
|
||||||
|
|
||||||
[dependencies.sp-api]
|
[dependencies.sp-api]
|
||||||
version = "2.0.0-rc6"
|
version = "2.0.0-rc6"
|
||||||
tag = 'v2.0.0-rc6'
|
tag = 'v2.0.0-rc6'
|
||||||
default-features = false
|
default-features = false
|
||||||
git = "https://github.com/paritytech/substrate.git"
|
git = "https://github.com/paritytech/substrate.git"
|
||||||
|
|
||||||
|
[dependencies.sp-std]
|
||||||
|
version = "2.0.0-rc6"
|
||||||
|
tag = 'v2.0.0-rc6'
|
||||||
|
default-features = false
|
||||||
|
git = "https://github.com/paritytech/substrate.git"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = [
|
std = [
|
||||||
"codec/std",
|
"codec/std",
|
||||||
"sp-api/std"
|
"frame-support/std",
|
||||||
|
"sp-api/std",
|
||||||
|
"sp-std/std"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Primitives for sending and receiving Substrate <-> Substrate messages.
|
//! Primitives of message lane module.
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
// RuntimeApi generated functions
|
// RuntimeApi generated functions
|
||||||
@@ -23,7 +23,12 @@
|
|||||||
#![allow(clippy::unnecessary_mut_passed)]
|
#![allow(clippy::unnecessary_mut_passed)]
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
|
use frame_support::RuntimeDebug;
|
||||||
use sp_api::decl_runtime_apis;
|
use sp_api::decl_runtime_apis;
|
||||||
|
use sp_std::prelude::*;
|
||||||
|
|
||||||
|
pub mod source_chain;
|
||||||
|
pub mod target_chain;
|
||||||
|
|
||||||
/// Lane identifier.
|
/// Lane identifier.
|
||||||
pub type LaneId = [u8; 4];
|
pub type LaneId = [u8; 4];
|
||||||
@@ -31,8 +36,11 @@ pub type LaneId = [u8; 4];
|
|||||||
/// Message nonce. Valid messages will never have 0 nonce.
|
/// Message nonce. Valid messages will never have 0 nonce.
|
||||||
pub type MessageNonce = u64;
|
pub type MessageNonce = u64;
|
||||||
|
|
||||||
|
/// Message id as a tuple.
|
||||||
|
pub type MessageId = (LaneId, MessageNonce);
|
||||||
|
|
||||||
/// Message key (unique message identifier) as it is stored in the storage.
|
/// Message key (unique message identifier) as it is stored in the storage.
|
||||||
#[derive(Encode, Decode, Clone)]
|
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
||||||
pub struct MessageKey {
|
pub struct MessageKey {
|
||||||
/// ID of the message lane.
|
/// ID of the message lane.
|
||||||
pub lane_id: LaneId,
|
pub lane_id: LaneId,
|
||||||
@@ -40,37 +48,33 @@ pub struct MessageKey {
|
|||||||
pub nonce: MessageNonce,
|
pub nonce: MessageNonce,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message as it is stored in the storage.
|
/// Message data as it is stored in the storage.
|
||||||
#[derive(Encode, Decode, Clone)]
|
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
||||||
pub struct Message<Payload> {
|
pub struct MessageData<Payload, Fee> {
|
||||||
/// Message key.
|
|
||||||
pub key: MessageKey,
|
|
||||||
/// Message payload.
|
/// Message payload.
|
||||||
pub payload: Payload,
|
pub payload: Payload,
|
||||||
|
/// Message delivery and dispatch fee, paid by the submitter.
|
||||||
|
pub fee: Fee,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when inbound message is received.
|
/// Message as it is stored in the storage.
|
||||||
pub trait OnMessageReceived<Payload> {
|
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
||||||
/// Called when inbound message is received.
|
pub struct Message<Payload, Fee> {
|
||||||
///
|
/// Message key.
|
||||||
/// It is up to the implementers of this trait to determine whether the message
|
pub key: MessageKey,
|
||||||
/// is invalid (i.e. improperly encoded, has too large weight, ...) or not.
|
/// Message data.
|
||||||
fn on_message_received(message: Message<Payload>);
|
pub data: MessageData<Payload, Fee>,
|
||||||
}
|
|
||||||
|
|
||||||
impl<Payload> OnMessageReceived<Payload> for () {
|
|
||||||
fn on_message_received(_message: Message<Payload>) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inbound lane data.
|
/// Inbound lane data.
|
||||||
#[derive(Default, Encode, Decode, Clone)]
|
#[derive(Default, Encode, Decode, Clone, RuntimeDebug, PartialEq)]
|
||||||
pub struct InboundLaneData {
|
pub struct InboundLaneData {
|
||||||
/// Nonce of latest message that we have received from bridged chain.
|
/// Nonce of latest message that we have received from bridged chain.
|
||||||
pub latest_received_nonce: MessageNonce,
|
pub latest_received_nonce: MessageNonce,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Outbound lane data.
|
/// Outbound lane data.
|
||||||
#[derive(Encode, Decode, Clone)]
|
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq)]
|
||||||
pub struct OutboundLaneData {
|
pub struct OutboundLaneData {
|
||||||
/// Nonce of oldest message that we haven't yet pruned. May point to not-yet-generated message if
|
/// Nonce of oldest message that we haven't yet pruned. May point to not-yet-generated message if
|
||||||
/// all sent messages are already pruned.
|
/// all sent messages are already pruned.
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Primitives of message lane module, that are used on the source chain.
|
||||||
|
|
||||||
|
use crate::{LaneId, MessageNonce};
|
||||||
|
|
||||||
|
use frame_support::Parameter;
|
||||||
|
use sp_std::fmt::Debug;
|
||||||
|
|
||||||
|
/// Target chain API. Used by source chain to verify target chain proofs.
|
||||||
|
///
|
||||||
|
/// All implementations of this trait should only work with finalized data that
|
||||||
|
/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane
|
||||||
|
/// that's stuck) and/or processing messages without paying fees.
|
||||||
|
pub trait TargetHeaderChain<Payload> {
|
||||||
|
/// Error type.
|
||||||
|
type Error: Debug + Into<&'static str>;
|
||||||
|
|
||||||
|
/// Proof that messages have been received by target chain.
|
||||||
|
type MessagesDeliveryProof: Parameter;
|
||||||
|
|
||||||
|
/// Verify message payload before we accept it.
|
||||||
|
///
|
||||||
|
/// **CAUTION**: this is very important function. Incorrect implementation may lead
|
||||||
|
/// to stuck lanes and/or relayers loses.
|
||||||
|
///
|
||||||
|
/// The proper implementation must ensure that the delivery-transaction with this
|
||||||
|
/// payload would (at least) be accepted into target chain transaction pool AND
|
||||||
|
/// eventually will be successfully 'mined'. The most obvious incorrect implementation
|
||||||
|
/// example would be implementation for BTC chain that accepts payloads larger than
|
||||||
|
/// 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
|
||||||
|
/// never be delivered.
|
||||||
|
fn verify_message(payload: &Payload) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
|
/// Verify messages delivery proof and return lane && nonce of the latest recevied message.
|
||||||
|
fn verify_messages_delivery_proof(
|
||||||
|
proof: Self::MessagesDeliveryProof,
|
||||||
|
) -> Result<(LaneId, MessageNonce), Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lane message verifier.
|
||||||
|
///
|
||||||
|
/// Runtime developer may implement any additional validation logic over message-lane mechanism.
|
||||||
|
/// E.g. if lanes should have some security (e.g. you can only accept Lane1 messages from
|
||||||
|
/// Submitter1, Lane2 messages for those who has submitted first message to this lane, disable
|
||||||
|
/// Lane3 until some block, ...), then it may be built using this verifier.
|
||||||
|
///
|
||||||
|
/// Any fee requirements should also be enforced here.
|
||||||
|
pub trait LaneMessageVerifier<Submitter, Payload, Fee> {
|
||||||
|
/// 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 lane.
|
||||||
|
fn verify_message(
|
||||||
|
submitter: &Submitter,
|
||||||
|
delivery_and_dispatch_fee: &Fee,
|
||||||
|
lane: &LaneId,
|
||||||
|
payload: &Payload,
|
||||||
|
) -> Result<(), Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message delivery payment. It is called as a part of submit-message transaction. Transaction
|
||||||
|
/// submitter is paying (in source chain tokens/assets) for:
|
||||||
|
///
|
||||||
|
/// 1) submit-message-transaction-fee itself. This fee is not included in the
|
||||||
|
/// `delivery_and_dispatch_fee` and is witheld by the regular transaction payment mechanism;
|
||||||
|
/// 2) message-delivery-transaction-fee. It is submitted to the target node by relayer;
|
||||||
|
/// 3) message-dispatch fee. It is paid by relayer for processing message by target chain;
|
||||||
|
/// 4) message-receiving-delivery-transaction-fee. It is submitted to the source node
|
||||||
|
/// by relayer.
|
||||||
|
///
|
||||||
|
/// So to be sure that any non-altruist relayer would agree to deliver message, submitter
|
||||||
|
/// should set `delivery_and_dispatch_fee` to at least (equialent of): sum of fees from (2)
|
||||||
|
/// to (4) above, plus some interest for the relayer.
|
||||||
|
pub trait MessageDeliveryAndDispatchPayment<AccountId, Balance> {
|
||||||
|
/// Error type.
|
||||||
|
type Error: Debug + Into<&'static str>;
|
||||||
|
|
||||||
|
/// Withhold/write-off delivery_and_dispatch_fee from submitter account to
|
||||||
|
/// some relayers-fund account.
|
||||||
|
fn pay_delivery_and_dispatch_fee(submitter: &AccountId, fee: &Balance) -> Result<(), Self::Error>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Bridges Common.
|
||||||
|
|
||||||
|
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Primitives of message lane module, that are used on the target chain.
|
||||||
|
|
||||||
|
use crate::Message;
|
||||||
|
|
||||||
|
use frame_support::{weights::Weight, Parameter};
|
||||||
|
use sp_std::{fmt::Debug, prelude::*};
|
||||||
|
|
||||||
|
/// Source chain API. Used by target chain, to verify source chain proofs.
|
||||||
|
///
|
||||||
|
/// All implementations of this trait should only work with finalized data that
|
||||||
|
/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane
|
||||||
|
/// that's stuck) and/or processing messages without paying fees.
|
||||||
|
pub trait SourceHeaderChain<Payload, Fee> {
|
||||||
|
/// Error type.
|
||||||
|
type Error: Debug + Into<&'static str>;
|
||||||
|
|
||||||
|
/// Proof that messages are sent from source chain.
|
||||||
|
type MessagesProof: Parameter;
|
||||||
|
|
||||||
|
/// Verify messages proof and return proved messages.
|
||||||
|
///
|
||||||
|
/// Messages vector is required to be sorted by nonce within each lane. Out-of-order
|
||||||
|
/// messages will be rejected.
|
||||||
|
fn verify_messages_proof(proof: Self::MessagesProof) -> Result<Vec<Message<Payload, Fee>>, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when inbound message is received.
|
||||||
|
pub trait MessageDispatch<Payload, Fee> {
|
||||||
|
/// Estimate dispatch weight.
|
||||||
|
///
|
||||||
|
/// This function must: (1) be instant and (2) return correct upper bound
|
||||||
|
/// of dispatch weight.
|
||||||
|
fn dispatch_weight(message: &Message<Payload, Fee>) -> Weight;
|
||||||
|
|
||||||
|
/// Called when inbound message is received.
|
||||||
|
///
|
||||||
|
/// It is up to the implementers of this trait to determine whether the message
|
||||||
|
/// is invalid (i.e. improperly encoded, has too large weight, ...) or not.
|
||||||
|
fn dispatch(message: Message<Payload, Fee>);
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
//! One-way message lane types. Within single one-way lane we have three 'races' where we try to:
|
//! One-way message lane types. Within single one-way lane we have three 'races' where we try to:
|
||||||
//!
|
//!
|
||||||
//! 1) relay new messages from source to target node;
|
//! 1) relay new messages from source to target node;
|
||||||
//! 2) relay proof-of-receiving from target to source node.
|
//! 2) relay proof-of-delivery from target to source node.
|
||||||
|
|
||||||
use relay_utils::HeaderId;
|
use relay_utils::HeaderId;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user