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:
Svyatoslav Nikolsky
2022-05-04 15:05:14 +03:00
committed by Bastian Köcher
parent dc96aeea35
commit d582061dff
58 changed files with 408 additions and 7062 deletions
+5
View File
@@ -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)
-7
View File
@@ -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",
] ]
+4 -93
View File
@@ -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,
} }
} }
-4
View File
@@ -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",
+1 -38
View File
@@ -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,
},
},
);
});
}
} }
+2 -6
View File
@@ -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",
-181
View File
@@ -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.
+2 -2
View File
@@ -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>>);
+57 -323
View File
@@ -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 {
-42
View File
@@ -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",
]
-63
View File
@@ -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
-2
View File
@@ -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",
-59
View File
@@ -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
-200
View File
@@ -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)
}
-93
View File
@@ -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 _
}
}
+6 -6
View File
@@ -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);
-38
View File
@@ -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",
]
-124
View File
@@ -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),
);
}
}
-4
View File
@@ -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(), .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!( _ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}", "Unsupported target bridge pallet with instance index: {}",
bridge_instance_index bridge_instance_index
), ),
},
_ => anyhow::bail!("Unsupported Kusama call: {:?}", call),
}) })
} }
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::Call::Balances(millau_runtime::BalancesCall::transfer {
dest: recipient.raw_id(),
value: amount.cast(),
})
.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 { millau_runtime::MessagesCall::send_message {
lane_id: lane.0, lane_id: lane,
payload, payload,
delivery_and_dispatch_fee: fee.cast(), delivery_and_dispatch_fee: fee,
}, },
) )
.into() .into(),
},
_ => anyhow::bail!( _ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}", "Unsupported target bridge pallet with instance index: {}",
bridge_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,
))
},
}
}
} }
+7 -133
View File
@@ -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(), .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!( _ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}", "Unsupported target bridge pallet with instance index: {}",
bridge_instance_index bridge_instance_index
), ),
},
_ => anyhow::bail!("Unsupported Polkadot call: {:?}", call),
}) })
} }
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::Call::Balances(rialto_runtime::BalancesCall::transfer {
dest: recipient.raw_id().into(),
value: amount.0,
})
.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 { rialto_runtime::MessagesCall::send_message {
lane_id: lane.0, lane_id: lane,
payload, payload,
delivery_and_dispatch_fee: fee.0, delivery_and_dispatch_fee: fee,
}, },
) )
.into() .into(),
},
_ => anyhow::bail!( _ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}", "Unsupported target bridge pallet with instance index: {}",
bridge_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(), .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!( _ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}", "Unsupported target bridge pallet with instance index: {}",
bridge_instance_index bridge_instance_index
), ),
},
_ => anyhow::bail!("The call is not supported"),
}) })
} }
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 =>
relay_wococo_client::runtime::Call::BridgeRococoMessages(
relay_wococo_client::runtime::BridgeRococoMessagesCall::send_message(
lane, payload, fee,
), ),
) )
.into(), .into(),
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
match *bridge_instance_index {
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!( _ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}", "Unsupported target bridge pallet with instance index: {}",
bridge_instance_index bridge_instance_index
), ),
},
_ => anyhow::bail!("The call is not supported"),
}) })
} }
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,
))
},
}
}
} }
+6 -25
View File
@@ -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(
Source::max_extrinsic_size(),
Target::max_extrinsic_size(),
);
vec![42; maximal_size as usize]
},
},
}) })
}
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
let payload = self.encode()?;
println!("{:?}", payload);
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),
} }
} }
); );
+2 -110
View File
@@ -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
)),
}
);
}
}
-2
View File
@@ -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
+1 -34
View File
@@ -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
+1 -34
View File
@@ -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,
-2
View File
@@ -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
+1 -34
View File
@@ -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,
-2
View File
@@ -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
+1 -34
View File
@@ -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,