mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 15:11:02 +00:00
Remove support for encoded-call messaging from relay and runtime integration code (#1376)
* remove support for encoded-call messaging * continue cleanup * continue cleanup * continue cleanup * more cleanpup * more cleanup * fmt * continue cleanup * spellcheck * rename * fix benchmarks * mention encoded-calls-messaging tag * fixing deployments * fix messages generation * fmt
This commit is contained in:
committed by
Bastian Köcher
parent
dc96aeea35
commit
d582061dff
@@ -10,6 +10,11 @@ Substrate chains.
|
|||||||
|
|
||||||
🚧 The bridges are currently under construction - a hardhat is recommended beyond this point 🚧
|
🚧 The bridges are currently under construction - a hardhat is recommended beyond this point 🚧
|
||||||
|
|
||||||
|
**IMPORTANT**: this documentation is outdated and it is mostly related to the previous version of our
|
||||||
|
bridge. Right there's an ongoing work to make our bridge work with XCM messages. Old bridge is still
|
||||||
|
available at [encoded-calls-messaging](https://github.com/paritytech/parity-bridges-common/releases/tag/encoded-calls-messaging)
|
||||||
|
tag.
|
||||||
|
|
||||||
## Contents
|
## Contents
|
||||||
|
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
|
|||||||
@@ -17,17 +17,14 @@ serde = { version = "1.0", optional = true, features = ["derive"] }
|
|||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
|
|
||||||
bp-header-chain = { path = "../../../primitives/header-chain", default-features = false }
|
bp-header-chain = { path = "../../../primitives/header-chain", default-features = false }
|
||||||
bp-message-dispatch = { path = "../../../primitives/message-dispatch", default-features = false }
|
|
||||||
bp-messages = { path = "../../../primitives/messages", default-features = false }
|
bp-messages = { path = "../../../primitives/messages", default-features = false }
|
||||||
bp-millau = { path = "../../../primitives/chain-millau", default-features = false }
|
bp-millau = { path = "../../../primitives/chain-millau", default-features = false }
|
||||||
bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false }
|
bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false }
|
||||||
bp-runtime = { path = "../../../primitives/runtime", default-features = false }
|
bp-runtime = { path = "../../../primitives/runtime", default-features = false }
|
||||||
bp-westend = { path = "../../../primitives/chain-westend", default-features = false }
|
bp-westend = { path = "../../../primitives/chain-westend", default-features = false }
|
||||||
bridge-runtime-common = { path = "../../runtime-common", default-features = false }
|
bridge-runtime-common = { path = "../../runtime-common", default-features = false }
|
||||||
pallet-bridge-dispatch = { path = "../../../modules/dispatch", default-features = false }
|
|
||||||
pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false }
|
pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false }
|
||||||
pallet-bridge-messages = { path = "../../../modules/messages", default-features = false }
|
pallet-bridge-messages = { path = "../../../modules/messages", default-features = false }
|
||||||
pallet-bridge-token-swap = { path = "../../../modules/token-swap", default-features = false }
|
|
||||||
pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false }
|
pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false }
|
||||||
|
|
||||||
# Substrate Dependencies
|
# Substrate Dependencies
|
||||||
@@ -77,7 +74,6 @@ default = ["std"]
|
|||||||
std = [
|
std = [
|
||||||
"beefy-primitives/std",
|
"beefy-primitives/std",
|
||||||
"bp-header-chain/std",
|
"bp-header-chain/std",
|
||||||
"bp-message-dispatch/std",
|
|
||||||
"bp-messages/std",
|
"bp-messages/std",
|
||||||
"bp-millau/std",
|
"bp-millau/std",
|
||||||
"bp-rialto/std",
|
"bp-rialto/std",
|
||||||
@@ -93,10 +89,8 @@ std = [
|
|||||||
"pallet-balances/std",
|
"pallet-balances/std",
|
||||||
"pallet-beefy/std",
|
"pallet-beefy/std",
|
||||||
"pallet-beefy-mmr/std",
|
"pallet-beefy-mmr/std",
|
||||||
"pallet-bridge-dispatch/std",
|
|
||||||
"pallet-bridge-grandpa/std",
|
"pallet-bridge-grandpa/std",
|
||||||
"pallet-bridge-messages/std",
|
"pallet-bridge-messages/std",
|
||||||
"pallet-bridge-token-swap/std",
|
|
||||||
"pallet-grandpa/std",
|
"pallet-grandpa/std",
|
||||||
"pallet-mmr/std",
|
"pallet-mmr/std",
|
||||||
"pallet-randomness-collective-flip/std",
|
"pallet-randomness-collective-flip/std",
|
||||||
@@ -129,6 +123,5 @@ runtime-benchmarks = [
|
|||||||
"frame-system/runtime-benchmarks",
|
"frame-system/runtime-benchmarks",
|
||||||
"libsecp256k1",
|
"libsecp256k1",
|
||||||
"pallet-bridge-messages/runtime-benchmarks",
|
"pallet-bridge-messages/runtime-benchmarks",
|
||||||
"pallet-bridge-token-swap/runtime-benchmarks",
|
|
||||||
"sp-runtime/runtime-benchmarks",
|
"sp-runtime/runtime-benchmarks",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ use sp_runtime::{
|
|||||||
create_runtime_str, generic, impl_opaque_keys,
|
create_runtime_str, generic, impl_opaque_keys,
|
||||||
traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys},
|
traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys},
|
||||||
transaction_validity::{TransactionSource, TransactionValidity},
|
transaction_validity::{TransactionSource, TransactionValidity},
|
||||||
ApplyExtrinsicResult, FixedPointNumber, FixedU128, MultiSignature, MultiSigner, Perquintill,
|
ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perquintill,
|
||||||
};
|
};
|
||||||
use sp_std::prelude::*;
|
use sp_std::prelude::*;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -225,18 +225,6 @@ impl pallet_beefy::Config for Runtime {
|
|||||||
type BeefyId = BeefyId;
|
type BeefyId = BeefyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_bridge_dispatch::Config for Runtime {
|
|
||||||
type Event = Event;
|
|
||||||
type BridgeMessageId = (bp_messages::LaneId, bp_messages::MessageNonce);
|
|
||||||
type Call = Call;
|
|
||||||
type CallFilter = frame_support::traits::Everything;
|
|
||||||
type EncodedCall = crate::rialto_messages::FromRialtoEncodedCall;
|
|
||||||
type SourceChainAccountId = bp_rialto::AccountId;
|
|
||||||
type TargetChainAccountPublic = MultiSigner;
|
|
||||||
type TargetChainSignature = MultiSignature;
|
|
||||||
type AccountIdConverter = bp_millau::AccountIdConverter;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pallet_grandpa::Config for Runtime {
|
impl pallet_grandpa::Config for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
@@ -471,38 +459,13 @@ impl pallet_bridge_messages::Config<WithRialtoMessagesInstance> for Runtime {
|
|||||||
GetDeliveryConfirmationTransactionFee,
|
GetDeliveryConfirmationTransactionFee,
|
||||||
>;
|
>;
|
||||||
type OnMessageAccepted = ();
|
type OnMessageAccepted = ();
|
||||||
type OnDeliveryConfirmed =
|
type OnDeliveryConfirmed = ();
|
||||||
pallet_bridge_token_swap::Pallet<Runtime, WithRialtoTokenSwapInstance>;
|
|
||||||
|
|
||||||
type SourceHeaderChain = crate::rialto_messages::Rialto;
|
type SourceHeaderChain = crate::rialto_messages::Rialto;
|
||||||
type MessageDispatch = crate::rialto_messages::FromRialtoMessageDispatch;
|
type MessageDispatch = crate::rialto_messages::FromRialtoMessageDispatch;
|
||||||
type BridgedChainId = RialtoChainId;
|
type BridgedChainId = RialtoChainId;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
|
||||||
pub const TokenSwapMessagesLane: bp_messages::LaneId = *b"swap";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Instance of the with-Rialto token swap pallet.
|
|
||||||
pub type WithRialtoTokenSwapInstance = ();
|
|
||||||
|
|
||||||
impl pallet_bridge_token_swap::Config<WithRialtoTokenSwapInstance> for Runtime {
|
|
||||||
type Event = Event;
|
|
||||||
type WeightInfo = ();
|
|
||||||
|
|
||||||
type BridgedChainId = RialtoChainId;
|
|
||||||
type OutboundMessageLaneId = TokenSwapMessagesLane;
|
|
||||||
#[cfg(not(feature = "runtime-benchmarks"))]
|
|
||||||
type MessagesBridge = pallet_bridge_messages::Pallet<Runtime, WithRialtoMessagesInstance>;
|
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
|
||||||
type MessagesBridge = bp_messages::source_chain::NoopMessagesBridge;
|
|
||||||
type ThisCurrency = pallet_balances::Pallet<Runtime>;
|
|
||||||
type FromSwapToThisAccountIdConverter = bp_rialto::AccountIdConverter;
|
|
||||||
|
|
||||||
type BridgedChain = bp_rialto::Rialto;
|
|
||||||
type FromBridgedToThisAccountIdConverter = bp_millau::AccountIdConverter;
|
|
||||||
}
|
|
||||||
|
|
||||||
construct_runtime!(
|
construct_runtime!(
|
||||||
pub enum Runtime where
|
pub enum Runtime where
|
||||||
Block = Block,
|
Block = Block,
|
||||||
@@ -532,9 +495,7 @@ construct_runtime!(
|
|||||||
|
|
||||||
// Rialto bridge modules.
|
// Rialto bridge modules.
|
||||||
BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage},
|
BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage},
|
||||||
BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event<T>},
|
|
||||||
BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>},
|
BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>},
|
||||||
BridgeRialtoTokenSwap: pallet_bridge_token_swap::{Pallet, Call, Storage, Event<T>, Origin<T>},
|
|
||||||
|
|
||||||
// Westend bridge modules.
|
// Westend bridge modules.
|
||||||
BridgeWestendGrandpa: pallet_bridge_grandpa::<Instance1>::{Pallet, Call, Config<T>, Storage},
|
BridgeWestendGrandpa: pallet_bridge_grandpa::<Instance1>::{Pallet, Call, Config<T>, Storage},
|
||||||
@@ -806,7 +767,6 @@ impl_runtime_apis! {
|
|||||||
|
|
||||||
let mut list = Vec::<BenchmarkList>::new();
|
let mut list = Vec::<BenchmarkList>::new();
|
||||||
|
|
||||||
list_benchmark!(list, extra, pallet_bridge_token_swap, BridgeRialtoTokenSwap);
|
|
||||||
list_benchmark!(list, extra, pallet_bridge_messages, MessagesBench::<Runtime, WithRialtoMessagesInstance>);
|
list_benchmark!(list, extra, pallet_bridge_messages, MessagesBench::<Runtime, WithRialtoMessagesInstance>);
|
||||||
list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa);
|
list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa);
|
||||||
|
|
||||||
@@ -878,8 +838,6 @@ impl_runtime_apis! {
|
|||||||
) -> (rialto_messages::FromRialtoMessagesProof, Weight) {
|
) -> (rialto_messages::FromRialtoMessagesProof, Weight) {
|
||||||
prepare_message_proof::<Runtime, (), (), WithRialtoMessageBridge, bp_rialto::Header, bp_rialto::Hasher>(
|
prepare_message_proof::<Runtime, (), (), WithRialtoMessageBridge, bp_rialto::Header, bp_rialto::Hasher>(
|
||||||
params,
|
params,
|
||||||
&VERSION,
|
|
||||||
Balance::MAX / 100,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -891,33 +849,11 @@ impl_runtime_apis! {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_message_dispatched(nonce: bp_messages::MessageNonce) -> bool {
|
fn is_message_dispatched(_nonce: bp_messages::MessageNonce) -> bool {
|
||||||
frame_system::Pallet::<Runtime>::events()
|
true
|
||||||
.into_iter()
|
|
||||||
.map(|event_record| event_record.event)
|
|
||||||
.any(|event| matches!(
|
|
||||||
event,
|
|
||||||
Event::BridgeDispatch(pallet_bridge_dispatch::Event::<Runtime, _>::MessageDispatched(
|
|
||||||
_, ([0, 0, 0, 0], nonce_from_event), _,
|
|
||||||
)) if nonce_from_event == nonce
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use pallet_bridge_token_swap::benchmarking::Config as TokenSwapConfig;
|
|
||||||
|
|
||||||
impl TokenSwapConfig<WithRialtoTokenSwapInstance> for Runtime {
|
|
||||||
fn initialize_environment() {
|
|
||||||
let relayers_fund_account = pallet_bridge_messages::relayer_fund_account_id::<
|
|
||||||
bp_millau::AccountId,
|
|
||||||
bp_millau::AccountIdConverter,
|
|
||||||
>();
|
|
||||||
pallet_balances::Pallet::<Runtime>::make_free_balance_be(
|
|
||||||
&relayers_fund_account,
|
|
||||||
Balance::MAX / 100,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add_benchmark!(
|
add_benchmark!(
|
||||||
params,
|
params,
|
||||||
@@ -926,37 +862,12 @@ impl_runtime_apis! {
|
|||||||
MessagesBench::<Runtime, WithRialtoMessagesInstance>
|
MessagesBench::<Runtime, WithRialtoMessagesInstance>
|
||||||
);
|
);
|
||||||
add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeRialtoGrandpa);
|
add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeRialtoGrandpa);
|
||||||
add_benchmark!(params, batches, pallet_bridge_token_swap, BridgeRialtoTokenSwap);
|
|
||||||
|
|
||||||
Ok(batches)
|
Ok(batches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rialto account ownership digest from Millau.
|
|
||||||
///
|
|
||||||
/// The byte vector returned by this function should be signed with a Rialto account private key.
|
|
||||||
/// This way, the owner of `millau_account_id` on Millau proves that the Rialto account private key
|
|
||||||
/// is also under his control.
|
|
||||||
pub fn millau_to_rialto_account_ownership_digest<Call, AccountId, SpecVersion>(
|
|
||||||
rialto_call: &Call,
|
|
||||||
millau_account_id: AccountId,
|
|
||||||
rialto_spec_version: SpecVersion,
|
|
||||||
) -> sp_std::vec::Vec<u8>
|
|
||||||
where
|
|
||||||
Call: codec::Encode,
|
|
||||||
AccountId: codec::Encode,
|
|
||||||
SpecVersion: codec::Encode,
|
|
||||||
{
|
|
||||||
pallet_bridge_dispatch::account_ownership_digest(
|
|
||||||
rialto_call,
|
|
||||||
millau_account_id,
|
|
||||||
rialto_spec_version,
|
|
||||||
bp_runtime::MILLAU_CHAIN_ID,
|
|
||||||
bp_runtime::RIALTO_CHAIN_ID,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ use frame_support::{
|
|||||||
};
|
};
|
||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
|
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
|
||||||
use sp_std::{convert::TryFrom, ops::RangeInclusive};
|
use sp_std::convert::TryFrom;
|
||||||
|
|
||||||
/// Initial value of `RialtoToMillauConversionRate` parameter.
|
/// Initial value of `RialtoToMillauConversionRate` parameter.
|
||||||
pub const INITIAL_RIALTO_TO_MILLAU_CONVERSION_RATE: FixedU128 =
|
pub const INITIAL_RIALTO_TO_MILLAU_CONVERSION_RATE: FixedU128 =
|
||||||
@@ -49,19 +49,14 @@ parameter_types! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Message payload for Millau -> Rialto messages.
|
/// Message payload for Millau -> Rialto messages.
|
||||||
pub type ToRialtoMessagePayload =
|
pub type ToRialtoMessagePayload = messages::source::FromThisChainMessagePayload;
|
||||||
messages::source::FromThisChainMessagePayload<WithRialtoMessageBridge>;
|
|
||||||
|
|
||||||
/// Message verifier for Millau -> Rialto messages.
|
/// Message verifier for Millau -> Rialto messages.
|
||||||
pub type ToRialtoMessageVerifier =
|
pub type ToRialtoMessageVerifier =
|
||||||
messages::source::FromThisChainMessageVerifier<WithRialtoMessageBridge>;
|
messages::source::FromThisChainMessageVerifier<WithRialtoMessageBridge>;
|
||||||
|
|
||||||
/// Message payload for Rialto -> Millau messages.
|
/// Message payload for Rialto -> Millau messages.
|
||||||
pub type FromRialtoMessagePayload =
|
pub type FromRialtoMessagePayload = messages::target::FromBridgedChainMessagePayload;
|
||||||
messages::target::FromBridgedChainMessagePayload<WithRialtoMessageBridge>;
|
|
||||||
|
|
||||||
/// Encoded Millau Call as it comes from Rialto.
|
|
||||||
pub type FromRialtoEncodedCall = messages::target::FromBridgedChainEncodedMessageCall<crate::Call>;
|
|
||||||
|
|
||||||
/// Messages proof for Rialto -> Millau messages.
|
/// Messages proof for Rialto -> Millau messages.
|
||||||
pub type FromRialtoMessagesProof = messages::target::FromBridgedChainMessagesProof<bp_rialto::Hash>;
|
pub type FromRialtoMessagesProof = messages::target::FromBridgedChainMessagesProof<bp_rialto::Hash>;
|
||||||
@@ -120,19 +115,7 @@ impl messages::ThisChainWithMessages for Millau {
|
|||||||
type Call = crate::Call;
|
type Call = crate::Call;
|
||||||
|
|
||||||
fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool {
|
fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool {
|
||||||
// lanes 0x00000000 && 0x00000001 are accepting any paid messages, while
|
(*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1]) && send_origin.linked_account().is_some()
|
||||||
// `TokenSwapMessageLane` only accepts messages from token swap pallet
|
|
||||||
let token_swap_dedicated_lane = crate::TokenSwapMessagesLane::get();
|
|
||||||
match *lane {
|
|
||||||
[0, 0, 0, 0] | [0, 0, 0, 1] => send_origin.linked_account().is_some(),
|
|
||||||
_ if *lane == token_swap_dedicated_lane => matches!(
|
|
||||||
send_origin.caller,
|
|
||||||
crate::OriginCaller::BridgeRialtoTokenSwap(
|
|
||||||
pallet_bridge_token_swap::RawOrigin::TokenSwap { .. }
|
|
||||||
)
|
|
||||||
),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
|
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
|
||||||
@@ -189,19 +172,8 @@ impl messages::BridgedChainWithMessages for Rialto {
|
|||||||
bp_rialto::Rialto::max_extrinsic_size()
|
bp_rialto::Rialto::max_extrinsic_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive<Weight> {
|
fn verify_dispatch_weight(_message_payload: &[u8]) -> bool {
|
||||||
// we don't want to relay too large messages + keep reserve for future upgrades
|
true
|
||||||
let upper_limit = messages::target::maximal_incoming_message_dispatch_weight(
|
|
||||||
bp_rialto::Rialto::max_extrinsic_weight(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// we're charging for payload bytes in `WithRialtoMessageBridge::transaction_payment`
|
|
||||||
// function
|
|
||||||
//
|
|
||||||
// this bridge may be used to deliver all kind of messages, so we're not making any
|
|
||||||
// assumptions about minimal dispatch weight here
|
|
||||||
|
|
||||||
0..=upper_limit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn estimate_delivery_transaction(
|
fn estimate_delivery_transaction(
|
||||||
@@ -296,12 +268,6 @@ impl SenderOrigin<crate::AccountId> for crate::Origin {
|
|||||||
crate::OriginCaller::system(frame_system::RawOrigin::Root) |
|
crate::OriginCaller::system(frame_system::RawOrigin::Root) |
|
||||||
crate::OriginCaller::system(frame_system::RawOrigin::None) =>
|
crate::OriginCaller::system(frame_system::RawOrigin::None) =>
|
||||||
crate::RootAccountForPayments::get(),
|
crate::RootAccountForPayments::get(),
|
||||||
crate::OriginCaller::BridgeRialtoTokenSwap(
|
|
||||||
pallet_bridge_token_swap::RawOrigin::TokenSwap {
|
|
||||||
ref swap_account_at_this_chain,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
) => Some(swap_account_at_this_chain.clone()),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,11 @@ serde = { version = "1.0", optional = true, features = ["derive"] }
|
|||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
|
|
||||||
bp-header-chain = { path = "../../../primitives/header-chain", default-features = false }
|
bp-header-chain = { path = "../../../primitives/header-chain", default-features = false }
|
||||||
bp-message-dispatch = { path = "../../../primitives/message-dispatch", default-features = false }
|
|
||||||
bp-messages = { path = "../../../primitives/messages", default-features = false }
|
bp-messages = { path = "../../../primitives/messages", default-features = false }
|
||||||
bp-millau = { path = "../../../primitives/chain-millau", default-features = false }
|
bp-millau = { path = "../../../primitives/chain-millau", default-features = false }
|
||||||
bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false }
|
bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false }
|
||||||
bp-runtime = { path = "../../../primitives/runtime", default-features = false }
|
bp-runtime = { path = "../../../primitives/runtime", default-features = false }
|
||||||
bridge-runtime-common = { path = "../../runtime-common", default-features = false }
|
bridge-runtime-common = { path = "../../runtime-common", default-features = false }
|
||||||
pallet-bridge-dispatch = { path = "../../../modules/dispatch", default-features = false }
|
|
||||||
pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false }
|
pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false }
|
||||||
pallet-bridge-messages = { path = "../../../modules/messages", default-features = false }
|
pallet-bridge-messages = { path = "../../../modules/messages", default-features = false }
|
||||||
pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false }
|
pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false }
|
||||||
@@ -85,7 +83,6 @@ default = ["std"]
|
|||||||
std = [
|
std = [
|
||||||
"beefy-primitives/std",
|
"beefy-primitives/std",
|
||||||
"bp-header-chain/std",
|
"bp-header-chain/std",
|
||||||
"bp-message-dispatch/std",
|
|
||||||
"bp-messages/std",
|
"bp-messages/std",
|
||||||
"bp-millau/std",
|
"bp-millau/std",
|
||||||
"bp-rialto/std",
|
"bp-rialto/std",
|
||||||
@@ -103,7 +100,6 @@ std = [
|
|||||||
"pallet-balances/std",
|
"pallet-balances/std",
|
||||||
"pallet-beefy/std",
|
"pallet-beefy/std",
|
||||||
"pallet-beefy-mmr/std",
|
"pallet-beefy-mmr/std",
|
||||||
"pallet-bridge-dispatch/std",
|
|
||||||
"pallet-bridge-grandpa/std",
|
"pallet-bridge-grandpa/std",
|
||||||
"pallet-bridge-messages/std",
|
"pallet-bridge-messages/std",
|
||||||
"pallet-grandpa/std",
|
"pallet-grandpa/std",
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ use sp_runtime::{
|
|||||||
create_runtime_str, generic, impl_opaque_keys,
|
create_runtime_str, generic, impl_opaque_keys,
|
||||||
traits::{AccountIdLookup, Block as BlockT, Keccak256, NumberFor, OpaqueKeys},
|
traits::{AccountIdLookup, Block as BlockT, Keccak256, NumberFor, OpaqueKeys},
|
||||||
transaction_validity::{TransactionSource, TransactionValidity},
|
transaction_validity::{TransactionSource, TransactionValidity},
|
||||||
ApplyExtrinsicResult, FixedPointNumber, FixedU128, MultiSignature, MultiSigner, Perquintill,
|
ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perquintill,
|
||||||
};
|
};
|
||||||
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
|
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -251,18 +251,6 @@ impl pallet_beefy::Config for Runtime {
|
|||||||
type BeefyId = BeefyId;
|
type BeefyId = BeefyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_bridge_dispatch::Config for Runtime {
|
|
||||||
type Event = Event;
|
|
||||||
type BridgeMessageId = (bp_messages::LaneId, bp_messages::MessageNonce);
|
|
||||||
type Call = Call;
|
|
||||||
type CallFilter = frame_support::traits::Everything;
|
|
||||||
type EncodedCall = crate::millau_messages::FromMillauEncodedCall;
|
|
||||||
type SourceChainAccountId = bp_millau::AccountId;
|
|
||||||
type TargetChainAccountPublic = MultiSigner;
|
|
||||||
type TargetChainSignature = MultiSignature;
|
|
||||||
type AccountIdConverter = bp_rialto::AccountIdConverter;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pallet_grandpa::Config for Runtime {
|
impl pallet_grandpa::Config for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
@@ -505,7 +493,6 @@ construct_runtime!(
|
|||||||
|
|
||||||
// Millau bridge modules.
|
// Millau bridge modules.
|
||||||
BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage},
|
BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage},
|
||||||
BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event<T>},
|
|
||||||
BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>},
|
BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>},
|
||||||
|
|
||||||
// Parachain modules.
|
// Parachain modules.
|
||||||
@@ -920,30 +907,6 @@ impl_runtime_apis! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Millau account ownership digest from Rialto.
|
|
||||||
///
|
|
||||||
/// The byte vector returned by this function should be signed with a Millau account private key.
|
|
||||||
/// This way, the owner of `rialto_account_id` on Rialto proves that the 'millau' account private
|
|
||||||
/// key is also under his control.
|
|
||||||
pub fn rialto_to_millau_account_ownership_digest<Call, AccountId, SpecVersion>(
|
|
||||||
millau_call: &Call,
|
|
||||||
rialto_account_id: AccountId,
|
|
||||||
millau_spec_version: SpecVersion,
|
|
||||||
) -> sp_std::vec::Vec<u8>
|
|
||||||
where
|
|
||||||
Call: codec::Encode,
|
|
||||||
AccountId: codec::Encode,
|
|
||||||
SpecVersion: codec::Encode,
|
|
||||||
{
|
|
||||||
pallet_bridge_dispatch::account_ownership_digest(
|
|
||||||
millau_call,
|
|
||||||
rialto_account_id,
|
|
||||||
millau_spec_version,
|
|
||||||
bp_runtime::RIALTO_CHAIN_ID,
|
|
||||||
bp_runtime::MILLAU_CHAIN_ID,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ use frame_support::{
|
|||||||
};
|
};
|
||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
|
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
|
||||||
use sp_std::{convert::TryFrom, ops::RangeInclusive};
|
use sp_std::convert::TryFrom;
|
||||||
|
|
||||||
/// Initial value of `MillauToRialtoConversionRate` parameter.
|
/// Initial value of `MillauToRialtoConversionRate` parameter.
|
||||||
pub const INITIAL_MILLAU_TO_RIALTO_CONVERSION_RATE: FixedU128 =
|
pub const INITIAL_MILLAU_TO_RIALTO_CONVERSION_RATE: FixedU128 =
|
||||||
@@ -49,19 +49,14 @@ parameter_types! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Message payload for Rialto -> Millau messages.
|
/// Message payload for Rialto -> Millau messages.
|
||||||
pub type ToMillauMessagePayload =
|
pub type ToMillauMessagePayload = messages::source::FromThisChainMessagePayload;
|
||||||
messages::source::FromThisChainMessagePayload<WithMillauMessageBridge>;
|
|
||||||
|
|
||||||
/// Message verifier for Rialto -> Millau messages.
|
/// Message verifier for Rialto -> Millau messages.
|
||||||
pub type ToMillauMessageVerifier =
|
pub type ToMillauMessageVerifier =
|
||||||
messages::source::FromThisChainMessageVerifier<WithMillauMessageBridge>;
|
messages::source::FromThisChainMessageVerifier<WithMillauMessageBridge>;
|
||||||
|
|
||||||
/// Message payload for Millau -> Rialto messages.
|
/// Message payload for Millau -> Rialto messages.
|
||||||
pub type FromMillauMessagePayload =
|
pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload;
|
||||||
messages::target::FromBridgedChainMessagePayload<WithMillauMessageBridge>;
|
|
||||||
|
|
||||||
/// Encoded Rialto Call as it comes from Millau.
|
|
||||||
pub type FromMillauEncodedCall = messages::target::FromBridgedChainEncodedMessageCall<crate::Call>;
|
|
||||||
|
|
||||||
/// Call-dispatch based message dispatch for Millau -> Rialto messages.
|
/// Call-dispatch based message dispatch for Millau -> Rialto messages.
|
||||||
pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDispatch<
|
pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDispatch<
|
||||||
@@ -177,19 +172,8 @@ impl messages::BridgedChainWithMessages for Millau {
|
|||||||
bp_millau::Millau::max_extrinsic_size()
|
bp_millau::Millau::max_extrinsic_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive<Weight> {
|
fn verify_dispatch_weight(_message_payload: &[u8]) -> bool {
|
||||||
// we don't want to relay too large messages + keep reserve for future upgrades
|
true
|
||||||
let upper_limit = messages::target::maximal_incoming_message_dispatch_weight(
|
|
||||||
bp_millau::Millau::max_extrinsic_weight(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// we're charging for payload bytes in `WithMillauMessageBridge::transaction_payment`
|
|
||||||
// function
|
|
||||||
//
|
|
||||||
// this bridge may be used to deliver all kind of messages, so we're not making any
|
|
||||||
// assumptions about minimal dispatch weight here
|
|
||||||
|
|
||||||
0..=upper_limit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn estimate_delivery_transaction(
|
fn estimate_delivery_transaction(
|
||||||
@@ -308,100 +292,15 @@ impl MessagesParameter for RialtoToMillauMessagesParameter {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{DbWeight, MillauGrandpaInstance, Runtime, WithMillauMessagesInstance};
|
||||||
AccountId, Call, DbWeight, ExistentialDeposit, MillauGrandpaInstance, Runtime, SystemCall,
|
use bp_runtime::Chain;
|
||||||
SystemConfig, WithMillauMessagesInstance, VERSION,
|
|
||||||
};
|
|
||||||
use bp_message_dispatch::CallOrigin;
|
|
||||||
use bp_messages::{
|
|
||||||
target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
|
|
||||||
MessageKey,
|
|
||||||
};
|
|
||||||
use bp_runtime::{derive_account_id, messages::DispatchFeePayment, Chain, SourceAccount};
|
|
||||||
use bridge_runtime_common::{
|
use bridge_runtime_common::{
|
||||||
assert_complete_bridge_types,
|
assert_complete_bridge_types,
|
||||||
integrity::{
|
integrity::{
|
||||||
assert_complete_bridge_constants, AssertBridgeMessagesPalletConstants,
|
assert_complete_bridge_constants, AssertBridgeMessagesPalletConstants,
|
||||||
AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants,
|
AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants,
|
||||||
},
|
},
|
||||||
messages::target::{FromBridgedChainEncodedMessageCall, FromBridgedChainMessagePayload},
|
|
||||||
};
|
};
|
||||||
use frame_support::{
|
|
||||||
traits::Currency,
|
|
||||||
weights::{GetDispatchInfo, WeightToFeePolynomial},
|
|
||||||
};
|
|
||||||
use sp_runtime::traits::Convert;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn transfer_happens_when_dispatch_fee_is_paid_at_target_chain() {
|
|
||||||
// this test actually belongs to the `bridge-runtime-common` crate, but there we have no
|
|
||||||
// mock runtime. Making another one there just for this test, given that both crates
|
|
||||||
// live n single repo is an overkill
|
|
||||||
let mut ext: sp_io::TestExternalities =
|
|
||||||
SystemConfig::default().build_storage::<Runtime>().unwrap().into();
|
|
||||||
ext.execute_with(|| {
|
|
||||||
let bridge = MILLAU_CHAIN_ID;
|
|
||||||
let call: Call = SystemCall::set_heap_pages { pages: 64 }.into();
|
|
||||||
let dispatch_weight = call.get_dispatch_info().weight;
|
|
||||||
let dispatch_fee = <Runtime as pallet_transaction_payment::Config>::WeightToFee::calc(
|
|
||||||
&dispatch_weight,
|
|
||||||
);
|
|
||||||
assert!(dispatch_fee > 0);
|
|
||||||
|
|
||||||
// create relayer account with minimal balance
|
|
||||||
let relayer_account: AccountId = [1u8; 32].into();
|
|
||||||
let initial_amount = ExistentialDeposit::get();
|
|
||||||
let _ = <pallet_balances::Pallet<Runtime> as Currency<AccountId>>::deposit_creating(
|
|
||||||
&relayer_account,
|
|
||||||
initial_amount,
|
|
||||||
);
|
|
||||||
|
|
||||||
// create dispatch account with minimal balance + dispatch fee
|
|
||||||
let dispatch_account = derive_account_id::<
|
|
||||||
<Runtime as pallet_bridge_dispatch::Config>::SourceChainAccountId,
|
|
||||||
>(bridge, SourceAccount::Root);
|
|
||||||
let dispatch_account =
|
|
||||||
<Runtime as pallet_bridge_dispatch::Config>::AccountIdConverter::convert(
|
|
||||||
dispatch_account,
|
|
||||||
);
|
|
||||||
let _ = <pallet_balances::Pallet<Runtime> as Currency<AccountId>>::deposit_creating(
|
|
||||||
&dispatch_account,
|
|
||||||
initial_amount + dispatch_fee,
|
|
||||||
);
|
|
||||||
|
|
||||||
// dispatch message with intention to pay dispatch fee at the target chain
|
|
||||||
FromMillauMessageDispatch::dispatch(
|
|
||||||
&relayer_account,
|
|
||||||
DispatchMessage {
|
|
||||||
key: MessageKey { lane_id: Default::default(), nonce: 0 },
|
|
||||||
data: DispatchMessageData {
|
|
||||||
payload: Ok(FromBridgedChainMessagePayload::<WithMillauMessageBridge> {
|
|
||||||
spec_version: VERSION.spec_version,
|
|
||||||
weight: dispatch_weight,
|
|
||||||
origin: CallOrigin::SourceRoot,
|
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
|
|
||||||
call: FromBridgedChainEncodedMessageCall::new(call.encode()),
|
|
||||||
}),
|
|
||||||
fee: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// ensure that fee has been transferred from dispatch to relayer account
|
|
||||||
assert_eq!(
|
|
||||||
<pallet_balances::Pallet<Runtime> as Currency<AccountId>>::free_balance(
|
|
||||||
&relayer_account
|
|
||||||
),
|
|
||||||
initial_amount + dispatch_fee,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
<pallet_balances::Pallet<Runtime> as Currency<AccountId>>::free_balance(
|
|
||||||
&dispatch_account
|
|
||||||
),
|
|
||||||
initial_amount,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ensure_rialto_message_lane_weights_are_correct() {
|
fn ensure_rialto_message_lane_weights_are_correct() {
|
||||||
@@ -490,69 +389,4 @@ mod tests {
|
|||||||
.0,
|
.0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn no_stack_overflow_when_decoding_nested_call_during_dispatch() {
|
|
||||||
// this test is normally ignored, because it only makes sense to run it in release mode
|
|
||||||
|
|
||||||
let mut ext: sp_io::TestExternalities =
|
|
||||||
SystemConfig::default().build_storage::<Runtime>().unwrap().into();
|
|
||||||
ext.execute_with(|| {
|
|
||||||
let bridge = MILLAU_CHAIN_ID;
|
|
||||||
|
|
||||||
let mut call: Call = SystemCall::set_heap_pages { pages: 64 }.into();
|
|
||||||
|
|
||||||
for _i in 0..3000 {
|
|
||||||
call = Call::Sudo(pallet_sudo::Call::sudo { call: Box::new(call) });
|
|
||||||
}
|
|
||||||
|
|
||||||
let dispatch_weight = 500;
|
|
||||||
let dispatch_fee = <Runtime as pallet_transaction_payment::Config>::WeightToFee::calc(
|
|
||||||
&dispatch_weight,
|
|
||||||
);
|
|
||||||
assert!(dispatch_fee > 0);
|
|
||||||
|
|
||||||
// create relayer account with minimal balance
|
|
||||||
let relayer_account: AccountId = [1u8; 32].into();
|
|
||||||
let initial_amount = ExistentialDeposit::get();
|
|
||||||
let _ = <pallet_balances::Pallet<Runtime> as Currency<AccountId>>::deposit_creating(
|
|
||||||
&relayer_account,
|
|
||||||
initial_amount,
|
|
||||||
);
|
|
||||||
|
|
||||||
// create dispatch account with minimal balance + dispatch fee
|
|
||||||
let dispatch_account = derive_account_id::<
|
|
||||||
<Runtime as pallet_bridge_dispatch::Config>::SourceChainAccountId,
|
|
||||||
>(bridge, SourceAccount::Root);
|
|
||||||
let dispatch_account =
|
|
||||||
<Runtime as pallet_bridge_dispatch::Config>::AccountIdConverter::convert(
|
|
||||||
dispatch_account,
|
|
||||||
);
|
|
||||||
let _ = <pallet_balances::Pallet<Runtime> as Currency<AccountId>>::deposit_creating(
|
|
||||||
&dispatch_account,
|
|
||||||
initial_amount + dispatch_fee,
|
|
||||||
);
|
|
||||||
|
|
||||||
// dispatch message with intention to pay dispatch fee at the target chain
|
|
||||||
//
|
|
||||||
// this is where the stack overflow has happened before the fix has been applied
|
|
||||||
FromMillauMessageDispatch::dispatch(
|
|
||||||
&relayer_account,
|
|
||||||
DispatchMessage {
|
|
||||||
key: MessageKey { lane_id: Default::default(), nonce: 0 },
|
|
||||||
data: DispatchMessageData {
|
|
||||||
payload: Ok(FromBridgedChainMessagePayload::<WithMillauMessageBridge> {
|
|
||||||
spec_version: VERSION.spec_version,
|
|
||||||
weight: dispatch_weight,
|
|
||||||
origin: CallOrigin::SourceRoot,
|
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
|
|
||||||
call: FromBridgedChainEncodedMessageCall::new(call.encode()),
|
|
||||||
}),
|
|
||||||
fee: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,17 +9,15 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
|
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
|
||||||
ed25519-dalek = { version = "1.0", default-features = false, optional = true }
|
|
||||||
hash-db = { version = "0.15.2", default-features = false }
|
hash-db = { version = "0.15.2", default-features = false }
|
||||||
|
log = { version = "0.4.14", default-features = false }
|
||||||
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
|
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
|
||||||
static_assertions = { version = "1.1", optional = true }
|
static_assertions = { version = "1.1", optional = true }
|
||||||
|
|
||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
|
|
||||||
bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false }
|
|
||||||
bp-messages = { path = "../../primitives/messages", default-features = false }
|
bp-messages = { path = "../../primitives/messages", default-features = false }
|
||||||
bp-runtime = { path = "../../primitives/runtime", default-features = false }
|
bp-runtime = { path = "../../primitives/runtime", default-features = false }
|
||||||
pallet-bridge-dispatch = { path = "../../modules/dispatch", default-features = false }
|
|
||||||
pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false }
|
pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false }
|
||||||
pallet-bridge-messages = { path = "../../modules/messages", default-features = false }
|
pallet-bridge-messages = { path = "../../modules/messages", default-features = false }
|
||||||
|
|
||||||
@@ -40,14 +38,13 @@ sp-version = { git = "https://github.com/paritytech/substrate", branch = "master
|
|||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = [
|
std = [
|
||||||
"bp-message-dispatch/std",
|
|
||||||
"bp-messages/std",
|
"bp-messages/std",
|
||||||
"bp-runtime/std",
|
"bp-runtime/std",
|
||||||
"codec/std",
|
"codec/std",
|
||||||
"frame-support/std",
|
"frame-support/std",
|
||||||
"frame-system/std",
|
"frame-system/std",
|
||||||
"hash-db/std",
|
"hash-db/std",
|
||||||
"pallet-bridge-dispatch/std",
|
"log/std",
|
||||||
"pallet-bridge-grandpa/std",
|
"pallet-bridge-grandpa/std",
|
||||||
"pallet-bridge-messages/std",
|
"pallet-bridge-messages/std",
|
||||||
"pallet-transaction-payment/std",
|
"pallet-transaction-payment/std",
|
||||||
@@ -60,7 +57,6 @@ std = [
|
|||||||
"sp-trie/std",
|
"sp-trie/std",
|
||||||
]
|
]
|
||||||
runtime-benchmarks = [
|
runtime-benchmarks = [
|
||||||
"ed25519-dalek/u64_backend",
|
|
||||||
"pallet-balances",
|
"pallet-balances",
|
||||||
"pallet-bridge-grandpa/runtime-benchmarks",
|
"pallet-bridge-grandpa/runtime-benchmarks",
|
||||||
"pallet-bridge-messages/runtime-benchmarks",
|
"pallet-bridge-messages/runtime-benchmarks",
|
||||||
|
|||||||
@@ -1,181 +0,0 @@
|
|||||||
# Helpers for Messages Module Integration
|
|
||||||
|
|
||||||
The [`messages`](./src/messages.rs) module of this crate contains a bunch of helpers for integrating
|
|
||||||
messages module into your runtime. Basic prerequisites of these helpers are:
|
|
||||||
- we're going to bridge Substrate-based chain with another Substrate-based chain;
|
|
||||||
- both chains have [messages module](../../modules/messages/README.md), Substrate bridge
|
|
||||||
module and the [call dispatch module](../../modules/dispatch/README.md);
|
|
||||||
- all message lanes are identical and may be used to transfer the same messages;
|
|
||||||
- the messages sent over the bridge are dispatched using
|
|
||||||
[call dispatch module](../../modules/dispatch/README.md);
|
|
||||||
- the messages are `bp_message_dispatch::MessagePayload` structures, where `call` field is
|
|
||||||
encoded `Call` of the target chain. This means that the `Call` is opaque to the
|
|
||||||
[messages module](../../modules/messages/README.md) instance at the source chain.
|
|
||||||
It is pre-encoded by the message submitter;
|
|
||||||
- all proofs in the [messages module](../../modules/messages/README.md) transactions are
|
|
||||||
based on the storage proofs from the bridged chain: storage proof of the outbound message (value
|
|
||||||
from the `pallet_bridge_messages::Store::MessagePayload` map), storage proof of the outbound lane
|
|
||||||
state (value from the `pallet_bridge_messages::Store::OutboundLanes` map) and storage proof of the
|
|
||||||
inbound lane state (value from the `pallet_bridge_messages::Store::InboundLanes` map);
|
|
||||||
- storage proofs are built at the finalized headers of the corresponding chain. So all message lane
|
|
||||||
transactions with proofs are verifying storage proofs against finalized chain headers from
|
|
||||||
Substrate bridge module.
|
|
||||||
|
|
||||||
**IMPORTANT NOTE**: after reading this document, you may refer to our test runtimes
|
|
||||||
([rialto_messages.rs](../millau/runtime/src/rialto_messages.rs) and/or
|
|
||||||
[millau_messages.rs](../rialto/runtime/src/millau_messages.rs)) to see how to use these helpers.
|
|
||||||
|
|
||||||
## Contents
|
|
||||||
- [`MessageBridge` Trait](#messagebridge-trait)
|
|
||||||
- [`ChainWithMessages` Trait ](#ChainWithMessages-trait)
|
|
||||||
- [Helpers for the Source Chain](#helpers-for-the-source-chain)
|
|
||||||
- [Helpers for the Target Chain](#helpers-for-the-target-chain)
|
|
||||||
|
|
||||||
## `MessageBridge` Trait
|
|
||||||
|
|
||||||
The essence of your integration will be a struct that implements a `MessageBridge` trait. It has
|
|
||||||
single method (`MessageBridge::bridged_balance_to_this_balance`), used to convert from bridged chain
|
|
||||||
tokens into this chain tokens. The bridge also requires two associated types to be specified -
|
|
||||||
`ThisChain` and `BridgedChain`.
|
|
||||||
|
|
||||||
Worth to say that if you're going to use hardcoded constant (conversion rate) in the
|
|
||||||
`MessageBridge::bridged_balance_to_this_balance` method (or in any other method of
|
|
||||||
`ThisChainWithMessages` or `BridgedChainWithMessages` traits), then you should take a
|
|
||||||
look at the
|
|
||||||
[messages parameters functionality](../../modules/messages/README.md#Non-Essential-Functionality).
|
|
||||||
They allow pallet owner to update constants more frequently than runtime upgrade happens.
|
|
||||||
|
|
||||||
## `ChainWithMessages` Trait
|
|
||||||
|
|
||||||
The trait is quite simple and can easily be implemented - you just need to specify types used at the
|
|
||||||
corresponding chain. There is single exception, though (it may be changed in the future):
|
|
||||||
|
|
||||||
- `ChainWithMessages::MessagesInstance`: this is used to compute runtime storage keys. There
|
|
||||||
may be several instances of messages pallet, included in the Runtime. Every instance stores
|
|
||||||
messages and these messages stored under different keys. When we are verifying storage proofs from
|
|
||||||
the bridged chain, we should know which instance we're talking to. This is fine, but there's
|
|
||||||
significant inconvenience with that - this chain runtime must have the same messages pallet
|
|
||||||
instance. This does not necessarily mean that we should use the same instance on both chains -
|
|
||||||
this instance may be used to bridge with another chain/instance, or may not be used at all.
|
|
||||||
|
|
||||||
## `ThisChainWithMessages` Trait
|
|
||||||
|
|
||||||
This trait represents this chain from bridge point of view. Let's review every method of this trait:
|
|
||||||
|
|
||||||
- `ThisChainWithMessages::is_message_accepted`: is used to check whether given lane accepts
|
|
||||||
messages. The send-message origin is passed to the function, so you may e.g. verify that only
|
|
||||||
given pallet is able to send messages over selected lane. **IMPORTANT**: if you assume that the
|
|
||||||
message must be paid by the sender, you must ensure that the sender origin has linked the account
|
|
||||||
for paying message delivery and dispatch fee.
|
|
||||||
|
|
||||||
- `ThisChainWithMessages::maximal_pending_messages_at_outbound_lane`: you should return maximal
|
|
||||||
number of pending (undelivered) messages from this function. Returning small values would require
|
|
||||||
relayers to operate faster and could make message sending logic more complicated. On the other
|
|
||||||
hand, returning large values could lead to chain state growth.
|
|
||||||
|
|
||||||
- `ThisChainWithMessages::estimate_delivery_confirmation_transaction`: you'll need to return
|
|
||||||
estimated size and dispatch weight of the delivery confirmation transaction (that happens on
|
|
||||||
this chain) from this function.
|
|
||||||
|
|
||||||
- `ThisChainWithMessages::transaction_payment`: you'll need to return fee that the submitter
|
|
||||||
must pay for given transaction on this chain. Normally, you would use transaction payment pallet
|
|
||||||
for this. However, if your chain has non-zero fee multiplier set, this would mean that the
|
|
||||||
payment will be computed using current value of this multiplier. But since this transaction
|
|
||||||
will be submitted in the future, you may want to choose other value instead. Otherwise,
|
|
||||||
non-altruistic relayer may choose not to submit this transaction until number of transactions
|
|
||||||
will decrease.
|
|
||||||
|
|
||||||
## `BridgedChainWithMessages` Trait
|
|
||||||
|
|
||||||
This trait represents this chain from bridge point of view. Let's review every method of this trait:
|
|
||||||
|
|
||||||
- `BridgedChainWithMessages::maximal_extrinsic_size`: you will need to return the maximal
|
|
||||||
extrinsic size of the target chain from this function.
|
|
||||||
|
|
||||||
- `MessageBridge::message_weight_limits`: you'll need to return a range of
|
|
||||||
dispatch weights that the outbound message may take at the target chain. Please keep in mind that
|
|
||||||
our helpers assume that the message is an encoded call of the target chain. But we never decode
|
|
||||||
this call at the source chain. So you can't simply get dispatch weight from pre-dispatch
|
|
||||||
information. Instead there are two options to prepare this range: if you know which calls are to
|
|
||||||
be sent over your bridge, then you may just return weight ranges for these particular calls.
|
|
||||||
Otherwise, if you're going to accept all kinds of calls, you may just return range `[0; maximal
|
|
||||||
incoming message dispatch weight]`. If you choose the latter, then you shall remember that the
|
|
||||||
delivery transaction itself has some weight, so you can't accept messages with weight equal to
|
|
||||||
maximal weight of extrinsic at the target chain. In our test chains, we reject all messages that
|
|
||||||
have declared dispatch weight larger than 50% of the maximal bridged extrinsic weight.
|
|
||||||
|
|
||||||
- `MessageBridge::estimate_delivery_transaction`: you will need to return estimated dispatch weight and
|
|
||||||
size of the delivery transaction that delivers a given message to the target chain. The transaction
|
|
||||||
weight must or must not include the weight of pay-dispatch-fee operation, depending on the value
|
|
||||||
of `include_pay_dispatch_fee_cost` argument.
|
|
||||||
|
|
||||||
- `MessageBridge::transaction_payment`: you'll need to return fee that the submitter
|
|
||||||
must pay for given transaction on bridged chain. The best case is when you have the same conversion
|
|
||||||
formula on both chains - then you may just reuse the `ThisChainWithMessages::transaction_payment`
|
|
||||||
implementation. Otherwise, you'll need to hardcode this formula into your runtime.
|
|
||||||
|
|
||||||
## Helpers for the Source Chain
|
|
||||||
|
|
||||||
The helpers for the Source Chain reside in the `source` submodule of the
|
|
||||||
[`messages`](./src/messages.rs) module. The structs are: `FromThisChainMessagePayload`,
|
|
||||||
`FromBridgedChainMessagesDeliveryProof`, `FromThisChainMessageVerifier`. And the helper functions
|
|
||||||
are: `maximal_message_size`, `verify_chain_message`, `verify_messages_delivery_proof` and
|
|
||||||
`estimate_message_dispatch_and_delivery_fee`.
|
|
||||||
|
|
||||||
`FromThisChainMessagePayload` is a message that the sender sends through our bridge. It is the
|
|
||||||
`bp_message_dispatch::MessagePayload`, where `call` field is encoded target chain call. So
|
|
||||||
at this chain we don't see internals of this call - we just know its size.
|
|
||||||
|
|
||||||
`FromThisChainMessageVerifier` is an implementation of `bp_messages::LaneMessageVerifier`. It
|
|
||||||
has following checks in its `verify_message` method:
|
|
||||||
|
|
||||||
1. it'll verify that the used outbound lane is enabled in our runtime;
|
|
||||||
|
|
||||||
1. it'll reject messages if there are too many undelivered outbound messages at this lane. The
|
|
||||||
sender need to wait while relayers will do their work before sending the message again;
|
|
||||||
|
|
||||||
1. it'll reject a message if it has the wrong dispatch origin declared. Like if the submitter is not
|
|
||||||
the root of this chain, but it tries to dispatch the message at the target chain using
|
|
||||||
`bp_message_dispatch::CallOrigin::SourceRoot` origin. Or he has provided wrong signature
|
|
||||||
in the `bp_message_dispatch::CallOrigin::TargetAccount` origin;
|
|
||||||
|
|
||||||
1. it'll reject a message if the delivery and dispatch fee that the submitter wants to pay is lesser
|
|
||||||
than the fee that is computed using the `estimate_message_dispatch_and_delivery_fee` function.
|
|
||||||
|
|
||||||
`estimate_message_dispatch_and_delivery_fee` returns a minimal fee that the submitter needs to pay
|
|
||||||
for sending a given message. The fee includes: payment for the delivery transaction at the target
|
|
||||||
chain, payment for delivery confirmation transaction on this chain, payment for `Call` dispatch at
|
|
||||||
the target chain and relayer interest.
|
|
||||||
|
|
||||||
`FromBridgedChainMessagesDeliveryProof` holds the lane identifier and the storage proof of this
|
|
||||||
inbound lane state at the bridged chain. This also holds the hash of the target chain header, that
|
|
||||||
was used to generate this storage proof. The proof is verified by the
|
|
||||||
`verify_messages_delivery_proof`, which simply checks that the target chain header is finalized
|
|
||||||
(using Substrate bridge module) and then reads the inbound lane state from the proof.
|
|
||||||
|
|
||||||
`verify_chain_message` function checks that the message may be delivered to the bridged chain. There
|
|
||||||
are two main checks:
|
|
||||||
|
|
||||||
1. that the message size is less than or equal to the `2/3` of maximal extrinsic size at the target
|
|
||||||
chain. We leave `1/3` for signed extras and for the storage proof overhead;
|
|
||||||
|
|
||||||
1. that the message dispatch weight is less than or equal to the `1/2` of maximal normal extrinsic
|
|
||||||
weight at the target chain. We leave `1/2` for the delivery transaction overhead.
|
|
||||||
|
|
||||||
## Helpers for the Target Chain
|
|
||||||
|
|
||||||
The helpers for the target chain reside in the `target` submodule of the
|
|
||||||
[`messages`](./src/messages.rs) module. The structs are: `FromBridgedChainMessagePayload`,
|
|
||||||
`FromBridgedChainMessagesProof`, `FromBridgedChainMessagesProof`. And the helper functions are:
|
|
||||||
`maximal_incoming_message_dispatch_weight`, `maximal_incoming_message_size` and
|
|
||||||
`verify_messages_proof`.
|
|
||||||
|
|
||||||
`FromBridgedChainMessagePayload` corresponds to the `FromThisChainMessagePayload` at the bridged
|
|
||||||
chain. We expect that messages with this payload are stored in the `OutboundMessages` storage map of
|
|
||||||
the [messages module](../../modules/messages/README.md). This map is used to build
|
|
||||||
`FromBridgedChainMessagesProof`. The proof holds the lane id, range of message nonces included in
|
|
||||||
the proof, storage proof of `OutboundMessages` entries and the hash of bridged chain header that has
|
|
||||||
been used to build the proof. Additionally, there's storage proof may contain the proof of outbound
|
|
||||||
lane state. It may be required to prune `relayers` entries at this chain (see
|
|
||||||
[messages module documentation](../../modules/messages/README.md#What-about-other-Constants-in-the-Messages-Module-Configuration-Trait)
|
|
||||||
for details). This proof is verified by the `verify_messages_proof` function.
|
|
||||||
@@ -120,10 +120,10 @@ macro_rules! assert_bridge_messages_pallet_types(
|
|||||||
use pallet_bridge_messages::Config as MessagesConfig;
|
use pallet_bridge_messages::Config as MessagesConfig;
|
||||||
use static_assertions::assert_type_eq_all;
|
use static_assertions::assert_type_eq_all;
|
||||||
|
|
||||||
assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload<$bridge>);
|
assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload);
|
||||||
assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundMessageFee, BalanceOf<ThisChain<$bridge>>);
|
assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundMessageFee, BalanceOf<ThisChain<$bridge>>);
|
||||||
|
|
||||||
assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload<$bridge>);
|
assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload);
|
||||||
assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundMessageFee, BalanceOf<BridgedChain<$bridge>>);
|
assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundMessageFee, BalanceOf<BridgedChain<$bridge>>);
|
||||||
assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf<BridgedChain<$bridge>>);
|
assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf<BridgedChain<$bridge>>);
|
||||||
|
|
||||||
|
|||||||
@@ -20,32 +20,21 @@
|
|||||||
//! pallet is used to dispatch incoming messages. Message identified by a tuple
|
//! pallet is used to dispatch incoming messages. Message identified by a tuple
|
||||||
//! of to elements - message lane id and message nonce.
|
//! of to elements - message lane id and message nonce.
|
||||||
|
|
||||||
use bp_message_dispatch::MessageDispatch as _;
|
|
||||||
use bp_messages::{
|
use bp_messages::{
|
||||||
source_chain::LaneMessageVerifier,
|
source_chain::LaneMessageVerifier,
|
||||||
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages},
|
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages},
|
||||||
InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
|
InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
|
||||||
};
|
};
|
||||||
use bp_runtime::{
|
use bp_runtime::{messages::MessageDispatchResult, ChainId, Size, StorageProofChecker};
|
||||||
messages::{DispatchFeePayment, MessageDispatchResult},
|
use codec::{Decode, Encode};
|
||||||
ChainId, Size, StorageProofChecker,
|
use frame_support::{traits::Currency, weights::Weight, RuntimeDebug};
|
||||||
};
|
|
||||||
use codec::{Decode, DecodeLimit, Encode};
|
|
||||||
use frame_support::{
|
|
||||||
traits::{Currency, ExistenceRequirement},
|
|
||||||
weights::{Weight, WeightToFeePolynomial},
|
|
||||||
RuntimeDebug,
|
|
||||||
};
|
|
||||||
use hash_db::Hasher;
|
use hash_db::Hasher;
|
||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul, Saturating, Zero},
|
traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul, Saturating},
|
||||||
FixedPointNumber, FixedPointOperand, FixedU128,
|
FixedPointNumber, FixedPointOperand, FixedU128,
|
||||||
};
|
};
|
||||||
use sp_std::{
|
use sp_std::{cmp::PartialOrd, convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec};
|
||||||
cmp::PartialOrd, convert::TryFrom, fmt::Debug, marker::PhantomData, ops::RangeInclusive,
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
use sp_trie::StorageProof;
|
use sp_trie::StorageProof;
|
||||||
|
|
||||||
/// Bidirectional message bridge.
|
/// Bidirectional message bridge.
|
||||||
@@ -136,16 +125,9 @@ pub trait BridgedChainWithMessages: ChainWithMessages {
|
|||||||
/// Maximal extrinsic size at Bridged chain.
|
/// Maximal extrinsic size at Bridged chain.
|
||||||
fn maximal_extrinsic_size() -> u32;
|
fn maximal_extrinsic_size() -> u32;
|
||||||
|
|
||||||
/// Returns feasible weights range for given message payload at the Bridged chain.
|
/// Returns `true` if message dispatch weight is withing expected limits. `false` means
|
||||||
///
|
/// that the message is too heavy to be sent over the bridge and shall be rejected.
|
||||||
/// If message is being sent with the weight that is out of this range, then it
|
fn verify_dispatch_weight(message_payload: &[u8]) -> bool;
|
||||||
/// should be rejected.
|
|
||||||
///
|
|
||||||
/// Weights returned from this function shall not include transaction overhead
|
|
||||||
/// (like weight of signature and signed extensions verification), because they're
|
|
||||||
/// already accounted by the `weight_of_delivery_transaction`. So this function should
|
|
||||||
/// return pure call dispatch weights range.
|
|
||||||
fn message_weight_limits(message_payload: &[u8]) -> RangeInclusive<Self::Weight>;
|
|
||||||
|
|
||||||
/// Estimate size and weight of single message delivery transaction at the Bridged chain.
|
/// Estimate size and weight of single message delivery transaction at the Bridged chain.
|
||||||
fn estimate_delivery_transaction(
|
fn estimate_delivery_transaction(
|
||||||
@@ -218,12 +200,7 @@ pub mod source {
|
|||||||
pub type BridgedChainOpaqueCall = Vec<u8>;
|
pub type BridgedChainOpaqueCall = Vec<u8>;
|
||||||
|
|
||||||
/// Message payload for This -> Bridged chain messages.
|
/// Message payload for This -> Bridged chain messages.
|
||||||
pub type FromThisChainMessagePayload<B> = bp_message_dispatch::MessagePayload<
|
pub type FromThisChainMessagePayload = Vec<u8>;
|
||||||
AccountIdOf<ThisChain<B>>,
|
|
||||||
SignerOf<BridgedChain<B>>,
|
|
||||||
SignatureOf<BridgedChain<B>>,
|
|
||||||
BridgedChainOpaqueCall,
|
|
||||||
>;
|
|
||||||
|
|
||||||
/// Messages delivery proof from bridged chain:
|
/// Messages delivery proof from bridged chain:
|
||||||
///
|
///
|
||||||
@@ -260,7 +237,6 @@ pub mod source {
|
|||||||
/// This verifier assumes following:
|
/// This verifier assumes following:
|
||||||
///
|
///
|
||||||
/// - all message lanes are equivalent, so all checks are the same;
|
/// - all message lanes are equivalent, so all checks are the same;
|
||||||
/// - messages are being dispatched using `pallet-bridge-dispatch` pallet on the target chain.
|
|
||||||
///
|
///
|
||||||
/// Following checks are made:
|
/// Following checks are made:
|
||||||
///
|
///
|
||||||
@@ -288,7 +264,7 @@ pub mod source {
|
|||||||
LaneMessageVerifier<
|
LaneMessageVerifier<
|
||||||
OriginOf<ThisChain<B>>,
|
OriginOf<ThisChain<B>>,
|
||||||
AccountIdOf<ThisChain<B>>,
|
AccountIdOf<ThisChain<B>>,
|
||||||
FromThisChainMessagePayload<B>,
|
FromThisChainMessagePayload,
|
||||||
BalanceOf<ThisChain<B>>,
|
BalanceOf<ThisChain<B>>,
|
||||||
> for FromThisChainMessageVerifier<B>
|
> for FromThisChainMessageVerifier<B>
|
||||||
where
|
where
|
||||||
@@ -305,7 +281,7 @@ pub mod source {
|
|||||||
delivery_and_dispatch_fee: &BalanceOf<ThisChain<B>>,
|
delivery_and_dispatch_fee: &BalanceOf<ThisChain<B>>,
|
||||||
lane: &LaneId,
|
lane: &LaneId,
|
||||||
lane_outbound_data: &OutboundLaneData,
|
lane_outbound_data: &OutboundLaneData,
|
||||||
payload: &FromThisChainMessagePayload<B>,
|
payload: &FromThisChainMessagePayload,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
// reject message if lane is blocked
|
// reject message if lane is blocked
|
||||||
if !ThisChain::<B>::is_message_accepted(submitter, lane) {
|
if !ThisChain::<B>::is_message_accepted(submitter, lane) {
|
||||||
@@ -321,24 +297,6 @@ pub mod source {
|
|||||||
return Err(TOO_MANY_PENDING_MESSAGES)
|
return Err(TOO_MANY_PENDING_MESSAGES)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the dispatch-specific check. We assume that the target chain uses
|
|
||||||
// `Dispatch`, so we verify the message accordingly.
|
|
||||||
let raw_origin_or_err: Result<
|
|
||||||
frame_system::RawOrigin<AccountIdOf<ThisChain<B>>>,
|
|
||||||
OriginOf<ThisChain<B>>,
|
|
||||||
> = submitter.clone().into();
|
|
||||||
if let Ok(raw_origin) = raw_origin_or_err {
|
|
||||||
pallet_bridge_dispatch::verify_message_origin(&raw_origin, payload)
|
|
||||||
.map(drop)
|
|
||||||
.map_err(|_| BAD_ORIGIN)?;
|
|
||||||
} else {
|
|
||||||
// so what it means that we've failed to convert origin to the
|
|
||||||
// `frame_system::RawOrigin`? now it means that the custom pallet origin has
|
|
||||||
// been used to send the message. Do we need to verify it? The answer is no,
|
|
||||||
// because pallet may craft any origin (e.g. root) && we can't verify whether it
|
|
||||||
// is valid, or not.
|
|
||||||
};
|
|
||||||
|
|
||||||
let minimal_fee_in_this_tokens = estimate_message_dispatch_and_delivery_fee::<B>(
|
let minimal_fee_in_this_tokens = estimate_message_dispatch_and_delivery_fee::<B>(
|
||||||
payload,
|
payload,
|
||||||
B::RELAYER_FEE_PERCENT,
|
B::RELAYER_FEE_PERCENT,
|
||||||
@@ -365,10 +323,9 @@ pub mod source {
|
|||||||
/// may be 'mined' by the target chain. But the lane may have its own checks (e.g. fee
|
/// may be 'mined' by the target chain. But the lane may have its own checks (e.g. fee
|
||||||
/// check) that would reject message (see `FromThisChainMessageVerifier`).
|
/// check) that would reject message (see `FromThisChainMessageVerifier`).
|
||||||
pub fn verify_chain_message<B: MessageBridge>(
|
pub fn verify_chain_message<B: MessageBridge>(
|
||||||
payload: &FromThisChainMessagePayload<B>,
|
payload: &FromThisChainMessagePayload,
|
||||||
) -> Result<(), &'static str> {
|
) -> Result<(), &'static str> {
|
||||||
let weight_limits = BridgedChain::<B>::message_weight_limits(&payload.call);
|
if !BridgedChain::<B>::verify_dispatch_weight(payload) {
|
||||||
if !weight_limits.contains(&payload.weight.into()) {
|
|
||||||
return Err("Incorrect message weight declared")
|
return Err("Incorrect message weight declared")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,7 +339,7 @@ pub mod source {
|
|||||||
// is enormously large, it should be several dozens/hundreds of bytes. The delivery
|
// is enormously large, it should be several dozens/hundreds of bytes. The delivery
|
||||||
// transaction also contains signatures and signed extensions. Because of this, we reserve
|
// 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.call.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("The message is too large to be sent over the lane")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +352,7 @@ pub mod source {
|
|||||||
/// The fee is paid in This chain Balance, but we use Bridged chain balance to avoid additional
|
/// The fee is paid in This chain Balance, but we use Bridged chain balance to avoid additional
|
||||||
/// conversions. Returns `None` if overflow has happened.
|
/// conversions. Returns `None` if overflow has happened.
|
||||||
pub fn estimate_message_dispatch_and_delivery_fee<B: MessageBridge>(
|
pub fn estimate_message_dispatch_and_delivery_fee<B: MessageBridge>(
|
||||||
payload: &FromThisChainMessagePayload<B>,
|
payload: &FromThisChainMessagePayload,
|
||||||
relayer_fee_percent: u32,
|
relayer_fee_percent: u32,
|
||||||
bridged_to_this_conversion_rate: Option<FixedU128>,
|
bridged_to_this_conversion_rate: Option<FixedU128>,
|
||||||
) -> Result<BalanceOf<ThisChain<B>>, &'static str> {
|
) -> Result<BalanceOf<ThisChain<B>>, &'static str> {
|
||||||
@@ -403,13 +360,8 @@ pub mod source {
|
|||||||
//
|
//
|
||||||
// if we're going to pay dispatch fee at the target chain, then we don't include weight
|
// if we're going to pay dispatch fee at the target chain, then we don't include weight
|
||||||
// of the message dispatch in the delivery transaction cost
|
// of the message dispatch in the delivery transaction cost
|
||||||
let pay_dispatch_fee_at_target_chain =
|
let delivery_transaction =
|
||||||
payload.dispatch_fee_payment == DispatchFeePayment::AtTargetChain;
|
BridgedChain::<B>::estimate_delivery_transaction(&payload.encode(), true, 0.into());
|
||||||
let delivery_transaction = BridgedChain::<B>::estimate_delivery_transaction(
|
|
||||||
&payload.encode(),
|
|
||||||
pay_dispatch_fee_at_target_chain,
|
|
||||||
if pay_dispatch_fee_at_target_chain { 0.into() } else { payload.weight.into() },
|
|
||||||
);
|
|
||||||
let delivery_transaction_fee = BridgedChain::<B>::transaction_payment(delivery_transaction);
|
let delivery_transaction_fee = BridgedChain::<B>::transaction_payment(delivery_transaction);
|
||||||
|
|
||||||
// the fee (in This tokens) of all transactions that are made on This chain
|
// the fee (in This tokens) of all transactions that are made on This chain
|
||||||
@@ -477,20 +429,8 @@ pub mod source {
|
|||||||
pub mod target {
|
pub mod target {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Call origin for Bridged -> This chain messages.
|
|
||||||
pub type FromBridgedChainMessageCallOrigin<B> = bp_message_dispatch::CallOrigin<
|
|
||||||
AccountIdOf<BridgedChain<B>>,
|
|
||||||
SignerOf<ThisChain<B>>,
|
|
||||||
SignatureOf<ThisChain<B>>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
/// Decoded Bridged -> This message payload.
|
/// Decoded Bridged -> This message payload.
|
||||||
pub type FromBridgedChainMessagePayload<B> = bp_message_dispatch::MessagePayload<
|
pub type FromBridgedChainMessagePayload = Vec<u8>;
|
||||||
AccountIdOf<BridgedChain<B>>,
|
|
||||||
SignerOf<ThisChain<B>>,
|
|
||||||
SignatureOf<ThisChain<B>>,
|
|
||||||
FromBridgedChainEncodedMessageCall<CallOf<ThisChain<B>>>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
/// Messages proof from bridged chain:
|
/// Messages proof from bridged chain:
|
||||||
///
|
///
|
||||||
@@ -522,35 +462,6 @@ pub mod target {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encoded Call of This chain as it is transferred over bridge.
|
|
||||||
///
|
|
||||||
/// Our Call is opaque (`Vec<u8>`) for Bridged chain. So it is encoded, prefixed with
|
|
||||||
/// vector length. Custom decode implementation here is exactly to deal with this.
|
|
||||||
#[derive(Decode, Encode, RuntimeDebug, PartialEq)]
|
|
||||||
pub struct FromBridgedChainEncodedMessageCall<DecodedCall> {
|
|
||||||
encoded_call: Vec<u8>,
|
|
||||||
_marker: PhantomData<DecodedCall>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<DecodedCall> FromBridgedChainEncodedMessageCall<DecodedCall> {
|
|
||||||
/// Create encoded call.
|
|
||||||
pub fn new(encoded_call: Vec<u8>) -> Self {
|
|
||||||
FromBridgedChainEncodedMessageCall { encoded_call, _marker: PhantomData::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<DecodedCall: Decode> From<FromBridgedChainEncodedMessageCall<DecodedCall>>
|
|
||||||
for Result<DecodedCall, ()>
|
|
||||||
{
|
|
||||||
fn from(encoded_call: FromBridgedChainEncodedMessageCall<DecodedCall>) -> Self {
|
|
||||||
DecodedCall::decode_with_depth_limit(
|
|
||||||
sp_api::MAX_EXTRINSIC_DEPTH,
|
|
||||||
&mut &encoded_call.encoded_call[..],
|
|
||||||
)
|
|
||||||
.map_err(drop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dispatching Bridged -> This chain messages.
|
/// Dispatching Bridged -> This chain messages.
|
||||||
#[derive(RuntimeDebug, Clone, Copy)]
|
#[derive(RuntimeDebug, Clone, Copy)]
|
||||||
pub struct FromBridgedChainMessageDispatch<B, ThisRuntime, ThisCurrency, ThisDispatchInstance> {
|
pub struct FromBridgedChainMessageDispatch<B, ThisRuntime, ThisCurrency, ThisDispatchInstance> {
|
||||||
@@ -563,60 +474,33 @@ pub mod target {
|
|||||||
where
|
where
|
||||||
BalanceOf<ThisChain<B>>: Saturating + FixedPointOperand,
|
BalanceOf<ThisChain<B>>: Saturating + FixedPointOperand,
|
||||||
ThisDispatchInstance: 'static,
|
ThisDispatchInstance: 'static,
|
||||||
ThisRuntime: pallet_bridge_dispatch::Config<
|
ThisRuntime: pallet_transaction_payment::Config,
|
||||||
ThisDispatchInstance,
|
|
||||||
BridgeMessageId = (LaneId, MessageNonce),
|
|
||||||
> + pallet_transaction_payment::Config,
|
|
||||||
<ThisRuntime as pallet_transaction_payment::Config>::OnChargeTransaction:
|
<ThisRuntime as pallet_transaction_payment::Config>::OnChargeTransaction:
|
||||||
pallet_transaction_payment::OnChargeTransaction<
|
pallet_transaction_payment::OnChargeTransaction<
|
||||||
ThisRuntime,
|
ThisRuntime,
|
||||||
Balance = BalanceOf<ThisChain<B>>,
|
Balance = BalanceOf<ThisChain<B>>,
|
||||||
>,
|
>,
|
||||||
ThisCurrency: Currency<AccountIdOf<ThisChain<B>>, Balance = BalanceOf<ThisChain<B>>>,
|
ThisCurrency: Currency<AccountIdOf<ThisChain<B>>, Balance = BalanceOf<ThisChain<B>>>,
|
||||||
pallet_bridge_dispatch::Pallet<ThisRuntime, ThisDispatchInstance>:
|
|
||||||
bp_message_dispatch::MessageDispatch<
|
|
||||||
AccountIdOf<ThisChain<B>>,
|
|
||||||
(LaneId, MessageNonce),
|
|
||||||
Message = FromBridgedChainMessagePayload<B>,
|
|
||||||
>,
|
|
||||||
{
|
{
|
||||||
type DispatchPayload = FromBridgedChainMessagePayload<B>;
|
type DispatchPayload = FromBridgedChainMessagePayload;
|
||||||
|
|
||||||
fn dispatch_weight(
|
fn dispatch_weight(
|
||||||
message: &DispatchMessage<Self::DispatchPayload, BalanceOf<BridgedChain<B>>>,
|
_message: &DispatchMessage<Self::DispatchPayload, BalanceOf<BridgedChain<B>>>,
|
||||||
) -> frame_support::weights::Weight {
|
) -> frame_support::weights::Weight {
|
||||||
message.data.payload.as_ref().map(|payload| payload.weight).unwrap_or(0)
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch(
|
fn dispatch(
|
||||||
relayer_account: &AccountIdOf<ThisChain<B>>,
|
_relayer_account: &AccountIdOf<ThisChain<B>>,
|
||||||
message: DispatchMessage<Self::DispatchPayload, BalanceOf<BridgedChain<B>>>,
|
message: DispatchMessage<Self::DispatchPayload, BalanceOf<BridgedChain<B>>>,
|
||||||
) -> MessageDispatchResult {
|
) -> MessageDispatchResult {
|
||||||
let message_id = (message.key.lane_id, message.key.nonce);
|
let message_id = (message.key.lane_id, message.key.nonce);
|
||||||
pallet_bridge_dispatch::Pallet::<ThisRuntime, ThisDispatchInstance>::dispatch(
|
log::trace!(target: "runtime::bridge-dispatch", "Incoming message {:?}: {:?}", message_id, message.data.payload);
|
||||||
B::BRIDGED_CHAIN_ID,
|
MessageDispatchResult {
|
||||||
B::THIS_CHAIN_ID,
|
dispatch_result: true,
|
||||||
message_id,
|
unspent_weight: 0,
|
||||||
message.data.payload.map_err(drop),
|
dispatch_fee_paid_during_dispatch: false,
|
||||||
|dispatch_origin, dispatch_weight| {
|
}
|
||||||
let unadjusted_weight_fee = ThisRuntime::WeightToFee::calc(&dispatch_weight);
|
|
||||||
let fee_multiplier =
|
|
||||||
pallet_transaction_payment::Pallet::<ThisRuntime>::next_fee_multiplier();
|
|
||||||
let adjusted_weight_fee =
|
|
||||||
fee_multiplier.saturating_mul_int(unadjusted_weight_fee);
|
|
||||||
if !adjusted_weight_fee.is_zero() {
|
|
||||||
ThisCurrency::transfer(
|
|
||||||
dispatch_origin,
|
|
||||||
relayer_account,
|
|
||||||
adjusted_weight_fee,
|
|
||||||
ExistenceRequirement::AllowDeath,
|
|
||||||
)
|
|
||||||
.map_err(drop)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,7 +697,8 @@ mod tests {
|
|||||||
const THIS_CHAIN_WEIGHT_TO_BALANCE_RATE: Weight = 2;
|
const THIS_CHAIN_WEIGHT_TO_BALANCE_RATE: Weight = 2;
|
||||||
const BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE: Weight = 4;
|
const BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE: Weight = 4;
|
||||||
const BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE: u32 = 6;
|
const BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE: u32 = 6;
|
||||||
const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: Weight = 2048;
|
const BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT: usize = 5;
|
||||||
|
const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048;
|
||||||
const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024;
|
const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024;
|
||||||
|
|
||||||
/// Bridge that is deployed on ThisChain and allows sending/receiving messages to/from
|
/// Bridge that is deployed on ThisChain and allows sending/receiving messages to/from
|
||||||
@@ -1016,7 +901,7 @@ mod tests {
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive<Self::Weight> {
|
fn verify_dispatch_weight(_message_payload: &[u8]) -> bool {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1074,10 +959,9 @@ mod tests {
|
|||||||
BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
|
BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_weight_limits(message_payload: &[u8]) -> RangeInclusive<Self::Weight> {
|
fn verify_dispatch_weight(message_payload: &[u8]) -> bool {
|
||||||
let begin =
|
message_payload.len() >= BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT &&
|
||||||
std::cmp::min(BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT, message_payload.len() as Weight);
|
message_payload.len() <= BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT
|
||||||
begin..=BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn estimate_delivery_transaction(
|
fn estimate_delivery_transaction(
|
||||||
@@ -1102,57 +986,16 @@ mod tests {
|
|||||||
OutboundLaneData::default()
|
OutboundLaneData::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn message_from_bridged_chain_is_decoded() {
|
|
||||||
// the message is encoded on the bridged chain
|
|
||||||
let message_on_bridged_chain =
|
|
||||||
source::FromThisChainMessagePayload::<OnBridgedChainBridge> {
|
|
||||||
spec_version: 1,
|
|
||||||
weight: 100,
|
|
||||||
origin: bp_message_dispatch::CallOrigin::SourceRoot,
|
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
|
|
||||||
call: ThisChainCall::Transfer.encode(),
|
|
||||||
}
|
|
||||||
.encode();
|
|
||||||
|
|
||||||
// and sent to this chain where it is decoded
|
|
||||||
let message_on_this_chain =
|
|
||||||
target::FromBridgedChainMessagePayload::<OnThisChainBridge>::decode(
|
|
||||||
&mut &message_on_bridged_chain[..],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
message_on_this_chain,
|
|
||||||
target::FromBridgedChainMessagePayload::<OnThisChainBridge> {
|
|
||||||
spec_version: 1,
|
|
||||||
weight: 100,
|
|
||||||
origin: bp_message_dispatch::CallOrigin::SourceRoot,
|
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
|
|
||||||
call: target::FromBridgedChainEncodedMessageCall::<ThisChainCall>::new(
|
|
||||||
ThisChainCall::Transfer.encode(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
assert_eq!(Ok(ThisChainCall::Transfer), message_on_this_chain.call.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
const TEST_LANE_ID: &LaneId = b"test";
|
const TEST_LANE_ID: &LaneId = b"test";
|
||||||
const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32;
|
const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32;
|
||||||
|
|
||||||
fn regular_outbound_message_payload() -> source::FromThisChainMessagePayload<OnThisChainBridge>
|
fn regular_outbound_message_payload() -> source::FromThisChainMessagePayload {
|
||||||
{
|
vec![42]
|
||||||
source::FromThisChainMessagePayload::<OnThisChainBridge> {
|
|
||||||
spec_version: 1,
|
|
||||||
weight: 100,
|
|
||||||
origin: bp_message_dispatch::CallOrigin::SourceRoot,
|
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
|
|
||||||
call: vec![42],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn message_fee_is_checked_by_verifier() {
|
fn message_fee_is_checked_by_verifier() {
|
||||||
const EXPECTED_MINIMAL_FEE: u32 = 5500;
|
const EXPECTED_MINIMAL_FEE: u32 = 2860;
|
||||||
|
|
||||||
// payload of the This -> Bridged chain message
|
// payload of the This -> Bridged chain message
|
||||||
let payload = regular_outbound_message_payload();
|
let payload = regular_outbound_message_payload();
|
||||||
@@ -1167,25 +1010,6 @@ mod tests {
|
|||||||
Ok(ThisChainBalance(EXPECTED_MINIMAL_FEE)),
|
Ok(ThisChainBalance(EXPECTED_MINIMAL_FEE)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// let's check if estimation is less than hardcoded, if dispatch is paid at target chain
|
|
||||||
let mut payload_with_pay_on_target = regular_outbound_message_payload();
|
|
||||||
payload_with_pay_on_target.dispatch_fee_payment = DispatchFeePayment::AtTargetChain;
|
|
||||||
let fee_at_source =
|
|
||||||
source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>(
|
|
||||||
&payload_with_pay_on_target,
|
|
||||||
OnThisChainBridge::RELAYER_FEE_PERCENT,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.expect(
|
|
||||||
"estimate_message_dispatch_and_delivery_fee failed for pay-at-target-chain message",
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
fee_at_source < EXPECTED_MINIMAL_FEE.into(),
|
|
||||||
"Computed fee {:?} without prepaid dispatch must be less than the fee with prepaid dispatch {}",
|
|
||||||
fee_at_source,
|
|
||||||
EXPECTED_MINIMAL_FEE,
|
|
||||||
);
|
|
||||||
|
|
||||||
// and now check that the verifier checks the fee
|
// and now check that the verifier checks the fee
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
|
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
|
||||||
@@ -1207,80 +1031,6 @@ mod tests {
|
|||||||
.is_ok(),);
|
.is_ok(),);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_disallow_root_calls_from_regular_accounts() {
|
|
||||||
// payload of the This -> Bridged chain message
|
|
||||||
let payload = source::FromThisChainMessagePayload::<OnThisChainBridge> {
|
|
||||||
spec_version: 1,
|
|
||||||
weight: 100,
|
|
||||||
origin: bp_message_dispatch::CallOrigin::SourceRoot,
|
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
|
|
||||||
call: vec![42],
|
|
||||||
};
|
|
||||||
|
|
||||||
// and now check that the verifier checks the fee
|
|
||||||
assert_eq!(
|
|
||||||
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
|
|
||||||
&ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))),
|
|
||||||
&ThisChainBalance(1_000_000),
|
|
||||||
TEST_LANE_ID,
|
|
||||||
&test_lane_outbound_data(),
|
|
||||||
&payload,
|
|
||||||
),
|
|
||||||
Err(source::BAD_ORIGIN)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
|
|
||||||
&ThisChainOrigin(Ok(frame_system::RawOrigin::None)),
|
|
||||||
&ThisChainBalance(1_000_000),
|
|
||||||
TEST_LANE_ID,
|
|
||||||
&test_lane_outbound_data(),
|
|
||||||
&payload,
|
|
||||||
),
|
|
||||||
Err(source::BAD_ORIGIN)
|
|
||||||
);
|
|
||||||
assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
|
|
||||||
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
|
|
||||||
&ThisChainBalance(1_000_000),
|
|
||||||
TEST_LANE_ID,
|
|
||||||
&test_lane_outbound_data(),
|
|
||||||
&payload,
|
|
||||||
)
|
|
||||||
.is_ok(),);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_verify_source_and_target_origin_matching() {
|
|
||||||
// payload of the This -> Bridged chain message
|
|
||||||
let payload = source::FromThisChainMessagePayload::<OnThisChainBridge> {
|
|
||||||
spec_version: 1,
|
|
||||||
weight: 100,
|
|
||||||
origin: bp_message_dispatch::CallOrigin::SourceAccount(ThisChainAccountId(1)),
|
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
|
|
||||||
call: vec![42],
|
|
||||||
};
|
|
||||||
|
|
||||||
// and now check that the verifier checks the fee
|
|
||||||
assert_eq!(
|
|
||||||
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
|
|
||||||
&ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))),
|
|
||||||
&ThisChainBalance(1_000_000),
|
|
||||||
TEST_LANE_ID,
|
|
||||||
&test_lane_outbound_data(),
|
|
||||||
&payload,
|
|
||||||
),
|
|
||||||
Err(source::BAD_ORIGIN)
|
|
||||||
);
|
|
||||||
assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
|
|
||||||
&ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(1)))),
|
|
||||||
&ThisChainBalance(1_000_000),
|
|
||||||
TEST_LANE_ID,
|
|
||||||
&test_lane_outbound_data(),
|
|
||||||
&payload,
|
|
||||||
)
|
|
||||||
.is_ok(),);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn message_is_rejected_when_sent_using_disabled_lane() {
|
fn message_is_rejected_when_sent_using_disabled_lane() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1315,58 +1065,42 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_chain_message_rejects_message_with_too_small_declared_weight() {
|
fn verify_chain_message_rejects_message_with_too_small_declared_weight() {
|
||||||
assert!(source::verify_chain_message::<OnThisChainBridge>(
|
assert!(source::verify_chain_message::<OnThisChainBridge>(&vec![
|
||||||
&source::FromThisChainMessagePayload::<OnThisChainBridge> {
|
42;
|
||||||
spec_version: 1,
|
BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT -
|
||||||
weight: 5,
|
1
|
||||||
origin: bp_message_dispatch::CallOrigin::SourceRoot,
|
])
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
|
|
||||||
call: vec![1, 2, 3, 4, 5, 6],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_chain_message_rejects_message_with_too_large_declared_weight() {
|
fn verify_chain_message_rejects_message_with_too_large_declared_weight() {
|
||||||
assert!(source::verify_chain_message::<OnThisChainBridge>(
|
assert!(source::verify_chain_message::<OnThisChainBridge>(&vec![
|
||||||
&source::FromThisChainMessagePayload::<OnThisChainBridge> {
|
42;
|
||||||
spec_version: 1,
|
BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT -
|
||||||
weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT + 1,
|
1
|
||||||
origin: bp_message_dispatch::CallOrigin::SourceRoot,
|
])
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
|
|
||||||
call: vec![1, 2, 3, 4, 5, 6],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_chain_message_rejects_message_too_large_message() {
|
fn verify_chain_message_rejects_message_too_large_message() {
|
||||||
assert!(source::verify_chain_message::<OnThisChainBridge>(
|
assert!(source::verify_chain_message::<OnThisChainBridge>(&vec![
|
||||||
&source::FromThisChainMessagePayload::<OnThisChainBridge> {
|
0;
|
||||||
spec_version: 1,
|
source::maximal_message_size::<OnThisChainBridge>()
|
||||||
weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT,
|
as usize + 1
|
||||||
origin: bp_message_dispatch::CallOrigin::SourceRoot,
|
],)
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
|
|
||||||
call: vec![0; source::maximal_message_size::<OnThisChainBridge>() as usize + 1],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_chain_message_accepts_maximal_message() {
|
fn verify_chain_message_accepts_maximal_message() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
source::verify_chain_message::<OnThisChainBridge>(
|
source::verify_chain_message::<OnThisChainBridge>(&vec![
|
||||||
&source::FromThisChainMessagePayload::<OnThisChainBridge> {
|
0;
|
||||||
spec_version: 1,
|
source::maximal_message_size::<OnThisChainBridge>()
|
||||||
weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT,
|
as _
|
||||||
origin: bp_message_dispatch::CallOrigin::SourceRoot,
|
],),
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
|
|
||||||
call: vec![0; source::maximal_message_size::<OnThisChainBridge>() as _],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Ok(()),
|
Ok(()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,9 @@
|
|||||||
|
|
||||||
//! Helpers for implementing various message-related runtime API mthods.
|
//! Helpers for implementing various message-related runtime API mthods.
|
||||||
|
|
||||||
use crate::messages::{source::FromThisChainMessagePayload, MessageBridge};
|
use crate::messages::MessageBridge;
|
||||||
|
|
||||||
use bp_messages::{LaneId, MessageDetails, MessageNonce};
|
use bp_messages::{LaneId, MessageDetails, MessageNonce};
|
||||||
use codec::Decode;
|
|
||||||
use sp_std::vec::Vec;
|
use sp_std::vec::Vec;
|
||||||
|
|
||||||
/// Implementation of the `To*OutboundLaneApi::message_details`.
|
/// Implementation of the `To*OutboundLaneApi::message_details`.
|
||||||
@@ -37,14 +36,12 @@ where
|
|||||||
.filter_map(|nonce| {
|
.filter_map(|nonce| {
|
||||||
let message_data =
|
let message_data =
|
||||||
pallet_bridge_messages::Pallet::<Runtime, MessagesPalletInstance>::outbound_message_data(lane, nonce)?;
|
pallet_bridge_messages::Pallet::<Runtime, MessagesPalletInstance>::outbound_message_data(lane, nonce)?;
|
||||||
let decoded_payload =
|
|
||||||
FromThisChainMessagePayload::<BridgeConfig>::decode(&mut &message_data.payload[..]).ok()?;
|
|
||||||
Some(MessageDetails {
|
Some(MessageDetails {
|
||||||
nonce,
|
nonce,
|
||||||
dispatch_weight: decoded_payload.weight,
|
dispatch_weight: 0,
|
||||||
size: message_data.payload.len() as _,
|
size: message_data.payload.len() as _,
|
||||||
delivery_and_dispatch_fee: message_data.fee,
|
delivery_and_dispatch_fee: message_data.fee,
|
||||||
dispatch_fee_payment: decoded_payload.dispatch_fee_payment,
|
dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtTargetChain,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|||||||
@@ -27,13 +27,8 @@ use crate::messages::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use bp_messages::{storage_keys, MessageData, MessageKey, MessagePayload};
|
use bp_messages::{storage_keys, MessageData, MessageKey, MessagePayload};
|
||||||
use bp_runtime::{messages::DispatchFeePayment, ChainId};
|
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use ed25519_dalek::{PublicKey, SecretKey, Signer, KEYPAIR_LENGTH, SECRET_KEY_LENGTH};
|
use frame_support::weights::{GetDispatchInfo, Weight};
|
||||||
use frame_support::{
|
|
||||||
traits::Currency,
|
|
||||||
weights::{GetDispatchInfo, Weight},
|
|
||||||
};
|
|
||||||
use pallet_bridge_messages::benchmarking::{
|
use pallet_bridge_messages::benchmarking::{
|
||||||
MessageDeliveryProofParams, MessageParams, MessageProofParams, ProofSize,
|
MessageDeliveryProofParams, MessageParams, MessageProofParams, ProofSize,
|
||||||
};
|
};
|
||||||
@@ -41,49 +36,16 @@ use sp_core::Hasher;
|
|||||||
use sp_runtime::traits::{Header, IdentifyAccount, MaybeSerializeDeserialize, Zero};
|
use sp_runtime::traits::{Header, IdentifyAccount, MaybeSerializeDeserialize, Zero};
|
||||||
use sp_std::{fmt::Debug, prelude::*};
|
use sp_std::{fmt::Debug, prelude::*};
|
||||||
use sp_trie::{record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut};
|
use sp_trie::{record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut};
|
||||||
use sp_version::RuntimeVersion;
|
|
||||||
|
|
||||||
/// Return this chain account, used to dispatch message.
|
|
||||||
pub fn dispatch_account<B>() -> AccountIdOf<ThisChain<B>>
|
|
||||||
where
|
|
||||||
B: MessageBridge,
|
|
||||||
SignerOf<ThisChain<B>>:
|
|
||||||
From<sp_core::ed25519::Public> + IdentifyAccount<AccountId = AccountIdOf<ThisChain<B>>>,
|
|
||||||
{
|
|
||||||
let this_raw_public = PublicKey::from(&dispatch_account_secret());
|
|
||||||
let this_public: SignerOf<ThisChain<B>> =
|
|
||||||
sp_core::ed25519::Public::from_raw(this_raw_public.to_bytes()).into();
|
|
||||||
this_public.into_account()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return public key of this chain account, used to dispatch message.
|
|
||||||
pub fn dispatch_account_secret() -> SecretKey {
|
|
||||||
// key from the repo example (https://docs.rs/ed25519-dalek/1.0.1/ed25519_dalek/struct.SecretKey.html)
|
|
||||||
SecretKey::from_bytes(&[
|
|
||||||
157, 097, 177, 157, 239, 253, 090, 096, 186, 132, 074, 244, 146, 236, 044, 196, 068, 073,
|
|
||||||
197, 105, 123, 050, 105, 025, 112, 059, 172, 003, 028, 174, 127, 096,
|
|
||||||
])
|
|
||||||
.expect("harcoded key is valid")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepare outbound message for the `send_message` call.
|
/// Prepare outbound message for the `send_message` call.
|
||||||
pub fn prepare_outbound_message<B>(
|
pub fn prepare_outbound_message<B>(
|
||||||
params: MessageParams<AccountIdOf<ThisChain<B>>>,
|
params: MessageParams<AccountIdOf<ThisChain<B>>>,
|
||||||
) -> FromThisChainMessagePayload<B>
|
) -> FromThisChainMessagePayload
|
||||||
where
|
where
|
||||||
B: MessageBridge,
|
B: MessageBridge,
|
||||||
BalanceOf<ThisChain<B>>: From<u64>,
|
BalanceOf<ThisChain<B>>: From<u64>,
|
||||||
{
|
{
|
||||||
let message_payload = vec![0; params.size as usize];
|
vec![0; params.size as usize]
|
||||||
let dispatch_origin = bp_message_dispatch::CallOrigin::SourceAccount(params.sender_account);
|
|
||||||
|
|
||||||
FromThisChainMessagePayload::<B> {
|
|
||||||
spec_version: 0,
|
|
||||||
weight: params.size as _,
|
|
||||||
origin: dispatch_origin,
|
|
||||||
call: message_payload,
|
|
||||||
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepare proof of messages for the `receive_messages_proof` call.
|
/// Prepare proof of messages for the `receive_messages_proof` call.
|
||||||
@@ -92,8 +54,6 @@ where
|
|||||||
/// proof.
|
/// proof.
|
||||||
pub fn prepare_message_proof<R, BI, FI, B, BH, BHH>(
|
pub fn prepare_message_proof<R, BI, FI, B, BH, BHH>(
|
||||||
params: MessageProofParams,
|
params: MessageProofParams,
|
||||||
version: &RuntimeVersion,
|
|
||||||
endow_amount: BalanceOf<ThisChain<B>>,
|
|
||||||
) -> (FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, Weight)
|
) -> (FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, Weight)
|
||||||
where
|
where
|
||||||
R: frame_system::Config<AccountId = AccountIdOf<ThisChain<B>>>
|
R: frame_system::Config<AccountId = AccountIdOf<ThisChain<B>>>
|
||||||
@@ -115,51 +75,10 @@ where
|
|||||||
+ From<sp_core::ed25519::Public>
|
+ From<sp_core::ed25519::Public>
|
||||||
+ IdentifyAccount<AccountId = AccountIdOf<ThisChain<B>>>,
|
+ IdentifyAccount<AccountId = AccountIdOf<ThisChain<B>>>,
|
||||||
{
|
{
|
||||||
// we'll be dispatching the same call at This chain
|
let message_payload = match params.size {
|
||||||
let remark = match params.size {
|
|
||||||
ProofSize::Minimal(ref size) => vec![0u8; *size as _],
|
ProofSize::Minimal(ref size) => vec![0u8; *size as _],
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
};
|
};
|
||||||
let call: CallOf<ThisChain<B>> = frame_system::Call::remark { remark }.into();
|
|
||||||
let call_weight = call.get_dispatch_info().weight;
|
|
||||||
|
|
||||||
// message payload needs to be signed, because we use `TargetAccount` call origin
|
|
||||||
// (which is 'heaviest' to verify)
|
|
||||||
let bridged_account_id: AccountIdOf<BridgedChain<B>> = [0u8; 32].into();
|
|
||||||
let (this_raw_public, this_raw_signature) = ed25519_sign(
|
|
||||||
&call,
|
|
||||||
&bridged_account_id,
|
|
||||||
version.spec_version,
|
|
||||||
B::BRIDGED_CHAIN_ID,
|
|
||||||
B::THIS_CHAIN_ID,
|
|
||||||
);
|
|
||||||
let this_public: SignerOf<ThisChain<B>> =
|
|
||||||
sp_core::ed25519::Public::from_raw(this_raw_public).into();
|
|
||||||
let this_signature: SignatureOf<ThisChain<B>> =
|
|
||||||
sp_core::ed25519::Signature::from_raw(this_raw_signature).into();
|
|
||||||
|
|
||||||
// if dispatch fee is paid at this chain, endow relayer account
|
|
||||||
if params.dispatch_fee_payment == DispatchFeePayment::AtTargetChain {
|
|
||||||
assert_eq!(this_public.clone().into_account(), dispatch_account::<B>());
|
|
||||||
pallet_balances::Pallet::<R, BI>::make_free_balance_be(
|
|
||||||
&this_public.clone().into_account(),
|
|
||||||
endow_amount,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare message payload that is stored in the Bridged chain storage
|
|
||||||
let message_payload = bp_message_dispatch::MessagePayload {
|
|
||||||
spec_version: version.spec_version,
|
|
||||||
weight: call_weight,
|
|
||||||
origin: bp_message_dispatch::CallOrigin::<
|
|
||||||
AccountIdOf<BridgedChain<B>>,
|
|
||||||
SignerOf<ThisChain<B>>,
|
|
||||||
SignatureOf<ThisChain<B>>,
|
|
||||||
>::TargetAccount(bridged_account_id, this_public, this_signature),
|
|
||||||
dispatch_fee_payment: params.dispatch_fee_payment.clone(),
|
|
||||||
call: call.encode(),
|
|
||||||
}
|
|
||||||
.encode();
|
|
||||||
|
|
||||||
// finally - prepare storage proof and update environment
|
// finally - prepare storage proof and update environment
|
||||||
let (state_root, storage_proof) =
|
let (state_root, storage_proof) =
|
||||||
@@ -174,11 +93,7 @@ where
|
|||||||
nonces_start: *params.message_nonces.start(),
|
nonces_start: *params.message_nonces.start(),
|
||||||
nonces_end: *params.message_nonces.end(),
|
nonces_end: *params.message_nonces.end(),
|
||||||
},
|
},
|
||||||
call_weight
|
0,
|
||||||
.checked_mul(
|
|
||||||
params.message_nonces.end().saturating_sub(*params.message_nonces.start()) + 1,
|
|
||||||
)
|
|
||||||
.expect("too many messages requested by benchmark"),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,40 +227,6 @@ where
|
|||||||
bridged_header_hash
|
bridged_header_hash
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate ed25519 signature to be used in
|
|
||||||
/// `pallet_brdige_call_dispatch::CallOrigin::TargetAccount`.
|
|
||||||
///
|
|
||||||
/// Returns public key of the signer and the signature itself.
|
|
||||||
fn ed25519_sign(
|
|
||||||
target_call: &impl Encode,
|
|
||||||
source_account_id: &impl Encode,
|
|
||||||
target_spec_version: u32,
|
|
||||||
source_chain_id: ChainId,
|
|
||||||
target_chain_id: ChainId,
|
|
||||||
) -> ([u8; 32], [u8; 64]) {
|
|
||||||
let target_secret = dispatch_account_secret();
|
|
||||||
let target_public: PublicKey = (&target_secret).into();
|
|
||||||
|
|
||||||
let mut target_pair_bytes = [0u8; KEYPAIR_LENGTH];
|
|
||||||
target_pair_bytes[..SECRET_KEY_LENGTH].copy_from_slice(&target_secret.to_bytes());
|
|
||||||
target_pair_bytes[SECRET_KEY_LENGTH..].copy_from_slice(&target_public.to_bytes());
|
|
||||||
let target_pair =
|
|
||||||
ed25519_dalek::Keypair::from_bytes(&target_pair_bytes).expect("hardcoded pair is valid");
|
|
||||||
|
|
||||||
let signature_message = pallet_bridge_dispatch::account_ownership_digest(
|
|
||||||
target_call,
|
|
||||||
source_account_id,
|
|
||||||
target_spec_version,
|
|
||||||
source_chain_id,
|
|
||||||
target_chain_id,
|
|
||||||
);
|
|
||||||
let target_origin_signature = target_pair
|
|
||||||
.try_sign(&signature_message)
|
|
||||||
.expect("Ed25519 try_sign should not fail in benchmarks");
|
|
||||||
|
|
||||||
(target_public.to_bytes(), target_origin_signature.to_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Populate trie with dummy keys+values until trie has at least given size.
|
/// Populate trie with dummy keys+values until trie has at least given size.
|
||||||
fn grow_trie<H: Hasher>(mut root: H::Out, mdb: &mut MemoryDB<H>, trie_size: ProofSize) -> H::Out {
|
fn grow_trie<H: Hasher>(mut root: H::Out, mdb: &mut MemoryDB<H>, trie_size: ProofSize) -> H::Out {
|
||||||
let (iterations, leaf_size, minimal_trie_size) = match trie_size {
|
let (iterations, leaf_size, minimal_trie_size) = match trie_size {
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "pallet-bridge-dispatch"
|
|
||||||
description = "A Substrate Runtime module that dispatches a bridge message, treating it simply as encoded Call"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
edition = "2021"
|
|
||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false }
|
|
||||||
log = { version = "0.4.14", default-features = false }
|
|
||||||
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
# Bridge dependencies
|
|
||||||
|
|
||||||
bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false }
|
|
||||||
bp-runtime = { path = "../../primitives/runtime", default-features = false }
|
|
||||||
|
|
||||||
# Substrate Dependencies
|
|
||||||
|
|
||||||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = [
|
|
||||||
"bp-message-dispatch/std",
|
|
||||||
"bp-runtime/std",
|
|
||||||
"frame-support/std",
|
|
||||||
"frame-system/std",
|
|
||||||
"log/std",
|
|
||||||
"scale-info/std",
|
|
||||||
"sp-core/std",
|
|
||||||
"sp-runtime/std",
|
|
||||||
"sp-std/std",
|
|
||||||
]
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
# Call Dispatch Module
|
|
||||||
|
|
||||||
The call dispatch module has a single internal (only callable by other runtime modules) entry point
|
|
||||||
for dispatching encoded calls (`pallet_bridge_dispatch::Module::dispatch`). Every dispatch
|
|
||||||
(successful or not) emits a corresponding module event. The module doesn't have any call-related
|
|
||||||
requirements - they may come from the bridged chain over some message lane, or they may be crafted
|
|
||||||
locally. But in this document we'll mostly talk about this module in the context of bridges.
|
|
||||||
|
|
||||||
Every message that is being dispatched has three main characteristics:
|
|
||||||
- `bridge` is the 4-bytes identifier of the bridge where this message comes from. This may be the
|
|
||||||
identifier of the bridged chain (like `b"rlto"` for messages coming from `Rialto`), or the
|
|
||||||
identifier of the bridge itself (`b"rimi"` for `Rialto` <-> `Millau` bridge);
|
|
||||||
- `id` is the unique id of the message within the given bridge. For messages coming from the
|
|
||||||
[messages module](../messages/README.md), it may worth to use a tuple
|
|
||||||
`(LaneId, MessageNonce)` to identify a message;
|
|
||||||
- `message` is the `bp_message_dispatch::MessagePayload` structure. The `call` field is set
|
|
||||||
to the (potentially) encoded `Call` of this chain.
|
|
||||||
|
|
||||||
The easiest way to understand what is happening when a `Call` is being dispatched, is to look at the
|
|
||||||
module events set:
|
|
||||||
|
|
||||||
- `MessageRejected` event is emitted if a message has been rejected even before it has reached the
|
|
||||||
module. Dispatch then is called just to reflect the fact that message has been received, but we
|
|
||||||
have failed to pre-process it (e.g. because we have failed to decode `MessagePayload` structure
|
|
||||||
from the proof);
|
|
||||||
- `MessageVersionSpecMismatch` event is emitted if current runtime specification version differs
|
|
||||||
from the version that has been used to encode the `Call`. The message payload has the
|
|
||||||
`spec_version`, that is filled by the message submitter. If this value differs from the current
|
|
||||||
runtime version, dispatch mechanism rejects to dispatch the message. Without this check, we may
|
|
||||||
decode the wrong `Call` for example if method arguments were changed;
|
|
||||||
- `MessageCallDecodeFailed` event is emitted if we have failed to decode `Call` from the payload.
|
|
||||||
This may happen if the submitter has provided incorrect value in the `call` field, or if source
|
|
||||||
chain storage has been corrupted. The `Call` is decoded after `spec_version` check, so we'll never
|
|
||||||
try to decode `Call` from other runtime version;
|
|
||||||
- `MessageSignatureMismatch` event is emitted if submitter has chose to dispatch message using
|
|
||||||
specified this chain account (`bp_message_dispatch::CallOrigin::TargetAccount` origin),
|
|
||||||
but he has failed to prove that he owns the private key for this account;
|
|
||||||
- `MessageCallRejected` event is emitted if the module has been deployed with some call filter and
|
|
||||||
this filter has rejected the `Call`. In your bridge you may choose to reject all messages except
|
|
||||||
e.g. balance transfer calls;
|
|
||||||
- `MessageWeightMismatch` event is emitted if the message submitter has specified invalid `Call`
|
|
||||||
dispatch weight in the `weight` field of the message payload. The value of this field is compared
|
|
||||||
to the pre-dispatch weight of the decoded `Call`. If it is less than the actual pre-dispatch
|
|
||||||
weight, the dispatch is rejected. Keep in mind, that even if post-dispatch weight will be less
|
|
||||||
than specified, the submitter still have to declare (and pay for) the maximal possible weight
|
|
||||||
(that is the pre-dispatch weight);
|
|
||||||
- `MessageDispatchPaymentFailed` event is emitted if the message submitter has selected to pay
|
|
||||||
dispatch fee at the target chain, but has failed to do that;
|
|
||||||
- `MessageDispatched` event is emitted if the message has passed all checks and we have actually
|
|
||||||
dispatched it. The dispatch may still fail, though - that's why we are including the dispatch
|
|
||||||
result in the event payload.
|
|
||||||
|
|
||||||
When we talk about module in context of bridges, these events are helping in following cases:
|
|
||||||
|
|
||||||
1. when the message submitter has access to the state of both chains and wants to monitor what has
|
|
||||||
happened with his message. Then he could use the message id (that he gets from the
|
|
||||||
[messages module events](../messages/README.md#General-Information)) to filter events of
|
|
||||||
call dispatch module at the target chain and actually see what has happened with his message;
|
|
||||||
|
|
||||||
1. when the message submitter only has access to the source chain state (for example, when sender is
|
|
||||||
the runtime module at the source chain). In this case, your bridge may have additional mechanism
|
|
||||||
to deliver dispatch proofs (which are storage proof of module events) back to the source chain,
|
|
||||||
thus allowing the submitter to see what has happened with his messages.
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,6 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
|||||||
|
|
||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
|
|
||||||
bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false }
|
|
||||||
bp-messages = { path = "../../primitives/messages", default-features = false }
|
bp-messages = { path = "../../primitives/messages", default-features = false }
|
||||||
bp-runtime = { path = "../../primitives/runtime", default-features = false }
|
bp-runtime = { path = "../../primitives/runtime", default-features = false }
|
||||||
|
|
||||||
@@ -36,7 +35,6 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "m
|
|||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = [
|
std = [
|
||||||
"bp-message-dispatch/std",
|
|
||||||
"bp-messages/std",
|
"bp-messages/std",
|
||||||
"bp-runtime/std",
|
"bp-runtime/std",
|
||||||
"codec/std",
|
"codec/std",
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "pallet-bridge-token-swap"
|
|
||||||
description = "An Substrate pallet that allows parties on different chains (bridged using messages pallet) to swap their tokens"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
edition = "2021"
|
|
||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false }
|
|
||||||
log = { version = "0.4.14", default-features = false }
|
|
||||||
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
|
|
||||||
serde = { version = "1.0", optional = true }
|
|
||||||
|
|
||||||
# Bridge dependencies
|
|
||||||
|
|
||||||
bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false }
|
|
||||||
bp-messages = { path = "../../primitives/messages", default-features = false }
|
|
||||||
bp-runtime = { path = "../../primitives/runtime", default-features = false }
|
|
||||||
bp-token-swap = { path = "../../primitives/token-swap", default-features = false }
|
|
||||||
pallet-bridge-dispatch = { path = "../dispatch", default-features = false }
|
|
||||||
pallet-bridge-messages = { path = "../messages", default-features = false }
|
|
||||||
|
|
||||||
# Substrate Dependencies
|
|
||||||
|
|
||||||
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
|
|
||||||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = [
|
|
||||||
"codec/std",
|
|
||||||
"bp-message-dispatch/std",
|
|
||||||
"bp-messages/std",
|
|
||||||
"bp-runtime/std",
|
|
||||||
"bp-token-swap/std",
|
|
||||||
"frame-support/std",
|
|
||||||
"frame-system/std",
|
|
||||||
"log/std",
|
|
||||||
"pallet-bridge-dispatch/std",
|
|
||||||
"pallet-bridge-messages/std",
|
|
||||||
"scale-info/std",
|
|
||||||
"serde",
|
|
||||||
"sp-core/std",
|
|
||||||
"sp-io/std",
|
|
||||||
"sp-runtime/std",
|
|
||||||
"sp-std/std",
|
|
||||||
]
|
|
||||||
runtime-benchmarks = [
|
|
||||||
"frame-benchmarking/runtime-benchmarks",
|
|
||||||
]
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Token-swap pallet benchmarking.
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
swap_account_id, target_account_at_this_chain, BridgedAccountIdOf, BridgedAccountPublicOf,
|
|
||||||
BridgedAccountSignatureOf, BridgedBalanceOf, Call, Origin, Pallet, ThisChainBalance,
|
|
||||||
TokenSwapCreationOf, TokenSwapOf,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bp_token_swap::{TokenSwap, TokenSwapCreation, TokenSwapState, TokenSwapType};
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use frame_benchmarking::{account, benchmarks_instance_pallet};
|
|
||||||
use frame_support::{traits::Currency, Parameter};
|
|
||||||
use frame_system::RawOrigin;
|
|
||||||
use sp_core::H256;
|
|
||||||
use sp_io::hashing::blake2_256;
|
|
||||||
use sp_runtime::traits::{Bounded, TrailingZeroInput};
|
|
||||||
use sp_std::{boxed::Box, vec::Vec};
|
|
||||||
|
|
||||||
const SEED: u32 = 0;
|
|
||||||
|
|
||||||
/// Trait that must be implemented by runtime.
|
|
||||||
pub trait Config<I: 'static>: crate::Config<I> {
|
|
||||||
/// Initialize environment for token swap.
|
|
||||||
fn initialize_environment();
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmarks_instance_pallet! {
|
|
||||||
where_clause {
|
|
||||||
where
|
|
||||||
Origin<T, I>: Into<T::Origin>,
|
|
||||||
BridgedAccountPublicOf<T, I>: Decode + Parameter,
|
|
||||||
BridgedAccountSignatureOf<T, I>: Decode,
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Benchmarks that are used directly by the runtime.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Benchmark `create_swap` extrinsic.
|
|
||||||
//
|
|
||||||
// This benchmark assumes that message is **NOT** actually sent. Instead we're using `send_message_weight`
|
|
||||||
// from the `WeightInfoExt` trait.
|
|
||||||
//
|
|
||||||
// There aren't any factors that affect `create_swap` performance, so everything
|
|
||||||
// is straightforward here.
|
|
||||||
create_swap {
|
|
||||||
T::initialize_environment();
|
|
||||||
|
|
||||||
let sender = funded_account::<T, I>("source_account_at_this_chain", 0);
|
|
||||||
let swap: TokenSwapOf<T, I> = test_swap::<T, I>(sender.clone(), true);
|
|
||||||
let swap_creation: TokenSwapCreationOf<T, I> = test_swap_creation::<T, I>();
|
|
||||||
}: create_swap(
|
|
||||||
RawOrigin::Signed(sender.clone()),
|
|
||||||
swap,
|
|
||||||
Box::new(swap_creation)
|
|
||||||
)
|
|
||||||
verify {
|
|
||||||
assert!(crate::PendingSwaps::<T, I>::contains_key(test_swap_hash::<T, I>(sender, true)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmark `claim_swap` extrinsic with the worst possible conditions:
|
|
||||||
//
|
|
||||||
// * swap is locked until some block, so current block number is read.
|
|
||||||
claim_swap {
|
|
||||||
T::initialize_environment();
|
|
||||||
|
|
||||||
let sender: T::AccountId = account("source_account_at_this_chain", 0, SEED);
|
|
||||||
crate::PendingSwaps::<T, I>::insert(
|
|
||||||
test_swap_hash::<T, I>(sender.clone(), false),
|
|
||||||
TokenSwapState::Confirmed,
|
|
||||||
);
|
|
||||||
|
|
||||||
let swap: TokenSwapOf<T, I> = test_swap::<T, I>(sender.clone(), false);
|
|
||||||
let claimer = target_account_at_this_chain::<T, I>(&swap);
|
|
||||||
let token_swap_account = swap_account_id::<T, I>(&swap);
|
|
||||||
T::ThisCurrency::make_free_balance_be(&token_swap_account, ThisChainBalance::<T, I>::max_value());
|
|
||||||
}: claim_swap(RawOrigin::Signed(claimer), swap)
|
|
||||||
verify {
|
|
||||||
assert!(!crate::PendingSwaps::<T, I>::contains_key(test_swap_hash::<T, I>(sender, false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmark `cancel_swap` extrinsic with the worst possible conditions:
|
|
||||||
//
|
|
||||||
// * swap is locked until some block, so current block number is read.
|
|
||||||
cancel_swap {
|
|
||||||
T::initialize_environment();
|
|
||||||
|
|
||||||
let sender: T::AccountId = account("source_account_at_this_chain", 0, SEED);
|
|
||||||
crate::PendingSwaps::<T, I>::insert(
|
|
||||||
test_swap_hash::<T, I>(sender.clone(), false),
|
|
||||||
TokenSwapState::Failed,
|
|
||||||
);
|
|
||||||
|
|
||||||
let swap: TokenSwapOf<T, I> = test_swap::<T, I>(sender.clone(), false);
|
|
||||||
let token_swap_account = swap_account_id::<T, I>(&swap);
|
|
||||||
T::ThisCurrency::make_free_balance_be(&token_swap_account, ThisChainBalance::<T, I>::max_value());
|
|
||||||
|
|
||||||
}: cancel_swap(RawOrigin::Signed(sender.clone()), swap)
|
|
||||||
verify {
|
|
||||||
assert!(!crate::PendingSwaps::<T, I>::contains_key(test_swap_hash::<T, I>(sender, false)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns test token swap.
|
|
||||||
fn test_swap<T: Config<I>, I: 'static>(sender: T::AccountId, is_create: bool) -> TokenSwapOf<T, I> {
|
|
||||||
TokenSwap {
|
|
||||||
swap_type: TokenSwapType::LockClaimUntilBlock(
|
|
||||||
if is_create { 10u32.into() } else { 0u32.into() },
|
|
||||||
0.into(),
|
|
||||||
),
|
|
||||||
source_balance_at_this_chain: source_balance_to_swap::<T, I>(),
|
|
||||||
source_account_at_this_chain: sender,
|
|
||||||
target_balance_at_bridged_chain: target_balance_to_swap::<T, I>(),
|
|
||||||
target_account_at_bridged_chain: target_account_at_bridged_chain::<T, I>(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns test token swap hash.
|
|
||||||
fn test_swap_hash<T: Config<I>, I: 'static>(sender: T::AccountId, is_create: bool) -> H256 {
|
|
||||||
test_swap::<T, I>(sender, is_create).using_encoded(blake2_256).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns test token swap creation params.
|
|
||||||
fn test_swap_creation<T: Config<I>, I: 'static>() -> TokenSwapCreationOf<T, I>
|
|
||||||
where
|
|
||||||
BridgedAccountPublicOf<T, I>: Decode,
|
|
||||||
BridgedAccountSignatureOf<T, I>: Decode,
|
|
||||||
{
|
|
||||||
TokenSwapCreation {
|
|
||||||
target_public_at_bridged_chain: target_public_at_bridged_chain::<T, I>(),
|
|
||||||
swap_delivery_and_dispatch_fee: swap_delivery_and_dispatch_fee::<T, I>(),
|
|
||||||
bridged_chain_spec_version: 0,
|
|
||||||
bridged_currency_transfer: Vec::new(),
|
|
||||||
bridged_currency_transfer_weight: 0,
|
|
||||||
bridged_currency_transfer_signature: bridged_currency_transfer_signature::<T, I>(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Account that has some balance.
|
|
||||||
fn funded_account<T: Config<I>, I: 'static>(name: &'static str, index: u32) -> T::AccountId {
|
|
||||||
let account: T::AccountId = account(name, index, SEED);
|
|
||||||
T::ThisCurrency::make_free_balance_be(&account, ThisChainBalance::<T, I>::max_value());
|
|
||||||
account
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Currency transfer message fee.
|
|
||||||
fn swap_delivery_and_dispatch_fee<T: Config<I>, I: 'static>() -> ThisChainBalance<T, I> {
|
|
||||||
ThisChainBalance::<T, I>::max_value() / 4u32.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Balance at the source chain that we're going to swap.
|
|
||||||
fn source_balance_to_swap<T: Config<I>, I: 'static>() -> ThisChainBalance<T, I> {
|
|
||||||
ThisChainBalance::<T, I>::max_value() / 2u32.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Balance at the target chain that we're going to swap.
|
|
||||||
fn target_balance_to_swap<T: Config<I>, I: 'static>() -> BridgedBalanceOf<T, I> {
|
|
||||||
BridgedBalanceOf::<T, I>::max_value() / 2u32.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Public key of `target_account_at_bridged_chain`.
|
|
||||||
fn target_public_at_bridged_chain<T: Config<I>, I: 'static>() -> BridgedAccountPublicOf<T, I>
|
|
||||||
where
|
|
||||||
BridgedAccountPublicOf<T, I>: Decode,
|
|
||||||
{
|
|
||||||
BridgedAccountPublicOf::<T, I>::decode(&mut TrailingZeroInput::zeroes())
|
|
||||||
.expect("failed to decode `BridgedAccountPublicOf` from zeroes")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Signature of `target_account_at_bridged_chain` over message.
|
|
||||||
fn bridged_currency_transfer_signature<T: Config<I>, I: 'static>() -> BridgedAccountSignatureOf<T, I>
|
|
||||||
where
|
|
||||||
BridgedAccountSignatureOf<T, I>: Decode,
|
|
||||||
{
|
|
||||||
BridgedAccountSignatureOf::<T, I>::decode(&mut TrailingZeroInput::zeroes())
|
|
||||||
.expect("failed to decode `BridgedAccountSignatureOf` from zeroes")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Account at the bridged chain that is participating in the swap.
|
|
||||||
fn target_account_at_bridged_chain<T: Config<I>, I: 'static>() -> BridgedAccountIdOf<T, I> {
|
|
||||||
account("target_account_at_bridged_chain", 0, SEED)
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,200 +0,0 @@
|
|||||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
use crate as pallet_bridge_token_swap;
|
|
||||||
use crate::MessagePayloadOf;
|
|
||||||
|
|
||||||
use bp_messages::{
|
|
||||||
source_chain::{MessagesBridge, SendMessageArtifacts},
|
|
||||||
LaneId, MessageNonce,
|
|
||||||
};
|
|
||||||
use bp_runtime::ChainId;
|
|
||||||
use frame_support::weights::Weight;
|
|
||||||
use sp_core::H256;
|
|
||||||
use sp_runtime::{
|
|
||||||
testing::Header as SubstrateHeader,
|
|
||||||
traits::{BlakeTwo256, IdentityLookup},
|
|
||||||
Perbill,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type AccountId = u64;
|
|
||||||
pub type Balance = u64;
|
|
||||||
pub type Block = frame_system::mocking::MockBlock<TestRuntime>;
|
|
||||||
pub type BridgedAccountId = u64;
|
|
||||||
pub type BridgedAccountPublic = sp_runtime::testing::UintAuthorityId;
|
|
||||||
pub type BridgedAccountSignature = sp_runtime::testing::TestSignature;
|
|
||||||
pub type BridgedBalance = u64;
|
|
||||||
pub type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<TestRuntime>;
|
|
||||||
|
|
||||||
pub const OK_TRANSFER_CALL: u8 = 1;
|
|
||||||
pub const BAD_TRANSFER_CALL: u8 = 2;
|
|
||||||
pub const MESSAGE_NONCE: MessageNonce = 3;
|
|
||||||
|
|
||||||
pub const THIS_CHAIN_ACCOUNT: AccountId = 1;
|
|
||||||
pub const THIS_CHAIN_ACCOUNT_BALANCE: Balance = 100_000;
|
|
||||||
|
|
||||||
pub const SWAP_DELIVERY_AND_DISPATCH_FEE: Balance = 1;
|
|
||||||
|
|
||||||
frame_support::construct_runtime! {
|
|
||||||
pub enum TestRuntime where
|
|
||||||
Block = Block,
|
|
||||||
NodeBlock = Block,
|
|
||||||
UncheckedExtrinsic = UncheckedExtrinsic,
|
|
||||||
{
|
|
||||||
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
|
|
||||||
Balances: pallet_balances::{Pallet, Call, Event<T>},
|
|
||||||
TokenSwap: pallet_bridge_token_swap::{Pallet, Call, Event<T>, Origin<T>},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_support::parameter_types! {
|
|
||||||
pub const BlockHashCount: u64 = 250;
|
|
||||||
pub const MaximumBlockWeight: Weight = 1024;
|
|
||||||
pub const MaximumBlockLength: u32 = 2 * 1024;
|
|
||||||
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl frame_system::Config for TestRuntime {
|
|
||||||
type Origin = Origin;
|
|
||||||
type Index = u64;
|
|
||||||
type Call = Call;
|
|
||||||
type BlockNumber = u64;
|
|
||||||
type Hash = H256;
|
|
||||||
type Hashing = BlakeTwo256;
|
|
||||||
type AccountId = AccountId;
|
|
||||||
type Lookup = IdentityLookup<Self::AccountId>;
|
|
||||||
type Header = SubstrateHeader;
|
|
||||||
type Event = Event;
|
|
||||||
type BlockHashCount = BlockHashCount;
|
|
||||||
type Version = ();
|
|
||||||
type PalletInfo = PalletInfo;
|
|
||||||
type AccountData = pallet_balances::AccountData<Balance>;
|
|
||||||
type OnNewAccount = ();
|
|
||||||
type OnKilledAccount = ();
|
|
||||||
type BaseCallFilter = frame_support::traits::Everything;
|
|
||||||
type SystemWeightInfo = ();
|
|
||||||
type BlockWeights = ();
|
|
||||||
type BlockLength = ();
|
|
||||||
type DbWeight = ();
|
|
||||||
type SS58Prefix = ();
|
|
||||||
type OnSetCode = ();
|
|
||||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_support::parameter_types! {
|
|
||||||
pub const ExistentialDeposit: u64 = 10;
|
|
||||||
pub const MaxReserves: u32 = 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pallet_balances::Config for TestRuntime {
|
|
||||||
type MaxLocks = ();
|
|
||||||
type Balance = Balance;
|
|
||||||
type DustRemoval = ();
|
|
||||||
type Event = Event;
|
|
||||||
type ExistentialDeposit = ExistentialDeposit;
|
|
||||||
type AccountStore = frame_system::Pallet<TestRuntime>;
|
|
||||||
type WeightInfo = ();
|
|
||||||
type MaxReserves = MaxReserves;
|
|
||||||
type ReserveIdentifier = [u8; 8];
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_support::parameter_types! {
|
|
||||||
pub const BridgedChainId: ChainId = *b"inst";
|
|
||||||
pub const OutboundMessageLaneId: LaneId = *b"lane";
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pallet_bridge_token_swap::Config for TestRuntime {
|
|
||||||
type Event = Event;
|
|
||||||
type WeightInfo = ();
|
|
||||||
|
|
||||||
type BridgedChainId = BridgedChainId;
|
|
||||||
type OutboundMessageLaneId = OutboundMessageLaneId;
|
|
||||||
type MessagesBridge = TestMessagesBridge;
|
|
||||||
|
|
||||||
type ThisCurrency = pallet_balances::Pallet<TestRuntime>;
|
|
||||||
type FromSwapToThisAccountIdConverter = TestAccountConverter;
|
|
||||||
|
|
||||||
type BridgedChain = BridgedChain;
|
|
||||||
type FromBridgedToThisAccountIdConverter = TestAccountConverter;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BridgedChain;
|
|
||||||
|
|
||||||
impl bp_runtime::Chain for BridgedChain {
|
|
||||||
type BlockNumber = u64;
|
|
||||||
type Hash = H256;
|
|
||||||
type Hasher = BlakeTwo256;
|
|
||||||
type Header = sp_runtime::generic::Header<u64, BlakeTwo256>;
|
|
||||||
|
|
||||||
type AccountId = BridgedAccountId;
|
|
||||||
type Balance = BridgedBalance;
|
|
||||||
type Index = u64;
|
|
||||||
type Signature = BridgedAccountSignature;
|
|
||||||
|
|
||||||
fn max_extrinsic_size() -> u32 {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
fn max_extrinsic_weight() -> Weight {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TestMessagesBridge;
|
|
||||||
|
|
||||||
impl MessagesBridge<Origin, AccountId, Balance, MessagePayloadOf<TestRuntime, ()>>
|
|
||||||
for TestMessagesBridge
|
|
||||||
{
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn send_message(
|
|
||||||
sender: Origin,
|
|
||||||
lane: LaneId,
|
|
||||||
message: MessagePayloadOf<TestRuntime, ()>,
|
|
||||||
delivery_and_dispatch_fee: Balance,
|
|
||||||
) -> Result<SendMessageArtifacts, Self::Error> {
|
|
||||||
assert_eq!(lane, OutboundMessageLaneId::get());
|
|
||||||
assert_eq!(delivery_and_dispatch_fee, SWAP_DELIVERY_AND_DISPATCH_FEE);
|
|
||||||
match sender.caller {
|
|
||||||
OriginCaller::TokenSwap(_) => (),
|
|
||||||
_ => panic!("unexpected origin"),
|
|
||||||
}
|
|
||||||
match message.call[0] {
|
|
||||||
OK_TRANSFER_CALL => Ok(SendMessageArtifacts { nonce: MESSAGE_NONCE, weight: 0 }),
|
|
||||||
BAD_TRANSFER_CALL => Err(()),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TestAccountConverter;
|
|
||||||
|
|
||||||
impl sp_runtime::traits::Convert<H256, AccountId> for TestAccountConverter {
|
|
||||||
fn convert(hash: H256) -> AccountId {
|
|
||||||
hash.to_low_u64_ne()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run pallet test.
|
|
||||||
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
|
|
||||||
let mut t = frame_system::GenesisConfig::default().build_storage::<TestRuntime>().unwrap();
|
|
||||||
pallet_balances::GenesisConfig::<TestRuntime> {
|
|
||||||
balances: vec![(THIS_CHAIN_ACCOUNT, THIS_CHAIN_ACCOUNT_BALANCE)],
|
|
||||||
}
|
|
||||||
.assimilate_storage(&mut t)
|
|
||||||
.unwrap();
|
|
||||||
let mut ext = sp_io::TestExternalities::new(t);
|
|
||||||
ext.execute_with(test)
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Autogenerated weights for `pallet_bridge_token_swap`
|
|
||||||
//!
|
|
||||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
|
||||||
//! DATE: 2021-12-28, STEPS: 50, REPEAT: 20
|
|
||||||
//! LOW RANGE: [], HIGH RANGE: []
|
|
||||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
|
|
||||||
//! CHAIN: Some("dev"), DB CACHE: 128
|
|
||||||
|
|
||||||
// Executed Command:
|
|
||||||
// target/release/millau-bridge-node
|
|
||||||
// benchmark
|
|
||||||
// --chain=dev
|
|
||||||
// --steps=50
|
|
||||||
// --repeat=20
|
|
||||||
// --pallet=pallet_bridge_token_swap
|
|
||||||
// --extrinsic=*
|
|
||||||
// --execution=wasm
|
|
||||||
// --wasm-execution=Compiled
|
|
||||||
// --heap-pages=4096
|
|
||||||
// --output=./modules/token-swap/src/weights.rs
|
|
||||||
// --template=./.maintain/millau-weight-template.hbs
|
|
||||||
|
|
||||||
#![allow(clippy::all)]
|
|
||||||
#![allow(unused_parens)]
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
|
|
||||||
use frame_support::{
|
|
||||||
traits::Get,
|
|
||||||
weights::{constants::RocksDbWeight, Weight},
|
|
||||||
};
|
|
||||||
use sp_std::marker::PhantomData;
|
|
||||||
|
|
||||||
/// Weight functions needed for `pallet_bridge_token_swap`.
|
|
||||||
pub trait WeightInfo {
|
|
||||||
fn create_swap() -> Weight;
|
|
||||||
fn claim_swap() -> Weight;
|
|
||||||
fn cancel_swap() -> Weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Weights for `pallet_bridge_token_swap` using the Millau node and recommended hardware.
|
|
||||||
pub struct MillauWeight<T>(PhantomData<T>);
|
|
||||||
impl<T: frame_system::Config> WeightInfo for MillauWeight<T> {
|
|
||||||
fn create_swap() -> Weight {
|
|
||||||
(90_368_000 as Weight)
|
|
||||||
.saturating_add(T::DbWeight::get().reads(3 as Weight))
|
|
||||||
.saturating_add(T::DbWeight::get().writes(4 as Weight))
|
|
||||||
}
|
|
||||||
fn claim_swap() -> Weight {
|
|
||||||
(88_397_000 as Weight)
|
|
||||||
.saturating_add(T::DbWeight::get().reads(3 as Weight))
|
|
||||||
.saturating_add(T::DbWeight::get().writes(3 as Weight))
|
|
||||||
}
|
|
||||||
fn cancel_swap() -> Weight {
|
|
||||||
(91_253_000 as Weight)
|
|
||||||
.saturating_add(T::DbWeight::get().reads(3 as Weight))
|
|
||||||
.saturating_add(T::DbWeight::get().writes(3 as Weight))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For backwards compatibility and tests
|
|
||||||
impl WeightInfo for () {
|
|
||||||
fn create_swap() -> Weight {
|
|
||||||
(90_368_000 as Weight)
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(4 as Weight))
|
|
||||||
}
|
|
||||||
fn claim_swap() -> Weight {
|
|
||||||
(88_397_000 as Weight)
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
|
|
||||||
}
|
|
||||||
fn cancel_swap() -> Weight {
|
|
||||||
(91_253_000 as Weight)
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Weight-related utilities.
|
|
||||||
|
|
||||||
use crate::weights::WeightInfo;
|
|
||||||
|
|
||||||
use bp_runtime::Size;
|
|
||||||
use frame_support::weights::{RuntimeDbWeight, Weight};
|
|
||||||
|
|
||||||
/// Extended weight info.
|
|
||||||
pub trait WeightInfoExt: WeightInfo {
|
|
||||||
// Functions that are directly mapped to extrinsics weights.
|
|
||||||
|
|
||||||
/// Weight of message send extrinsic.
|
|
||||||
fn send_message_weight(message: &impl Size, db_weight: RuntimeDbWeight) -> Weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WeightInfoExt for () {
|
|
||||||
fn send_message_weight(message: &impl Size, db_weight: RuntimeDbWeight) -> Weight {
|
|
||||||
<() as pallet_bridge_messages::WeightInfoExt>::send_message_weight(message, db_weight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: frame_system::Config> WeightInfoExt for crate::weights::MillauWeight<T> {
|
|
||||||
fn send_message_weight(message: &impl Size, db_weight: RuntimeDbWeight) -> Weight {
|
|
||||||
<() as pallet_bridge_messages::WeightInfoExt>::send_message_weight(message, db_weight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -269,9 +269,6 @@ pub const WITH_MILLAU_MESSAGES_PALLET_NAME: &str = "BridgeMillauMessages";
|
|||||||
/// Name of the Rialto->Millau (actually DOT->KSM) conversion rate stored in the Millau runtime.
|
/// Name of the Rialto->Millau (actually DOT->KSM) conversion rate stored in the Millau runtime.
|
||||||
pub const RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME: &str = "RialtoToMillauConversionRate";
|
pub const RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME: &str = "RialtoToMillauConversionRate";
|
||||||
|
|
||||||
/// Name of the With-Rialto token swap pallet instance in the Millau runtime.
|
|
||||||
pub const WITH_RIALTO_TOKEN_SWAP_PALLET_NAME: &str = "BridgeRialtoTokenSwap";
|
|
||||||
|
|
||||||
/// Name of the `MillauFinalityApi::best_finalized` runtime method.
|
/// Name of the `MillauFinalityApi::best_finalized` runtime method.
|
||||||
pub const BEST_FINALIZED_MILLAU_HEADER_METHOD: &str = "MillauFinalityApi_best_finalized";
|
pub const BEST_FINALIZED_MILLAU_HEADER_METHOD: &str = "MillauFinalityApi_best_finalized";
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "bp-message-dispatch"
|
|
||||||
description = "Primitives of bridge messages dispatch modules."
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
edition = "2021"
|
|
||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bp-runtime = { path = "../runtime", default-features = false }
|
|
||||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false }
|
|
||||||
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
# Substrate Dependencies
|
|
||||||
|
|
||||||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = [
|
|
||||||
"bp-runtime/std",
|
|
||||||
"codec/std",
|
|
||||||
"frame-support/std",
|
|
||||||
"scale-info/std",
|
|
||||||
"sp-std/std",
|
|
||||||
]
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! A common interface for all Bridge Message Dispatch modules.
|
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
|
|
||||||
use bp_runtime::{
|
|
||||||
messages::{DispatchFeePayment, MessageDispatchResult},
|
|
||||||
ChainId, Size,
|
|
||||||
};
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use frame_support::RuntimeDebug;
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
use sp_std::prelude::*;
|
|
||||||
|
|
||||||
/// Message dispatch weight.
|
|
||||||
pub type Weight = u64;
|
|
||||||
|
|
||||||
/// Spec version type.
|
|
||||||
pub type SpecVersion = u32;
|
|
||||||
|
|
||||||
/// A generic trait to dispatch arbitrary messages delivered over the bridge.
|
|
||||||
pub trait MessageDispatch<AccountId, BridgeMessageId> {
|
|
||||||
/// A type of the message to be dispatched.
|
|
||||||
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.
|
|
||||||
///
|
|
||||||
/// `source_chain` indicates the chain where the message came from.
|
|
||||||
/// `target_chain` indicates the chain where message dispatch happens.
|
|
||||||
///
|
|
||||||
/// `id` is a short unique identifier of the message.
|
|
||||||
///
|
|
||||||
/// If message is `Ok`, then it should be dispatched. If it is `Err`, then it's just
|
|
||||||
/// a sign that some other component has rejected the message even before it has
|
|
||||||
/// reached `dispatch` method (right now this may only be caused if we fail to decode
|
|
||||||
/// the whole message).
|
|
||||||
///
|
|
||||||
/// Returns unspent dispatch weight.
|
|
||||||
fn dispatch<P: FnOnce(&AccountId, Weight) -> Result<(), ()>>(
|
|
||||||
source_chain: ChainId,
|
|
||||||
target_chain: ChainId,
|
|
||||||
id: BridgeMessageId,
|
|
||||||
message: Result<Self::Message, ()>,
|
|
||||||
pay_dispatch_fee: P,
|
|
||||||
) -> MessageDispatchResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Origin of a Call when it is dispatched on the target chain.
|
|
||||||
///
|
|
||||||
/// The source chain can (and should) verify that the message can be dispatched on the target chain
|
|
||||||
/// with a particular origin given the source chain's origin. This can be done with the
|
|
||||||
/// `verify_message_origin()` function.
|
|
||||||
#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
|
|
||||||
pub enum CallOrigin<SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature> {
|
|
||||||
/// Call is sent by the Root origin on the source chain. On the target chain it is dispatched
|
|
||||||
/// from a derived account.
|
|
||||||
///
|
|
||||||
/// The derived account represents the source Root account on the target chain. This is useful
|
|
||||||
/// if the target chain needs some way of knowing that a call came from a privileged origin on
|
|
||||||
/// the source chain (maybe to allow a configuration change for example).
|
|
||||||
SourceRoot,
|
|
||||||
|
|
||||||
/// Call is sent by `SourceChainAccountId` on the source chain. On the target chain it is
|
|
||||||
/// dispatched from an account controlled by a private key on the target chain.
|
|
||||||
///
|
|
||||||
/// The account can be identified by `TargetChainAccountPublic`. The proof that the
|
|
||||||
/// `SourceChainAccountId` controls `TargetChainAccountPublic` is the `TargetChainSignature`
|
|
||||||
/// over `(Call, SourceChainAccountId, TargetChainSpecVersion, SourceChainBridgeId).encode()`.
|
|
||||||
///
|
|
||||||
/// NOTE sending messages using this origin (or any other) does not have replay protection!
|
|
||||||
/// The assumption is that both the source account and the target account is controlled by
|
|
||||||
/// the same entity, so source-chain replay protection is sufficient.
|
|
||||||
/// As a consequence, it's extremely important for the target chain user to never produce
|
|
||||||
/// a signature with their target-private key on something that could be sent over the bridge,
|
|
||||||
/// i.e. if the target user signs `(<some-source-account-id>, Call::Transfer(X, 5))`
|
|
||||||
/// The owner of `some-source-account-id` can send that message multiple times, which would
|
|
||||||
/// result with multiple transfer calls being dispatched on the target chain.
|
|
||||||
/// So please, NEVER USE YOUR PRIVATE KEY TO SIGN SOMETHING YOU DON'T FULLY UNDERSTAND!
|
|
||||||
TargetAccount(SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature),
|
|
||||||
|
|
||||||
/// Call is sent by the `SourceChainAccountId` on the source chain. On the target chain it is
|
|
||||||
/// dispatched from a derived account ID.
|
|
||||||
///
|
|
||||||
/// The account ID on the target chain is derived from the source account ID. This is useful if
|
|
||||||
/// you need a way to represent foreign accounts on this chain for call dispatch purposes.
|
|
||||||
///
|
|
||||||
/// Note that the derived account does not need to have a private key on the target chain. This
|
|
||||||
/// origin can therefore represent proxies, pallets, etc. as well as "regular" accounts.
|
|
||||||
SourceAccount(SourceChainAccountId),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Message payload type used by dispatch module.
|
|
||||||
#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
|
|
||||||
pub struct MessagePayload<
|
|
||||||
SourceChainAccountId,
|
|
||||||
TargetChainAccountPublic,
|
|
||||||
TargetChainSignature,
|
|
||||||
Call,
|
|
||||||
> {
|
|
||||||
/// Runtime specification version. We only dispatch messages that have the same
|
|
||||||
/// runtime version. Otherwise we risk to misinterpret encoded calls.
|
|
||||||
pub spec_version: SpecVersion,
|
|
||||||
/// Weight of the call, declared by the message sender. If it is less than actual
|
|
||||||
/// static weight, the call is not dispatched.
|
|
||||||
pub weight: Weight,
|
|
||||||
/// Call origin to be used during dispatch.
|
|
||||||
pub origin: CallOrigin<SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature>,
|
|
||||||
/// Where the fee for dispatching message is paid?
|
|
||||||
pub dispatch_fee_payment: DispatchFeePayment,
|
|
||||||
/// The call itself.
|
|
||||||
pub call: Call,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature> Size
|
|
||||||
for MessagePayload<SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature, Vec<u8>>
|
|
||||||
{
|
|
||||||
fn size_hint(&self) -> u32 {
|
|
||||||
self.call.len() as _
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -136,18 +136,18 @@ pub trait Size {
|
|||||||
fn size_hint(&self) -> u32;
|
fn size_hint(&self) -> u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Size for &[u8] {
|
|
||||||
fn size_hint(&self) -> u32 {
|
|
||||||
self.len() as _
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Size for () {
|
impl Size for () {
|
||||||
fn size_hint(&self) -> u32 {
|
fn size_hint(&self) -> u32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Size for Vec<u8> {
|
||||||
|
fn size_hint(&self) -> u32 {
|
||||||
|
self.len() as _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Pre-computed size.
|
/// Pre-computed size.
|
||||||
pub struct PreComputedSize(pub usize);
|
pub struct PreComputedSize(pub usize);
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "bp-token-swap"
|
|
||||||
description = "Primitives of the pallet-bridge-token-swap pallet"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
edition = "2021"
|
|
||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false }
|
|
||||||
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
# Bridge Dependencies
|
|
||||||
|
|
||||||
bp-runtime = { path = "../runtime", default-features = false }
|
|
||||||
|
|
||||||
# Substrate Dependencies
|
|
||||||
|
|
||||||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
hex = "0.4"
|
|
||||||
hex-literal = "0.3"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = [
|
|
||||||
"bp-runtime/std",
|
|
||||||
"codec/std",
|
|
||||||
"frame-support/std",
|
|
||||||
"scale-info/std",
|
|
||||||
"sp-core/std",
|
|
||||||
"sp-io/std",
|
|
||||||
"sp-std/std",
|
|
||||||
]
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
pub mod storage_keys;
|
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use frame_support::{weights::Weight, RuntimeDebug};
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
use sp_core::{H256, U256};
|
|
||||||
use sp_io::hashing::blake2_256;
|
|
||||||
use sp_std::vec::Vec;
|
|
||||||
|
|
||||||
/// Pending token swap state.
|
|
||||||
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)]
|
|
||||||
pub enum TokenSwapState {
|
|
||||||
/// The swap has been started using the `start_claim` call, but we have no proof that it has
|
|
||||||
/// happened at the Bridged chain.
|
|
||||||
Started,
|
|
||||||
/// The swap has happened at the Bridged chain and may be claimed by the Bridged chain party
|
|
||||||
/// using the `claim_swap` call.
|
|
||||||
Confirmed,
|
|
||||||
/// The swap has failed at the Bridged chain and This chain party may cancel it using the
|
|
||||||
/// `cancel_swap` call.
|
|
||||||
Failed,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Token swap type.
|
|
||||||
///
|
|
||||||
/// Different swap types give a different guarantees regarding possible swap
|
|
||||||
/// replay protection.
|
|
||||||
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)]
|
|
||||||
pub enum TokenSwapType<ThisBlockNumber> {
|
|
||||||
/// The `target_account_at_bridged_chain` is temporary and only have funds for single swap.
|
|
||||||
///
|
|
||||||
/// ***WARNING**: if `target_account_at_bridged_chain` still exists after the swap has been
|
|
||||||
/// completed (either by claiming or canceling), the `source_account_at_this_chain` will be
|
|
||||||
/// able to restart the swap again and repeat the swap until `target_account_at_bridged_chain`
|
|
||||||
/// depletes.
|
|
||||||
TemporaryTargetAccountAtBridgedChain,
|
|
||||||
/// This swap type prevents `source_account_at_this_chain` from restarting the swap after it
|
|
||||||
/// has been completed. There are two consequences:
|
|
||||||
///
|
|
||||||
/// 1) the `source_account_at_this_chain` won't be able to call `start_swap` after given
|
|
||||||
/// <ThisBlockNumber>; 2) the `target_account_at_bridged_chain` won't be able to call
|
|
||||||
/// `claim_swap` (over the bridge) before block `<ThisBlockNumber + 1>`.
|
|
||||||
///
|
|
||||||
/// The second element is the nonce of the swap. You must care about its uniqueness if you're
|
|
||||||
/// planning to perform another swap with exactly the same parameters (i.e. same amount, same
|
|
||||||
/// accounts, same `ThisBlockNumber`) to avoid collisions.
|
|
||||||
LockClaimUntilBlock(ThisBlockNumber, U256),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An intention to swap `source_balance_at_this_chain` owned by `source_account_at_this_chain`
|
|
||||||
/// to `target_balance_at_bridged_chain` owned by `target_account_at_bridged_chain`.
|
|
||||||
///
|
|
||||||
/// **IMPORTANT NOTE**: this structure is always the same during single token swap. So even
|
|
||||||
/// when chain changes, the meaning of This and Bridged are still used to point to the same chains.
|
|
||||||
/// This chain is always the chain where swap has been started. And the Bridged chain is the other
|
|
||||||
/// chain.
|
|
||||||
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)]
|
|
||||||
pub struct TokenSwap<ThisBlockNumber, ThisBalance, ThisAccountId, BridgedBalance, BridgedAccountId>
|
|
||||||
{
|
|
||||||
/// The type of the swap.
|
|
||||||
pub swap_type: TokenSwapType<ThisBlockNumber>,
|
|
||||||
/// This chain balance to be swapped with `target_balance_at_bridged_chain`.
|
|
||||||
pub source_balance_at_this_chain: ThisBalance,
|
|
||||||
/// Account id of the party acting at This chain and owning the `source_account_at_this_chain`.
|
|
||||||
pub source_account_at_this_chain: ThisAccountId,
|
|
||||||
/// Bridged chain balance to be swapped with `source_balance_at_this_chain`.
|
|
||||||
pub target_balance_at_bridged_chain: BridgedBalance,
|
|
||||||
/// Account id of the party acting at the Bridged chain and owning the
|
|
||||||
/// `target_balance_at_bridged_chain`.
|
|
||||||
pub target_account_at_bridged_chain: BridgedAccountId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ThisBlockNumber, ThisBalance, ThisAccountId, BridgedBalance, BridgedAccountId>
|
|
||||||
TokenSwap<ThisBlockNumber, ThisBalance, ThisAccountId, BridgedBalance, BridgedAccountId>
|
|
||||||
where
|
|
||||||
TokenSwap<ThisBlockNumber, ThisBalance, ThisAccountId, BridgedBalance, BridgedAccountId>:
|
|
||||||
Encode,
|
|
||||||
{
|
|
||||||
/// Returns hash, used to identify this token swap.
|
|
||||||
pub fn hash(&self) -> H256 {
|
|
||||||
self.using_encoded(blake2_256).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SCALE-encoded `Currency::transfer` call on the bridged chain.
|
|
||||||
pub type RawBridgedTransferCall = Vec<u8>;
|
|
||||||
|
|
||||||
/// Token swap creation parameters.
|
|
||||||
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)]
|
|
||||||
pub struct TokenSwapCreation<BridgedAccountPublic, ThisChainBalance, BridgedAccountSignature> {
|
|
||||||
/// Public key of the `target_account_at_bridged_chain` account used to verify
|
|
||||||
/// `bridged_currency_transfer_signature`.
|
|
||||||
pub target_public_at_bridged_chain: BridgedAccountPublic,
|
|
||||||
/// Fee that the `source_account_at_this_chain` is ready to pay for the tokens
|
|
||||||
/// transfer message delivery and dispatch.
|
|
||||||
pub swap_delivery_and_dispatch_fee: ThisChainBalance,
|
|
||||||
/// Specification version of the Bridged chain.
|
|
||||||
pub bridged_chain_spec_version: u32,
|
|
||||||
/// SCALE-encoded tokens transfer call at the Bridged chain.
|
|
||||||
pub bridged_currency_transfer: RawBridgedTransferCall,
|
|
||||||
/// Dispatch weight of the tokens transfer call at the Bridged chain.
|
|
||||||
pub bridged_currency_transfer_weight: Weight,
|
|
||||||
/// The signature of the `target_account_at_bridged_chain` for the message
|
|
||||||
/// returned by the `pallet_bridge_dispatch::account_ownership_digest()` function call.
|
|
||||||
pub bridged_currency_transfer_signature: BridgedAccountSignature,
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Storage keys of bridge token swap pallet.
|
|
||||||
|
|
||||||
use frame_support::Identity;
|
|
||||||
use sp_core::{storage::StorageKey, H256};
|
|
||||||
|
|
||||||
/// Name of the `PendingSwaps` storage map.
|
|
||||||
pub const PENDING_SWAPS_MAP_NAME: &str = "PendingSwaps";
|
|
||||||
|
|
||||||
/// Storage key of `PendingSwaps` value with given token swap hash.
|
|
||||||
pub fn pending_swaps_key(pallet_prefix: &str, token_swap_hash: H256) -> StorageKey {
|
|
||||||
bp_runtime::storage_map_final_key::<Identity>(
|
|
||||||
pallet_prefix,
|
|
||||||
PENDING_SWAPS_MAP_NAME,
|
|
||||||
token_swap_hash.as_ref(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use hex_literal::hex;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pending_swaps_key_computed_properly() {
|
|
||||||
// If this test fails, then something has been changed in module storage that may break
|
|
||||||
// all previous swaps.
|
|
||||||
let storage_key = pending_swaps_key("BridgeTokenSwap", [42u8; 32].into()).0;
|
|
||||||
assert_eq!(
|
|
||||||
storage_key,
|
|
||||||
hex!("76276da64e7a4f454760eedeb4bad11adca2227fef56ad07cc424f1f5d128b9a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a").to_vec(),
|
|
||||||
"Unexpected storage key: {}",
|
|
||||||
hex::encode(&storage_key),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,24 +25,20 @@ strum = { version = "0.21.0", features = ["derive"] }
|
|||||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||||
bp-kusama = { path = "../../primitives/chain-kusama" }
|
bp-kusama = { path = "../../primitives/chain-kusama" }
|
||||||
bp-messages = { path = "../../primitives/messages" }
|
bp-messages = { path = "../../primitives/messages" }
|
||||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
|
||||||
bp-millau = { path = "../../primitives/chain-millau" }
|
bp-millau = { path = "../../primitives/chain-millau" }
|
||||||
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
||||||
bp-rialto = { path = "../../primitives/chain-rialto" }
|
bp-rialto = { path = "../../primitives/chain-rialto" }
|
||||||
bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" }
|
bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" }
|
||||||
bp-rococo = { path = "../../primitives/chain-rococo" }
|
bp-rococo = { path = "../../primitives/chain-rococo" }
|
||||||
bp-runtime = { path = "../../primitives/runtime" }
|
bp-runtime = { path = "../../primitives/runtime" }
|
||||||
bp-token-swap = { path = "../../primitives/token-swap" }
|
|
||||||
bp-westend = { path = "../../primitives/chain-westend" }
|
bp-westend = { path = "../../primitives/chain-westend" }
|
||||||
bp-wococo = { path = "../../primitives/chain-wococo" }
|
bp-wococo = { path = "../../primitives/chain-wococo" }
|
||||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||||
finality-relay = { path = "../finality" }
|
finality-relay = { path = "../finality" }
|
||||||
messages-relay = { path = "../messages" }
|
messages-relay = { path = "../messages" }
|
||||||
millau-runtime = { path = "../../bin/millau/runtime" }
|
millau-runtime = { path = "../../bin/millau/runtime" }
|
||||||
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
|
||||||
pallet-bridge-grandpa = { path = "../../modules/grandpa" }
|
pallet-bridge-grandpa = { path = "../../modules/grandpa" }
|
||||||
pallet-bridge-messages = { path = "../../modules/messages" }
|
pallet-bridge-messages = { path = "../../modules/messages" }
|
||||||
pallet-bridge-token-swap = { path = "../../modules/token-swap" }
|
|
||||||
relay-kusama-client = { path = "../client-kusama" }
|
relay-kusama-client = { path = "../client-kusama" }
|
||||||
relay-millau-client = { path = "../client-millau" }
|
relay-millau-client = { path = "../client-millau" }
|
||||||
relay-polkadot-client = { path = "../client-polkadot" }
|
relay-polkadot-client = { path = "../client-polkadot" }
|
||||||
|
|||||||
@@ -14,82 +14,46 @@
|
|||||||
// 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 anyhow::anyhow;
|
use bp_messages::LaneId;
|
||||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
|
||||||
use bp_runtime::EncodedOrDecodedCall;
|
use bp_runtime::EncodedOrDecodedCall;
|
||||||
use codec::Decode;
|
|
||||||
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
|
||||||
use relay_kusama_client::Kusama;
|
use relay_kusama_client::Kusama;
|
||||||
|
use relay_substrate_client::BalanceOf;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
bridge,
|
bridge,
|
||||||
encode_call::{self, Call, CliEncodeCall},
|
encode_message::{CliEncodeMessage, RawMessage},
|
||||||
encode_message,
|
|
||||||
send_message::{self, DispatchFeePayment},
|
|
||||||
CliChain,
|
CliChain,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Weight of the `system::remark` call at Kusama.
|
impl CliEncodeMessage for Kusama {
|
||||||
///
|
fn encode_send_message_call(
|
||||||
/// This weight is larger (x2) than actual weight at current Kusama runtime to avoid unsuccessful
|
lane: LaneId,
|
||||||
/// calls in the future. But since it is used only in tests (and on test chains), this is ok.
|
payload: RawMessage,
|
||||||
pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000;
|
fee: BalanceOf<Self>,
|
||||||
|
bridge_instance_index: u8,
|
||||||
impl CliEncodeCall for Kusama {
|
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
Ok(match bridge_instance_index {
|
||||||
Ok(match call {
|
bridge::KUSAMA_TO_POLKADOT_INDEX =>
|
||||||
Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()),
|
relay_kusama_client::runtime::Call::BridgePolkadotMessages(
|
||||||
Call::Remark { remark_payload, .. } => relay_kusama_client::runtime::Call::System(
|
relay_kusama_client::runtime::BridgePolkadotMessagesCall::send_message(
|
||||||
relay_kusama_client::runtime::SystemCall::remark(
|
lane, payload, fee,
|
||||||
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
|
|
||||||
match *bridge_instance_index {
|
|
||||||
bridge::KUSAMA_TO_POLKADOT_INDEX => {
|
|
||||||
let payload = Decode::decode(&mut &*payload.0)?;
|
|
||||||
relay_kusama_client::runtime::Call::BridgePolkadotMessages(
|
|
||||||
relay_kusama_client::runtime::BridgePolkadotMessagesCall::send_message(
|
|
||||||
lane.0, payload, fee.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
},
|
|
||||||
_ => anyhow::bail!(
|
|
||||||
"Unsupported target bridge pallet with instance index: {}",
|
|
||||||
bridge_instance_index
|
|
||||||
),
|
),
|
||||||
},
|
)
|
||||||
_ => anyhow::bail!("Unsupported Kusama call: {:?}", call),
|
.into(),
|
||||||
|
_ => anyhow::bail!(
|
||||||
|
"Unsupported target bridge pallet with instance index: {}",
|
||||||
|
bridge_instance_index
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
|
||||||
match *call {
|
|
||||||
EncodedOrDecodedCall::Decoded(relay_kusama_client::runtime::Call::System(
|
|
||||||
relay_kusama_client::runtime::SystemCall::remark(_),
|
|
||||||
)) => Ok(DispatchInfo {
|
|
||||||
weight: crate::chains::kusama::SYSTEM_REMARK_CALL_WEIGHT,
|
|
||||||
class: DispatchClass::Normal,
|
|
||||||
pays_fee: Pays::Yes,
|
|
||||||
}),
|
|
||||||
_ => anyhow::bail!("Unsupported Kusama call: {:?}", call),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliChain for Kusama {
|
impl CliChain for Kusama {
|
||||||
const RUNTIME_VERSION: RuntimeVersion = bp_kusama::VERSION;
|
const RUNTIME_VERSION: RuntimeVersion = bp_kusama::VERSION;
|
||||||
|
|
||||||
type KeyPair = sp_core::sr25519::Pair;
|
type KeyPair = sp_core::sr25519::Pair;
|
||||||
type MessagePayload = MessagePayload<
|
type MessagePayload = Vec<u8>;
|
||||||
bp_kusama::AccountId,
|
|
||||||
bp_polkadot::AccountPublic,
|
|
||||||
bp_polkadot::Signature,
|
|
||||||
Vec<u8>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn ss58_format() -> u16 {
|
fn ss58_format() -> u16 {
|
||||||
sp_core::crypto::Ss58AddressFormat::from(
|
sp_core::crypto::Ss58AddressFormat::from(
|
||||||
@@ -97,39 +61,4 @@ impl CliChain for Kusama {
|
|||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_message(
|
|
||||||
message: encode_message::MessagePayload,
|
|
||||||
) -> anyhow::Result<Self::MessagePayload> {
|
|
||||||
match message {
|
|
||||||
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
|
||||||
.map_err(|e| anyhow!("Failed to decode Kusama's MessagePayload: {:?}", e)),
|
|
||||||
encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => {
|
|
||||||
type Source = Kusama;
|
|
||||||
type Target = relay_polkadot_client::Polkadot;
|
|
||||||
|
|
||||||
sender.enforce_chain::<Source>();
|
|
||||||
let spec_version = Target::RUNTIME_VERSION.spec_version;
|
|
||||||
let origin = CallOrigin::SourceAccount(sender.raw_id());
|
|
||||||
encode_call::preprocess_call::<Source, Target>(
|
|
||||||
&mut call,
|
|
||||||
bridge::KUSAMA_TO_POLKADOT_INDEX,
|
|
||||||
);
|
|
||||||
let call = Target::encode_call(&call)?;
|
|
||||||
let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| {
|
|
||||||
Err(anyhow::format_err!(
|
|
||||||
"Please specify dispatch weight of the encoded Polkadot call"
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(send_message::message_payload(
|
|
||||||
spec_version,
|
|
||||||
dispatch_weight,
|
|
||||||
origin,
|
|
||||||
&call,
|
|
||||||
DispatchFeePayment::AtSourceChain,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,106 +18,46 @@
|
|||||||
|
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
bridge,
|
bridge,
|
||||||
encode_call::{self, Call, CliEncodeCall},
|
encode_message::{CliEncodeMessage, RawMessage},
|
||||||
encode_message,
|
|
||||||
send_message::{self, DispatchFeePayment},
|
|
||||||
CliChain,
|
CliChain,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use bp_messages::LaneId;
|
||||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
|
||||||
use bp_runtime::EncodedOrDecodedCall;
|
use bp_runtime::EncodedOrDecodedCall;
|
||||||
use codec::Decode;
|
|
||||||
use frame_support::weights::{DispatchInfo, GetDispatchInfo};
|
|
||||||
use relay_millau_client::Millau;
|
use relay_millau_client::Millau;
|
||||||
|
use relay_substrate_client::BalanceOf;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
impl CliEncodeCall for Millau {
|
impl CliEncodeMessage for Millau {
|
||||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
fn encode_send_message_call(
|
||||||
Ok(match call {
|
lane: LaneId,
|
||||||
Call::Raw { data } => Self::Call::decode(&mut &*data.0)?.into(),
|
payload: RawMessage,
|
||||||
Call::Remark { remark_payload, .. } =>
|
fee: BalanceOf<Self>,
|
||||||
millau_runtime::Call::System(millau_runtime::SystemCall::remark {
|
bridge_instance_index: u8,
|
||||||
remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||||
})
|
Ok(match bridge_instance_index {
|
||||||
.into(),
|
bridge::MILLAU_TO_RIALTO_INDEX => millau_runtime::Call::BridgeRialtoMessages(
|
||||||
Call::Transfer { recipient, amount } =>
|
millau_runtime::MessagesCall::send_message {
|
||||||
millau_runtime::Call::Balances(millau_runtime::BalancesCall::transfer {
|
lane_id: lane,
|
||||||
dest: recipient.raw_id(),
|
payload,
|
||||||
value: amount.cast(),
|
delivery_and_dispatch_fee: fee,
|
||||||
})
|
|
||||||
.into(),
|
|
||||||
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
|
|
||||||
match *bridge_instance_index {
|
|
||||||
bridge::MILLAU_TO_RIALTO_INDEX => {
|
|
||||||
let payload = Decode::decode(&mut &*payload.0)?;
|
|
||||||
millau_runtime::Call::BridgeRialtoMessages(
|
|
||||||
millau_runtime::MessagesCall::send_message {
|
|
||||||
lane_id: lane.0,
|
|
||||||
payload,
|
|
||||||
delivery_and_dispatch_fee: fee.cast(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
},
|
|
||||||
_ => anyhow::bail!(
|
|
||||||
"Unsupported target bridge pallet with instance index: {}",
|
|
||||||
bridge_instance_index
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
_ => anyhow::bail!(
|
||||||
|
"Unsupported target bridge pallet with instance index: {}",
|
||||||
|
bridge_instance_index
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
|
||||||
Ok(call.to_decoded()?.get_dispatch_info())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliChain for Millau {
|
impl CliChain for Millau {
|
||||||
const RUNTIME_VERSION: RuntimeVersion = millau_runtime::VERSION;
|
const RUNTIME_VERSION: RuntimeVersion = millau_runtime::VERSION;
|
||||||
|
|
||||||
type KeyPair = sp_core::sr25519::Pair;
|
type KeyPair = sp_core::sr25519::Pair;
|
||||||
type MessagePayload = MessagePayload<
|
type MessagePayload = Vec<u8>;
|
||||||
bp_millau::AccountId,
|
|
||||||
bp_rialto::AccountSigner,
|
|
||||||
bp_rialto::Signature,
|
|
||||||
Vec<u8>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn ss58_format() -> u16 {
|
fn ss58_format() -> u16 {
|
||||||
millau_runtime::SS58Prefix::get() as u16
|
millau_runtime::SS58Prefix::get() as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO [#854|#843] support multiple bridges?
|
|
||||||
fn encode_message(
|
|
||||||
message: encode_message::MessagePayload,
|
|
||||||
) -> anyhow::Result<Self::MessagePayload> {
|
|
||||||
match message {
|
|
||||||
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
|
||||||
.map_err(|e| anyhow!("Failed to decode Millau's MessagePayload: {:?}", e)),
|
|
||||||
encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => {
|
|
||||||
type Source = Millau;
|
|
||||||
type Target = relay_rialto_client::Rialto;
|
|
||||||
|
|
||||||
sender.enforce_chain::<Source>();
|
|
||||||
let spec_version = Target::RUNTIME_VERSION.spec_version;
|
|
||||||
let origin = CallOrigin::SourceAccount(sender.raw_id());
|
|
||||||
encode_call::preprocess_call::<Source, Target>(
|
|
||||||
&mut call,
|
|
||||||
bridge::MILLAU_TO_RIALTO_INDEX,
|
|
||||||
);
|
|
||||||
let call = Target::encode_call(&call)?;
|
|
||||||
let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| {
|
|
||||||
call.to_decoded().map(|call| call.get_dispatch_info().weight)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(send_message::message_payload(
|
|
||||||
spec_version,
|
|
||||||
dispatch_weight,
|
|
||||||
origin,
|
|
||||||
&call,
|
|
||||||
DispatchFeePayment::AtSourceChain,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,95 +41,28 @@ mod wococo;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::cli::{encode_call, send_message};
|
use crate::cli::encode_message;
|
||||||
use bp_messages::source_chain::TargetHeaderChain;
|
use bp_messages::source_chain::TargetHeaderChain;
|
||||||
use bp_runtime::Chain as _;
|
use bp_runtime::Chain as _;
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use frame_support::dispatch::GetDispatchInfo;
|
|
||||||
use relay_millau_client::Millau;
|
use relay_millau_client::Millau;
|
||||||
use relay_rialto_client::Rialto;
|
use relay_rialto_client::Rialto;
|
||||||
use relay_substrate_client::{SignParam, TransactionSignScheme, UnsignedTransaction};
|
use relay_substrate_client::{SignParam, TransactionSignScheme, UnsignedTransaction};
|
||||||
use sp_core::Pair;
|
|
||||||
use sp_runtime::traits::{IdentifyAccount, Verify};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn millau_signature_is_valid_on_rialto() {
|
fn maximal_rialto_to_millau_message_size_is_computed_correctly() {
|
||||||
let millau_sign = relay_millau_client::SigningParams::from_string("//Dave", None).unwrap();
|
|
||||||
|
|
||||||
let call =
|
|
||||||
rialto_runtime::Call::System(rialto_runtime::SystemCall::remark { remark: vec![] });
|
|
||||||
|
|
||||||
let millau_public: bp_millau::AccountSigner = millau_sign.public().into();
|
|
||||||
let millau_account_id: bp_millau::AccountId = millau_public.into_account();
|
|
||||||
|
|
||||||
let digest = millau_runtime::millau_to_rialto_account_ownership_digest(
|
|
||||||
&call,
|
|
||||||
millau_account_id,
|
|
||||||
rialto_runtime::VERSION.spec_version,
|
|
||||||
);
|
|
||||||
|
|
||||||
let rialto_signer =
|
|
||||||
relay_rialto_client::SigningParams::from_string("//Dave", None).unwrap();
|
|
||||||
let signature = rialto_signer.sign(&digest);
|
|
||||||
|
|
||||||
assert!(signature.verify(&digest[..], &rialto_signer.public()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn rialto_signature_is_valid_on_millau() {
|
|
||||||
let rialto_sign = relay_rialto_client::SigningParams::from_string("//Dave", None).unwrap();
|
|
||||||
|
|
||||||
let call =
|
|
||||||
millau_runtime::Call::System(millau_runtime::SystemCall::remark { remark: vec![] });
|
|
||||||
|
|
||||||
let rialto_public: bp_rialto::AccountSigner = rialto_sign.public().into();
|
|
||||||
let rialto_account_id: bp_rialto::AccountId = rialto_public.into_account();
|
|
||||||
|
|
||||||
let digest = rialto_runtime::rialto_to_millau_account_ownership_digest(
|
|
||||||
&call,
|
|
||||||
rialto_account_id,
|
|
||||||
millau_runtime::VERSION.spec_version,
|
|
||||||
);
|
|
||||||
|
|
||||||
let millau_signer =
|
|
||||||
relay_millau_client::SigningParams::from_string("//Dave", None).unwrap();
|
|
||||||
let signature = millau_signer.sign(&digest);
|
|
||||||
|
|
||||||
assert!(signature.verify(&digest[..], &millau_signer.public()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn maximal_rialto_to_millau_message_arguments_size_is_computed_correctly() {
|
|
||||||
use rialto_runtime::millau_messages::Millau;
|
use rialto_runtime::millau_messages::Millau;
|
||||||
|
|
||||||
let maximal_remark_size = encode_call::compute_maximal_message_arguments_size(
|
let maximal_message_size = encode_message::compute_maximal_message_size(
|
||||||
bp_rialto::Rialto::max_extrinsic_size(),
|
bp_rialto::Rialto::max_extrinsic_size(),
|
||||||
bp_millau::Millau::max_extrinsic_size(),
|
bp_millau::Millau::max_extrinsic_size(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let call: millau_runtime::Call =
|
let message = vec![42; maximal_message_size as _];
|
||||||
millau_runtime::SystemCall::remark { remark: vec![42; maximal_remark_size as _] }
|
assert_eq!(Millau::verify_message(&message), Ok(()));
|
||||||
.into();
|
|
||||||
let payload = send_message::message_payload(
|
|
||||||
Default::default(),
|
|
||||||
call.get_dispatch_info().weight,
|
|
||||||
bp_message_dispatch::CallOrigin::SourceRoot,
|
|
||||||
&call,
|
|
||||||
send_message::DispatchFeePayment::AtSourceChain,
|
|
||||||
);
|
|
||||||
assert_eq!(Millau::verify_message(&payload), Ok(()));
|
|
||||||
|
|
||||||
let call: millau_runtime::Call =
|
let message = vec![42; (maximal_message_size + 1) as _];
|
||||||
millau_runtime::SystemCall::remark { remark: vec![42; (maximal_remark_size + 1) as _] }
|
assert!(Millau::verify_message(&message).is_err());
|
||||||
.into();
|
|
||||||
let payload = send_message::message_payload(
|
|
||||||
Default::default(),
|
|
||||||
call.get_dispatch_info().weight,
|
|
||||||
bp_message_dispatch::CallOrigin::SourceRoot,
|
|
||||||
&call,
|
|
||||||
send_message::DispatchFeePayment::AtSourceChain,
|
|
||||||
);
|
|
||||||
assert!(Millau::verify_message(&payload).is_err());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -141,65 +74,6 @@ mod tests {
|
|||||||
"We can't actually send maximal messages to Rialto from Millau, because Millau extrinsics can't be that large",
|
"We can't actually send maximal messages to Rialto from Millau, because Millau extrinsics can't be that large",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn maximal_rialto_to_millau_message_dispatch_weight_is_computed_correctly() {
|
|
||||||
use rialto_runtime::millau_messages::Millau;
|
|
||||||
|
|
||||||
let maximal_dispatch_weight = send_message::compute_maximal_message_dispatch_weight(
|
|
||||||
bp_millau::Millau::max_extrinsic_weight(),
|
|
||||||
);
|
|
||||||
let call: millau_runtime::Call =
|
|
||||||
rialto_runtime::SystemCall::remark { remark: vec![] }.into();
|
|
||||||
|
|
||||||
let payload = send_message::message_payload(
|
|
||||||
Default::default(),
|
|
||||||
maximal_dispatch_weight,
|
|
||||||
bp_message_dispatch::CallOrigin::SourceRoot,
|
|
||||||
&call,
|
|
||||||
send_message::DispatchFeePayment::AtSourceChain,
|
|
||||||
);
|
|
||||||
assert_eq!(Millau::verify_message(&payload), Ok(()));
|
|
||||||
|
|
||||||
let payload = send_message::message_payload(
|
|
||||||
Default::default(),
|
|
||||||
maximal_dispatch_weight + 1,
|
|
||||||
bp_message_dispatch::CallOrigin::SourceRoot,
|
|
||||||
&call,
|
|
||||||
send_message::DispatchFeePayment::AtSourceChain,
|
|
||||||
);
|
|
||||||
assert!(Millau::verify_message(&payload).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn maximal_weight_fill_block_to_rialto_is_generated_correctly() {
|
|
||||||
use millau_runtime::rialto_messages::Rialto;
|
|
||||||
|
|
||||||
let maximal_dispatch_weight = send_message::compute_maximal_message_dispatch_weight(
|
|
||||||
bp_rialto::Rialto::max_extrinsic_weight(),
|
|
||||||
);
|
|
||||||
let call: rialto_runtime::Call =
|
|
||||||
millau_runtime::SystemCall::remark { remark: vec![] }.into();
|
|
||||||
|
|
||||||
let payload = send_message::message_payload(
|
|
||||||
Default::default(),
|
|
||||||
maximal_dispatch_weight,
|
|
||||||
bp_message_dispatch::CallOrigin::SourceRoot,
|
|
||||||
&call,
|
|
||||||
send_message::DispatchFeePayment::AtSourceChain,
|
|
||||||
);
|
|
||||||
assert_eq!(Rialto::verify_message(&payload), Ok(()));
|
|
||||||
|
|
||||||
let payload = send_message::message_payload(
|
|
||||||
Default::default(),
|
|
||||||
maximal_dispatch_weight + 1,
|
|
||||||
bp_message_dispatch::CallOrigin::SourceRoot,
|
|
||||||
&call,
|
|
||||||
send_message::DispatchFeePayment::AtSourceChain,
|
|
||||||
);
|
|
||||||
assert!(Rialto::verify_message(&payload).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rialto_tx_extra_bytes_constant_is_correct() {
|
fn rialto_tx_extra_bytes_constant_is_correct() {
|
||||||
let rialto_call =
|
let rialto_call =
|
||||||
|
|||||||
@@ -14,82 +14,46 @@
|
|||||||
// 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 anyhow::anyhow;
|
use bp_messages::LaneId;
|
||||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
|
||||||
use bp_runtime::EncodedOrDecodedCall;
|
use bp_runtime::EncodedOrDecodedCall;
|
||||||
use codec::Decode;
|
|
||||||
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
|
||||||
use relay_polkadot_client::Polkadot;
|
use relay_polkadot_client::Polkadot;
|
||||||
|
use relay_substrate_client::BalanceOf;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
bridge,
|
bridge,
|
||||||
encode_call::{self, Call, CliEncodeCall},
|
encode_message::{CliEncodeMessage, RawMessage},
|
||||||
encode_message,
|
|
||||||
send_message::{self, DispatchFeePayment},
|
|
||||||
CliChain,
|
CliChain,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Weight of the `system::remark` call at Polkadot.
|
impl CliEncodeMessage for Polkadot {
|
||||||
///
|
fn encode_send_message_call(
|
||||||
/// This weight is larger (x2) than actual weight at current Polkadot runtime to avoid unsuccessful
|
lane: LaneId,
|
||||||
/// calls in the future. But since it is used only in tests (and on test chains), this is ok.
|
payload: RawMessage,
|
||||||
pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000;
|
fee: BalanceOf<Self>,
|
||||||
|
bridge_instance_index: u8,
|
||||||
impl CliEncodeCall for Polkadot {
|
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
Ok(match bridge_instance_index {
|
||||||
Ok(match call {
|
bridge::POLKADOT_TO_KUSAMA_INDEX =>
|
||||||
Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()),
|
relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
|
||||||
Call::Remark { remark_payload, .. } => relay_polkadot_client::runtime::Call::System(
|
relay_polkadot_client::runtime::BridgeKusamaMessagesCall::send_message(
|
||||||
relay_polkadot_client::runtime::SystemCall::remark(
|
lane, payload, fee,
|
||||||
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
|
|
||||||
match *bridge_instance_index {
|
|
||||||
bridge::POLKADOT_TO_KUSAMA_INDEX => {
|
|
||||||
let payload = Decode::decode(&mut &*payload.0)?;
|
|
||||||
relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
|
|
||||||
relay_polkadot_client::runtime::BridgeKusamaMessagesCall::send_message(
|
|
||||||
lane.0, payload, fee.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
},
|
|
||||||
_ => anyhow::bail!(
|
|
||||||
"Unsupported target bridge pallet with instance index: {}",
|
|
||||||
bridge_instance_index
|
|
||||||
),
|
),
|
||||||
},
|
)
|
||||||
_ => anyhow::bail!("Unsupported Polkadot call: {:?}", call),
|
.into(),
|
||||||
|
_ => anyhow::bail!(
|
||||||
|
"Unsupported target bridge pallet with instance index: {}",
|
||||||
|
bridge_instance_index
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
|
||||||
match *call {
|
|
||||||
EncodedOrDecodedCall::Decoded(relay_polkadot_client::runtime::Call::System(
|
|
||||||
relay_polkadot_client::runtime::SystemCall::remark(_),
|
|
||||||
)) => Ok(DispatchInfo {
|
|
||||||
weight: crate::chains::polkadot::SYSTEM_REMARK_CALL_WEIGHT,
|
|
||||||
class: DispatchClass::Normal,
|
|
||||||
pays_fee: Pays::Yes,
|
|
||||||
}),
|
|
||||||
_ => anyhow::bail!("Unsupported Polkadot call: {:?}", call),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliChain for Polkadot {
|
impl CliChain for Polkadot {
|
||||||
const RUNTIME_VERSION: RuntimeVersion = bp_polkadot::VERSION;
|
const RUNTIME_VERSION: RuntimeVersion = bp_polkadot::VERSION;
|
||||||
|
|
||||||
type KeyPair = sp_core::sr25519::Pair;
|
type KeyPair = sp_core::sr25519::Pair;
|
||||||
type MessagePayload = MessagePayload<
|
type MessagePayload = Vec<u8>;
|
||||||
bp_polkadot::AccountId,
|
|
||||||
bp_kusama::AccountPublic,
|
|
||||||
bp_kusama::Signature,
|
|
||||||
Vec<u8>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn ss58_format() -> u16 {
|
fn ss58_format() -> u16 {
|
||||||
sp_core::crypto::Ss58AddressFormat::from(
|
sp_core::crypto::Ss58AddressFormat::from(
|
||||||
@@ -97,39 +61,4 @@ impl CliChain for Polkadot {
|
|||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_message(
|
|
||||||
message: encode_message::MessagePayload,
|
|
||||||
) -> anyhow::Result<Self::MessagePayload> {
|
|
||||||
match message {
|
|
||||||
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
|
||||||
.map_err(|e| anyhow!("Failed to decode Polkadot's MessagePayload: {:?}", e)),
|
|
||||||
encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => {
|
|
||||||
type Source = Polkadot;
|
|
||||||
type Target = relay_kusama_client::Kusama;
|
|
||||||
|
|
||||||
sender.enforce_chain::<Source>();
|
|
||||||
let spec_version = Target::RUNTIME_VERSION.spec_version;
|
|
||||||
let origin = CallOrigin::SourceAccount(sender.raw_id());
|
|
||||||
encode_call::preprocess_call::<Source, Target>(
|
|
||||||
&mut call,
|
|
||||||
bridge::POLKADOT_TO_KUSAMA_INDEX,
|
|
||||||
);
|
|
||||||
let call = Target::encode_call(&call)?;
|
|
||||||
let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| {
|
|
||||||
Err(anyhow::format_err!(
|
|
||||||
"Please specify dispatch weight of the encoded Kusama call"
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(send_message::message_payload(
|
|
||||||
spec_version,
|
|
||||||
dispatch_weight,
|
|
||||||
origin,
|
|
||||||
&call,
|
|
||||||
DispatchFeePayment::AtSourceChain,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,105 +18,46 @@
|
|||||||
|
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
bridge,
|
bridge,
|
||||||
encode_call::{self, Call, CliEncodeCall},
|
encode_message::{CliEncodeMessage, RawMessage},
|
||||||
encode_message,
|
|
||||||
send_message::{self, DispatchFeePayment},
|
|
||||||
CliChain,
|
CliChain,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use bp_messages::LaneId;
|
||||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
|
||||||
use bp_runtime::EncodedOrDecodedCall;
|
use bp_runtime::EncodedOrDecodedCall;
|
||||||
use codec::Decode;
|
|
||||||
use frame_support::weights::{DispatchInfo, GetDispatchInfo};
|
|
||||||
use relay_rialto_client::Rialto;
|
use relay_rialto_client::Rialto;
|
||||||
|
use relay_substrate_client::BalanceOf;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
impl CliEncodeCall for Rialto {
|
impl CliEncodeMessage for Rialto {
|
||||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
fn encode_send_message_call(
|
||||||
Ok(match call {
|
lane: LaneId,
|
||||||
Call::Raw { data } => Self::Call::decode(&mut &*data.0)?.into(),
|
payload: RawMessage,
|
||||||
Call::Remark { remark_payload, .. } =>
|
fee: BalanceOf<Self>,
|
||||||
rialto_runtime::Call::System(rialto_runtime::SystemCall::remark {
|
bridge_instance_index: u8,
|
||||||
remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||||
})
|
Ok(match bridge_instance_index {
|
||||||
.into(),
|
bridge::RIALTO_TO_MILLAU_INDEX => rialto_runtime::Call::BridgeMillauMessages(
|
||||||
Call::Transfer { recipient, amount } =>
|
rialto_runtime::MessagesCall::send_message {
|
||||||
rialto_runtime::Call::Balances(rialto_runtime::BalancesCall::transfer {
|
lane_id: lane,
|
||||||
dest: recipient.raw_id().into(),
|
payload,
|
||||||
value: amount.0,
|
delivery_and_dispatch_fee: fee,
|
||||||
})
|
|
||||||
.into(),
|
|
||||||
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
|
|
||||||
match *bridge_instance_index {
|
|
||||||
bridge::RIALTO_TO_MILLAU_INDEX => {
|
|
||||||
let payload = Decode::decode(&mut &*payload.0)?;
|
|
||||||
rialto_runtime::Call::BridgeMillauMessages(
|
|
||||||
rialto_runtime::MessagesCall::send_message {
|
|
||||||
lane_id: lane.0,
|
|
||||||
payload,
|
|
||||||
delivery_and_dispatch_fee: fee.0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
},
|
|
||||||
_ => anyhow::bail!(
|
|
||||||
"Unsupported target bridge pallet with instance index: {}",
|
|
||||||
bridge_instance_index
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
_ => anyhow::bail!(
|
||||||
|
"Unsupported target bridge pallet with instance index: {}",
|
||||||
|
bridge_instance_index
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
|
||||||
Ok(call.to_decoded()?.get_dispatch_info())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliChain for Rialto {
|
impl CliChain for Rialto {
|
||||||
const RUNTIME_VERSION: RuntimeVersion = rialto_runtime::VERSION;
|
const RUNTIME_VERSION: RuntimeVersion = rialto_runtime::VERSION;
|
||||||
|
|
||||||
type KeyPair = sp_core::sr25519::Pair;
|
type KeyPair = sp_core::sr25519::Pair;
|
||||||
type MessagePayload = MessagePayload<
|
type MessagePayload = Vec<u8>;
|
||||||
bp_rialto::AccountId,
|
|
||||||
bp_millau::AccountSigner,
|
|
||||||
bp_millau::Signature,
|
|
||||||
Vec<u8>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn ss58_format() -> u16 {
|
fn ss58_format() -> u16 {
|
||||||
rialto_runtime::SS58Prefix::get() as u16
|
rialto_runtime::SS58Prefix::get() as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_message(
|
|
||||||
message: encode_message::MessagePayload,
|
|
||||||
) -> anyhow::Result<Self::MessagePayload> {
|
|
||||||
match message {
|
|
||||||
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
|
||||||
.map_err(|e| anyhow!("Failed to decode Rialto's MessagePayload: {:?}", e)),
|
|
||||||
encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => {
|
|
||||||
type Source = Rialto;
|
|
||||||
type Target = relay_millau_client::Millau;
|
|
||||||
|
|
||||||
sender.enforce_chain::<Source>();
|
|
||||||
let spec_version = Target::RUNTIME_VERSION.spec_version;
|
|
||||||
let origin = CallOrigin::SourceAccount(sender.raw_id());
|
|
||||||
encode_call::preprocess_call::<Source, Target>(
|
|
||||||
&mut call,
|
|
||||||
bridge::RIALTO_TO_MILLAU_INDEX,
|
|
||||||
);
|
|
||||||
let call = Target::encode_call(&call)?;
|
|
||||||
let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| {
|
|
||||||
call.to_decoded().map(|call| call.get_dispatch_info().weight)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(send_message::message_payload(
|
|
||||||
spec_version,
|
|
||||||
dispatch_weight,
|
|
||||||
origin,
|
|
||||||
&call,
|
|
||||||
DispatchFeePayment::AtSourceChain,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,63 +16,17 @@
|
|||||||
|
|
||||||
//! Rialto parachain specification for CLI.
|
//! Rialto parachain specification for CLI.
|
||||||
|
|
||||||
use crate::cli::{
|
use crate::cli::CliChain;
|
||||||
encode_call::{Call, CliEncodeCall},
|
|
||||||
encode_message, CliChain,
|
|
||||||
};
|
|
||||||
use bp_message_dispatch::MessagePayload;
|
|
||||||
use bp_runtime::EncodedOrDecodedCall;
|
|
||||||
use codec::Decode;
|
|
||||||
use frame_support::weights::{DispatchInfo, GetDispatchInfo};
|
|
||||||
use relay_rialto_parachain_client::RialtoParachain;
|
use relay_rialto_parachain_client::RialtoParachain;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
impl CliEncodeCall for RialtoParachain {
|
|
||||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
|
||||||
Ok(match call {
|
|
||||||
Call::Raw { data } => Self::Call::decode(&mut &*data.0)?.into(),
|
|
||||||
Call::Remark { remark_payload, .. } => rialto_parachain_runtime::Call::System(
|
|
||||||
rialto_parachain_runtime::SystemCall::remark {
|
|
||||||
remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
Call::Transfer { recipient, amount } => rialto_parachain_runtime::Call::Balances(
|
|
||||||
rialto_parachain_runtime::BalancesCall::transfer {
|
|
||||||
dest: recipient.raw_id().into(),
|
|
||||||
value: amount.0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
Call::BridgeSendMessage { .. } => {
|
|
||||||
anyhow::bail!("Bridge messages are not (yet) supported here",)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
|
||||||
Ok(call.to_decoded()?.get_dispatch_info())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CliChain for RialtoParachain {
|
impl CliChain for RialtoParachain {
|
||||||
const RUNTIME_VERSION: RuntimeVersion = rialto_parachain_runtime::VERSION;
|
const RUNTIME_VERSION: RuntimeVersion = rialto_parachain_runtime::VERSION;
|
||||||
|
|
||||||
type KeyPair = sp_core::sr25519::Pair;
|
type KeyPair = sp_core::sr25519::Pair;
|
||||||
type MessagePayload = MessagePayload<
|
type MessagePayload = Vec<u8>;
|
||||||
bp_rialto_parachain::AccountId,
|
|
||||||
bp_millau::AccountSigner,
|
|
||||||
bp_millau::Signature,
|
|
||||||
Vec<u8>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn ss58_format() -> u16 {
|
fn ss58_format() -> u16 {
|
||||||
rialto_parachain_runtime::SS58Prefix::get() as u16
|
rialto_parachain_runtime::SS58Prefix::get() as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_message(
|
|
||||||
_message: encode_message::MessagePayload,
|
|
||||||
) -> anyhow::Result<Self::MessagePayload> {
|
|
||||||
anyhow::bail!("Not supported")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,119 +14,47 @@
|
|||||||
// 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 anyhow::anyhow;
|
use bp_messages::LaneId;
|
||||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
|
||||||
use bp_runtime::EncodedOrDecodedCall;
|
use bp_runtime::EncodedOrDecodedCall;
|
||||||
use codec::Decode;
|
|
||||||
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
|
||||||
use relay_rococo_client::Rococo;
|
use relay_rococo_client::Rococo;
|
||||||
|
use relay_substrate_client::BalanceOf;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
bridge,
|
bridge,
|
||||||
encode_call::{self, Call, CliEncodeCall},
|
encode_message::{CliEncodeMessage, RawMessage},
|
||||||
encode_message,
|
|
||||||
send_message::{self, DispatchFeePayment},
|
|
||||||
CliChain,
|
CliChain,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Weight of the `system::remark` call at Rococo.
|
impl CliEncodeMessage for Rococo {
|
||||||
///
|
fn encode_send_message_call(
|
||||||
/// This weight is larger (x2) than actual weight at current Rococo runtime to avoid unsuccessful
|
lane: LaneId,
|
||||||
/// calls in the future. But since it is used only in tests (and on test chains), this is ok.
|
payload: RawMessage,
|
||||||
pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000;
|
fee: BalanceOf<Self>,
|
||||||
|
bridge_instance_index: u8,
|
||||||
impl CliEncodeCall for Rococo {
|
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
Ok(match bridge_instance_index {
|
||||||
Ok(match call {
|
bridge::ROCOCO_TO_WOCOCO_INDEX =>
|
||||||
Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()),
|
relay_rococo_client::runtime::Call::BridgeWococoMessages(
|
||||||
Call::Remark { remark_payload, .. } => relay_rococo_client::runtime::Call::System(
|
relay_rococo_client::runtime::BridgeWococoMessagesCall::send_message(
|
||||||
relay_rococo_client::runtime::SystemCall::remark(
|
lane, payload, fee,
|
||||||
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
|
|
||||||
match *bridge_instance_index {
|
|
||||||
bridge::ROCOCO_TO_WOCOCO_INDEX => {
|
|
||||||
let payload = Decode::decode(&mut &*payload.0)?;
|
|
||||||
relay_rococo_client::runtime::Call::BridgeWococoMessages(
|
|
||||||
relay_rococo_client::runtime::BridgeWococoMessagesCall::send_message(
|
|
||||||
lane.0, payload, fee.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
},
|
|
||||||
_ => anyhow::bail!(
|
|
||||||
"Unsupported target bridge pallet with instance index: {}",
|
|
||||||
bridge_instance_index
|
|
||||||
),
|
),
|
||||||
},
|
)
|
||||||
_ => anyhow::bail!("The call is not supported"),
|
.into(),
|
||||||
|
_ => anyhow::bail!(
|
||||||
|
"Unsupported target bridge pallet with instance index: {}",
|
||||||
|
bridge_instance_index
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
|
||||||
match *call {
|
|
||||||
EncodedOrDecodedCall::Decoded(relay_rococo_client::runtime::Call::System(
|
|
||||||
relay_rococo_client::runtime::SystemCall::remark(_),
|
|
||||||
)) => Ok(DispatchInfo {
|
|
||||||
weight: SYSTEM_REMARK_CALL_WEIGHT,
|
|
||||||
class: DispatchClass::Normal,
|
|
||||||
pays_fee: Pays::Yes,
|
|
||||||
}),
|
|
||||||
_ => anyhow::bail!("Unsupported Rococo call: {:?}", call),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliChain for Rococo {
|
impl CliChain for Rococo {
|
||||||
const RUNTIME_VERSION: RuntimeVersion = bp_rococo::VERSION;
|
const RUNTIME_VERSION: RuntimeVersion = bp_rococo::VERSION;
|
||||||
|
|
||||||
type KeyPair = sp_core::sr25519::Pair;
|
type KeyPair = sp_core::sr25519::Pair;
|
||||||
type MessagePayload = MessagePayload<
|
type MessagePayload = Vec<u8>;
|
||||||
bp_rococo::AccountId,
|
|
||||||
bp_wococo::AccountPublic,
|
|
||||||
bp_wococo::Signature,
|
|
||||||
Vec<u8>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn ss58_format() -> u16 {
|
fn ss58_format() -> u16 {
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_message(
|
|
||||||
message: encode_message::MessagePayload,
|
|
||||||
) -> anyhow::Result<Self::MessagePayload> {
|
|
||||||
match message {
|
|
||||||
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
|
||||||
.map_err(|e| anyhow!("Failed to decode Rococo's MessagePayload: {:?}", e)),
|
|
||||||
encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => {
|
|
||||||
type Source = Rococo;
|
|
||||||
type Target = relay_wococo_client::Wococo;
|
|
||||||
|
|
||||||
sender.enforce_chain::<Source>();
|
|
||||||
let spec_version = Target::RUNTIME_VERSION.spec_version;
|
|
||||||
let origin = CallOrigin::SourceAccount(sender.raw_id());
|
|
||||||
encode_call::preprocess_call::<Source, Target>(
|
|
||||||
&mut call,
|
|
||||||
bridge::ROCOCO_TO_WOCOCO_INDEX,
|
|
||||||
);
|
|
||||||
let call = Target::encode_call(&call)?;
|
|
||||||
let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| {
|
|
||||||
Err(anyhow::format_err!(
|
|
||||||
"Please specify dispatch weight of the encoded Wococo call"
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(send_message::message_payload(
|
|
||||||
spec_version,
|
|
||||||
dispatch_weight,
|
|
||||||
origin,
|
|
||||||
&call,
|
|
||||||
DispatchFeePayment::AtSourceChain,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,7 @@
|
|||||||
|
|
||||||
//! Westend chain specification for CLI.
|
//! Westend chain specification for CLI.
|
||||||
|
|
||||||
use crate::cli::{encode_message, CliChain};
|
use crate::cli::CliChain;
|
||||||
use anyhow::anyhow;
|
|
||||||
use relay_westend_client::Westend;
|
use relay_westend_client::Westend;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ impl CliChain for Westend {
|
|||||||
const RUNTIME_VERSION: RuntimeVersion = bp_westend::VERSION;
|
const RUNTIME_VERSION: RuntimeVersion = bp_westend::VERSION;
|
||||||
|
|
||||||
type KeyPair = sp_core::sr25519::Pair;
|
type KeyPair = sp_core::sr25519::Pair;
|
||||||
type MessagePayload = ();
|
type MessagePayload = Vec<u8>;
|
||||||
|
|
||||||
fn ss58_format() -> u16 {
|
fn ss58_format() -> u16 {
|
||||||
sp_core::crypto::Ss58AddressFormat::from(
|
sp_core::crypto::Ss58AddressFormat::from(
|
||||||
@@ -33,10 +32,4 @@ impl CliChain for Westend {
|
|||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_message(
|
|
||||||
_message: encode_message::MessagePayload,
|
|
||||||
) -> anyhow::Result<Self::MessagePayload> {
|
|
||||||
Err(anyhow!("Sending messages from Westend is not yet supported."))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,113 +14,48 @@
|
|||||||
// 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 anyhow::anyhow;
|
use bp_messages::LaneId;
|
||||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
|
||||||
use bp_runtime::EncodedOrDecodedCall;
|
use bp_runtime::EncodedOrDecodedCall;
|
||||||
use codec::Decode;
|
use relay_substrate_client::BalanceOf;
|
||||||
use frame_support::weights::{DispatchClass, DispatchInfo, Pays};
|
|
||||||
use relay_wococo_client::Wococo;
|
use relay_wococo_client::Wococo;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
bridge,
|
bridge,
|
||||||
encode_call::{self, Call, CliEncodeCall},
|
encode_message::{CliEncodeMessage, RawMessage},
|
||||||
encode_message,
|
|
||||||
send_message::{self, DispatchFeePayment},
|
|
||||||
CliChain,
|
CliChain,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl CliEncodeCall for Wococo {
|
impl CliEncodeMessage for Wococo {
|
||||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
fn encode_send_message_call(
|
||||||
Ok(match call {
|
lane: LaneId,
|
||||||
Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()),
|
payload: RawMessage,
|
||||||
Call::Remark { remark_payload, .. } => relay_wococo_client::runtime::Call::System(
|
fee: BalanceOf<Self>,
|
||||||
relay_wococo_client::runtime::SystemCall::remark(
|
bridge_instance_index: u8,
|
||||||
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||||
),
|
Ok(match bridge_instance_index {
|
||||||
)
|
bridge::WOCOCO_TO_ROCOCO_INDEX =>
|
||||||
.into(),
|
relay_wococo_client::runtime::Call::BridgeRococoMessages(
|
||||||
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
|
relay_wococo_client::runtime::BridgeRococoMessagesCall::send_message(
|
||||||
match *bridge_instance_index {
|
lane, payload, fee,
|
||||||
bridge::WOCOCO_TO_ROCOCO_INDEX => {
|
|
||||||
let payload = Decode::decode(&mut &*payload.0)?;
|
|
||||||
relay_wococo_client::runtime::Call::BridgeRococoMessages(
|
|
||||||
relay_wococo_client::runtime::BridgeRococoMessagesCall::send_message(
|
|
||||||
lane.0, payload, fee.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
},
|
|
||||||
_ => anyhow::bail!(
|
|
||||||
"Unsupported target bridge pallet with instance index: {}",
|
|
||||||
bridge_instance_index
|
|
||||||
),
|
),
|
||||||
},
|
)
|
||||||
_ => anyhow::bail!("The call is not supported"),
|
.into(),
|
||||||
|
_ => anyhow::bail!(
|
||||||
|
"Unsupported target bridge pallet with instance index: {}",
|
||||||
|
bridge_instance_index
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
|
||||||
match *call {
|
|
||||||
EncodedOrDecodedCall::Decoded(relay_wococo_client::runtime::Call::System(
|
|
||||||
relay_wococo_client::runtime::SystemCall::remark(_),
|
|
||||||
)) => Ok(DispatchInfo {
|
|
||||||
weight: crate::chains::rococo::SYSTEM_REMARK_CALL_WEIGHT,
|
|
||||||
class: DispatchClass::Normal,
|
|
||||||
pays_fee: Pays::Yes,
|
|
||||||
}),
|
|
||||||
_ => anyhow::bail!("Unsupported Wococo call: {:?}", call),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliChain for Wococo {
|
impl CliChain for Wococo {
|
||||||
const RUNTIME_VERSION: RuntimeVersion = bp_wococo::VERSION;
|
const RUNTIME_VERSION: RuntimeVersion = bp_wococo::VERSION;
|
||||||
|
|
||||||
type KeyPair = sp_core::sr25519::Pair;
|
type KeyPair = sp_core::sr25519::Pair;
|
||||||
type MessagePayload = MessagePayload<
|
type MessagePayload = Vec<u8>;
|
||||||
bp_wococo::AccountId,
|
|
||||||
bp_rococo::AccountPublic,
|
|
||||||
bp_rococo::Signature,
|
|
||||||
Vec<u8>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn ss58_format() -> u16 {
|
fn ss58_format() -> u16 {
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_message(
|
|
||||||
message: encode_message::MessagePayload,
|
|
||||||
) -> anyhow::Result<Self::MessagePayload> {
|
|
||||||
match message {
|
|
||||||
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
|
||||||
.map_err(|e| anyhow!("Failed to decode Wococo's MessagePayload: {:?}", e)),
|
|
||||||
encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => {
|
|
||||||
type Source = Wococo;
|
|
||||||
type Target = relay_rococo_client::Rococo;
|
|
||||||
|
|
||||||
sender.enforce_chain::<Source>();
|
|
||||||
let spec_version = Target::RUNTIME_VERSION.spec_version;
|
|
||||||
let origin = CallOrigin::SourceAccount(sender.raw_id());
|
|
||||||
encode_call::preprocess_call::<Source, Target>(
|
|
||||||
&mut call,
|
|
||||||
bridge::WOCOCO_TO_ROCOCO_INDEX,
|
|
||||||
);
|
|
||||||
let call = Target::encode_call(&call)?;
|
|
||||||
let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| {
|
|
||||||
Err(anyhow::format_err!(
|
|
||||||
"Please specify dispatch weight of the encoded Rococo call"
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(send_message::message_payload(
|
|
||||||
spec_version,
|
|
||||||
dispatch_weight,
|
|
||||||
origin,
|
|
||||||
&call,
|
|
||||||
DispatchFeePayment::AtSourceChain,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,12 +73,9 @@ macro_rules! select_full_bridge {
|
|||||||
// Send-message / Estimate-fee
|
// Send-message / Estimate-fee
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use bp_rialto::TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
use bp_rialto::TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
||||||
// Send-message
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use millau_runtime::millau_to_rialto_account_ownership_digest as account_ownership_digest;
|
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
},
|
||||||
FullBridge::RialtoToMillau => {
|
FullBridge::RialtoToMillau => {
|
||||||
type Source = relay_rialto_client::Rialto;
|
type Source = relay_rialto_client::Rialto;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -96,12 +93,8 @@ macro_rules! select_full_bridge {
|
|||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use bp_millau::TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
use bp_millau::TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
||||||
|
|
||||||
// Send-message
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use rialto_runtime::rialto_to_millau_account_ownership_digest as account_ownership_digest;
|
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
},
|
||||||
FullBridge::RococoToWococo => {
|
FullBridge::RococoToWococo => {
|
||||||
type Source = relay_rococo_client::Rococo;
|
type Source = relay_rococo_client::Rococo;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -118,12 +111,9 @@ macro_rules! select_full_bridge {
|
|||||||
// Send-message / Estimate-fee
|
// Send-message / Estimate-fee
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use bp_wococo::TO_WOCOCO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
use bp_wococo::TO_WOCOCO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
||||||
// Send-message
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use relay_rococo_client::runtime::rococo_to_wococo_account_ownership_digest as account_ownership_digest;
|
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
},
|
||||||
FullBridge::WococoToRococo => {
|
FullBridge::WococoToRococo => {
|
||||||
type Source = relay_wococo_client::Wococo;
|
type Source = relay_wococo_client::Wococo;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -140,12 +130,9 @@ macro_rules! select_full_bridge {
|
|||||||
// Send-message / Estimate-fee
|
// Send-message / Estimate-fee
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use bp_rococo::TO_ROCOCO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
use bp_rococo::TO_ROCOCO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
||||||
// Send-message
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use relay_wococo_client::runtime::wococo_to_rococo_account_ownership_digest as account_ownership_digest;
|
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
},
|
||||||
FullBridge::KusamaToPolkadot => {
|
FullBridge::KusamaToPolkadot => {
|
||||||
type Source = relay_kusama_client::Kusama;
|
type Source = relay_kusama_client::Kusama;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -162,12 +149,9 @@ macro_rules! select_full_bridge {
|
|||||||
// Send-message / Estimate-fee
|
// Send-message / Estimate-fee
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use bp_polkadot::TO_POLKADOT_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
use bp_polkadot::TO_POLKADOT_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
||||||
// Send-message
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use relay_kusama_client::runtime::kusama_to_polkadot_account_ownership_digest as account_ownership_digest;
|
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
},
|
||||||
FullBridge::PolkadotToKusama => {
|
FullBridge::PolkadotToKusama => {
|
||||||
type Source = relay_polkadot_client::Polkadot;
|
type Source = relay_polkadot_client::Polkadot;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -184,12 +168,9 @@ macro_rules! select_full_bridge {
|
|||||||
// Send-message / Estimate-fee
|
// Send-message / Estimate-fee
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use bp_kusama::TO_KUSAMA_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
use bp_kusama::TO_KUSAMA_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
||||||
// Send-message
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use relay_polkadot_client::runtime::polkadot_to_kusama_account_ownership_digest as account_ownership_digest;
|
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
cli::{bridge::FullBridge, AccountId},
|
|
||||||
select_full_bridge,
|
|
||||||
};
|
|
||||||
use relay_substrate_client::Chain;
|
|
||||||
use structopt::StructOpt;
|
|
||||||
use strum::VariantNames;
|
|
||||||
|
|
||||||
/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target chain.
|
|
||||||
///
|
|
||||||
/// The (derived) target chain `AccountId` is going to be used as dispatch origin of the call
|
|
||||||
/// that has been sent over the bridge.
|
|
||||||
/// This account can also be used to receive target-chain funds (or other form of ownership),
|
|
||||||
/// since messages sent over the bridge will be able to spend these.
|
|
||||||
#[derive(StructOpt)]
|
|
||||||
pub struct DeriveAccount {
|
|
||||||
/// A bridge instance to initialize.
|
|
||||||
#[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)]
|
|
||||||
bridge: FullBridge,
|
|
||||||
/// Source-chain address to derive Target-chain address from.
|
|
||||||
account: AccountId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeriveAccount {
|
|
||||||
/// Parse CLI arguments and derive account.
|
|
||||||
///
|
|
||||||
/// Returns both the Source account in correct SS58 format and the derived account.
|
|
||||||
fn derive_account(&self) -> (AccountId, AccountId) {
|
|
||||||
select_full_bridge!(self.bridge, {
|
|
||||||
let mut account = self.account.clone();
|
|
||||||
account.enforce_chain::<Source>();
|
|
||||||
let acc = bp_runtime::SourceAccount::Account(account.raw_id());
|
|
||||||
let id = derive_account(acc);
|
|
||||||
let derived_account = AccountId::from_raw::<Target>(id);
|
|
||||||
(account, derived_account)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the command.
|
|
||||||
pub async fn run(self) -> anyhow::Result<()> {
|
|
||||||
select_full_bridge!(self.bridge, {
|
|
||||||
let (account, derived_account) = self.derive_account();
|
|
||||||
println!("Source address:\n{} ({})", account, Source::NAME);
|
|
||||||
println!("->Corresponding (derived) address:\n{} ({})", derived_account, Target::NAME,);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn derive_account_cli(bridge: &str, account: &str) -> (AccountId, AccountId) {
|
|
||||||
DeriveAccount::from_iter(vec!["derive-account", bridge, account]).derive_account()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_derive_accounts_correctly() {
|
|
||||||
// given
|
|
||||||
let rialto = "5sauUXUfPjmwxSgmb3tZ5d6yx24eZX4wWJ2JtVUBaQqFbvEU";
|
|
||||||
let millau = "752paRyW1EGfq9YLTSSqcSJ5hqnBDidBmaftGhBo8fy6ypW9";
|
|
||||||
|
|
||||||
// when
|
|
||||||
let (rialto_parsed, rialto_derived) = derive_account_cli("rialto-to-millau", rialto);
|
|
||||||
let (millau_parsed, millau_derived) = derive_account_cli("millau-to-rialto", millau);
|
|
||||||
let (millau2_parsed, millau2_derived) = derive_account_cli("millau-to-rialto", rialto);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(format!("{}", rialto_parsed), rialto);
|
|
||||||
assert_eq!(format!("{}", millau_parsed), millau);
|
|
||||||
assert_eq!(format!("{}", millau2_parsed), millau);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
format!("{}", rialto_derived),
|
|
||||||
"74GNQjmkcfstRftSQPJgMREchqHM56EvAUXRc266cZ1NYVW5"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
format!("{}", millau_derived),
|
|
||||||
"5rERgaT1Z8nM3et2epA5i1VtEBfp5wkhwHtVE8HK7BRbjAH2"
|
|
||||||
);
|
|
||||||
assert_eq!(millau_derived, millau2_derived);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,354 +0,0 @@
|
|||||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
cli::{
|
|
||||||
bridge::FullBridge, AccountId, Balance, CliChain, ExplicitOrMaximal, HexBytes, HexLaneId,
|
|
||||||
},
|
|
||||||
select_full_bridge,
|
|
||||||
};
|
|
||||||
use bp_runtime::EncodedOrDecodedCall;
|
|
||||||
use frame_support::weights::DispatchInfo;
|
|
||||||
use relay_substrate_client::Chain;
|
|
||||||
use structopt::StructOpt;
|
|
||||||
use strum::VariantNames;
|
|
||||||
|
|
||||||
/// Encode source chain runtime call.
|
|
||||||
#[derive(StructOpt, Debug)]
|
|
||||||
pub struct EncodeCall {
|
|
||||||
/// A bridge instance to encode call for.
|
|
||||||
#[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)]
|
|
||||||
bridge: FullBridge,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
call: Call,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All possible messages that may be delivered to generic Substrate chain.
|
|
||||||
///
|
|
||||||
/// Note this enum may be used in the context of both Source (as part of `encode-call`)
|
|
||||||
/// and Target chain (as part of `encode-message/send-message`).
|
|
||||||
#[derive(StructOpt, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Call {
|
|
||||||
/// Raw bytes for the message
|
|
||||||
Raw {
|
|
||||||
/// Raw, SCALE-encoded message
|
|
||||||
data: HexBytes,
|
|
||||||
},
|
|
||||||
/// Make an on-chain remark (comment).
|
|
||||||
Remark {
|
|
||||||
/// Explicit remark payload.
|
|
||||||
#[structopt(long, conflicts_with("remark-size"))]
|
|
||||||
remark_payload: Option<HexBytes>,
|
|
||||||
/// Remark size. If not passed, small UTF8-encoded string is generated by relay as remark.
|
|
||||||
#[structopt(long, conflicts_with("remark-payload"))]
|
|
||||||
remark_size: Option<ExplicitOrMaximal<usize>>,
|
|
||||||
},
|
|
||||||
/// Transfer the specified `amount` of native tokens to a particular `recipient`.
|
|
||||||
Transfer {
|
|
||||||
/// Address of an account to receive the transfer.
|
|
||||||
#[structopt(long)]
|
|
||||||
recipient: AccountId,
|
|
||||||
/// Amount of target tokens to send in target chain base currency units.
|
|
||||||
#[structopt(long)]
|
|
||||||
amount: Balance,
|
|
||||||
},
|
|
||||||
/// A call to the specific Bridge Messages pallet to queue message to be sent over a bridge.
|
|
||||||
BridgeSendMessage {
|
|
||||||
/// An index of the bridge instance which represents the expected target chain.
|
|
||||||
#[structopt(skip = 255)]
|
|
||||||
bridge_instance_index: u8,
|
|
||||||
/// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`.
|
|
||||||
#[structopt(long, default_value = "00000000")]
|
|
||||||
lane: HexLaneId,
|
|
||||||
/// Raw SCALE-encoded Message Payload to submit to the messages pallet.
|
|
||||||
///
|
|
||||||
/// This can be obtained by encoding call for the target chain.
|
|
||||||
#[structopt(long)]
|
|
||||||
payload: HexBytes,
|
|
||||||
/// Declared delivery and dispatch fee in base source-chain currency units.
|
|
||||||
#[structopt(long)]
|
|
||||||
fee: Balance,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait CliEncodeCall: Chain {
|
|
||||||
/// Encode a CLI call.
|
|
||||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>>;
|
|
||||||
|
|
||||||
/// Get dispatch info for the call.
|
|
||||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EncodeCall {
|
|
||||||
fn encode(&mut self) -> anyhow::Result<HexBytes> {
|
|
||||||
select_full_bridge!(self.bridge, {
|
|
||||||
preprocess_call::<Source, Target>(&mut self.call, self.bridge.bridge_instance_index());
|
|
||||||
let call = Source::encode_call(&self.call)?;
|
|
||||||
|
|
||||||
let encoded = HexBytes::encode(&call);
|
|
||||||
|
|
||||||
log::info!(target: "bridge", "Generated {} call: {:#?}", Source::NAME, call);
|
|
||||||
log::info!(target: "bridge", "Weight of {} call: {}", Source::NAME, Source::get_dispatch_info(&call)
|
|
||||||
.map(|dispatch_info| format!("{}", dispatch_info.weight))
|
|
||||||
.unwrap_or_else(|_| "<unknown>".to_string())
|
|
||||||
);
|
|
||||||
log::info!(target: "bridge", "Encoded {} call: {:?}", Source::NAME, encoded);
|
|
||||||
|
|
||||||
Ok(encoded)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the command.
|
|
||||||
pub async fn run(mut self) -> anyhow::Result<()> {
|
|
||||||
println!("{:?}", self.encode()?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepare the call to be passed to [`CliEncodeCall::encode_call`].
|
|
||||||
///
|
|
||||||
/// This function will fill in all optional and missing pieces and will make sure that
|
|
||||||
/// values are converted to bridge-specific ones.
|
|
||||||
///
|
|
||||||
/// Most importantly, the method will fill-in [`bridge_instance_index`] parameter for
|
|
||||||
/// target-chain specific calls.
|
|
||||||
pub(crate) fn preprocess_call<Source: CliEncodeCall + CliChain, Target: CliEncodeCall>(
|
|
||||||
call: &mut Call,
|
|
||||||
bridge_instance: u8,
|
|
||||||
) {
|
|
||||||
match *call {
|
|
||||||
Call::Raw { .. } => {},
|
|
||||||
Call::Remark { ref remark_size, ref mut remark_payload } =>
|
|
||||||
if remark_payload.is_none() {
|
|
||||||
*remark_payload = Some(HexBytes(generate_remark_payload(
|
|
||||||
remark_size,
|
|
||||||
compute_maximal_message_arguments_size(
|
|
||||||
Source::max_extrinsic_size(),
|
|
||||||
Target::max_extrinsic_size(),
|
|
||||||
),
|
|
||||||
)));
|
|
||||||
},
|
|
||||||
Call::Transfer { ref mut recipient, .. } => {
|
|
||||||
recipient.enforce_chain::<Source>();
|
|
||||||
},
|
|
||||||
Call::BridgeSendMessage { ref mut bridge_instance_index, .. } => {
|
|
||||||
*bridge_instance_index = bridge_instance;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_remark_payload(
|
|
||||||
remark_size: &Option<ExplicitOrMaximal<usize>>,
|
|
||||||
maximal_allowed_size: u32,
|
|
||||||
) -> Vec<u8> {
|
|
||||||
match remark_size {
|
|
||||||
Some(ExplicitOrMaximal::Explicit(remark_size)) => vec![0; *remark_size],
|
|
||||||
Some(ExplicitOrMaximal::Maximal) => vec![0; maximal_allowed_size as _],
|
|
||||||
None => format!(
|
|
||||||
"Unix time: {}",
|
|
||||||
std::time::SystemTime::now()
|
|
||||||
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.as_secs(),
|
|
||||||
)
|
|
||||||
.as_bytes()
|
|
||||||
.to_vec(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn compute_maximal_message_arguments_size(
|
|
||||||
maximal_source_extrinsic_size: u32,
|
|
||||||
maximal_target_extrinsic_size: u32,
|
|
||||||
) -> u32 {
|
|
||||||
// assume that both signed extensions and other arguments fit 1KB
|
|
||||||
let service_tx_bytes_on_source_chain = 1024;
|
|
||||||
let maximal_source_extrinsic_size =
|
|
||||||
maximal_source_extrinsic_size - service_tx_bytes_on_source_chain;
|
|
||||||
let maximal_call_size = bridge_runtime_common::messages::target::maximal_incoming_message_size(
|
|
||||||
maximal_target_extrinsic_size,
|
|
||||||
);
|
|
||||||
let maximal_call_size = if maximal_call_size > maximal_source_extrinsic_size {
|
|
||||||
maximal_source_extrinsic_size
|
|
||||||
} else {
|
|
||||||
maximal_call_size
|
|
||||||
};
|
|
||||||
|
|
||||||
// bytes in Call encoding that are used to encode everything except arguments
|
|
||||||
let service_bytes = 1 + 1 + 4;
|
|
||||||
maximal_call_size - service_bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::cli::send_message::SendMessage;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_encode_transfer_call() {
|
|
||||||
// given
|
|
||||||
let mut encode_call = EncodeCall::from_iter(vec![
|
|
||||||
"encode-call",
|
|
||||||
"rialto-to-millau",
|
|
||||||
"transfer",
|
|
||||||
"--amount",
|
|
||||||
"12345",
|
|
||||||
"--recipient",
|
|
||||||
"5sauUXUfPjmwxSgmb3tZ5d6yx24eZX4wWJ2JtVUBaQqFbvEU",
|
|
||||||
]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let hex = encode_call.encode().unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", hex),
|
|
||||||
"0x040000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27de5c0"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_encode_remark_with_default_payload() {
|
|
||||||
// given
|
|
||||||
let mut encode_call =
|
|
||||||
EncodeCall::from_iter(vec!["encode-call", "rialto-to-millau", "remark"]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let hex = encode_call.encode().unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert!(format!("{:?}", hex).starts_with("0x000154556e69782074696d653a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_encode_remark_with_explicit_payload() {
|
|
||||||
// given
|
|
||||||
let mut encode_call = EncodeCall::from_iter(vec![
|
|
||||||
"encode-call",
|
|
||||||
"rialto-to-millau",
|
|
||||||
"remark",
|
|
||||||
"--remark-payload",
|
|
||||||
"1234",
|
|
||||||
]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let hex = encode_call.encode().unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(format!("{:?}", hex), "0x0001081234");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_encode_remark_with_size() {
|
|
||||||
// given
|
|
||||||
let mut encode_call = EncodeCall::from_iter(vec![
|
|
||||||
"encode-call",
|
|
||||||
"rialto-to-millau",
|
|
||||||
"remark",
|
|
||||||
"--remark-size",
|
|
||||||
"12",
|
|
||||||
]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let hex = encode_call.encode().unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(format!("{:?}", hex), "0x000130000000000000000000000000");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_disallow_both_payload_and_size() {
|
|
||||||
// when
|
|
||||||
let err = EncodeCall::from_iter_safe(vec![
|
|
||||||
"encode-call",
|
|
||||||
"rialto-to-millau",
|
|
||||||
"remark",
|
|
||||||
"--remark-payload",
|
|
||||||
"1234",
|
|
||||||
"--remark-size",
|
|
||||||
"12",
|
|
||||||
])
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(err.kind, structopt::clap::ErrorKind::ArgumentConflict);
|
|
||||||
|
|
||||||
let info = err.info.unwrap();
|
|
||||||
assert!(
|
|
||||||
info.contains(&"remark-payload".to_string()) |
|
|
||||||
info.contains(&"remark-size".to_string())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_encode_raw_call() {
|
|
||||||
// given
|
|
||||||
let mut encode_call = EncodeCall::from_iter(vec![
|
|
||||||
"encode-call",
|
|
||||||
"rialto-to-millau",
|
|
||||||
"raw",
|
|
||||||
"040000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27de5c0",
|
|
||||||
]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let hex = encode_call.encode().unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", hex),
|
|
||||||
"0x040000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27de5c0"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn should_encode_bridge_send_message_call() {
|
|
||||||
// given
|
|
||||||
let encode_message = SendMessage::from_iter(vec![
|
|
||||||
"send-message",
|
|
||||||
"millau-to-rialto",
|
|
||||||
"--source-port",
|
|
||||||
"10946",
|
|
||||||
"--source-signer",
|
|
||||||
"//Alice",
|
|
||||||
"--target-signer",
|
|
||||||
"//Alice",
|
|
||||||
"--origin",
|
|
||||||
"Target",
|
|
||||||
"remark",
|
|
||||||
])
|
|
||||||
.encode_payload()
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut encode_call = EncodeCall::from_iter(vec![
|
|
||||||
"encode-call",
|
|
||||||
"rialto-to-millau",
|
|
||||||
"bridge-send-message",
|
|
||||||
"--fee",
|
|
||||||
"12345",
|
|
||||||
"--payload",
|
|
||||||
format!("{:}", &HexBytes::encode(&encode_message)).as_str(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let call_hex = encode_call.encode().unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert!(format!("{:?}", call_hex).starts_with(
|
|
||||||
"0x0f030000000001000000000000000000000001d43593c715fdd31c61141abd04a99fd6822c8558854cc\
|
|
||||||
de39a5684e7a56da27d01d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,107 +14,80 @@
|
|||||||
// 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 crate::{
|
use crate::cli::{ExplicitOrMaximal, HexBytes};
|
||||||
cli::{bridge::FullBridge, AccountId, CliChain, HexBytes},
|
use bp_messages::LaneId;
|
||||||
select_full_bridge,
|
use bp_runtime::EncodedOrDecodedCall;
|
||||||
};
|
use relay_substrate_client::Chain;
|
||||||
use frame_support::weights::Weight;
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use strum::VariantNames;
|
|
||||||
|
|
||||||
/// Generic message payload.
|
/// All possible messages that may be delivered to generic Substrate chain.
|
||||||
|
///
|
||||||
|
/// Note this enum may be used in the context of both Source (as part of `encode-call`)
|
||||||
|
/// and Target chain (as part of `encode-message/send-message`).
|
||||||
#[derive(StructOpt, Debug, PartialEq, Eq)]
|
#[derive(StructOpt, Debug, PartialEq, Eq)]
|
||||||
pub enum MessagePayload {
|
pub enum Message {
|
||||||
/// Raw, SCALE-encoded `MessagePayload`.
|
/// Raw bytes for the message.
|
||||||
Raw {
|
Raw {
|
||||||
/// Hex-encoded SCALE data.
|
/// Raw message bytes.
|
||||||
data: HexBytes,
|
data: HexBytes,
|
||||||
},
|
},
|
||||||
/// Construct message to send over the bridge.
|
/// Message with given size.
|
||||||
Call {
|
Sized {
|
||||||
/// Message details.
|
/// Sized of the message.
|
||||||
#[structopt(flatten)]
|
size: ExplicitOrMaximal<u32>,
|
||||||
call: crate::cli::encode_call::Call,
|
|
||||||
/// SS58 encoded Source account that will send the payload.
|
|
||||||
#[structopt(long)]
|
|
||||||
sender: AccountId,
|
|
||||||
/// Weight of the call.
|
|
||||||
///
|
|
||||||
/// It must be specified if the chain runtime is not bundled with the relay, or if
|
|
||||||
/// you want to override bundled weight.
|
|
||||||
#[structopt(long)]
|
|
||||||
dispatch_weight: Option<Weight>,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `MessagePayload` to encode.
|
/// Raw, SCALE-encoded message payload used in expected deployment.
|
||||||
#[derive(StructOpt)]
|
pub type RawMessage = Vec<u8>;
|
||||||
pub struct EncodeMessage {
|
|
||||||
/// A bridge instance to initialize.
|
pub trait CliEncodeMessage: Chain {
|
||||||
#[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)]
|
/// Encode a send message call.
|
||||||
bridge: FullBridge,
|
fn encode_send_message_call(
|
||||||
#[structopt(flatten)]
|
lane: LaneId,
|
||||||
payload: MessagePayload,
|
message: RawMessage,
|
||||||
|
fee: Self::Balance,
|
||||||
|
bridge_instance_index: u8,
|
||||||
|
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EncodeMessage {
|
/// Encode message payload passed through CLI flags.
|
||||||
/// Run the command.
|
pub(crate) fn encode_message<Source: Chain, Target: Chain>(
|
||||||
pub fn encode(self) -> anyhow::Result<HexBytes> {
|
message: &Message,
|
||||||
select_full_bridge!(self.bridge, {
|
) -> anyhow::Result<RawMessage> {
|
||||||
let payload =
|
Ok(match message {
|
||||||
Source::encode_message(self.payload).map_err(|e| anyhow::format_err!("{}", e))?;
|
Message::Raw { ref data } => data.0.clone(),
|
||||||
Ok(HexBytes::encode(&payload))
|
Message::Sized { ref size } => match *size {
|
||||||
})
|
ExplicitOrMaximal::Explicit(size) => vec![42; size as usize],
|
||||||
}
|
ExplicitOrMaximal::Maximal => {
|
||||||
|
let maximal_size = compute_maximal_message_size(
|
||||||
/// Run the command.
|
Source::max_extrinsic_size(),
|
||||||
pub async fn run(self) -> anyhow::Result<()> {
|
Target::max_extrinsic_size(),
|
||||||
let payload = self.encode()?;
|
);
|
||||||
println!("{:?}", payload);
|
vec![42; maximal_size as usize]
|
||||||
Ok(())
|
},
|
||||||
}
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
/// Compute maximal message size, given max extrinsic size at source and target chains.
|
||||||
mod tests {
|
pub(crate) fn compute_maximal_message_size(
|
||||||
use super::*;
|
maximal_source_extrinsic_size: u32,
|
||||||
use sp_core::crypto::Ss58Codec;
|
maximal_target_extrinsic_size: u32,
|
||||||
|
) -> u32 {
|
||||||
|
// assume that both signed extensions and other arguments fit 1KB
|
||||||
|
let service_tx_bytes_on_source_chain = 1024;
|
||||||
|
let maximal_source_extrinsic_size =
|
||||||
|
maximal_source_extrinsic_size - service_tx_bytes_on_source_chain;
|
||||||
|
let maximal_message_size =
|
||||||
|
bridge_runtime_common::messages::target::maximal_incoming_message_size(
|
||||||
|
maximal_target_extrinsic_size,
|
||||||
|
);
|
||||||
|
let maximal_message_size = if maximal_message_size > maximal_source_extrinsic_size {
|
||||||
|
maximal_source_extrinsic_size
|
||||||
|
} else {
|
||||||
|
maximal_message_size
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
maximal_message_size
|
||||||
fn should_encode_raw_message() {
|
|
||||||
// given
|
|
||||||
let msg = "01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c040130000000000000000000000000";
|
|
||||||
let encode_message =
|
|
||||||
EncodeMessage::from_iter(vec!["encode-message", "rialto-to-millau", "raw", msg]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let hex = encode_message.encode().unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(format!("{:?}", hex), format!("0x{}", msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_encode_remark_with_size() {
|
|
||||||
// given
|
|
||||||
let sender = sp_keyring::AccountKeyring::Alice.to_account_id().to_ss58check();
|
|
||||||
let encode_message = EncodeMessage::from_iter(vec![
|
|
||||||
"encode-message",
|
|
||||||
"rialto-to-millau",
|
|
||||||
"call",
|
|
||||||
"--sender",
|
|
||||||
&sender,
|
|
||||||
"--dispatch-weight",
|
|
||||||
"42",
|
|
||||||
"remark",
|
|
||||||
"--remark-size",
|
|
||||||
"12",
|
|
||||||
]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let hex = encode_message.encode().unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(format!("{:?}", hex), "0x010000002a0000000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c000130000000000000000000000000");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
cli::{
|
cli::{
|
||||||
bridge::FullBridge, relay_headers_and_messages::CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO,
|
bridge::FullBridge, relay_headers_and_messages::CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO,
|
||||||
Balance, CliChain, HexBytes, HexLaneId, SourceConnectionParams,
|
Balance, HexBytes, HexLaneId, SourceConnectionParams,
|
||||||
},
|
},
|
||||||
select_full_bridge,
|
select_full_bridge,
|
||||||
};
|
};
|
||||||
@@ -48,7 +48,7 @@ pub struct EstimateFee {
|
|||||||
conversion_rate_override: Option<ConversionRateOverride>,
|
conversion_rate_override: Option<ConversionRateOverride>,
|
||||||
/// Payload to send over the bridge.
|
/// Payload to send over the bridge.
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
payload: crate::cli::encode_message::MessagePayload,
|
payload: crate::cli::encode_message::Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A way to override conversion rate between bridge tokens.
|
/// A way to override conversion rate between bridge tokens.
|
||||||
@@ -82,8 +82,8 @@ impl EstimateFee {
|
|||||||
select_full_bridge!(bridge, {
|
select_full_bridge!(bridge, {
|
||||||
let source_client = source.to_client::<Source>().await?;
|
let source_client = source.to_client::<Source>().await?;
|
||||||
let lane = lane.into();
|
let lane = lane.into();
|
||||||
let payload =
|
let payload = crate::cli::encode_message::encode_message::<Source, Target>(&payload)
|
||||||
Source::encode_message(payload).map_err(|e| anyhow::format_err!("{:?}", e))?;
|
.map_err(|e| anyhow::format_err!("{:?}", e))?;
|
||||||
|
|
||||||
let fee = estimate_message_delivery_and_dispatch_fee::<Source, Target, _>(
|
let fee = estimate_message_delivery_and_dispatch_fee::<Source, Target, _>(
|
||||||
&source_client,
|
&source_client,
|
||||||
@@ -219,14 +219,10 @@ async fn do_estimate_message_delivery_and_dispatch_fee<Source: Chain, P: Encode>
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::cli::{encode_call, RuntimeVersionType, SourceRuntimeVersionParams};
|
use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams};
|
||||||
use sp_core::crypto::Ss58Codec;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_parse_cli_options() {
|
fn should_parse_cli_options() {
|
||||||
// given
|
|
||||||
let alice = sp_keyring::AccountKeyring::Alice.to_account_id().to_ss58check();
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = EstimateFee::from_iter(vec![
|
let res = EstimateFee::from_iter(vec![
|
||||||
"estimate_fee",
|
"estimate_fee",
|
||||||
@@ -235,13 +231,7 @@ mod tests {
|
|||||||
"1234",
|
"1234",
|
||||||
"--conversion-rate-override",
|
"--conversion-rate-override",
|
||||||
"42.5",
|
"42.5",
|
||||||
"call",
|
"raw",
|
||||||
"--sender",
|
|
||||||
&alice,
|
|
||||||
"--dispatch-weight",
|
|
||||||
"42",
|
|
||||||
"remark",
|
|
||||||
"--remark-payload",
|
|
||||||
"1234",
|
"1234",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -262,13 +252,8 @@ mod tests {
|
|||||||
source_transaction_version: None,
|
source_transaction_version: None,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
payload: crate::cli::encode_message::MessagePayload::Call {
|
payload: crate::cli::encode_message::Message::Raw {
|
||||||
sender: alice.parse().unwrap(),
|
data: HexBytes(vec![0x12, 0x34])
|
||||||
call: encode_call::Call::Remark {
|
|
||||||
remark_payload: Some(HexBytes(vec![0x12, 0x34])),
|
|
||||||
remark_size: None,
|
|
||||||
},
|
|
||||||
dispatch_weight: Some(42),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,19 +20,16 @@ use std::convert::TryInto;
|
|||||||
|
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use relay_substrate_client::ChainRuntimeVersion;
|
use relay_substrate_client::ChainRuntimeVersion;
|
||||||
use sp_runtime::app_crypto::Ss58Codec;
|
|
||||||
use structopt::{clap::arg_enum, StructOpt};
|
use structopt::{clap::arg_enum, StructOpt};
|
||||||
use strum::{EnumString, EnumVariantNames};
|
use strum::{EnumString, EnumVariantNames};
|
||||||
|
|
||||||
use bp_messages::LaneId;
|
use bp_messages::LaneId;
|
||||||
|
|
||||||
pub(crate) mod bridge;
|
pub(crate) mod bridge;
|
||||||
pub(crate) mod encode_call;
|
|
||||||
pub(crate) mod encode_message;
|
pub(crate) mod encode_message;
|
||||||
pub(crate) mod estimate_fee;
|
pub(crate) mod estimate_fee;
|
||||||
pub(crate) mod send_message;
|
pub(crate) mod send_message;
|
||||||
|
|
||||||
mod derive_account;
|
|
||||||
mod init_bridge;
|
mod init_bridge;
|
||||||
mod register_parachain;
|
mod register_parachain;
|
||||||
mod reinit_bridge;
|
mod reinit_bridge;
|
||||||
@@ -40,7 +37,6 @@ mod relay_headers;
|
|||||||
mod relay_headers_and_messages;
|
mod relay_headers_and_messages;
|
||||||
mod relay_messages;
|
mod relay_messages;
|
||||||
mod resubmit_transactions;
|
mod resubmit_transactions;
|
||||||
mod swap_tokens;
|
|
||||||
|
|
||||||
/// Parse relay CLI args.
|
/// Parse relay CLI args.
|
||||||
pub fn parse_args() -> Command {
|
pub fn parse_args() -> Command {
|
||||||
@@ -83,25 +79,10 @@ pub enum Command {
|
|||||||
/// The message is being sent to the source chain, delivered to the target chain and dispatched
|
/// The message is being sent to the source chain, delivered to the target chain and dispatched
|
||||||
/// there.
|
/// there.
|
||||||
SendMessage(send_message::SendMessage),
|
SendMessage(send_message::SendMessage),
|
||||||
/// Generate SCALE-encoded `Call` for choosen network.
|
|
||||||
///
|
|
||||||
/// The call can be used either as message payload or can be wrapped into a transaction
|
|
||||||
/// and executed on the chain directly.
|
|
||||||
EncodeCall(encode_call::EncodeCall),
|
|
||||||
/// Generate SCALE-encoded `MessagePayload` object that can be sent over selected bridge.
|
|
||||||
///
|
|
||||||
/// The `MessagePayload` can be then fed to `Messages::send_message` function and sent over
|
|
||||||
/// the bridge.
|
|
||||||
EncodeMessage(encode_message::EncodeMessage),
|
|
||||||
/// Estimate Delivery and Dispatch Fee required for message submission to messages pallet.
|
/// Estimate Delivery and Dispatch Fee required for message submission to messages pallet.
|
||||||
EstimateFee(estimate_fee::EstimateFee),
|
EstimateFee(estimate_fee::EstimateFee),
|
||||||
/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target
|
|
||||||
/// chain.
|
|
||||||
DeriveAccount(derive_account::DeriveAccount),
|
|
||||||
/// Resubmit transactions with increased tip if they are stalled.
|
/// Resubmit transactions with increased tip if they are stalled.
|
||||||
ResubmitTransactions(resubmit_transactions::ResubmitTransactions),
|
ResubmitTransactions(resubmit_transactions::ResubmitTransactions),
|
||||||
/// Swap tokens using token-swap bridge.
|
|
||||||
SwapTokens(swap_tokens::SwapTokens),
|
|
||||||
/// Register parachain.
|
/// Register parachain.
|
||||||
RegisterParachain(register_parachain::RegisterParachain),
|
RegisterParachain(register_parachain::RegisterParachain),
|
||||||
}
|
}
|
||||||
@@ -134,12 +115,8 @@ impl Command {
|
|||||||
Self::InitBridge(arg) => arg.run().await?,
|
Self::InitBridge(arg) => arg.run().await?,
|
||||||
Self::ReinitBridge(arg) => arg.run().await?,
|
Self::ReinitBridge(arg) => arg.run().await?,
|
||||||
Self::SendMessage(arg) => arg.run().await?,
|
Self::SendMessage(arg) => arg.run().await?,
|
||||||
Self::EncodeCall(arg) => arg.run().await?,
|
|
||||||
Self::EncodeMessage(arg) => arg.run().await?,
|
|
||||||
Self::EstimateFee(arg) => arg.run().await?,
|
Self::EstimateFee(arg) => arg.run().await?,
|
||||||
Self::DeriveAccount(arg) => arg.run().await?,
|
|
||||||
Self::ResubmitTransactions(arg) => arg.run().await?,
|
Self::ResubmitTransactions(arg) => arg.run().await?,
|
||||||
Self::SwapTokens(arg) => arg.run().await?,
|
|
||||||
Self::RegisterParachain(arg) => arg.run().await?,
|
Self::RegisterParachain(arg) => arg.run().await?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -184,66 +161,7 @@ impl Balance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic account id with custom parser.
|
// Bridge-supported network definition.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct AccountId {
|
|
||||||
account: sp_runtime::AccountId32,
|
|
||||||
ss58_format: sp_core::crypto::Ss58AddressFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for AccountId {
|
|
||||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
write!(fmt, "{}", self.account.to_ss58check_with_version(self.ss58_format))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::str::FromStr for AccountId {
|
|
||||||
type Err = String;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let (account, ss58_format) = sp_runtime::AccountId32::from_ss58check_with_version(s)
|
|
||||||
.map_err(|err| format!("Unable to decode SS58 address: {:?}", err))?;
|
|
||||||
Ok(Self { account, ss58_format })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SS58_FORMAT_PROOF: &str = "u16 -> Ss58Format is infallible; qed";
|
|
||||||
|
|
||||||
impl AccountId {
|
|
||||||
/// Create new SS58-formatted address from raw account id.
|
|
||||||
pub fn from_raw<T: CliChain>(account: sp_runtime::AccountId32) -> Self {
|
|
||||||
Self { account, ss58_format: T::ss58_format().try_into().expect(SS58_FORMAT_PROOF) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enforces formatting account to be for given [`CliChain`] type.
|
|
||||||
///
|
|
||||||
/// This will change the `ss58format` of the account to match the requested one.
|
|
||||||
/// Note that a warning will be produced in case the current format does not match
|
|
||||||
/// the requested one, but the conversion always succeeds.
|
|
||||||
pub fn enforce_chain<T: CliChain>(&mut self) {
|
|
||||||
let original = self.clone();
|
|
||||||
self.ss58_format = T::ss58_format().try_into().expect(SS58_FORMAT_PROOF);
|
|
||||||
log::debug!("{} SS58 format: {} (RAW: {})", self, self.ss58_format, self.account);
|
|
||||||
if original.ss58_format != self.ss58_format {
|
|
||||||
log::warn!(
|
|
||||||
target: "bridge",
|
|
||||||
"Address {} does not seem to match {}'s SS58 format (got: {}, expected: {}).\nConverted to: {}",
|
|
||||||
original,
|
|
||||||
T::NAME,
|
|
||||||
original.ss58_format,
|
|
||||||
self.ss58_format,
|
|
||||||
self,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the raw (no SS58-prefixed) account id.
|
|
||||||
pub fn raw_id(&self) -> sp_runtime::AccountId32 {
|
|
||||||
self.account.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bridge-supported network definition.
|
|
||||||
///
|
///
|
||||||
/// Used to abstract away CLI commands.
|
/// Used to abstract away CLI commands.
|
||||||
pub trait CliChain: relay_substrate_client::Chain {
|
pub trait CliChain: relay_substrate_client::Chain {
|
||||||
@@ -262,11 +180,6 @@ pub trait CliChain: relay_substrate_client::Chain {
|
|||||||
|
|
||||||
/// Numeric value of SS58 format.
|
/// Numeric value of SS58 format.
|
||||||
fn ss58_format() -> u16;
|
fn ss58_format() -> u16;
|
||||||
|
|
||||||
/// Construct message payload to be sent over the bridge.
|
|
||||||
fn encode_message(
|
|
||||||
message: crate::cli::encode_message::MessagePayload,
|
|
||||||
) -> anyhow::Result<Self::MessagePayload>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lane id.
|
/// Lane id.
|
||||||
@@ -623,29 +536,8 @@ declare_chain_options!(Parachain, parachain);
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use sp_core::Pair;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use sp_core::Pair;
|
||||||
#[test]
|
|
||||||
fn should_format_addresses_with_ss58_format() {
|
|
||||||
// given
|
|
||||||
let rialto1 = "5sauUXUfPjmwxSgmb3tZ5d6yx24eZX4wWJ2JtVUBaQqFbvEU";
|
|
||||||
let rialto2 = "5rERgaT1Z8nM3et2epA5i1VtEBfp5wkhwHtVE8HK7BRbjAH2";
|
|
||||||
let millau1 = "752paRyW1EGfq9YLTSSqcSJ5hqnBDidBmaftGhBo8fy6ypW9";
|
|
||||||
let millau2 = "74GNQjmkcfstRftSQPJgMREchqHM56EvAUXRc266cZ1NYVW5";
|
|
||||||
|
|
||||||
let expected = vec![rialto1, rialto2, millau1, millau2];
|
|
||||||
|
|
||||||
// when
|
|
||||||
let parsed = expected.iter().map(|s| AccountId::from_str(s).unwrap()).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let actual = parsed.iter().map(|a| format!("{}", a)).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
assert_eq!(actual, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hex_bytes_display_matches_from_str_for_clap() {
|
fn hex_bytes_display_matches_from_str_for_clap() {
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
// 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 crate::cli::{
|
use crate::cli::{
|
||||||
swap_tokens::wait_until_transaction_is_finalized, Balance, ParachainConnectionParams,
|
Balance, ParachainConnectionParams, RelaychainConnectionParams, RelaychainSigningParams,
|
||||||
RelaychainConnectionParams, RelaychainSigningParams,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
@@ -30,7 +29,8 @@ use polkadot_runtime_common::{
|
|||||||
};
|
};
|
||||||
use polkadot_runtime_parachains::paras::ParaLifecycle;
|
use polkadot_runtime_parachains::paras::ParaLifecycle;
|
||||||
use relay_substrate_client::{
|
use relay_substrate_client::{
|
||||||
AccountIdOf, CallOf, Chain, Client, SignParam, TransactionSignScheme, UnsignedTransaction,
|
AccountIdOf, CallOf, Chain, Client, HashOf, SignParam, Subscription, TransactionSignScheme,
|
||||||
|
TransactionStatusOf, UnsignedTransaction,
|
||||||
};
|
};
|
||||||
use rialto_runtime::SudoCall;
|
use rialto_runtime::SudoCall;
|
||||||
use sp_core::{
|
use sp_core::{
|
||||||
@@ -270,6 +270,46 @@ impl RegisterParachain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wait until transaction is included into finalized block.
|
||||||
|
///
|
||||||
|
/// Returns the hash of the finalized block with transaction.
|
||||||
|
pub(crate) async fn wait_until_transaction_is_finalized<C: Chain>(
|
||||||
|
subscription: Subscription<TransactionStatusOf<C>>,
|
||||||
|
) -> anyhow::Result<HashOf<C>> {
|
||||||
|
loop {
|
||||||
|
let transaction_status = subscription.next().await?;
|
||||||
|
match transaction_status {
|
||||||
|
Some(TransactionStatusOf::<C>::FinalityTimeout(_)) |
|
||||||
|
Some(TransactionStatusOf::<C>::Usurped(_)) |
|
||||||
|
Some(TransactionStatusOf::<C>::Dropped) |
|
||||||
|
Some(TransactionStatusOf::<C>::Invalid) |
|
||||||
|
None =>
|
||||||
|
return Err(anyhow::format_err!(
|
||||||
|
"We've been waiting for finalization of {} transaction, but it now has the {:?} status",
|
||||||
|
C::NAME,
|
||||||
|
transaction_status,
|
||||||
|
)),
|
||||||
|
Some(TransactionStatusOf::<C>::Finalized(block_hash)) => {
|
||||||
|
log::trace!(
|
||||||
|
target: "bridge",
|
||||||
|
"{} transaction has been finalized at block {}",
|
||||||
|
C::NAME,
|
||||||
|
block_hash,
|
||||||
|
);
|
||||||
|
return Ok(block_hash)
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
log::trace!(
|
||||||
|
target: "bridge",
|
||||||
|
"Received intermediate status of {} transaction: {:?}",
|
||||||
|
C::NAME,
|
||||||
|
transaction_status,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wait until parachain state is changed.
|
/// Wait until parachain state is changed.
|
||||||
async fn wait_para_state<Relaychain: Chain>(
|
async fn wait_para_state<Relaychain: Chain>(
|
||||||
relay_client: &Client<Relaychain>,
|
relay_client: &Client<Relaychain>,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use crate::{
|
|||||||
polkadot_headers_to_kusama::PolkadotFinalityToKusama,
|
polkadot_headers_to_kusama::PolkadotFinalityToKusama,
|
||||||
},
|
},
|
||||||
cli::{
|
cli::{
|
||||||
swap_tokens::wait_until_transaction_is_finalized, SourceConnectionParams,
|
register_parachain::wait_until_transaction_is_finalized, SourceConnectionParams,
|
||||||
TargetConnectionParams, TargetSigningParams,
|
TargetConnectionParams, TargetSigningParams,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,18 +16,14 @@
|
|||||||
|
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
bridge::FullBridge,
|
bridge::FullBridge,
|
||||||
encode_call::{self, CliEncodeCall},
|
encode_message::{self, CliEncodeMessage},
|
||||||
estimate_fee::{estimate_message_delivery_and_dispatch_fee, ConversionRateOverride},
|
estimate_fee::{estimate_message_delivery_and_dispatch_fee, ConversionRateOverride},
|
||||||
Balance, ExplicitOrMaximal, HexBytes, HexLaneId, Origins, SourceConnectionParams,
|
Balance, HexBytes, HexLaneId, SourceConnectionParams, SourceSigningParams,
|
||||||
SourceSigningParams, TargetConnectionParams, TargetSigningParams,
|
|
||||||
};
|
};
|
||||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
|
||||||
use bp_runtime::Chain as _;
|
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use frame_support::weights::Weight;
|
|
||||||
use relay_substrate_client::{Chain, SignParam, TransactionSignScheme, UnsignedTransaction};
|
use relay_substrate_client::{Chain, SignParam, TransactionSignScheme, UnsignedTransaction};
|
||||||
use sp_core::{Bytes, Pair};
|
use sp_core::{Bytes, Pair};
|
||||||
use sp_runtime::{traits::IdentifyAccount, AccountId32, MultiSignature, MultiSigner};
|
use sp_runtime::AccountId32;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use strum::{EnumString, EnumVariantNames, VariantNames};
|
use strum::{EnumString, EnumVariantNames, VariantNames};
|
||||||
@@ -61,8 +57,6 @@ pub struct SendMessage {
|
|||||||
source: SourceConnectionParams,
|
source: SourceConnectionParams,
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
source_sign: SourceSigningParams,
|
source_sign: SourceSigningParams,
|
||||||
#[structopt(flatten)]
|
|
||||||
target_sign: TargetSigningParams,
|
|
||||||
/// Hex-encoded lane id. Defaults to `00000000`.
|
/// Hex-encoded lane id. Defaults to `00000000`.
|
||||||
#[structopt(long, default_value = "00000000")]
|
#[structopt(long, default_value = "00000000")]
|
||||||
lane: HexLaneId,
|
lane: HexLaneId,
|
||||||
@@ -72,104 +66,20 @@ pub struct SendMessage {
|
|||||||
/// your message won't be relayed.
|
/// your message won't be relayed.
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
conversion_rate_override: Option<ConversionRateOverride>,
|
conversion_rate_override: Option<ConversionRateOverride>,
|
||||||
/// Where dispatch fee is paid?
|
|
||||||
#[structopt(
|
|
||||||
long,
|
|
||||||
possible_values = DispatchFeePayment::VARIANTS,
|
|
||||||
case_insensitive = true,
|
|
||||||
default_value = "at-source-chain",
|
|
||||||
)]
|
|
||||||
dispatch_fee_payment: DispatchFeePayment,
|
|
||||||
/// Dispatch weight of the message. If not passed, determined automatically.
|
|
||||||
#[structopt(long)]
|
|
||||||
dispatch_weight: Option<ExplicitOrMaximal<Weight>>,
|
|
||||||
/// Delivery and dispatch fee in source chain base currency units. If not passed, determined
|
/// Delivery and dispatch fee in source chain base currency units. If not passed, determined
|
||||||
/// automatically.
|
/// automatically.
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
fee: Option<Balance>,
|
fee: Option<Balance>,
|
||||||
/// Message type.
|
/// Message type.
|
||||||
#[structopt(subcommand)]
|
#[structopt(subcommand)]
|
||||||
message: crate::cli::encode_call::Call,
|
message: crate::cli::encode_message::Message,
|
||||||
/// The origin to use when dispatching the message on the target chain. Defaults to
|
|
||||||
/// `SourceAccount`.
|
|
||||||
#[structopt(long, possible_values = &Origins::variants(), default_value = "Source")]
|
|
||||||
origin: Origins,
|
|
||||||
|
|
||||||
// Normally we don't need to connect to the target chain to send message. But for testing
|
|
||||||
// we may want to use **actual** `spec_version` of the target chain when composing a message.
|
|
||||||
// Then we'll need to read version from the target chain node.
|
|
||||||
#[structopt(flatten)]
|
|
||||||
target: TargetConnectionParams,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SendMessage {
|
impl SendMessage {
|
||||||
pub async fn encode_payload(
|
|
||||||
&mut self,
|
|
||||||
) -> anyhow::Result<MessagePayload<AccountId32, MultiSigner, MultiSignature, Vec<u8>>> {
|
|
||||||
crate::select_full_bridge!(self.bridge, {
|
|
||||||
let SendMessage {
|
|
||||||
source_sign,
|
|
||||||
target_sign,
|
|
||||||
ref mut message,
|
|
||||||
dispatch_fee_payment,
|
|
||||||
dispatch_weight,
|
|
||||||
origin,
|
|
||||||
bridge,
|
|
||||||
..
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
let source_sign = source_sign.to_keypair::<Source>()?;
|
|
||||||
|
|
||||||
encode_call::preprocess_call::<Source, Target>(message, bridge.bridge_instance_index());
|
|
||||||
let target_call = Target::encode_call(message)?;
|
|
||||||
let target_spec_version = self.target.selected_chain_spec_version::<Target>().await?;
|
|
||||||
|
|
||||||
let payload = {
|
|
||||||
let target_call_weight = prepare_call_dispatch_weight(
|
|
||||||
dispatch_weight,
|
|
||||||
|| {
|
|
||||||
Ok(ExplicitOrMaximal::Explicit(
|
|
||||||
Target::get_dispatch_info(&target_call)?.weight,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
compute_maximal_message_dispatch_weight(Target::max_extrinsic_weight()),
|
|
||||||
)?;
|
|
||||||
let source_sender_public: MultiSigner = source_sign.public().into();
|
|
||||||
let source_account_id = source_sender_public.into_account();
|
|
||||||
|
|
||||||
message_payload(
|
|
||||||
target_spec_version,
|
|
||||||
target_call_weight,
|
|
||||||
match origin {
|
|
||||||
Origins::Source => CallOrigin::SourceAccount(source_account_id),
|
|
||||||
Origins::Target => {
|
|
||||||
let target_sign = target_sign.to_keypair::<Target>()?;
|
|
||||||
let digest = account_ownership_digest(
|
|
||||||
&target_call,
|
|
||||||
source_account_id.clone(),
|
|
||||||
target_spec_version,
|
|
||||||
);
|
|
||||||
let target_origin_public = target_sign.public();
|
|
||||||
let digest_signature = target_sign.sign(&digest);
|
|
||||||
CallOrigin::TargetAccount(
|
|
||||||
source_account_id,
|
|
||||||
target_origin_public.into(),
|
|
||||||
digest_signature.into(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&target_call,
|
|
||||||
*dispatch_fee_payment,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
Ok(payload)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the command.
|
/// Run the command.
|
||||||
pub async fn run(mut self) -> anyhow::Result<()> {
|
pub async fn run(self) -> anyhow::Result<()> {
|
||||||
crate::select_full_bridge!(self.bridge, {
|
crate::select_full_bridge!(self.bridge, {
|
||||||
let payload = self.encode_payload().await?;
|
let payload = encode_message::encode_message::<Source, Target>(&self.message)?;
|
||||||
|
|
||||||
let source_client = self.source.to_client::<Source>().await?;
|
let source_client = self.source.to_client::<Source>().await?;
|
||||||
let source_sign = self.source_sign.to_keypair::<Source>()?;
|
let source_sign = self.source_sign.to_keypair::<Source>()?;
|
||||||
@@ -189,14 +99,13 @@ impl SendMessage {
|
|||||||
.await? as _,
|
.await? as _,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let dispatch_weight = payload.weight;
|
|
||||||
let payload_len = payload.encode().len();
|
let payload_len = payload.encode().len();
|
||||||
let send_message_call = Source::encode_call(&encode_call::Call::BridgeSendMessage {
|
let send_message_call = Source::encode_send_message_call(
|
||||||
bridge_instance_index: self.bridge.bridge_instance_index(),
|
self.lane.0,
|
||||||
lane: self.lane,
|
payload,
|
||||||
payload: HexBytes::encode(&payload),
|
fee.cast().into(),
|
||||||
fee,
|
self.bridge.bridge_instance_index(),
|
||||||
})?;
|
)?;
|
||||||
|
|
||||||
let source_genesis_hash = *source_client.genesis_hash();
|
let source_genesis_hash = *source_client.genesis_hash();
|
||||||
let (spec_version, transaction_version) =
|
let (spec_version, transaction_version) =
|
||||||
@@ -228,11 +137,10 @@ impl SendMessage {
|
|||||||
|
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
"Sending message to {}. Lane: {:?}. Size: {}. Dispatch weight: {}. Fee: {}",
|
"Sending message to {}. Lane: {:?}. Size: {}. Fee: {}",
|
||||||
Target::NAME,
|
Target::NAME,
|
||||||
lane,
|
lane,
|
||||||
payload_len,
|
payload_len,
|
||||||
dispatch_weight,
|
|
||||||
fee,
|
fee,
|
||||||
);
|
);
|
||||||
log::info!(
|
log::info!(
|
||||||
@@ -260,66 +168,15 @@ impl SendMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_call_dispatch_weight(
|
|
||||||
user_specified_dispatch_weight: &Option<ExplicitOrMaximal<Weight>>,
|
|
||||||
weight_from_pre_dispatch_call: impl Fn() -> anyhow::Result<ExplicitOrMaximal<Weight>>,
|
|
||||||
maximal_allowed_weight: Weight,
|
|
||||||
) -> anyhow::Result<Weight> {
|
|
||||||
match user_specified_dispatch_weight
|
|
||||||
.clone()
|
|
||||||
.map(Ok)
|
|
||||||
.unwrap_or_else(weight_from_pre_dispatch_call)?
|
|
||||||
{
|
|
||||||
ExplicitOrMaximal::Explicit(weight) => Ok(weight),
|
|
||||||
ExplicitOrMaximal::Maximal => Ok(maximal_allowed_weight),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn message_payload<SAccountId, TPublic, TSignature>(
|
|
||||||
spec_version: u32,
|
|
||||||
weight: Weight,
|
|
||||||
origin: CallOrigin<SAccountId, TPublic, TSignature>,
|
|
||||||
call: &impl Encode,
|
|
||||||
dispatch_fee_payment: DispatchFeePayment,
|
|
||||||
) -> MessagePayload<SAccountId, TPublic, TSignature, Vec<u8>>
|
|
||||||
where
|
|
||||||
SAccountId: Encode + Debug,
|
|
||||||
TPublic: Encode + Debug,
|
|
||||||
TSignature: Encode + Debug,
|
|
||||||
{
|
|
||||||
// Display nicely formatted call.
|
|
||||||
let payload = MessagePayload {
|
|
||||||
spec_version,
|
|
||||||
weight,
|
|
||||||
origin,
|
|
||||||
dispatch_fee_payment: dispatch_fee_payment.into(),
|
|
||||||
call: HexBytes::encode(call),
|
|
||||||
};
|
|
||||||
|
|
||||||
log::info!(target: "bridge", "Created Message Payload: {:#?}", payload);
|
|
||||||
log::info!(target: "bridge", "Encoded Message Payload: {:?}", HexBytes::encode(&payload));
|
|
||||||
|
|
||||||
// re-pack to return `Vec<u8>`
|
|
||||||
let MessagePayload { spec_version, weight, origin, dispatch_fee_payment, call } = payload;
|
|
||||||
MessagePayload { spec_version, weight, origin, dispatch_fee_payment, call: call.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn compute_maximal_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight {
|
|
||||||
bridge_runtime_common::messages::target::maximal_incoming_message_dispatch_weight(
|
|
||||||
maximal_extrinsic_weight,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::cli::CliChain;
|
use crate::cli::ExplicitOrMaximal;
|
||||||
use hex_literal::hex;
|
|
||||||
|
|
||||||
#[async_std::test]
|
#[test]
|
||||||
async fn send_remark_rialto_to_millau() {
|
fn send_raw_rialto_to_millau() {
|
||||||
// given
|
// given
|
||||||
let mut send_message = SendMessage::from_iter(vec![
|
let send_message = SendMessage::from_iter(vec![
|
||||||
"send-message",
|
"send-message",
|
||||||
"rialto-to-millau",
|
"rialto-to-millau",
|
||||||
"--source-port",
|
"--source-port",
|
||||||
@@ -328,117 +185,48 @@ mod tests {
|
|||||||
"//Alice",
|
"//Alice",
|
||||||
"--conversion-rate-override",
|
"--conversion-rate-override",
|
||||||
"0.75",
|
"0.75",
|
||||||
"remark",
|
"raw",
|
||||||
"--remark-payload",
|
"dead",
|
||||||
"1234",
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// when
|
|
||||||
let payload = send_message.encode_payload().await.unwrap();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assert_eq!(send_message.bridge, FullBridge::RialtoToMillau);
|
||||||
|
assert_eq!(send_message.source.source_port, 1234);
|
||||||
|
assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
payload,
|
send_message.conversion_rate_override,
|
||||||
MessagePayload {
|
Some(ConversionRateOverride::Explicit(0.75))
|
||||||
spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version,
|
|
||||||
weight: 0,
|
|
||||||
origin: CallOrigin::SourceAccount(
|
|
||||||
sp_keyring::AccountKeyring::Alice.to_account_id()
|
|
||||||
),
|
|
||||||
dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtSourceChain,
|
|
||||||
call: hex!("0001081234").to_vec(),
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn send_remark_millau_to_rialto() {
|
|
||||||
// given
|
|
||||||
let mut send_message = SendMessage::from_iter(vec![
|
|
||||||
"send-message",
|
|
||||||
"millau-to-rialto",
|
|
||||||
"--source-port",
|
|
||||||
"1234",
|
|
||||||
"--source-signer",
|
|
||||||
"//Alice",
|
|
||||||
"--origin",
|
|
||||||
"Target",
|
|
||||||
"--target-signer",
|
|
||||||
"//Bob",
|
|
||||||
"--conversion-rate-override",
|
|
||||||
"metric",
|
|
||||||
"remark",
|
|
||||||
"--remark-payload",
|
|
||||||
"1234",
|
|
||||||
]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let payload = send_message.encode_payload().await.unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
// Since signatures are randomized we extract it from here and only check the rest.
|
|
||||||
let signature = match payload.origin {
|
|
||||||
CallOrigin::TargetAccount(_, _, ref sig) => sig.clone(),
|
|
||||||
_ => panic!("Unexpected `CallOrigin`: {:?}", payload),
|
|
||||||
};
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
payload,
|
send_message.message,
|
||||||
MessagePayload {
|
crate::cli::encode_message::Message::Raw { data: HexBytes(vec![0xDE, 0xAD]) }
|
||||||
spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version,
|
|
||||||
weight: 0,
|
|
||||||
origin: CallOrigin::TargetAccount(
|
|
||||||
sp_keyring::AccountKeyring::Alice.to_account_id(),
|
|
||||||
sp_keyring::AccountKeyring::Bob.into(),
|
|
||||||
signature,
|
|
||||||
),
|
|
||||||
dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtSourceChain,
|
|
||||||
call: hex!("0001081234").to_vec(),
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn accepts_send_message_command_without_target_sign_options() {
|
fn send_sized_rialto_to_millau() {
|
||||||
// given
|
// given
|
||||||
let send_message = SendMessage::from_iter_safe(vec![
|
let send_message = SendMessage::from_iter(vec![
|
||||||
"send-message",
|
"send-message",
|
||||||
"rialto-to-millau",
|
"rialto-to-millau",
|
||||||
"--source-port",
|
"--source-port",
|
||||||
"1234",
|
"1234",
|
||||||
"--source-signer",
|
"--source-signer",
|
||||||
"//Alice",
|
"//Alice",
|
||||||
"--origin",
|
"--conversion-rate-override",
|
||||||
"Target",
|
"metric",
|
||||||
"remark",
|
"sized",
|
||||||
"--remark-payload",
|
"max",
|
||||||
"1234",
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert!(send_message.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn accepts_non_default_dispatch_fee_payment() {
|
|
||||||
// given
|
|
||||||
let mut send_message = SendMessage::from_iter(vec![
|
|
||||||
"send-message",
|
|
||||||
"rialto-to-millau",
|
|
||||||
"--source-port",
|
|
||||||
"1234",
|
|
||||||
"--source-signer",
|
|
||||||
"//Alice",
|
|
||||||
"--dispatch-fee-payment",
|
|
||||||
"at-target-chain",
|
|
||||||
"remark",
|
|
||||||
]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let payload = send_message.encode_payload().await.unwrap();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assert_eq!(send_message.bridge, FullBridge::RialtoToMillau);
|
||||||
|
assert_eq!(send_message.source.source_port, 1234);
|
||||||
|
assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into()));
|
||||||
|
assert_eq!(send_message.conversion_rate_override, Some(ConversionRateOverride::Metric));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
payload.dispatch_fee_payment,
|
send_message.message,
|
||||||
bp_runtime::messages::DispatchFeePayment::AtTargetChain
|
crate::cli::encode_message::Message::Sized { size: ExplicitOrMaximal::Maximal }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,869 +0,0 @@
|
|||||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity Bridges Common.
|
|
||||||
|
|
||||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Tokens swap using token-swap bridge pallet.
|
|
||||||
|
|
||||||
// TokenSwapBalances fields are never directly accessed, but the whole struct is printed
|
|
||||||
// to show token swap progress
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use codec::Encode;
|
|
||||||
use num_traits::One;
|
|
||||||
use rand::random;
|
|
||||||
use structopt::StructOpt;
|
|
||||||
use strum::{EnumString, EnumVariantNames, VariantNames};
|
|
||||||
|
|
||||||
use frame_support::dispatch::GetDispatchInfo;
|
|
||||||
use relay_substrate_client::{
|
|
||||||
AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, CallOf, Chain, ChainWithBalances,
|
|
||||||
Client, Error as SubstrateError, HashOf, SignParam, SignatureOf, Subscription,
|
|
||||||
TransactionSignScheme, TransactionStatusOf, UnsignedTransaction,
|
|
||||||
};
|
|
||||||
use sp_core::{blake2_256, storage::StorageKey, Bytes, Pair, U256};
|
|
||||||
use sp_runtime::traits::{Convert, Header as HeaderT};
|
|
||||||
|
|
||||||
use crate::cli::{
|
|
||||||
estimate_fee::ConversionRateOverride, Balance, CliChain, SourceConnectionParams,
|
|
||||||
SourceSigningParams, TargetConnectionParams, TargetSigningParams,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Swap tokens.
|
|
||||||
#[derive(StructOpt, Debug, PartialEq)]
|
|
||||||
pub struct SwapTokens {
|
|
||||||
/// A bridge instance to use in token swap.
|
|
||||||
#[structopt(possible_values = SwapTokensBridge::VARIANTS, case_insensitive = true)]
|
|
||||||
bridge: SwapTokensBridge,
|
|
||||||
|
|
||||||
#[structopt(flatten)]
|
|
||||||
source: SourceConnectionParams,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
source_sign: SourceSigningParams,
|
|
||||||
|
|
||||||
#[structopt(flatten)]
|
|
||||||
target: TargetConnectionParams,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
target_sign: TargetSigningParams,
|
|
||||||
|
|
||||||
#[structopt(subcommand)]
|
|
||||||
swap_type: TokenSwapType,
|
|
||||||
/// Source chain balance that source signer wants to swap.
|
|
||||||
#[structopt(long)]
|
|
||||||
source_balance: Balance,
|
|
||||||
/// Target chain balance that target signer wants to swap.
|
|
||||||
#[structopt(long)]
|
|
||||||
target_balance: Balance,
|
|
||||||
/// A way to override conversion rate from target to source tokens.
|
|
||||||
///
|
|
||||||
/// If not specified, conversion rate from runtime storage is used. It may be obsolete and
|
|
||||||
/// your message won't be relayed.
|
|
||||||
#[structopt(long)]
|
|
||||||
target_to_source_conversion_rate_override: Option<ConversionRateOverride>,
|
|
||||||
/// A way to override conversion rate from source to target tokens.
|
|
||||||
///
|
|
||||||
/// If not specified, conversion rate from runtime storage is used. It may be obsolete and
|
|
||||||
/// your message won't be relayed.
|
|
||||||
#[structopt(long)]
|
|
||||||
source_to_target_conversion_rate_override: Option<ConversionRateOverride>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Token swap type.
|
|
||||||
#[derive(StructOpt, Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub enum TokenSwapType {
|
|
||||||
/// The `target_sign` is temporary and only have funds for single swap.
|
|
||||||
NoLock,
|
|
||||||
/// This swap type prevents `source_signer` from restarting the swap after it has been
|
|
||||||
/// completed.
|
|
||||||
LockUntilBlock {
|
|
||||||
/// Number of blocks before the swap expires.
|
|
||||||
#[structopt(long)]
|
|
||||||
blocks_before_expire: u32,
|
|
||||||
/// Unique swap nonce.
|
|
||||||
#[structopt(long)]
|
|
||||||
swap_nonce: Option<U256>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Swap tokens bridge.
|
|
||||||
#[derive(Debug, EnumString, EnumVariantNames, PartialEq)]
|
|
||||||
#[strum(serialize_all = "kebab_case")]
|
|
||||||
pub enum SwapTokensBridge {
|
|
||||||
/// Use token-swap pallet deployed at Millau to swap tokens with Rialto.
|
|
||||||
MillauToRialto,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! select_bridge {
|
|
||||||
($bridge: expr, $generic: tt) => {
|
|
||||||
match $bridge {
|
|
||||||
SwapTokensBridge::MillauToRialto => {
|
|
||||||
type Source = relay_millau_client::Millau;
|
|
||||||
type Target = relay_rialto_client::Rialto;
|
|
||||||
const SOURCE_SPEC_VERSION: u32 = millau_runtime::VERSION.spec_version;
|
|
||||||
const TARGET_SPEC_VERSION: u32 = rialto_runtime::VERSION.spec_version;
|
|
||||||
|
|
||||||
type FromSwapToThisAccountIdConverter = bp_rialto::AccountIdConverter;
|
|
||||||
|
|
||||||
use bp_millau::{
|
|
||||||
derive_account_from_rialto_id as derive_source_account_from_target_account,
|
|
||||||
TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_TARGET_TO_SOURCE_MESSAGE_FEE_METHOD,
|
|
||||||
WITH_RIALTO_TOKEN_SWAP_PALLET_NAME as TOKEN_SWAP_PALLET_NAME,
|
|
||||||
};
|
|
||||||
use bp_rialto::{
|
|
||||||
derive_account_from_millau_id as derive_target_account_from_source_account,
|
|
||||||
TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_SOURCE_TO_TARGET_MESSAGE_FEE_METHOD,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SOURCE_CHAIN_ID: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID;
|
|
||||||
const TARGET_CHAIN_ID: bp_runtime::ChainId = bp_runtime::RIALTO_CHAIN_ID;
|
|
||||||
|
|
||||||
const SOURCE_TO_TARGET_LANE_ID: bp_messages::LaneId = *b"swap";
|
|
||||||
const TARGET_TO_SOURCE_LANE_ID: bp_messages::LaneId = [0, 0, 0, 0];
|
|
||||||
|
|
||||||
$generic
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SwapTokens {
|
|
||||||
/// Run the command.
|
|
||||||
pub async fn run(self) -> anyhow::Result<()> {
|
|
||||||
select_bridge!(self.bridge, {
|
|
||||||
let source_client = self.source.to_client::<Source>().await?;
|
|
||||||
let source_sign = self.source_sign.to_keypair::<Target>()?;
|
|
||||||
let target_client = self.target.to_client::<Target>().await?;
|
|
||||||
let target_sign = self.target_sign.to_keypair::<Target>()?;
|
|
||||||
let target_to_source_conversion_rate_override =
|
|
||||||
self.target_to_source_conversion_rate_override;
|
|
||||||
let source_to_target_conversion_rate_override =
|
|
||||||
self.source_to_target_conversion_rate_override;
|
|
||||||
|
|
||||||
// names of variables in this function are matching names used by the
|
|
||||||
// `pallet-bridge-token-swap`
|
|
||||||
|
|
||||||
// prepare token swap intention
|
|
||||||
let token_swap = self
|
|
||||||
.prepare_token_swap::<Source, Target>(&source_client, &source_sign, &target_sign)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// group all accounts that will be used later
|
|
||||||
let accounts = TokenSwapAccounts {
|
|
||||||
source_account_at_bridged_chain: derive_target_account_from_source_account(
|
|
||||||
bp_runtime::SourceAccount::Account(
|
|
||||||
token_swap.source_account_at_this_chain.clone(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
target_account_at_this_chain: derive_source_account_from_target_account(
|
|
||||||
bp_runtime::SourceAccount::Account(
|
|
||||||
token_swap.target_account_at_bridged_chain.clone(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
source_account_at_this_chain: token_swap.source_account_at_this_chain.clone(),
|
|
||||||
target_account_at_bridged_chain: token_swap.target_account_at_bridged_chain.clone(),
|
|
||||||
swap_account: FromSwapToThisAccountIdConverter::convert(
|
|
||||||
token_swap.using_encoded(blake2_256).into(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
// account balances are used to demonstrate what's happening :)
|
|
||||||
let initial_balances =
|
|
||||||
read_account_balances(&accounts, &source_client, &target_client).await?;
|
|
||||||
|
|
||||||
// before calling something that may fail, log what we're trying to do
|
|
||||||
log::info!(target: "bridge", "Starting swap: {:?}", token_swap);
|
|
||||||
log::info!(target: "bridge", "Swap accounts: {:?}", accounts);
|
|
||||||
log::info!(target: "bridge", "Initial account balances: {:?}", initial_balances);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Step 1: swap is created
|
|
||||||
//
|
|
||||||
|
|
||||||
// prepare `Currency::transfer` call that will happen at the target chain
|
|
||||||
let bridged_currency_transfer: CallOf<Target> = pallet_balances::Call::transfer {
|
|
||||||
dest: accounts.source_account_at_bridged_chain.clone().into(),
|
|
||||||
value: token_swap.target_balance_at_bridged_chain,
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
let bridged_currency_transfer_weight =
|
|
||||||
bridged_currency_transfer.get_dispatch_info().weight;
|
|
||||||
|
|
||||||
// sign message
|
|
||||||
let bridged_chain_spec_version = TARGET_SPEC_VERSION;
|
|
||||||
let signature_payload = pallet_bridge_dispatch::account_ownership_digest(
|
|
||||||
&bridged_currency_transfer,
|
|
||||||
&accounts.swap_account,
|
|
||||||
&bridged_chain_spec_version,
|
|
||||||
SOURCE_CHAIN_ID,
|
|
||||||
TARGET_CHAIN_ID,
|
|
||||||
);
|
|
||||||
let bridged_currency_transfer_signature: SignatureOf<Target> =
|
|
||||||
target_sign.sign(&signature_payload).into();
|
|
||||||
|
|
||||||
// prepare `create_swap` call
|
|
||||||
let target_public_at_bridged_chain: AccountPublicOf<Target> =
|
|
||||||
target_sign.public().into();
|
|
||||||
let swap_delivery_and_dispatch_fee =
|
|
||||||
crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee::<
|
|
||||||
Source,
|
|
||||||
Target,
|
|
||||||
_,
|
|
||||||
>(
|
|
||||||
&source_client,
|
|
||||||
target_to_source_conversion_rate_override,
|
|
||||||
ESTIMATE_SOURCE_TO_TARGET_MESSAGE_FEE_METHOD,
|
|
||||||
SOURCE_TO_TARGET_LANE_ID,
|
|
||||||
bp_message_dispatch::MessagePayload {
|
|
||||||
spec_version: TARGET_SPEC_VERSION,
|
|
||||||
weight: bridged_currency_transfer_weight,
|
|
||||||
origin: bp_message_dispatch::CallOrigin::TargetAccount(
|
|
||||||
accounts.swap_account.clone(),
|
|
||||||
target_public_at_bridged_chain.clone(),
|
|
||||||
bridged_currency_transfer_signature.clone(),
|
|
||||||
),
|
|
||||||
dispatch_fee_payment:
|
|
||||||
bp_runtime::messages::DispatchFeePayment::AtTargetChain,
|
|
||||||
call: bridged_currency_transfer.encode(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let create_swap_call: CallOf<Source> = pallet_bridge_token_swap::Call::create_swap {
|
|
||||||
swap: token_swap.clone(),
|
|
||||||
swap_creation_params: Box::new(bp_token_swap::TokenSwapCreation {
|
|
||||||
target_public_at_bridged_chain,
|
|
||||||
swap_delivery_and_dispatch_fee,
|
|
||||||
bridged_chain_spec_version,
|
|
||||||
bridged_currency_transfer: bridged_currency_transfer.encode(),
|
|
||||||
bridged_currency_transfer_weight,
|
|
||||||
bridged_currency_transfer_signature,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
|
|
||||||
// start tokens swap
|
|
||||||
let source_genesis_hash = *source_client.genesis_hash();
|
|
||||||
let create_swap_signer = source_sign.clone();
|
|
||||||
let (spec_version, transaction_version) =
|
|
||||||
source_client.simple_runtime_version().await?;
|
|
||||||
let swap_created_at = wait_until_transaction_is_finalized::<Source>(
|
|
||||||
source_client
|
|
||||||
.submit_and_watch_signed_extrinsic(
|
|
||||||
accounts.source_account_at_this_chain.clone(),
|
|
||||||
move |_, transaction_nonce| {
|
|
||||||
Ok(Bytes(
|
|
||||||
Source::sign_transaction(SignParam {
|
|
||||||
spec_version,
|
|
||||||
transaction_version,
|
|
||||||
genesis_hash: source_genesis_hash,
|
|
||||||
signer: create_swap_signer,
|
|
||||||
era: relay_substrate_client::TransactionEra::immortal(),
|
|
||||||
unsigned: UnsignedTransaction::new(
|
|
||||||
create_swap_call.into(),
|
|
||||||
transaction_nonce,
|
|
||||||
),
|
|
||||||
})?
|
|
||||||
.encode(),
|
|
||||||
))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// read state of swap after it has been created
|
|
||||||
let token_swap_hash = token_swap.hash();
|
|
||||||
let token_swap_storage_key = bp_token_swap::storage_keys::pending_swaps_key(
|
|
||||||
TOKEN_SWAP_PALLET_NAME,
|
|
||||||
token_swap_hash,
|
|
||||||
);
|
|
||||||
match read_token_swap_state(&source_client, swap_created_at, &token_swap_storage_key)
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
Some(bp_token_swap::TokenSwapState::Started) => {
|
|
||||||
log::info!(target: "bridge", "Swap has been successfully started");
|
|
||||||
let intermediate_balances =
|
|
||||||
read_account_balances(&accounts, &source_client, &target_client).await?;
|
|
||||||
log::info!(target: "bridge", "Intermediate balances: {:?}", intermediate_balances);
|
|
||||||
},
|
|
||||||
Some(token_swap_state) =>
|
|
||||||
return Err(anyhow::format_err!(
|
|
||||||
"Fresh token swap has unexpected state: {:?}",
|
|
||||||
token_swap_state,
|
|
||||||
)),
|
|
||||||
None => return Err(anyhow::format_err!("Failed to start token swap")),
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Step 2: message is being relayed to the target chain and dispathed there
|
|
||||||
//
|
|
||||||
|
|
||||||
// wait until message is dispatched at the target chain and dispatch result delivered
|
|
||||||
// back to source chain
|
|
||||||
let token_swap_state = wait_until_token_swap_state_is_changed(
|
|
||||||
&source_client,
|
|
||||||
&token_swap_storage_key,
|
|
||||||
bp_token_swap::TokenSwapState::Started,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let is_transfer_succeeded = match token_swap_state {
|
|
||||||
Some(bp_token_swap::TokenSwapState::Started) => {
|
|
||||||
unreachable!("wait_until_token_swap_state_is_changed only returns if state is not Started; qed",)
|
|
||||||
},
|
|
||||||
None =>
|
|
||||||
return Err(anyhow::format_err!("Fresh token swap has disappeared unexpectedly")),
|
|
||||||
Some(bp_token_swap::TokenSwapState::Confirmed) => {
|
|
||||||
log::info!(
|
|
||||||
target: "bridge",
|
|
||||||
"Transfer has been successfully dispatched at the target chain. Swap can be claimed",
|
|
||||||
);
|
|
||||||
true
|
|
||||||
},
|
|
||||||
Some(bp_token_swap::TokenSwapState::Failed) => {
|
|
||||||
log::info!(
|
|
||||||
target: "bridge",
|
|
||||||
"Transfer has been dispatched with an error at the target chain. Swap can be canceled",
|
|
||||||
);
|
|
||||||
false
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// by this time: (1) token swap account has been created and (2) if transfer has been
|
|
||||||
// successfully dispatched, both target chain balances have changed
|
|
||||||
let intermediate_balances =
|
|
||||||
read_account_balances(&accounts, &source_client, &target_client).await?;
|
|
||||||
log::info!(target: "bridge", "Intermediate balances: {:?}", intermediate_balances);
|
|
||||||
|
|
||||||
// transfer has been dispatched, but we may need to wait until block where swap can be
|
|
||||||
// claimed/canceled
|
|
||||||
if let bp_token_swap::TokenSwapType::LockClaimUntilBlock(
|
|
||||||
ref last_available_block_number,
|
|
||||||
_,
|
|
||||||
) = token_swap.swap_type
|
|
||||||
{
|
|
||||||
wait_until_swap_unlocked(
|
|
||||||
&source_client,
|
|
||||||
last_available_block_number + BlockNumberOf::<Source>::one(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Step 3: we may now claim or cancel the swap
|
|
||||||
//
|
|
||||||
|
|
||||||
if is_transfer_succeeded {
|
|
||||||
log::info!(target: "bridge", "Claiming the swap");
|
|
||||||
|
|
||||||
// prepare `claim_swap` message that will be sent over the bridge
|
|
||||||
let claim_swap_call: CallOf<Source> =
|
|
||||||
pallet_bridge_token_swap::Call::claim_swap { swap: token_swap }.into();
|
|
||||||
let claim_swap_message = bp_message_dispatch::MessagePayload {
|
|
||||||
spec_version: SOURCE_SPEC_VERSION,
|
|
||||||
weight: claim_swap_call.get_dispatch_info().weight,
|
|
||||||
origin: bp_message_dispatch::CallOrigin::SourceAccount(
|
|
||||||
accounts.target_account_at_bridged_chain.clone(),
|
|
||||||
),
|
|
||||||
dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtSourceChain,
|
|
||||||
call: claim_swap_call.encode(),
|
|
||||||
};
|
|
||||||
let claim_swap_delivery_and_dispatch_fee =
|
|
||||||
crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee::<
|
|
||||||
Target,
|
|
||||||
Source,
|
|
||||||
_,
|
|
||||||
>(
|
|
||||||
&target_client,
|
|
||||||
source_to_target_conversion_rate_override,
|
|
||||||
ESTIMATE_TARGET_TO_SOURCE_MESSAGE_FEE_METHOD,
|
|
||||||
TARGET_TO_SOURCE_LANE_ID,
|
|
||||||
claim_swap_message.clone(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let send_message_call: CallOf<Target> =
|
|
||||||
pallet_bridge_messages::Call::send_message {
|
|
||||||
lane_id: TARGET_TO_SOURCE_LANE_ID,
|
|
||||||
payload: claim_swap_message,
|
|
||||||
delivery_and_dispatch_fee: claim_swap_delivery_and_dispatch_fee,
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
|
|
||||||
// send `claim_swap` message
|
|
||||||
let target_genesis_hash = *target_client.genesis_hash();
|
|
||||||
let (spec_version, transaction_version) =
|
|
||||||
target_client.simple_runtime_version().await?;
|
|
||||||
let _ = wait_until_transaction_is_finalized::<Target>(
|
|
||||||
target_client
|
|
||||||
.submit_and_watch_signed_extrinsic(
|
|
||||||
accounts.target_account_at_bridged_chain.clone(),
|
|
||||||
move |_, transaction_nonce| {
|
|
||||||
Ok(Bytes(
|
|
||||||
Target::sign_transaction(SignParam {
|
|
||||||
spec_version,
|
|
||||||
transaction_version,
|
|
||||||
genesis_hash: target_genesis_hash,
|
|
||||||
signer: target_sign,
|
|
||||||
era: relay_substrate_client::TransactionEra::immortal(),
|
|
||||||
unsigned: UnsignedTransaction::new(
|
|
||||||
send_message_call.into(),
|
|
||||||
transaction_nonce,
|
|
||||||
),
|
|
||||||
})?
|
|
||||||
.encode(),
|
|
||||||
))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// wait until swap state is updated
|
|
||||||
let token_swap_state = wait_until_token_swap_state_is_changed(
|
|
||||||
&source_client,
|
|
||||||
&token_swap_storage_key,
|
|
||||||
bp_token_swap::TokenSwapState::Confirmed,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
if token_swap_state != None {
|
|
||||||
return Err(anyhow::format_err!(
|
|
||||||
"Confirmed token swap state has been changed to {:?} unexpectedly",
|
|
||||||
token_swap_state
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::info!(target: "bridge", "Cancelling the swap");
|
|
||||||
let cancel_swap_call: CallOf<Source> =
|
|
||||||
pallet_bridge_token_swap::Call::cancel_swap { swap: token_swap.clone() }.into();
|
|
||||||
let (spec_version, transaction_version) =
|
|
||||||
source_client.simple_runtime_version().await?;
|
|
||||||
let _ = wait_until_transaction_is_finalized::<Source>(
|
|
||||||
source_client
|
|
||||||
.submit_and_watch_signed_extrinsic(
|
|
||||||
accounts.source_account_at_this_chain.clone(),
|
|
||||||
move |_, transaction_nonce| {
|
|
||||||
Ok(Bytes(
|
|
||||||
Source::sign_transaction(SignParam {
|
|
||||||
spec_version,
|
|
||||||
transaction_version,
|
|
||||||
genesis_hash: source_genesis_hash,
|
|
||||||
signer: source_sign,
|
|
||||||
era: relay_substrate_client::TransactionEra::immortal(),
|
|
||||||
unsigned: UnsignedTransaction::new(
|
|
||||||
cancel_swap_call.into(),
|
|
||||||
transaction_nonce,
|
|
||||||
),
|
|
||||||
})?
|
|
||||||
.encode(),
|
|
||||||
))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// print final balances
|
|
||||||
let final_balances =
|
|
||||||
read_account_balances(&accounts, &source_client, &target_client).await?;
|
|
||||||
log::info!(target: "bridge", "Final account balances: {:?}", final_balances);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepare token swap intention.
|
|
||||||
async fn prepare_token_swap<Source: CliChain, Target: CliChain>(
|
|
||||||
&self,
|
|
||||||
source_client: &Client<Source>,
|
|
||||||
source_sign: &Source::KeyPair,
|
|
||||||
target_sign: &Target::KeyPair,
|
|
||||||
) -> anyhow::Result<
|
|
||||||
bp_token_swap::TokenSwap<
|
|
||||||
BlockNumberOf<Source>,
|
|
||||||
BalanceOf<Source>,
|
|
||||||
AccountIdOf<Source>,
|
|
||||||
BalanceOf<Target>,
|
|
||||||
AccountIdOf<Target>,
|
|
||||||
>,
|
|
||||||
>
|
|
||||||
where
|
|
||||||
AccountIdOf<Source>: From<<Source::KeyPair as Pair>::Public>,
|
|
||||||
AccountIdOf<Target>: From<<Target::KeyPair as Pair>::Public>,
|
|
||||||
BalanceOf<Source>: From<u64>,
|
|
||||||
BalanceOf<Target>: From<u64>,
|
|
||||||
{
|
|
||||||
// accounts that are directly controlled by participants
|
|
||||||
let source_account_at_this_chain: AccountIdOf<Source> = source_sign.public().into();
|
|
||||||
let target_account_at_bridged_chain: AccountIdOf<Target> = target_sign.public().into();
|
|
||||||
|
|
||||||
// balances that we're going to swap
|
|
||||||
let source_balance_at_this_chain: BalanceOf<Source> = self.source_balance.cast().into();
|
|
||||||
let target_balance_at_bridged_chain: BalanceOf<Target> = self.target_balance.cast().into();
|
|
||||||
|
|
||||||
// prepare token swap intention
|
|
||||||
Ok(bp_token_swap::TokenSwap {
|
|
||||||
swap_type: self.prepare_token_swap_type(source_client).await?,
|
|
||||||
source_balance_at_this_chain,
|
|
||||||
source_account_at_this_chain: source_account_at_this_chain.clone(),
|
|
||||||
target_balance_at_bridged_chain,
|
|
||||||
target_account_at_bridged_chain: target_account_at_bridged_chain.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepare token swap type.
|
|
||||||
async fn prepare_token_swap_type<Source: Chain>(
|
|
||||||
&self,
|
|
||||||
source_client: &Client<Source>,
|
|
||||||
) -> anyhow::Result<bp_token_swap::TokenSwapType<BlockNumberOf<Source>>> {
|
|
||||||
match self.swap_type {
|
|
||||||
TokenSwapType::NoLock =>
|
|
||||||
Ok(bp_token_swap::TokenSwapType::TemporaryTargetAccountAtBridgedChain),
|
|
||||||
TokenSwapType::LockUntilBlock { blocks_before_expire, ref swap_nonce } => {
|
|
||||||
let blocks_before_expire: BlockNumberOf<Source> = blocks_before_expire.into();
|
|
||||||
let current_source_block_number = *source_client.best_header().await?.number();
|
|
||||||
Ok(bp_token_swap::TokenSwapType::LockClaimUntilBlock(
|
|
||||||
current_source_block_number + blocks_before_expire,
|
|
||||||
swap_nonce.unwrap_or_else(|| {
|
|
||||||
U256::from(random::<u128>()).overflowing_mul(U256::from(random::<u128>())).0
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Accounts that are participating in the swap.
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct TokenSwapAccounts<ThisAccountId, BridgedAccountId> {
|
|
||||||
source_account_at_this_chain: ThisAccountId,
|
|
||||||
source_account_at_bridged_chain: BridgedAccountId,
|
|
||||||
target_account_at_bridged_chain: BridgedAccountId,
|
|
||||||
target_account_at_this_chain: ThisAccountId,
|
|
||||||
swap_account: ThisAccountId,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Swap accounts balances.
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct TokenSwapBalances<ThisBalance, BridgedBalance> {
|
|
||||||
source_account_at_this_chain_balance: Option<ThisBalance>,
|
|
||||||
source_account_at_bridged_chain_balance: Option<BridgedBalance>,
|
|
||||||
target_account_at_bridged_chain_balance: Option<BridgedBalance>,
|
|
||||||
target_account_at_this_chain_balance: Option<ThisBalance>,
|
|
||||||
swap_account_balance: Option<ThisBalance>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read swap accounts balances.
|
|
||||||
async fn read_account_balances<Source: ChainWithBalances, Target: ChainWithBalances>(
|
|
||||||
accounts: &TokenSwapAccounts<AccountIdOf<Source>, AccountIdOf<Target>>,
|
|
||||||
source_client: &Client<Source>,
|
|
||||||
target_client: &Client<Target>,
|
|
||||||
) -> anyhow::Result<TokenSwapBalances<BalanceOf<Source>, BalanceOf<Target>>> {
|
|
||||||
Ok(TokenSwapBalances {
|
|
||||||
source_account_at_this_chain_balance: read_account_balance(
|
|
||||||
source_client,
|
|
||||||
&accounts.source_account_at_this_chain,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
source_account_at_bridged_chain_balance: read_account_balance(
|
|
||||||
target_client,
|
|
||||||
&accounts.source_account_at_bridged_chain,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
target_account_at_bridged_chain_balance: read_account_balance(
|
|
||||||
target_client,
|
|
||||||
&accounts.target_account_at_bridged_chain,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
target_account_at_this_chain_balance: read_account_balance(
|
|
||||||
source_client,
|
|
||||||
&accounts.target_account_at_this_chain,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
swap_account_balance: read_account_balance(source_client, &accounts.swap_account).await?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read account balance.
|
|
||||||
async fn read_account_balance<C: ChainWithBalances>(
|
|
||||||
client: &Client<C>,
|
|
||||||
account: &AccountIdOf<C>,
|
|
||||||
) -> anyhow::Result<Option<BalanceOf<C>>> {
|
|
||||||
match client.free_native_balance(account.clone()).await {
|
|
||||||
Ok(balance) => Ok(Some(balance)),
|
|
||||||
Err(SubstrateError::AccountDoesNotExist) => Ok(None),
|
|
||||||
Err(error) => Err(anyhow::format_err!(
|
|
||||||
"Failed to read balance of {} account {:?}: {:?}",
|
|
||||||
C::NAME,
|
|
||||||
account,
|
|
||||||
error,
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wait until transaction is included into finalized block.
|
|
||||||
///
|
|
||||||
/// Returns the hash of the finalized block with transaction.
|
|
||||||
pub(crate) async fn wait_until_transaction_is_finalized<C: Chain>(
|
|
||||||
subscription: Subscription<TransactionStatusOf<C>>,
|
|
||||||
) -> anyhow::Result<HashOf<C>> {
|
|
||||||
loop {
|
|
||||||
let transaction_status = subscription.next().await?;
|
|
||||||
match transaction_status {
|
|
||||||
Some(TransactionStatusOf::<C>::FinalityTimeout(_)) |
|
|
||||||
Some(TransactionStatusOf::<C>::Usurped(_)) |
|
|
||||||
Some(TransactionStatusOf::<C>::Dropped) |
|
|
||||||
Some(TransactionStatusOf::<C>::Invalid) |
|
|
||||||
None =>
|
|
||||||
return Err(anyhow::format_err!(
|
|
||||||
"We've been waiting for finalization of {} transaction, but it now has the {:?} status",
|
|
||||||
C::NAME,
|
|
||||||
transaction_status,
|
|
||||||
)),
|
|
||||||
Some(TransactionStatusOf::<C>::Finalized(block_hash)) => {
|
|
||||||
log::trace!(
|
|
||||||
target: "bridge",
|
|
||||||
"{} transaction has been finalized at block {}",
|
|
||||||
C::NAME,
|
|
||||||
block_hash,
|
|
||||||
);
|
|
||||||
return Ok(block_hash)
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
log::trace!(
|
|
||||||
target: "bridge",
|
|
||||||
"Received intermediate status of {} transaction: {:?}",
|
|
||||||
C::NAME,
|
|
||||||
transaction_status,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Waits until token swap state is changed from `Started` to something else.
|
|
||||||
async fn wait_until_token_swap_state_is_changed<C: Chain>(
|
|
||||||
client: &Client<C>,
|
|
||||||
swap_state_storage_key: &StorageKey,
|
|
||||||
previous_token_swap_state: bp_token_swap::TokenSwapState,
|
|
||||||
) -> anyhow::Result<Option<bp_token_swap::TokenSwapState>> {
|
|
||||||
log::trace!(target: "bridge", "Waiting for token swap state change");
|
|
||||||
loop {
|
|
||||||
async_std::task::sleep(C::AVERAGE_BLOCK_INTERVAL).await;
|
|
||||||
|
|
||||||
let best_block = client.best_finalized_header_number().await?;
|
|
||||||
let best_block_hash = client.block_hash_by_number(best_block).await?;
|
|
||||||
log::trace!(target: "bridge", "Inspecting {} block {}/{}", C::NAME, best_block, best_block_hash);
|
|
||||||
|
|
||||||
let token_swap_state =
|
|
||||||
read_token_swap_state(client, best_block_hash, swap_state_storage_key).await?;
|
|
||||||
match token_swap_state {
|
|
||||||
Some(new_token_swap_state) if new_token_swap_state == previous_token_swap_state => {},
|
|
||||||
_ => {
|
|
||||||
log::trace!(
|
|
||||||
target: "bridge",
|
|
||||||
"Token swap state has been changed from {:?} to {:?}",
|
|
||||||
previous_token_swap_state,
|
|
||||||
token_swap_state,
|
|
||||||
);
|
|
||||||
return Ok(token_swap_state)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Waits until swap can be claimed or canceled.
|
|
||||||
async fn wait_until_swap_unlocked<C: Chain>(
|
|
||||||
client: &Client<C>,
|
|
||||||
required_block_number: BlockNumberOf<C>,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
log::trace!(target: "bridge", "Waiting for token swap unlock");
|
|
||||||
loop {
|
|
||||||
async_std::task::sleep(C::AVERAGE_BLOCK_INTERVAL).await;
|
|
||||||
|
|
||||||
let best_block = client.best_finalized_header_number().await?;
|
|
||||||
let best_block_hash = client.block_hash_by_number(best_block).await?;
|
|
||||||
if best_block >= required_block_number {
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!(target: "bridge", "Skipping {} block {}/{}", C::NAME, best_block, best_block_hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read state of the active token swap.
|
|
||||||
async fn read_token_swap_state<C: Chain>(
|
|
||||||
client: &Client<C>,
|
|
||||||
at_block: C::Hash,
|
|
||||||
swap_state_storage_key: &StorageKey,
|
|
||||||
) -> anyhow::Result<Option<bp_token_swap::TokenSwapState>> {
|
|
||||||
Ok(client.storage_value(swap_state_storage_key.clone(), Some(at_block)).await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams, TargetRuntimeVersionParams};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn swap_tokens_millau_to_rialto_no_lock() {
|
|
||||||
let swap_tokens = SwapTokens::from_iter(vec![
|
|
||||||
"swap-tokens",
|
|
||||||
"millau-to-rialto",
|
|
||||||
"--source-host",
|
|
||||||
"127.0.0.1",
|
|
||||||
"--source-port",
|
|
||||||
"9000",
|
|
||||||
"--source-signer",
|
|
||||||
"//Alice",
|
|
||||||
"--source-balance",
|
|
||||||
"8000000000",
|
|
||||||
"--target-host",
|
|
||||||
"127.0.0.1",
|
|
||||||
"--target-port",
|
|
||||||
"9001",
|
|
||||||
"--target-signer",
|
|
||||||
"//Bob",
|
|
||||||
"--target-balance",
|
|
||||||
"9000000000",
|
|
||||||
"no-lock",
|
|
||||||
]);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
swap_tokens,
|
|
||||||
SwapTokens {
|
|
||||||
bridge: SwapTokensBridge::MillauToRialto,
|
|
||||||
source: SourceConnectionParams {
|
|
||||||
source_host: "127.0.0.1".into(),
|
|
||||||
source_port: 9000,
|
|
||||||
source_secure: false,
|
|
||||||
source_runtime_version: SourceRuntimeVersionParams {
|
|
||||||
source_version_mode: RuntimeVersionType::Bundle,
|
|
||||||
source_spec_version: None,
|
|
||||||
source_transaction_version: None,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
source_sign: SourceSigningParams {
|
|
||||||
source_signer: Some("//Alice".into()),
|
|
||||||
source_signer_password: None,
|
|
||||||
source_signer_file: None,
|
|
||||||
source_signer_password_file: None,
|
|
||||||
source_transactions_mortality: None,
|
|
||||||
},
|
|
||||||
target: TargetConnectionParams {
|
|
||||||
target_host: "127.0.0.1".into(),
|
|
||||||
target_port: 9001,
|
|
||||||
target_secure: false,
|
|
||||||
target_runtime_version: TargetRuntimeVersionParams {
|
|
||||||
target_version_mode: RuntimeVersionType::Bundle,
|
|
||||||
target_spec_version: None,
|
|
||||||
target_transaction_version: None,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
target_sign: TargetSigningParams {
|
|
||||||
target_signer: Some("//Bob".into()),
|
|
||||||
target_signer_password: None,
|
|
||||||
target_signer_file: None,
|
|
||||||
target_signer_password_file: None,
|
|
||||||
target_transactions_mortality: None,
|
|
||||||
},
|
|
||||||
swap_type: TokenSwapType::NoLock,
|
|
||||||
source_balance: Balance(8000000000),
|
|
||||||
target_balance: Balance(9000000000),
|
|
||||||
target_to_source_conversion_rate_override: None,
|
|
||||||
source_to_target_conversion_rate_override: None,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn swap_tokens_millau_to_rialto_lock_until() {
|
|
||||||
let swap_tokens = SwapTokens::from_iter(vec![
|
|
||||||
"swap-tokens",
|
|
||||||
"millau-to-rialto",
|
|
||||||
"--source-host",
|
|
||||||
"127.0.0.1",
|
|
||||||
"--source-port",
|
|
||||||
"9000",
|
|
||||||
"--source-signer",
|
|
||||||
"//Alice",
|
|
||||||
"--source-balance",
|
|
||||||
"8000000000",
|
|
||||||
"--target-host",
|
|
||||||
"127.0.0.1",
|
|
||||||
"--target-port",
|
|
||||||
"9001",
|
|
||||||
"--target-signer",
|
|
||||||
"//Bob",
|
|
||||||
"--target-balance",
|
|
||||||
"9000000000",
|
|
||||||
"--target-to-source-conversion-rate-override",
|
|
||||||
"metric",
|
|
||||||
"--source-to-target-conversion-rate-override",
|
|
||||||
"84.56",
|
|
||||||
"lock-until-block",
|
|
||||||
"--blocks-before-expire",
|
|
||||||
"1",
|
|
||||||
]);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
swap_tokens,
|
|
||||||
SwapTokens {
|
|
||||||
bridge: SwapTokensBridge::MillauToRialto,
|
|
||||||
source: SourceConnectionParams {
|
|
||||||
source_host: "127.0.0.1".into(),
|
|
||||||
source_port: 9000,
|
|
||||||
source_secure: false,
|
|
||||||
source_runtime_version: SourceRuntimeVersionParams {
|
|
||||||
source_version_mode: RuntimeVersionType::Bundle,
|
|
||||||
source_spec_version: None,
|
|
||||||
source_transaction_version: None,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
source_sign: SourceSigningParams {
|
|
||||||
source_signer: Some("//Alice".into()),
|
|
||||||
source_signer_password: None,
|
|
||||||
source_signer_file: None,
|
|
||||||
source_signer_password_file: None,
|
|
||||||
source_transactions_mortality: None,
|
|
||||||
},
|
|
||||||
target: TargetConnectionParams {
|
|
||||||
target_host: "127.0.0.1".into(),
|
|
||||||
target_port: 9001,
|
|
||||||
target_secure: false,
|
|
||||||
target_runtime_version: TargetRuntimeVersionParams {
|
|
||||||
target_version_mode: RuntimeVersionType::Bundle,
|
|
||||||
target_spec_version: None,
|
|
||||||
target_transaction_version: None,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
target_sign: TargetSigningParams {
|
|
||||||
target_signer: Some("//Bob".into()),
|
|
||||||
target_signer_password: None,
|
|
||||||
target_signer_file: None,
|
|
||||||
target_signer_password_file: None,
|
|
||||||
target_transactions_mortality: None,
|
|
||||||
},
|
|
||||||
swap_type: TokenSwapType::LockUntilBlock {
|
|
||||||
blocks_before_expire: 1,
|
|
||||||
swap_nonce: None,
|
|
||||||
},
|
|
||||||
source_balance: Balance(8000000000),
|
|
||||||
target_balance: Balance(9000000000),
|
|
||||||
target_to_source_conversion_rate_override: Some(ConversionRateOverride::Metric),
|
|
||||||
source_to_target_conversion_rate_override: Some(ConversionRateOverride::Explicit(
|
|
||||||
84.56
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,13 +15,11 @@ scale-info = { version = "2.1.1", features = ["derive"] }
|
|||||||
|
|
||||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||||
bp-kusama = { path = "../../primitives/chain-kusama" }
|
bp-kusama = { path = "../../primitives/chain-kusama" }
|
||||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
|
||||||
bp-messages = { path = "../../primitives/messages" }
|
bp-messages = { path = "../../primitives/messages" }
|
||||||
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
||||||
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||||
bp-runtime = { path = "../../primitives/runtime" }
|
bp-runtime = { path = "../../primitives/runtime" }
|
||||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||||
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
|
||||||
|
|
||||||
# Substrate Dependencies
|
# Substrate Dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -27,30 +27,6 @@ use sp_runtime::FixedU128;
|
|||||||
/// Unchecked Kusama extrinsic.
|
/// Unchecked Kusama extrinsic.
|
||||||
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
|
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
|
||||||
|
|
||||||
/// Polkadot account ownership digest from Kusama.
|
|
||||||
///
|
|
||||||
/// The byte vector returned by this function should be signed with a Polkadot account private key.
|
|
||||||
/// This way, the owner of `kusama_account_id` on Kusama proves that the Polkadot account private
|
|
||||||
/// key is also under his control.
|
|
||||||
pub fn kusama_to_polkadot_account_ownership_digest<Call, AccountId, SpecVersion>(
|
|
||||||
polkadot_call: &Call,
|
|
||||||
kusama_account_id: AccountId,
|
|
||||||
polkadot_spec_version: SpecVersion,
|
|
||||||
) -> Vec<u8>
|
|
||||||
where
|
|
||||||
Call: codec::Encode,
|
|
||||||
AccountId: codec::Encode,
|
|
||||||
SpecVersion: codec::Encode,
|
|
||||||
{
|
|
||||||
pallet_bridge_dispatch::account_ownership_digest(
|
|
||||||
polkadot_call,
|
|
||||||
kusama_account_id,
|
|
||||||
polkadot_spec_version,
|
|
||||||
bp_runtime::KUSAMA_CHAIN_ID,
|
|
||||||
bp_runtime::POLKADOT_CHAIN_ID,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Kusama Runtime `Call` enum.
|
/// Kusama Runtime `Call` enum.
|
||||||
///
|
///
|
||||||
/// The enum represents a subset of possible `Call`s we can send to Kusama chain.
|
/// The enum represents a subset of possible `Call`s we can send to Kusama chain.
|
||||||
@@ -115,16 +91,7 @@ pub enum BridgePolkadotMessagesCall {
|
|||||||
#[codec(index = 2)]
|
#[codec(index = 2)]
|
||||||
update_pallet_parameter(BridgePolkadotMessagesParameter),
|
update_pallet_parameter(BridgePolkadotMessagesParameter),
|
||||||
#[codec(index = 3)]
|
#[codec(index = 3)]
|
||||||
send_message(
|
send_message(LaneId, Vec<u8>, bp_kusama::Balance),
|
||||||
LaneId,
|
|
||||||
bp_message_dispatch::MessagePayload<
|
|
||||||
bp_kusama::AccountId,
|
|
||||||
bp_polkadot::AccountId,
|
|
||||||
bp_polkadot::AccountPublic,
|
|
||||||
Vec<u8>,
|
|
||||||
>,
|
|
||||||
bp_kusama::Balance,
|
|
||||||
),
|
|
||||||
#[codec(index = 5)]
|
#[codec(index = 5)]
|
||||||
receive_messages_proof(
|
receive_messages_proof(
|
||||||
bp_polkadot::AccountId,
|
bp_polkadot::AccountId,
|
||||||
|
|||||||
@@ -15,13 +15,11 @@ scale-info = { version = "2.1.1", features = ["derive"] }
|
|||||||
|
|
||||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||||
bp-kusama = { path = "../../primitives/chain-kusama" }
|
bp-kusama = { path = "../../primitives/chain-kusama" }
|
||||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
|
||||||
bp-messages = { path = "../../primitives/messages" }
|
bp-messages = { path = "../../primitives/messages" }
|
||||||
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
||||||
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||||
bp-runtime = { path = "../../primitives/runtime" }
|
bp-runtime = { path = "../../primitives/runtime" }
|
||||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||||
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
|
||||||
|
|
||||||
# Substrate Dependencies
|
# Substrate Dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -27,30 +27,6 @@ use sp_runtime::FixedU128;
|
|||||||
/// Unchecked Polkadot extrinsic.
|
/// Unchecked Polkadot extrinsic.
|
||||||
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
|
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
|
||||||
|
|
||||||
/// Kusama account ownership digest from Polkadot.
|
|
||||||
///
|
|
||||||
/// The byte vector returned by this function should be signed with a Kusama account private key.
|
|
||||||
/// This way, the owner of `kusam_account_id` on Polkadot proves that the Kusama account private key
|
|
||||||
/// is also under his control.
|
|
||||||
pub fn polkadot_to_kusama_account_ownership_digest<Call, AccountId, SpecVersion>(
|
|
||||||
kusama_call: &Call,
|
|
||||||
kusam_account_id: AccountId,
|
|
||||||
kusama_spec_version: SpecVersion,
|
|
||||||
) -> Vec<u8>
|
|
||||||
where
|
|
||||||
Call: codec::Encode,
|
|
||||||
AccountId: codec::Encode,
|
|
||||||
SpecVersion: codec::Encode,
|
|
||||||
{
|
|
||||||
pallet_bridge_dispatch::account_ownership_digest(
|
|
||||||
kusama_call,
|
|
||||||
kusam_account_id,
|
|
||||||
kusama_spec_version,
|
|
||||||
bp_runtime::POLKADOT_CHAIN_ID,
|
|
||||||
bp_runtime::KUSAMA_CHAIN_ID,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Polkadot Runtime `Call` enum.
|
/// Polkadot Runtime `Call` enum.
|
||||||
///
|
///
|
||||||
/// The enum represents a subset of possible `Call`s we can send to Polkadot chain.
|
/// The enum represents a subset of possible `Call`s we can send to Polkadot chain.
|
||||||
@@ -115,16 +91,7 @@ pub enum BridgeKusamaMessagesCall {
|
|||||||
#[codec(index = 2)]
|
#[codec(index = 2)]
|
||||||
update_pallet_parameter(BridgeKusamaMessagesParameter),
|
update_pallet_parameter(BridgeKusamaMessagesParameter),
|
||||||
#[codec(index = 3)]
|
#[codec(index = 3)]
|
||||||
send_message(
|
send_message(LaneId, Vec<u8>, bp_polkadot::Balance),
|
||||||
LaneId,
|
|
||||||
bp_message_dispatch::MessagePayload<
|
|
||||||
bp_polkadot::AccountId,
|
|
||||||
bp_kusama::AccountId,
|
|
||||||
bp_kusama::AccountPublic,
|
|
||||||
Vec<u8>,
|
|
||||||
>,
|
|
||||||
bp_polkadot::Balance,
|
|
||||||
),
|
|
||||||
#[codec(index = 5)]
|
#[codec(index = 5)]
|
||||||
receive_messages_proof(
|
receive_messages_proof(
|
||||||
bp_kusama::AccountId,
|
bp_kusama::AccountId,
|
||||||
|
|||||||
@@ -15,13 +15,11 @@ scale-info = { version = "2.1.1", features = ["derive"] }
|
|||||||
|
|
||||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
|
||||||
bp-messages = { path = "../../primitives/messages" }
|
bp-messages = { path = "../../primitives/messages" }
|
||||||
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||||
bp-rococo = { path = "../../primitives/chain-rococo" }
|
bp-rococo = { path = "../../primitives/chain-rococo" }
|
||||||
bp-runtime = { path = "../../primitives/runtime" }
|
bp-runtime = { path = "../../primitives/runtime" }
|
||||||
bp-wococo = { path = "../../primitives/chain-wococo" }
|
bp-wococo = { path = "../../primitives/chain-wococo" }
|
||||||
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
|
||||||
pallet-bridge-messages = { path = "../../modules/messages" }
|
pallet-bridge-messages = { path = "../../modules/messages" }
|
||||||
|
|
||||||
# Substrate Dependencies
|
# Substrate Dependencies
|
||||||
|
|||||||
@@ -26,30 +26,6 @@ use scale_info::TypeInfo;
|
|||||||
/// Unchecked Rococo extrinsic.
|
/// Unchecked Rococo extrinsic.
|
||||||
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
|
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
|
||||||
|
|
||||||
/// Wococo account ownership digest from Rococo.
|
|
||||||
///
|
|
||||||
/// The byte vector returned by this function should be signed with a Wococo account private key.
|
|
||||||
/// This way, the owner of `rococo_account_id` on Rococo proves that the Wococo account private key
|
|
||||||
/// is also under his control.
|
|
||||||
pub fn rococo_to_wococo_account_ownership_digest<Call, AccountId, SpecVersion>(
|
|
||||||
wococo_call: &Call,
|
|
||||||
rococo_account_id: AccountId,
|
|
||||||
wococo_spec_version: SpecVersion,
|
|
||||||
) -> Vec<u8>
|
|
||||||
where
|
|
||||||
Call: codec::Encode,
|
|
||||||
AccountId: codec::Encode,
|
|
||||||
SpecVersion: codec::Encode,
|
|
||||||
{
|
|
||||||
pallet_bridge_dispatch::account_ownership_digest(
|
|
||||||
wococo_call,
|
|
||||||
rococo_account_id,
|
|
||||||
wococo_spec_version,
|
|
||||||
bp_runtime::ROCOCO_CHAIN_ID,
|
|
||||||
bp_runtime::WOCOCO_CHAIN_ID,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rococo Runtime `Call` enum.
|
/// Rococo Runtime `Call` enum.
|
||||||
///
|
///
|
||||||
/// The enum represents a subset of possible `Call`s we can send to Rococo chain.
|
/// The enum represents a subset of possible `Call`s we can send to Rococo chain.
|
||||||
@@ -107,16 +83,7 @@ pub enum BridgeGrandpaWococoCall {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub enum BridgeWococoMessagesCall {
|
pub enum BridgeWococoMessagesCall {
|
||||||
#[codec(index = 3)]
|
#[codec(index = 3)]
|
||||||
send_message(
|
send_message(LaneId, Vec<u8>, bp_rococo::Balance),
|
||||||
LaneId,
|
|
||||||
bp_message_dispatch::MessagePayload<
|
|
||||||
bp_rococo::AccountId,
|
|
||||||
bp_wococo::AccountId,
|
|
||||||
bp_wococo::AccountPublic,
|
|
||||||
Vec<u8>,
|
|
||||||
>,
|
|
||||||
bp_rococo::Balance,
|
|
||||||
),
|
|
||||||
#[codec(index = 5)]
|
#[codec(index = 5)]
|
||||||
receive_messages_proof(
|
receive_messages_proof(
|
||||||
bp_wococo::AccountId,
|
bp_wococo::AccountId,
|
||||||
|
|||||||
@@ -14,13 +14,11 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive"
|
|||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
|
||||||
bp-messages = { path = "../../primitives/messages" }
|
bp-messages = { path = "../../primitives/messages" }
|
||||||
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||||
bp-rococo = { path = "../../primitives/chain-rococo" }
|
bp-rococo = { path = "../../primitives/chain-rococo" }
|
||||||
bp-runtime = { path = "../../primitives/runtime" }
|
bp-runtime = { path = "../../primitives/runtime" }
|
||||||
bp-wococo = { path = "../../primitives/chain-wococo" }
|
bp-wococo = { path = "../../primitives/chain-wococo" }
|
||||||
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
|
||||||
pallet-bridge-messages = { path = "../../modules/messages" }
|
pallet-bridge-messages = { path = "../../modules/messages" }
|
||||||
|
|
||||||
# Substrate Dependencies
|
# Substrate Dependencies
|
||||||
|
|||||||
@@ -26,30 +26,6 @@ use scale_info::TypeInfo;
|
|||||||
/// Unchecked Wococo extrinsic.
|
/// Unchecked Wococo extrinsic.
|
||||||
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
|
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
|
||||||
|
|
||||||
/// Rococo account ownership digest from Wococo.
|
|
||||||
///
|
|
||||||
/// The byte vector returned by this function should be signed with a Rococo account private key.
|
|
||||||
/// This way, the owner of `wococo_account_id` on Rococo proves that the Rococo account private key
|
|
||||||
/// is also under his control.
|
|
||||||
pub fn wococo_to_rococo_account_ownership_digest<Call, AccountId, SpecVersion>(
|
|
||||||
rococo_call: &Call,
|
|
||||||
wococo_account_id: AccountId,
|
|
||||||
rococo_spec_version: SpecVersion,
|
|
||||||
) -> Vec<u8>
|
|
||||||
where
|
|
||||||
Call: codec::Encode,
|
|
||||||
AccountId: codec::Encode,
|
|
||||||
SpecVersion: codec::Encode,
|
|
||||||
{
|
|
||||||
pallet_bridge_dispatch::account_ownership_digest(
|
|
||||||
rococo_call,
|
|
||||||
wococo_account_id,
|
|
||||||
rococo_spec_version,
|
|
||||||
bp_runtime::WOCOCO_CHAIN_ID,
|
|
||||||
bp_runtime::ROCOCO_CHAIN_ID,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wococo Runtime `Call` enum.
|
/// Wococo Runtime `Call` enum.
|
||||||
///
|
///
|
||||||
/// The enum represents a subset of possible `Call`s we can send to Rococo chain.
|
/// The enum represents a subset of possible `Call`s we can send to Rococo chain.
|
||||||
@@ -107,16 +83,7 @@ pub enum BridgeGrandpaRococoCall {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub enum BridgeRococoMessagesCall {
|
pub enum BridgeRococoMessagesCall {
|
||||||
#[codec(index = 3)]
|
#[codec(index = 3)]
|
||||||
send_message(
|
send_message(LaneId, Vec<u8>, bp_rococo::Balance),
|
||||||
LaneId,
|
|
||||||
bp_message_dispatch::MessagePayload<
|
|
||||||
bp_rococo::AccountId,
|
|
||||||
bp_wococo::AccountId,
|
|
||||||
bp_wococo::AccountPublic,
|
|
||||||
Vec<u8>,
|
|
||||||
>,
|
|
||||||
bp_rococo::Balance,
|
|
||||||
),
|
|
||||||
#[codec(index = 5)]
|
#[codec(index = 5)]
|
||||||
receive_messages_proof(
|
receive_messages_proof(
|
||||||
bp_rococo::AccountId,
|
bp_rococo::AccountId,
|
||||||
|
|||||||
Reference in New Issue
Block a user