mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 18:41:03 +00:00
Remove support for encoded-call messaging from relay and runtime integration code (#1376)
* remove support for encoded-call messaging * continue cleanup * continue cleanup * continue cleanup * more cleanpup * more cleanup * fmt * continue cleanup * spellcheck * rename * fix benchmarks * mention encoded-calls-messaging tag * fixing deployments * fix messages generation * fmt
This commit is contained in:
committed by
Bastian Köcher
parent
dc96aeea35
commit
d582061dff
@@ -25,24 +25,20 @@ strum = { version = "0.21.0", features = ["derive"] }
|
||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||
bp-kusama = { path = "../../primitives/chain-kusama" }
|
||||
bp-messages = { path = "../../primitives/messages" }
|
||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
||||
bp-millau = { path = "../../primitives/chain-millau" }
|
||||
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
||||
bp-rialto = { path = "../../primitives/chain-rialto" }
|
||||
bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" }
|
||||
bp-rococo = { path = "../../primitives/chain-rococo" }
|
||||
bp-runtime = { path = "../../primitives/runtime" }
|
||||
bp-token-swap = { path = "../../primitives/token-swap" }
|
||||
bp-westend = { path = "../../primitives/chain-westend" }
|
||||
bp-wococo = { path = "../../primitives/chain-wococo" }
|
||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||
finality-relay = { path = "../finality" }
|
||||
messages-relay = { path = "../messages" }
|
||||
millau-runtime = { path = "../../bin/millau/runtime" }
|
||||
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
||||
pallet-bridge-grandpa = { path = "../../modules/grandpa" }
|
||||
pallet-bridge-messages = { path = "../../modules/messages" }
|
||||
pallet-bridge-token-swap = { path = "../../modules/token-swap" }
|
||||
relay-kusama-client = { path = "../client-kusama" }
|
||||
relay-millau-client = { path = "../client-millau" }
|
||||
relay-polkadot-client = { path = "../client-polkadot" }
|
||||
|
||||
@@ -14,82 +14,46 @@
|
||||
// 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 anyhow::anyhow;
|
||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||
use bp_messages::LaneId;
|
||||
use bp_runtime::EncodedOrDecodedCall;
|
||||
use codec::Decode;
|
||||
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
||||
use relay_kusama_client::Kusama;
|
||||
use relay_substrate_client::BalanceOf;
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
use crate::cli::{
|
||||
bridge,
|
||||
encode_call::{self, Call, CliEncodeCall},
|
||||
encode_message,
|
||||
send_message::{self, DispatchFeePayment},
|
||||
encode_message::{CliEncodeMessage, RawMessage},
|
||||
CliChain,
|
||||
};
|
||||
|
||||
/// Weight of the `system::remark` call at Kusama.
|
||||
///
|
||||
/// This weight is larger (x2) than actual weight at current Kusama runtime to avoid unsuccessful
|
||||
/// calls in the future. But since it is used only in tests (and on test chains), this is ok.
|
||||
pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000;
|
||||
|
||||
impl CliEncodeCall for Kusama {
|
||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||
Ok(match call {
|
||||
Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()),
|
||||
Call::Remark { remark_payload, .. } => relay_kusama_client::runtime::Call::System(
|
||||
relay_kusama_client::runtime::SystemCall::remark(
|
||||
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
|
||||
match *bridge_instance_index {
|
||||
bridge::KUSAMA_TO_POLKADOT_INDEX => {
|
||||
let payload = Decode::decode(&mut &*payload.0)?;
|
||||
relay_kusama_client::runtime::Call::BridgePolkadotMessages(
|
||||
relay_kusama_client::runtime::BridgePolkadotMessagesCall::send_message(
|
||||
lane.0, payload, fee.0,
|
||||
),
|
||||
)
|
||||
.into()
|
||||
},
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
impl CliEncodeMessage for Kusama {
|
||||
fn encode_send_message_call(
|
||||
lane: LaneId,
|
||||
payload: RawMessage,
|
||||
fee: BalanceOf<Self>,
|
||||
bridge_instance_index: u8,
|
||||
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||
Ok(match bridge_instance_index {
|
||||
bridge::KUSAMA_TO_POLKADOT_INDEX =>
|
||||
relay_kusama_client::runtime::Call::BridgePolkadotMessages(
|
||||
relay_kusama_client::runtime::BridgePolkadotMessagesCall::send_message(
|
||||
lane, payload, fee,
|
||||
),
|
||||
},
|
||||
_ => anyhow::bail!("Unsupported Kusama call: {:?}", call),
|
||||
)
|
||||
.into(),
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
||||
match *call {
|
||||
EncodedOrDecodedCall::Decoded(relay_kusama_client::runtime::Call::System(
|
||||
relay_kusama_client::runtime::SystemCall::remark(_),
|
||||
)) => Ok(DispatchInfo {
|
||||
weight: crate::chains::kusama::SYSTEM_REMARK_CALL_WEIGHT,
|
||||
class: DispatchClass::Normal,
|
||||
pays_fee: Pays::Yes,
|
||||
}),
|
||||
_ => anyhow::bail!("Unsupported Kusama call: {:?}", call),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CliChain for Kusama {
|
||||
const RUNTIME_VERSION: RuntimeVersion = bp_kusama::VERSION;
|
||||
|
||||
type KeyPair = sp_core::sr25519::Pair;
|
||||
type MessagePayload = MessagePayload<
|
||||
bp_kusama::AccountId,
|
||||
bp_polkadot::AccountPublic,
|
||||
bp_polkadot::Signature,
|
||||
Vec<u8>,
|
||||
>;
|
||||
type MessagePayload = Vec<u8>;
|
||||
|
||||
fn ss58_format() -> u16 {
|
||||
sp_core::crypto::Ss58AddressFormat::from(
|
||||
@@ -97,39 +61,4 @@ impl CliChain for Kusama {
|
||||
)
|
||||
.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::{
|
||||
bridge,
|
||||
encode_call::{self, Call, CliEncodeCall},
|
||||
encode_message,
|
||||
send_message::{self, DispatchFeePayment},
|
||||
encode_message::{CliEncodeMessage, RawMessage},
|
||||
CliChain,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||
use bp_messages::LaneId;
|
||||
use bp_runtime::EncodedOrDecodedCall;
|
||||
use codec::Decode;
|
||||
use frame_support::weights::{DispatchInfo, GetDispatchInfo};
|
||||
use relay_millau_client::Millau;
|
||||
use relay_substrate_client::BalanceOf;
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
impl CliEncodeCall for Millau {
|
||||
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, .. } =>
|
||||
millau_runtime::Call::System(millau_runtime::SystemCall::remark {
|
||||
remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
||||
})
|
||||
.into(),
|
||||
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 {
|
||||
lane_id: lane.0,
|
||||
payload,
|
||||
delivery_and_dispatch_fee: fee.cast(),
|
||||
},
|
||||
)
|
||||
.into()
|
||||
},
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
),
|
||||
impl CliEncodeMessage for Millau {
|
||||
fn encode_send_message_call(
|
||||
lane: LaneId,
|
||||
payload: RawMessage,
|
||||
fee: BalanceOf<Self>,
|
||||
bridge_instance_index: u8,
|
||||
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||
Ok(match bridge_instance_index {
|
||||
bridge::MILLAU_TO_RIALTO_INDEX => millau_runtime::Call::BridgeRialtoMessages(
|
||||
millau_runtime::MessagesCall::send_message {
|
||||
lane_id: lane,
|
||||
payload,
|
||||
delivery_and_dispatch_fee: fee,
|
||||
},
|
||||
)
|
||||
.into(),
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
||||
Ok(call.to_decoded()?.get_dispatch_info())
|
||||
}
|
||||
}
|
||||
|
||||
impl CliChain for Millau {
|
||||
const RUNTIME_VERSION: RuntimeVersion = millau_runtime::VERSION;
|
||||
|
||||
type KeyPair = sp_core::sr25519::Pair;
|
||||
type MessagePayload = MessagePayload<
|
||||
bp_millau::AccountId,
|
||||
bp_rialto::AccountSigner,
|
||||
bp_rialto::Signature,
|
||||
Vec<u8>,
|
||||
>;
|
||||
type MessagePayload = Vec<u8>;
|
||||
|
||||
fn ss58_format() -> u16 {
|
||||
millau_runtime::SS58Prefix::get() as u16
|
||||
}
|
||||
|
||||
// TODO [#854|#843] support multiple bridges?
|
||||
fn encode_message(
|
||||
message: encode_message::MessagePayload,
|
||||
) -> anyhow::Result<Self::MessagePayload> {
|
||||
match message {
|
||||
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
||||
.map_err(|e| anyhow!("Failed to decode Millau's MessagePayload: {:?}", e)),
|
||||
encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => {
|
||||
type Source = Millau;
|
||||
type Target = relay_rialto_client::Rialto;
|
||||
|
||||
sender.enforce_chain::<Source>();
|
||||
let spec_version = Target::RUNTIME_VERSION.spec_version;
|
||||
let origin = CallOrigin::SourceAccount(sender.raw_id());
|
||||
encode_call::preprocess_call::<Source, Target>(
|
||||
&mut call,
|
||||
bridge::MILLAU_TO_RIALTO_INDEX,
|
||||
);
|
||||
let call = Target::encode_call(&call)?;
|
||||
let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| {
|
||||
call.to_decoded().map(|call| call.get_dispatch_info().weight)
|
||||
})?;
|
||||
|
||||
Ok(send_message::message_payload(
|
||||
spec_version,
|
||||
dispatch_weight,
|
||||
origin,
|
||||
&call,
|
||||
DispatchFeePayment::AtSourceChain,
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,95 +41,28 @@ mod wococo;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::cli::{encode_call, send_message};
|
||||
use crate::cli::encode_message;
|
||||
use bp_messages::source_chain::TargetHeaderChain;
|
||||
use bp_runtime::Chain as _;
|
||||
use codec::Encode;
|
||||
use frame_support::dispatch::GetDispatchInfo;
|
||||
use relay_millau_client::Millau;
|
||||
use relay_rialto_client::Rialto;
|
||||
use relay_substrate_client::{SignParam, TransactionSignScheme, UnsignedTransaction};
|
||||
use sp_core::Pair;
|
||||
use sp_runtime::traits::{IdentifyAccount, Verify};
|
||||
|
||||
#[test]
|
||||
fn millau_signature_is_valid_on_rialto() {
|
||||
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() {
|
||||
fn maximal_rialto_to_millau_message_size_is_computed_correctly() {
|
||||
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_millau::Millau::max_extrinsic_size(),
|
||||
);
|
||||
|
||||
let call: millau_runtime::Call =
|
||||
millau_runtime::SystemCall::remark { remark: vec![42; maximal_remark_size as _] }
|
||||
.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 message = vec![42; maximal_message_size as _];
|
||||
assert_eq!(Millau::verify_message(&message), Ok(()));
|
||||
|
||||
let call: millau_runtime::Call =
|
||||
millau_runtime::SystemCall::remark { remark: vec![42; (maximal_remark_size + 1) as _] }
|
||||
.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());
|
||||
let message = vec![42; (maximal_message_size + 1) as _];
|
||||
assert!(Millau::verify_message(&message).is_err());
|
||||
}
|
||||
|
||||
#[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",
|
||||
)
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn rialto_tx_extra_bytes_constant_is_correct() {
|
||||
let rialto_call =
|
||||
|
||||
@@ -14,82 +14,46 @@
|
||||
// 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 anyhow::anyhow;
|
||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||
use bp_messages::LaneId;
|
||||
use bp_runtime::EncodedOrDecodedCall;
|
||||
use codec::Decode;
|
||||
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
||||
use relay_polkadot_client::Polkadot;
|
||||
use relay_substrate_client::BalanceOf;
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
use crate::cli::{
|
||||
bridge,
|
||||
encode_call::{self, Call, CliEncodeCall},
|
||||
encode_message,
|
||||
send_message::{self, DispatchFeePayment},
|
||||
encode_message::{CliEncodeMessage, RawMessage},
|
||||
CliChain,
|
||||
};
|
||||
|
||||
/// Weight of the `system::remark` call at Polkadot.
|
||||
///
|
||||
/// This weight is larger (x2) than actual weight at current Polkadot runtime to avoid unsuccessful
|
||||
/// calls in the future. But since it is used only in tests (and on test chains), this is ok.
|
||||
pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000;
|
||||
|
||||
impl CliEncodeCall for Polkadot {
|
||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||
Ok(match call {
|
||||
Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()),
|
||||
Call::Remark { remark_payload, .. } => relay_polkadot_client::runtime::Call::System(
|
||||
relay_polkadot_client::runtime::SystemCall::remark(
|
||||
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
|
||||
match *bridge_instance_index {
|
||||
bridge::POLKADOT_TO_KUSAMA_INDEX => {
|
||||
let payload = Decode::decode(&mut &*payload.0)?;
|
||||
relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
|
||||
relay_polkadot_client::runtime::BridgeKusamaMessagesCall::send_message(
|
||||
lane.0, payload, fee.0,
|
||||
),
|
||||
)
|
||||
.into()
|
||||
},
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
impl CliEncodeMessage for Polkadot {
|
||||
fn encode_send_message_call(
|
||||
lane: LaneId,
|
||||
payload: RawMessage,
|
||||
fee: BalanceOf<Self>,
|
||||
bridge_instance_index: u8,
|
||||
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||
Ok(match bridge_instance_index {
|
||||
bridge::POLKADOT_TO_KUSAMA_INDEX =>
|
||||
relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
|
||||
relay_polkadot_client::runtime::BridgeKusamaMessagesCall::send_message(
|
||||
lane, payload, fee,
|
||||
),
|
||||
},
|
||||
_ => anyhow::bail!("Unsupported Polkadot call: {:?}", call),
|
||||
)
|
||||
.into(),
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
||||
match *call {
|
||||
EncodedOrDecodedCall::Decoded(relay_polkadot_client::runtime::Call::System(
|
||||
relay_polkadot_client::runtime::SystemCall::remark(_),
|
||||
)) => Ok(DispatchInfo {
|
||||
weight: crate::chains::polkadot::SYSTEM_REMARK_CALL_WEIGHT,
|
||||
class: DispatchClass::Normal,
|
||||
pays_fee: Pays::Yes,
|
||||
}),
|
||||
_ => anyhow::bail!("Unsupported Polkadot call: {:?}", call),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CliChain for Polkadot {
|
||||
const RUNTIME_VERSION: RuntimeVersion = bp_polkadot::VERSION;
|
||||
|
||||
type KeyPair = sp_core::sr25519::Pair;
|
||||
type MessagePayload = MessagePayload<
|
||||
bp_polkadot::AccountId,
|
||||
bp_kusama::AccountPublic,
|
||||
bp_kusama::Signature,
|
||||
Vec<u8>,
|
||||
>;
|
||||
type MessagePayload = Vec<u8>;
|
||||
|
||||
fn ss58_format() -> u16 {
|
||||
sp_core::crypto::Ss58AddressFormat::from(
|
||||
@@ -97,39 +61,4 @@ impl CliChain for Polkadot {
|
||||
)
|
||||
.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::{
|
||||
bridge,
|
||||
encode_call::{self, Call, CliEncodeCall},
|
||||
encode_message,
|
||||
send_message::{self, DispatchFeePayment},
|
||||
encode_message::{CliEncodeMessage, RawMessage},
|
||||
CliChain,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||
use bp_messages::LaneId;
|
||||
use bp_runtime::EncodedOrDecodedCall;
|
||||
use codec::Decode;
|
||||
use frame_support::weights::{DispatchInfo, GetDispatchInfo};
|
||||
use relay_rialto_client::Rialto;
|
||||
use relay_substrate_client::BalanceOf;
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
impl CliEncodeCall for Rialto {
|
||||
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_runtime::Call::System(rialto_runtime::SystemCall::remark {
|
||||
remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
||||
})
|
||||
.into(),
|
||||
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 {
|
||||
lane_id: lane.0,
|
||||
payload,
|
||||
delivery_and_dispatch_fee: fee.0,
|
||||
},
|
||||
)
|
||||
.into()
|
||||
},
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
),
|
||||
impl CliEncodeMessage for Rialto {
|
||||
fn encode_send_message_call(
|
||||
lane: LaneId,
|
||||
payload: RawMessage,
|
||||
fee: BalanceOf<Self>,
|
||||
bridge_instance_index: u8,
|
||||
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||
Ok(match bridge_instance_index {
|
||||
bridge::RIALTO_TO_MILLAU_INDEX => rialto_runtime::Call::BridgeMillauMessages(
|
||||
rialto_runtime::MessagesCall::send_message {
|
||||
lane_id: lane,
|
||||
payload,
|
||||
delivery_and_dispatch_fee: fee,
|
||||
},
|
||||
)
|
||||
.into(),
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
||||
Ok(call.to_decoded()?.get_dispatch_info())
|
||||
}
|
||||
}
|
||||
|
||||
impl CliChain for Rialto {
|
||||
const RUNTIME_VERSION: RuntimeVersion = rialto_runtime::VERSION;
|
||||
|
||||
type KeyPair = sp_core::sr25519::Pair;
|
||||
type MessagePayload = MessagePayload<
|
||||
bp_rialto::AccountId,
|
||||
bp_millau::AccountSigner,
|
||||
bp_millau::Signature,
|
||||
Vec<u8>,
|
||||
>;
|
||||
type MessagePayload = Vec<u8>;
|
||||
|
||||
fn ss58_format() -> 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.
|
||||
|
||||
use crate::cli::{
|
||||
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 crate::cli::CliChain;
|
||||
use relay_rialto_parachain_client::RialtoParachain;
|
||||
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 {
|
||||
const RUNTIME_VERSION: RuntimeVersion = rialto_parachain_runtime::VERSION;
|
||||
|
||||
type KeyPair = sp_core::sr25519::Pair;
|
||||
type MessagePayload = MessagePayload<
|
||||
bp_rialto_parachain::AccountId,
|
||||
bp_millau::AccountSigner,
|
||||
bp_millau::Signature,
|
||||
Vec<u8>,
|
||||
>;
|
||||
type MessagePayload = Vec<u8>;
|
||||
|
||||
fn ss58_format() -> 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
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use anyhow::anyhow;
|
||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||
use bp_messages::LaneId;
|
||||
use bp_runtime::EncodedOrDecodedCall;
|
||||
use codec::Decode;
|
||||
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
||||
use relay_rococo_client::Rococo;
|
||||
use relay_substrate_client::BalanceOf;
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
use crate::cli::{
|
||||
bridge,
|
||||
encode_call::{self, Call, CliEncodeCall},
|
||||
encode_message,
|
||||
send_message::{self, DispatchFeePayment},
|
||||
encode_message::{CliEncodeMessage, RawMessage},
|
||||
CliChain,
|
||||
};
|
||||
|
||||
/// Weight of the `system::remark` call at Rococo.
|
||||
///
|
||||
/// This weight is larger (x2) than actual weight at current Rococo runtime to avoid unsuccessful
|
||||
/// calls in the future. But since it is used only in tests (and on test chains), this is ok.
|
||||
pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000;
|
||||
|
||||
impl CliEncodeCall for Rococo {
|
||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||
Ok(match call {
|
||||
Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()),
|
||||
Call::Remark { remark_payload, .. } => relay_rococo_client::runtime::Call::System(
|
||||
relay_rococo_client::runtime::SystemCall::remark(
|
||||
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
|
||||
match *bridge_instance_index {
|
||||
bridge::ROCOCO_TO_WOCOCO_INDEX => {
|
||||
let payload = Decode::decode(&mut &*payload.0)?;
|
||||
relay_rococo_client::runtime::Call::BridgeWococoMessages(
|
||||
relay_rococo_client::runtime::BridgeWococoMessagesCall::send_message(
|
||||
lane.0, payload, fee.0,
|
||||
),
|
||||
)
|
||||
.into()
|
||||
},
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
impl CliEncodeMessage for Rococo {
|
||||
fn encode_send_message_call(
|
||||
lane: LaneId,
|
||||
payload: RawMessage,
|
||||
fee: BalanceOf<Self>,
|
||||
bridge_instance_index: u8,
|
||||
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||
Ok(match bridge_instance_index {
|
||||
bridge::ROCOCO_TO_WOCOCO_INDEX =>
|
||||
relay_rococo_client::runtime::Call::BridgeWococoMessages(
|
||||
relay_rococo_client::runtime::BridgeWococoMessagesCall::send_message(
|
||||
lane, payload, fee,
|
||||
),
|
||||
},
|
||||
_ => anyhow::bail!("The call is not supported"),
|
||||
)
|
||||
.into(),
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
||||
match *call {
|
||||
EncodedOrDecodedCall::Decoded(relay_rococo_client::runtime::Call::System(
|
||||
relay_rococo_client::runtime::SystemCall::remark(_),
|
||||
)) => Ok(DispatchInfo {
|
||||
weight: SYSTEM_REMARK_CALL_WEIGHT,
|
||||
class: DispatchClass::Normal,
|
||||
pays_fee: Pays::Yes,
|
||||
}),
|
||||
_ => anyhow::bail!("Unsupported Rococo call: {:?}", call),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CliChain for Rococo {
|
||||
const RUNTIME_VERSION: RuntimeVersion = bp_rococo::VERSION;
|
||||
|
||||
type KeyPair = sp_core::sr25519::Pair;
|
||||
type MessagePayload = MessagePayload<
|
||||
bp_rococo::AccountId,
|
||||
bp_wococo::AccountPublic,
|
||||
bp_wococo::Signature,
|
||||
Vec<u8>,
|
||||
>;
|
||||
type MessagePayload = Vec<u8>;
|
||||
|
||||
fn ss58_format() -> u16 {
|
||||
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.
|
||||
|
||||
use crate::cli::{encode_message, CliChain};
|
||||
use anyhow::anyhow;
|
||||
use crate::cli::CliChain;
|
||||
use relay_westend_client::Westend;
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
@@ -25,7 +24,7 @@ impl CliChain for Westend {
|
||||
const RUNTIME_VERSION: RuntimeVersion = bp_westend::VERSION;
|
||||
|
||||
type KeyPair = sp_core::sr25519::Pair;
|
||||
type MessagePayload = ();
|
||||
type MessagePayload = Vec<u8>;
|
||||
|
||||
fn ss58_format() -> u16 {
|
||||
sp_core::crypto::Ss58AddressFormat::from(
|
||||
@@ -33,10 +32,4 @@ impl CliChain for Westend {
|
||||
)
|
||||
.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
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use anyhow::anyhow;
|
||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||
use bp_messages::LaneId;
|
||||
use bp_runtime::EncodedOrDecodedCall;
|
||||
use codec::Decode;
|
||||
use frame_support::weights::{DispatchClass, DispatchInfo, Pays};
|
||||
use relay_substrate_client::BalanceOf;
|
||||
use relay_wococo_client::Wococo;
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
use crate::cli::{
|
||||
bridge,
|
||||
encode_call::{self, Call, CliEncodeCall},
|
||||
encode_message,
|
||||
send_message::{self, DispatchFeePayment},
|
||||
encode_message::{CliEncodeMessage, RawMessage},
|
||||
CliChain,
|
||||
};
|
||||
|
||||
impl CliEncodeCall for Wococo {
|
||||
fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
|
||||
Ok(match call {
|
||||
Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()),
|
||||
Call::Remark { remark_payload, .. } => relay_wococo_client::runtime::Call::System(
|
||||
relay_wococo_client::runtime::SystemCall::remark(
|
||||
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } =>
|
||||
match *bridge_instance_index {
|
||||
bridge::WOCOCO_TO_ROCOCO_INDEX => {
|
||||
let payload = Decode::decode(&mut &*payload.0)?;
|
||||
relay_wococo_client::runtime::Call::BridgeRococoMessages(
|
||||
relay_wococo_client::runtime::BridgeRococoMessagesCall::send_message(
|
||||
lane.0, payload, fee.0,
|
||||
),
|
||||
)
|
||||
.into()
|
||||
},
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
impl CliEncodeMessage for Wococo {
|
||||
fn encode_send_message_call(
|
||||
lane: LaneId,
|
||||
payload: RawMessage,
|
||||
fee: BalanceOf<Self>,
|
||||
bridge_instance_index: u8,
|
||||
) -> 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,
|
||||
),
|
||||
},
|
||||
_ => anyhow::bail!("The call is not supported"),
|
||||
)
|
||||
.into(),
|
||||
_ => anyhow::bail!(
|
||||
"Unsupported target bridge pallet with instance index: {}",
|
||||
bridge_instance_index
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> {
|
||||
match *call {
|
||||
EncodedOrDecodedCall::Decoded(relay_wococo_client::runtime::Call::System(
|
||||
relay_wococo_client::runtime::SystemCall::remark(_),
|
||||
)) => Ok(DispatchInfo {
|
||||
weight: crate::chains::rococo::SYSTEM_REMARK_CALL_WEIGHT,
|
||||
class: DispatchClass::Normal,
|
||||
pays_fee: Pays::Yes,
|
||||
}),
|
||||
_ => anyhow::bail!("Unsupported Wococo call: {:?}", call),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CliChain for Wococo {
|
||||
const RUNTIME_VERSION: RuntimeVersion = bp_wococo::VERSION;
|
||||
|
||||
type KeyPair = sp_core::sr25519::Pair;
|
||||
type MessagePayload = MessagePayload<
|
||||
bp_wococo::AccountId,
|
||||
bp_rococo::AccountPublic,
|
||||
bp_rococo::Signature,
|
||||
Vec<u8>,
|
||||
>;
|
||||
type MessagePayload = Vec<u8>;
|
||||
|
||||
fn ss58_format() -> u16 {
|
||||
42
|
||||
}
|
||||
|
||||
fn encode_message(
|
||||
message: encode_message::MessagePayload,
|
||||
) -> anyhow::Result<Self::MessagePayload> {
|
||||
match message {
|
||||
encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0)
|
||||
.map_err(|e| anyhow!("Failed to decode Wococo's MessagePayload: {:?}", e)),
|
||||
encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => {
|
||||
type Source = Wococo;
|
||||
type Target = relay_rococo_client::Rococo;
|
||||
|
||||
sender.enforce_chain::<Source>();
|
||||
let spec_version = Target::RUNTIME_VERSION.spec_version;
|
||||
let origin = CallOrigin::SourceAccount(sender.raw_id());
|
||||
encode_call::preprocess_call::<Source, Target>(
|
||||
&mut call,
|
||||
bridge::WOCOCO_TO_ROCOCO_INDEX,
|
||||
);
|
||||
let call = Target::encode_call(&call)?;
|
||||
let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| {
|
||||
Err(anyhow::format_err!(
|
||||
"Please specify dispatch weight of the encoded Rococo call"
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(send_message::message_payload(
|
||||
spec_version,
|
||||
dispatch_weight,
|
||||
origin,
|
||||
&call,
|
||||
DispatchFeePayment::AtSourceChain,
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,12 +73,9 @@ macro_rules! select_full_bridge {
|
||||
// Send-message / Estimate-fee
|
||||
#[allow(unused_imports)]
|
||||
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
|
||||
}
|
||||
},
|
||||
FullBridge::RialtoToMillau => {
|
||||
type Source = relay_rialto_client::Rialto;
|
||||
#[allow(dead_code)]
|
||||
@@ -96,12 +93,8 @@ macro_rules! select_full_bridge {
|
||||
#[allow(unused_imports)]
|
||||
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
|
||||
}
|
||||
},
|
||||
FullBridge::RococoToWococo => {
|
||||
type Source = relay_rococo_client::Rococo;
|
||||
#[allow(dead_code)]
|
||||
@@ -118,12 +111,9 @@ macro_rules! select_full_bridge {
|
||||
// Send-message / Estimate-fee
|
||||
#[allow(unused_imports)]
|
||||
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
|
||||
}
|
||||
},
|
||||
FullBridge::WococoToRococo => {
|
||||
type Source = relay_wococo_client::Wococo;
|
||||
#[allow(dead_code)]
|
||||
@@ -140,12 +130,9 @@ macro_rules! select_full_bridge {
|
||||
// Send-message / Estimate-fee
|
||||
#[allow(unused_imports)]
|
||||
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
|
||||
}
|
||||
},
|
||||
FullBridge::KusamaToPolkadot => {
|
||||
type Source = relay_kusama_client::Kusama;
|
||||
#[allow(dead_code)]
|
||||
@@ -162,12 +149,9 @@ macro_rules! select_full_bridge {
|
||||
// Send-message / Estimate-fee
|
||||
#[allow(unused_imports)]
|
||||
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
|
||||
}
|
||||
},
|
||||
FullBridge::PolkadotToKusama => {
|
||||
type Source = relay_polkadot_client::Polkadot;
|
||||
#[allow(dead_code)]
|
||||
@@ -184,12 +168,9 @@ macro_rules! select_full_bridge {
|
||||
// Send-message / Estimate-fee
|
||||
#[allow(unused_imports)]
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
cli::{bridge::FullBridge, AccountId, CliChain, HexBytes},
|
||||
select_full_bridge,
|
||||
};
|
||||
use frame_support::weights::Weight;
|
||||
use crate::cli::{ExplicitOrMaximal, HexBytes};
|
||||
use bp_messages::LaneId;
|
||||
use bp_runtime::EncodedOrDecodedCall;
|
||||
use relay_substrate_client::Chain;
|
||||
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)]
|
||||
pub enum MessagePayload {
|
||||
/// Raw, SCALE-encoded `MessagePayload`.
|
||||
pub enum Message {
|
||||
/// Raw bytes for the message.
|
||||
Raw {
|
||||
/// Hex-encoded SCALE data.
|
||||
/// Raw message bytes.
|
||||
data: HexBytes,
|
||||
},
|
||||
/// Construct message to send over the bridge.
|
||||
Call {
|
||||
/// Message details.
|
||||
#[structopt(flatten)]
|
||||
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>,
|
||||
/// Message with given size.
|
||||
Sized {
|
||||
/// Sized of the message.
|
||||
size: ExplicitOrMaximal<u32>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A `MessagePayload` to encode.
|
||||
#[derive(StructOpt)]
|
||||
pub struct EncodeMessage {
|
||||
/// A bridge instance to initialize.
|
||||
#[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)]
|
||||
bridge: FullBridge,
|
||||
#[structopt(flatten)]
|
||||
payload: MessagePayload,
|
||||
/// Raw, SCALE-encoded message payload used in expected deployment.
|
||||
pub type RawMessage = Vec<u8>;
|
||||
|
||||
pub trait CliEncodeMessage: Chain {
|
||||
/// Encode a send message call.
|
||||
fn encode_send_message_call(
|
||||
lane: LaneId,
|
||||
message: RawMessage,
|
||||
fee: Self::Balance,
|
||||
bridge_instance_index: u8,
|
||||
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>>;
|
||||
}
|
||||
|
||||
impl EncodeMessage {
|
||||
/// Run the command.
|
||||
pub fn encode(self) -> anyhow::Result<HexBytes> {
|
||||
select_full_bridge!(self.bridge, {
|
||||
let payload =
|
||||
Source::encode_message(self.payload).map_err(|e| anyhow::format_err!("{}", e))?;
|
||||
Ok(HexBytes::encode(&payload))
|
||||
})
|
||||
}
|
||||
|
||||
/// Run the command.
|
||||
pub async fn run(self) -> anyhow::Result<()> {
|
||||
let payload = self.encode()?;
|
||||
println!("{:?}", payload);
|
||||
Ok(())
|
||||
}
|
||||
/// Encode message payload passed through CLI flags.
|
||||
pub(crate) fn encode_message<Source: Chain, Target: Chain>(
|
||||
message: &Message,
|
||||
) -> anyhow::Result<RawMessage> {
|
||||
Ok(match message {
|
||||
Message::Raw { ref data } => data.0.clone(),
|
||||
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]
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sp_core::crypto::Ss58Codec;
|
||||
/// Compute maximal message size, given max extrinsic size at source and target chains.
|
||||
pub(crate) fn compute_maximal_message_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_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]
|
||||
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");
|
||||
}
|
||||
maximal_message_size
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use crate::{
|
||||
cli::{
|
||||
bridge::FullBridge, relay_headers_and_messages::CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO,
|
||||
Balance, CliChain, HexBytes, HexLaneId, SourceConnectionParams,
|
||||
Balance, HexBytes, HexLaneId, SourceConnectionParams,
|
||||
},
|
||||
select_full_bridge,
|
||||
};
|
||||
@@ -48,7 +48,7 @@ pub struct EstimateFee {
|
||||
conversion_rate_override: Option<ConversionRateOverride>,
|
||||
/// Payload to send over the bridge.
|
||||
#[structopt(flatten)]
|
||||
payload: crate::cli::encode_message::MessagePayload,
|
||||
payload: crate::cli::encode_message::Message,
|
||||
}
|
||||
|
||||
/// A way to override conversion rate between bridge tokens.
|
||||
@@ -82,8 +82,8 @@ impl EstimateFee {
|
||||
select_full_bridge!(bridge, {
|
||||
let source_client = source.to_client::<Source>().await?;
|
||||
let lane = lane.into();
|
||||
let payload =
|
||||
Source::encode_message(payload).map_err(|e| anyhow::format_err!("{:?}", e))?;
|
||||
let payload = crate::cli::encode_message::encode_message::<Source, Target>(&payload)
|
||||
.map_err(|e| anyhow::format_err!("{:?}", e))?;
|
||||
|
||||
let fee = estimate_message_delivery_and_dispatch_fee::<Source, Target, _>(
|
||||
&source_client,
|
||||
@@ -219,14 +219,10 @@ async fn do_estimate_message_delivery_and_dispatch_fee<Source: Chain, P: Encode>
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cli::{encode_call, RuntimeVersionType, SourceRuntimeVersionParams};
|
||||
use sp_core::crypto::Ss58Codec;
|
||||
use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams};
|
||||
|
||||
#[test]
|
||||
fn should_parse_cli_options() {
|
||||
// given
|
||||
let alice = sp_keyring::AccountKeyring::Alice.to_account_id().to_ss58check();
|
||||
|
||||
// when
|
||||
let res = EstimateFee::from_iter(vec![
|
||||
"estimate_fee",
|
||||
@@ -235,13 +231,7 @@ mod tests {
|
||||
"1234",
|
||||
"--conversion-rate-override",
|
||||
"42.5",
|
||||
"call",
|
||||
"--sender",
|
||||
&alice,
|
||||
"--dispatch-weight",
|
||||
"42",
|
||||
"remark",
|
||||
"--remark-payload",
|
||||
"raw",
|
||||
"1234",
|
||||
]);
|
||||
|
||||
@@ -262,13 +252,8 @@ mod tests {
|
||||
source_transaction_version: None,
|
||||
}
|
||||
},
|
||||
payload: crate::cli::encode_message::MessagePayload::Call {
|
||||
sender: alice.parse().unwrap(),
|
||||
call: encode_call::Call::Remark {
|
||||
remark_payload: Some(HexBytes(vec![0x12, 0x34])),
|
||||
remark_size: None,
|
||||
},
|
||||
dispatch_weight: Some(42),
|
||||
payload: crate::cli::encode_message::Message::Raw {
|
||||
data: HexBytes(vec![0x12, 0x34])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -20,19 +20,16 @@ use std::convert::TryInto;
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use relay_substrate_client::ChainRuntimeVersion;
|
||||
use sp_runtime::app_crypto::Ss58Codec;
|
||||
use structopt::{clap::arg_enum, StructOpt};
|
||||
use strum::{EnumString, EnumVariantNames};
|
||||
|
||||
use bp_messages::LaneId;
|
||||
|
||||
pub(crate) mod bridge;
|
||||
pub(crate) mod encode_call;
|
||||
pub(crate) mod encode_message;
|
||||
pub(crate) mod estimate_fee;
|
||||
pub(crate) mod send_message;
|
||||
|
||||
mod derive_account;
|
||||
mod init_bridge;
|
||||
mod register_parachain;
|
||||
mod reinit_bridge;
|
||||
@@ -40,7 +37,6 @@ mod relay_headers;
|
||||
mod relay_headers_and_messages;
|
||||
mod relay_messages;
|
||||
mod resubmit_transactions;
|
||||
mod swap_tokens;
|
||||
|
||||
/// Parse relay CLI args.
|
||||
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
|
||||
/// there.
|
||||
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.
|
||||
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.
|
||||
ResubmitTransactions(resubmit_transactions::ResubmitTransactions),
|
||||
/// Swap tokens using token-swap bridge.
|
||||
SwapTokens(swap_tokens::SwapTokens),
|
||||
/// Register parachain.
|
||||
RegisterParachain(register_parachain::RegisterParachain),
|
||||
}
|
||||
@@ -134,12 +115,8 @@ impl Command {
|
||||
Self::InitBridge(arg) => arg.run().await?,
|
||||
Self::ReinitBridge(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::DeriveAccount(arg) => arg.run().await?,
|
||||
Self::ResubmitTransactions(arg) => arg.run().await?,
|
||||
Self::SwapTokens(arg) => arg.run().await?,
|
||||
Self::RegisterParachain(arg) => arg.run().await?,
|
||||
}
|
||||
Ok(())
|
||||
@@ -184,66 +161,7 @@ impl Balance {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic account id with custom parser.
|
||||
#[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.
|
||||
// Bridge-supported network definition.
|
||||
///
|
||||
/// Used to abstract away CLI commands.
|
||||
pub trait CliChain: relay_substrate_client::Chain {
|
||||
@@ -262,11 +180,6 @@ pub trait CliChain: relay_substrate_client::Chain {
|
||||
|
||||
/// Numeric value of SS58 format.
|
||||
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.
|
||||
@@ -623,29 +536,8 @@ declare_chain_options!(Parachain, parachain);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use sp_core::Pair;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[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)
|
||||
}
|
||||
use sp_core::Pair;
|
||||
|
||||
#[test]
|
||||
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/>.
|
||||
|
||||
use crate::cli::{
|
||||
swap_tokens::wait_until_transaction_is_finalized, Balance, ParachainConnectionParams,
|
||||
RelaychainConnectionParams, RelaychainSigningParams,
|
||||
Balance, ParachainConnectionParams, RelaychainConnectionParams, RelaychainSigningParams,
|
||||
};
|
||||
|
||||
use codec::Encode;
|
||||
@@ -30,7 +29,8 @@ use polkadot_runtime_common::{
|
||||
};
|
||||
use polkadot_runtime_parachains::paras::ParaLifecycle;
|
||||
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 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.
|
||||
async fn wait_para_state<Relaychain: Chain>(
|
||||
relay_client: &Client<Relaychain>,
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::{
|
||||
polkadot_headers_to_kusama::PolkadotFinalityToKusama,
|
||||
},
|
||||
cli::{
|
||||
swap_tokens::wait_until_transaction_is_finalized, SourceConnectionParams,
|
||||
register_parachain::wait_until_transaction_is_finalized, SourceConnectionParams,
|
||||
TargetConnectionParams, TargetSigningParams,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -16,18 +16,14 @@
|
||||
|
||||
use crate::cli::{
|
||||
bridge::FullBridge,
|
||||
encode_call::{self, CliEncodeCall},
|
||||
encode_message::{self, CliEncodeMessage},
|
||||
estimate_fee::{estimate_message_delivery_and_dispatch_fee, ConversionRateOverride},
|
||||
Balance, ExplicitOrMaximal, HexBytes, HexLaneId, Origins, SourceConnectionParams,
|
||||
SourceSigningParams, TargetConnectionParams, TargetSigningParams,
|
||||
Balance, HexBytes, HexLaneId, SourceConnectionParams, SourceSigningParams,
|
||||
};
|
||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||
use bp_runtime::Chain as _;
|
||||
use codec::Encode;
|
||||
use frame_support::weights::Weight;
|
||||
use relay_substrate_client::{Chain, SignParam, TransactionSignScheme, UnsignedTransaction};
|
||||
use sp_core::{Bytes, Pair};
|
||||
use sp_runtime::{traits::IdentifyAccount, AccountId32, MultiSignature, MultiSigner};
|
||||
use sp_runtime::AccountId32;
|
||||
use std::fmt::Debug;
|
||||
use structopt::StructOpt;
|
||||
use strum::{EnumString, EnumVariantNames, VariantNames};
|
||||
@@ -61,8 +57,6 @@ pub struct SendMessage {
|
||||
source: SourceConnectionParams,
|
||||
#[structopt(flatten)]
|
||||
source_sign: SourceSigningParams,
|
||||
#[structopt(flatten)]
|
||||
target_sign: TargetSigningParams,
|
||||
/// Hex-encoded lane id. Defaults to `00000000`.
|
||||
#[structopt(long, default_value = "00000000")]
|
||||
lane: HexLaneId,
|
||||
@@ -72,104 +66,20 @@ pub struct SendMessage {
|
||||
/// your message won't be relayed.
|
||||
#[structopt(long)]
|
||||
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
|
||||
/// automatically.
|
||||
#[structopt(long)]
|
||||
fee: Option<Balance>,
|
||||
/// Message type.
|
||||
#[structopt(subcommand)]
|
||||
message: crate::cli::encode_call::Call,
|
||||
/// 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,
|
||||
message: crate::cli::encode_message::Message,
|
||||
}
|
||||
|
||||
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.
|
||||
pub async fn run(mut self) -> anyhow::Result<()> {
|
||||
pub async fn run(self) -> anyhow::Result<()> {
|
||||
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_sign = self.source_sign.to_keypair::<Source>()?;
|
||||
@@ -189,14 +99,13 @@ impl SendMessage {
|
||||
.await? as _,
|
||||
),
|
||||
};
|
||||
let dispatch_weight = payload.weight;
|
||||
let payload_len = payload.encode().len();
|
||||
let send_message_call = Source::encode_call(&encode_call::Call::BridgeSendMessage {
|
||||
bridge_instance_index: self.bridge.bridge_instance_index(),
|
||||
lane: self.lane,
|
||||
payload: HexBytes::encode(&payload),
|
||||
fee,
|
||||
})?;
|
||||
let send_message_call = Source::encode_send_message_call(
|
||||
self.lane.0,
|
||||
payload,
|
||||
fee.cast().into(),
|
||||
self.bridge.bridge_instance_index(),
|
||||
)?;
|
||||
|
||||
let source_genesis_hash = *source_client.genesis_hash();
|
||||
let (spec_version, transaction_version) =
|
||||
@@ -228,11 +137,10 @@ impl SendMessage {
|
||||
|
||||
log::info!(
|
||||
target: "bridge",
|
||||
"Sending message to {}. Lane: {:?}. Size: {}. Dispatch weight: {}. Fee: {}",
|
||||
"Sending message to {}. Lane: {:?}. Size: {}. Fee: {}",
|
||||
Target::NAME,
|
||||
lane,
|
||||
payload_len,
|
||||
dispatch_weight,
|
||||
fee,
|
||||
);
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cli::CliChain;
|
||||
use hex_literal::hex;
|
||||
use crate::cli::ExplicitOrMaximal;
|
||||
|
||||
#[async_std::test]
|
||||
async fn send_remark_rialto_to_millau() {
|
||||
#[test]
|
||||
fn send_raw_rialto_to_millau() {
|
||||
// given
|
||||
let mut send_message = SendMessage::from_iter(vec![
|
||||
let send_message = SendMessage::from_iter(vec![
|
||||
"send-message",
|
||||
"rialto-to-millau",
|
||||
"--source-port",
|
||||
@@ -328,117 +185,48 @@ mod tests {
|
||||
"//Alice",
|
||||
"--conversion-rate-override",
|
||||
"0.75",
|
||||
"remark",
|
||||
"--remark-payload",
|
||||
"1234",
|
||||
"raw",
|
||||
"dead",
|
||||
]);
|
||||
|
||||
// when
|
||||
let payload = send_message.encode_payload().await.unwrap();
|
||||
|
||||
// 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!(
|
||||
payload,
|
||||
MessagePayload {
|
||||
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(),
|
||||
}
|
||||
send_message.conversion_rate_override,
|
||||
Some(ConversionRateOverride::Explicit(0.75))
|
||||
);
|
||||
}
|
||||
|
||||
#[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!(
|
||||
payload,
|
||||
MessagePayload {
|
||||
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(),
|
||||
}
|
||||
send_message.message,
|
||||
crate::cli::encode_message::Message::Raw { data: HexBytes(vec![0xDE, 0xAD]) }
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accepts_send_message_command_without_target_sign_options() {
|
||||
fn send_sized_rialto_to_millau() {
|
||||
// given
|
||||
let send_message = SendMessage::from_iter_safe(vec![
|
||||
let send_message = SendMessage::from_iter(vec![
|
||||
"send-message",
|
||||
"rialto-to-millau",
|
||||
"--source-port",
|
||||
"1234",
|
||||
"--source-signer",
|
||||
"//Alice",
|
||||
"--origin",
|
||||
"Target",
|
||||
"remark",
|
||||
"--remark-payload",
|
||||
"1234",
|
||||
"--conversion-rate-override",
|
||||
"metric",
|
||||
"sized",
|
||||
"max",
|
||||
]);
|
||||
|
||||
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
|
||||
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!(
|
||||
payload.dispatch_fee_payment,
|
||||
bp_runtime::messages::DispatchFeePayment::AtTargetChain
|
||||
send_message.message,
|
||||
crate::cli::encode_message::Message::Sized { size: ExplicitOrMaximal::Maximal }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,869 +0,0 @@
|
||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tokens swap using token-swap bridge pallet.
|
||||
|
||||
// TokenSwapBalances fields are never directly accessed, but the whole struct is printed
|
||||
// to show token swap progress
|
||||
#![allow(dead_code)]
|
||||
|
||||
use codec::Encode;
|
||||
use num_traits::One;
|
||||
use rand::random;
|
||||
use structopt::StructOpt;
|
||||
use strum::{EnumString, EnumVariantNames, VariantNames};
|
||||
|
||||
use frame_support::dispatch::GetDispatchInfo;
|
||||
use relay_substrate_client::{
|
||||
AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, CallOf, Chain, ChainWithBalances,
|
||||
Client, Error as SubstrateError, HashOf, SignParam, SignatureOf, Subscription,
|
||||
TransactionSignScheme, TransactionStatusOf, UnsignedTransaction,
|
||||
};
|
||||
use sp_core::{blake2_256, storage::StorageKey, Bytes, Pair, U256};
|
||||
use sp_runtime::traits::{Convert, Header as HeaderT};
|
||||
|
||||
use crate::cli::{
|
||||
estimate_fee::ConversionRateOverride, Balance, CliChain, SourceConnectionParams,
|
||||
SourceSigningParams, TargetConnectionParams, TargetSigningParams,
|
||||
};
|
||||
|
||||
/// Swap tokens.
|
||||
#[derive(StructOpt, Debug, PartialEq)]
|
||||
pub struct SwapTokens {
|
||||
/// A bridge instance to use in token swap.
|
||||
#[structopt(possible_values = SwapTokensBridge::VARIANTS, case_insensitive = true)]
|
||||
bridge: SwapTokensBridge,
|
||||
|
||||
#[structopt(flatten)]
|
||||
source: SourceConnectionParams,
|
||||
#[structopt(flatten)]
|
||||
source_sign: SourceSigningParams,
|
||||
|
||||
#[structopt(flatten)]
|
||||
target: TargetConnectionParams,
|
||||
#[structopt(flatten)]
|
||||
target_sign: TargetSigningParams,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
swap_type: TokenSwapType,
|
||||
/// Source chain balance that source signer wants to swap.
|
||||
#[structopt(long)]
|
||||
source_balance: Balance,
|
||||
/// Target chain balance that target signer wants to swap.
|
||||
#[structopt(long)]
|
||||
target_balance: Balance,
|
||||
/// A way to override conversion rate from target to source tokens.
|
||||
///
|
||||
/// If not specified, conversion rate from runtime storage is used. It may be obsolete and
|
||||
/// your message won't be relayed.
|
||||
#[structopt(long)]
|
||||
target_to_source_conversion_rate_override: Option<ConversionRateOverride>,
|
||||
/// A way to override conversion rate from source to target tokens.
|
||||
///
|
||||
/// If not specified, conversion rate from runtime storage is used. It may be obsolete and
|
||||
/// your message won't be relayed.
|
||||
#[structopt(long)]
|
||||
source_to_target_conversion_rate_override: Option<ConversionRateOverride>,
|
||||
}
|
||||
|
||||
/// Token swap type.
|
||||
#[derive(StructOpt, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum TokenSwapType {
|
||||
/// The `target_sign` is temporary and only have funds for single swap.
|
||||
NoLock,
|
||||
/// This swap type prevents `source_signer` from restarting the swap after it has been
|
||||
/// completed.
|
||||
LockUntilBlock {
|
||||
/// Number of blocks before the swap expires.
|
||||
#[structopt(long)]
|
||||
blocks_before_expire: u32,
|
||||
/// Unique swap nonce.
|
||||
#[structopt(long)]
|
||||
swap_nonce: Option<U256>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Swap tokens bridge.
|
||||
#[derive(Debug, EnumString, EnumVariantNames, PartialEq)]
|
||||
#[strum(serialize_all = "kebab_case")]
|
||||
pub enum SwapTokensBridge {
|
||||
/// Use token-swap pallet deployed at Millau to swap tokens with Rialto.
|
||||
MillauToRialto,
|
||||
}
|
||||
|
||||
macro_rules! select_bridge {
|
||||
($bridge: expr, $generic: tt) => {
|
||||
match $bridge {
|
||||
SwapTokensBridge::MillauToRialto => {
|
||||
type Source = relay_millau_client::Millau;
|
||||
type Target = relay_rialto_client::Rialto;
|
||||
const SOURCE_SPEC_VERSION: u32 = millau_runtime::VERSION.spec_version;
|
||||
const TARGET_SPEC_VERSION: u32 = rialto_runtime::VERSION.spec_version;
|
||||
|
||||
type FromSwapToThisAccountIdConverter = bp_rialto::AccountIdConverter;
|
||||
|
||||
use bp_millau::{
|
||||
derive_account_from_rialto_id as derive_source_account_from_target_account,
|
||||
TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_TARGET_TO_SOURCE_MESSAGE_FEE_METHOD,
|
||||
WITH_RIALTO_TOKEN_SWAP_PALLET_NAME as TOKEN_SWAP_PALLET_NAME,
|
||||
};
|
||||
use bp_rialto::{
|
||||
derive_account_from_millau_id as derive_target_account_from_source_account,
|
||||
TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_SOURCE_TO_TARGET_MESSAGE_FEE_METHOD,
|
||||
};
|
||||
|
||||
const SOURCE_CHAIN_ID: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID;
|
||||
const TARGET_CHAIN_ID: bp_runtime::ChainId = bp_runtime::RIALTO_CHAIN_ID;
|
||||
|
||||
const SOURCE_TO_TARGET_LANE_ID: bp_messages::LaneId = *b"swap";
|
||||
const TARGET_TO_SOURCE_LANE_ID: bp_messages::LaneId = [0, 0, 0, 0];
|
||||
|
||||
$generic
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl SwapTokens {
|
||||
/// Run the command.
|
||||
pub async fn run(self) -> anyhow::Result<()> {
|
||||
select_bridge!(self.bridge, {
|
||||
let source_client = self.source.to_client::<Source>().await?;
|
||||
let source_sign = self.source_sign.to_keypair::<Target>()?;
|
||||
let target_client = self.target.to_client::<Target>().await?;
|
||||
let target_sign = self.target_sign.to_keypair::<Target>()?;
|
||||
let target_to_source_conversion_rate_override =
|
||||
self.target_to_source_conversion_rate_override;
|
||||
let source_to_target_conversion_rate_override =
|
||||
self.source_to_target_conversion_rate_override;
|
||||
|
||||
// names of variables in this function are matching names used by the
|
||||
// `pallet-bridge-token-swap`
|
||||
|
||||
// prepare token swap intention
|
||||
let token_swap = self
|
||||
.prepare_token_swap::<Source, Target>(&source_client, &source_sign, &target_sign)
|
||||
.await?;
|
||||
|
||||
// group all accounts that will be used later
|
||||
let accounts = TokenSwapAccounts {
|
||||
source_account_at_bridged_chain: derive_target_account_from_source_account(
|
||||
bp_runtime::SourceAccount::Account(
|
||||
token_swap.source_account_at_this_chain.clone(),
|
||||
),
|
||||
),
|
||||
target_account_at_this_chain: derive_source_account_from_target_account(
|
||||
bp_runtime::SourceAccount::Account(
|
||||
token_swap.target_account_at_bridged_chain.clone(),
|
||||
),
|
||||
),
|
||||
source_account_at_this_chain: token_swap.source_account_at_this_chain.clone(),
|
||||
target_account_at_bridged_chain: token_swap.target_account_at_bridged_chain.clone(),
|
||||
swap_account: FromSwapToThisAccountIdConverter::convert(
|
||||
token_swap.using_encoded(blake2_256).into(),
|
||||
),
|
||||
};
|
||||
|
||||
// account balances are used to demonstrate what's happening :)
|
||||
let initial_balances =
|
||||
read_account_balances(&accounts, &source_client, &target_client).await?;
|
||||
|
||||
// before calling something that may fail, log what we're trying to do
|
||||
log::info!(target: "bridge", "Starting swap: {:?}", token_swap);
|
||||
log::info!(target: "bridge", "Swap accounts: {:?}", accounts);
|
||||
log::info!(target: "bridge", "Initial account balances: {:?}", initial_balances);
|
||||
|
||||
//
|
||||
// Step 1: swap is created
|
||||
//
|
||||
|
||||
// prepare `Currency::transfer` call that will happen at the target chain
|
||||
let bridged_currency_transfer: CallOf<Target> = pallet_balances::Call::transfer {
|
||||
dest: accounts.source_account_at_bridged_chain.clone().into(),
|
||||
value: token_swap.target_balance_at_bridged_chain,
|
||||
}
|
||||
.into();
|
||||
let bridged_currency_transfer_weight =
|
||||
bridged_currency_transfer.get_dispatch_info().weight;
|
||||
|
||||
// sign message
|
||||
let bridged_chain_spec_version = TARGET_SPEC_VERSION;
|
||||
let signature_payload = pallet_bridge_dispatch::account_ownership_digest(
|
||||
&bridged_currency_transfer,
|
||||
&accounts.swap_account,
|
||||
&bridged_chain_spec_version,
|
||||
SOURCE_CHAIN_ID,
|
||||
TARGET_CHAIN_ID,
|
||||
);
|
||||
let bridged_currency_transfer_signature: SignatureOf<Target> =
|
||||
target_sign.sign(&signature_payload).into();
|
||||
|
||||
// prepare `create_swap` call
|
||||
let target_public_at_bridged_chain: AccountPublicOf<Target> =
|
||||
target_sign.public().into();
|
||||
let swap_delivery_and_dispatch_fee =
|
||||
crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee::<
|
||||
Source,
|
||||
Target,
|
||||
_,
|
||||
>(
|
||||
&source_client,
|
||||
target_to_source_conversion_rate_override,
|
||||
ESTIMATE_SOURCE_TO_TARGET_MESSAGE_FEE_METHOD,
|
||||
SOURCE_TO_TARGET_LANE_ID,
|
||||
bp_message_dispatch::MessagePayload {
|
||||
spec_version: TARGET_SPEC_VERSION,
|
||||
weight: bridged_currency_transfer_weight,
|
||||
origin: bp_message_dispatch::CallOrigin::TargetAccount(
|
||||
accounts.swap_account.clone(),
|
||||
target_public_at_bridged_chain.clone(),
|
||||
bridged_currency_transfer_signature.clone(),
|
||||
),
|
||||
dispatch_fee_payment:
|
||||
bp_runtime::messages::DispatchFeePayment::AtTargetChain,
|
||||
call: bridged_currency_transfer.encode(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let create_swap_call: CallOf<Source> = pallet_bridge_token_swap::Call::create_swap {
|
||||
swap: token_swap.clone(),
|
||||
swap_creation_params: Box::new(bp_token_swap::TokenSwapCreation {
|
||||
target_public_at_bridged_chain,
|
||||
swap_delivery_and_dispatch_fee,
|
||||
bridged_chain_spec_version,
|
||||
bridged_currency_transfer: bridged_currency_transfer.encode(),
|
||||
bridged_currency_transfer_weight,
|
||||
bridged_currency_transfer_signature,
|
||||
}),
|
||||
}
|
||||
.into();
|
||||
|
||||
// start tokens swap
|
||||
let source_genesis_hash = *source_client.genesis_hash();
|
||||
let create_swap_signer = source_sign.clone();
|
||||
let (spec_version, transaction_version) =
|
||||
source_client.simple_runtime_version().await?;
|
||||
let swap_created_at = wait_until_transaction_is_finalized::<Source>(
|
||||
source_client
|
||||
.submit_and_watch_signed_extrinsic(
|
||||
accounts.source_account_at_this_chain.clone(),
|
||||
move |_, transaction_nonce| {
|
||||
Ok(Bytes(
|
||||
Source::sign_transaction(SignParam {
|
||||
spec_version,
|
||||
transaction_version,
|
||||
genesis_hash: source_genesis_hash,
|
||||
signer: create_swap_signer,
|
||||
era: relay_substrate_client::TransactionEra::immortal(),
|
||||
unsigned: UnsignedTransaction::new(
|
||||
create_swap_call.into(),
|
||||
transaction_nonce,
|
||||
),
|
||||
})?
|
||||
.encode(),
|
||||
))
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// read state of swap after it has been created
|
||||
let token_swap_hash = token_swap.hash();
|
||||
let token_swap_storage_key = bp_token_swap::storage_keys::pending_swaps_key(
|
||||
TOKEN_SWAP_PALLET_NAME,
|
||||
token_swap_hash,
|
||||
);
|
||||
match read_token_swap_state(&source_client, swap_created_at, &token_swap_storage_key)
|
||||
.await?
|
||||
{
|
||||
Some(bp_token_swap::TokenSwapState::Started) => {
|
||||
log::info!(target: "bridge", "Swap has been successfully started");
|
||||
let intermediate_balances =
|
||||
read_account_balances(&accounts, &source_client, &target_client).await?;
|
||||
log::info!(target: "bridge", "Intermediate balances: {:?}", intermediate_balances);
|
||||
},
|
||||
Some(token_swap_state) =>
|
||||
return Err(anyhow::format_err!(
|
||||
"Fresh token swap has unexpected state: {:?}",
|
||||
token_swap_state,
|
||||
)),
|
||||
None => return Err(anyhow::format_err!("Failed to start token swap")),
|
||||
};
|
||||
|
||||
//
|
||||
// Step 2: message is being relayed to the target chain and dispathed there
|
||||
//
|
||||
|
||||
// wait until message is dispatched at the target chain and dispatch result delivered
|
||||
// back to source chain
|
||||
let token_swap_state = wait_until_token_swap_state_is_changed(
|
||||
&source_client,
|
||||
&token_swap_storage_key,
|
||||
bp_token_swap::TokenSwapState::Started,
|
||||
)
|
||||
.await?;
|
||||
let is_transfer_succeeded = match token_swap_state {
|
||||
Some(bp_token_swap::TokenSwapState::Started) => {
|
||||
unreachable!("wait_until_token_swap_state_is_changed only returns if state is not Started; qed",)
|
||||
},
|
||||
None =>
|
||||
return Err(anyhow::format_err!("Fresh token swap has disappeared unexpectedly")),
|
||||
Some(bp_token_swap::TokenSwapState::Confirmed) => {
|
||||
log::info!(
|
||||
target: "bridge",
|
||||
"Transfer has been successfully dispatched at the target chain. Swap can be claimed",
|
||||
);
|
||||
true
|
||||
},
|
||||
Some(bp_token_swap::TokenSwapState::Failed) => {
|
||||
log::info!(
|
||||
target: "bridge",
|
||||
"Transfer has been dispatched with an error at the target chain. Swap can be canceled",
|
||||
);
|
||||
false
|
||||
},
|
||||
};
|
||||
|
||||
// by this time: (1) token swap account has been created and (2) if transfer has been
|
||||
// successfully dispatched, both target chain balances have changed
|
||||
let intermediate_balances =
|
||||
read_account_balances(&accounts, &source_client, &target_client).await?;
|
||||
log::info!(target: "bridge", "Intermediate balances: {:?}", intermediate_balances);
|
||||
|
||||
// transfer has been dispatched, but we may need to wait until block where swap can be
|
||||
// claimed/canceled
|
||||
if let bp_token_swap::TokenSwapType::LockClaimUntilBlock(
|
||||
ref last_available_block_number,
|
||||
_,
|
||||
) = token_swap.swap_type
|
||||
{
|
||||
wait_until_swap_unlocked(
|
||||
&source_client,
|
||||
last_available_block_number + BlockNumberOf::<Source>::one(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
//
|
||||
// Step 3: we may now claim or cancel the swap
|
||||
//
|
||||
|
||||
if is_transfer_succeeded {
|
||||
log::info!(target: "bridge", "Claiming the swap");
|
||||
|
||||
// prepare `claim_swap` message that will be sent over the bridge
|
||||
let claim_swap_call: CallOf<Source> =
|
||||
pallet_bridge_token_swap::Call::claim_swap { swap: token_swap }.into();
|
||||
let claim_swap_message = bp_message_dispatch::MessagePayload {
|
||||
spec_version: SOURCE_SPEC_VERSION,
|
||||
weight: claim_swap_call.get_dispatch_info().weight,
|
||||
origin: bp_message_dispatch::CallOrigin::SourceAccount(
|
||||
accounts.target_account_at_bridged_chain.clone(),
|
||||
),
|
||||
dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtSourceChain,
|
||||
call: claim_swap_call.encode(),
|
||||
};
|
||||
let claim_swap_delivery_and_dispatch_fee =
|
||||
crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee::<
|
||||
Target,
|
||||
Source,
|
||||
_,
|
||||
>(
|
||||
&target_client,
|
||||
source_to_target_conversion_rate_override,
|
||||
ESTIMATE_TARGET_TO_SOURCE_MESSAGE_FEE_METHOD,
|
||||
TARGET_TO_SOURCE_LANE_ID,
|
||||
claim_swap_message.clone(),
|
||||
)
|
||||
.await?;
|
||||
let send_message_call: CallOf<Target> =
|
||||
pallet_bridge_messages::Call::send_message {
|
||||
lane_id: TARGET_TO_SOURCE_LANE_ID,
|
||||
payload: claim_swap_message,
|
||||
delivery_and_dispatch_fee: claim_swap_delivery_and_dispatch_fee,
|
||||
}
|
||||
.into();
|
||||
|
||||
// send `claim_swap` message
|
||||
let target_genesis_hash = *target_client.genesis_hash();
|
||||
let (spec_version, transaction_version) =
|
||||
target_client.simple_runtime_version().await?;
|
||||
let _ = wait_until_transaction_is_finalized::<Target>(
|
||||
target_client
|
||||
.submit_and_watch_signed_extrinsic(
|
||||
accounts.target_account_at_bridged_chain.clone(),
|
||||
move |_, transaction_nonce| {
|
||||
Ok(Bytes(
|
||||
Target::sign_transaction(SignParam {
|
||||
spec_version,
|
||||
transaction_version,
|
||||
genesis_hash: target_genesis_hash,
|
||||
signer: target_sign,
|
||||
era: relay_substrate_client::TransactionEra::immortal(),
|
||||
unsigned: UnsignedTransaction::new(
|
||||
send_message_call.into(),
|
||||
transaction_nonce,
|
||||
),
|
||||
})?
|
||||
.encode(),
|
||||
))
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// wait until swap state is updated
|
||||
let token_swap_state = wait_until_token_swap_state_is_changed(
|
||||
&source_client,
|
||||
&token_swap_storage_key,
|
||||
bp_token_swap::TokenSwapState::Confirmed,
|
||||
)
|
||||
.await?;
|
||||
if token_swap_state != None {
|
||||
return Err(anyhow::format_err!(
|
||||
"Confirmed token swap state has been changed to {:?} unexpectedly",
|
||||
token_swap_state
|
||||
))
|
||||
}
|
||||
} else {
|
||||
log::info!(target: "bridge", "Cancelling the swap");
|
||||
let cancel_swap_call: CallOf<Source> =
|
||||
pallet_bridge_token_swap::Call::cancel_swap { swap: token_swap.clone() }.into();
|
||||
let (spec_version, transaction_version) =
|
||||
source_client.simple_runtime_version().await?;
|
||||
let _ = wait_until_transaction_is_finalized::<Source>(
|
||||
source_client
|
||||
.submit_and_watch_signed_extrinsic(
|
||||
accounts.source_account_at_this_chain.clone(),
|
||||
move |_, transaction_nonce| {
|
||||
Ok(Bytes(
|
||||
Source::sign_transaction(SignParam {
|
||||
spec_version,
|
||||
transaction_version,
|
||||
genesis_hash: source_genesis_hash,
|
||||
signer: source_sign,
|
||||
era: relay_substrate_client::TransactionEra::immortal(),
|
||||
unsigned: UnsignedTransaction::new(
|
||||
cancel_swap_call.into(),
|
||||
transaction_nonce,
|
||||
),
|
||||
})?
|
||||
.encode(),
|
||||
))
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// print final balances
|
||||
let final_balances =
|
||||
read_account_balances(&accounts, &source_client, &target_client).await?;
|
||||
log::info!(target: "bridge", "Final account balances: {:?}", final_balances);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Prepare token swap intention.
|
||||
async fn prepare_token_swap<Source: CliChain, Target: CliChain>(
|
||||
&self,
|
||||
source_client: &Client<Source>,
|
||||
source_sign: &Source::KeyPair,
|
||||
target_sign: &Target::KeyPair,
|
||||
) -> anyhow::Result<
|
||||
bp_token_swap::TokenSwap<
|
||||
BlockNumberOf<Source>,
|
||||
BalanceOf<Source>,
|
||||
AccountIdOf<Source>,
|
||||
BalanceOf<Target>,
|
||||
AccountIdOf<Target>,
|
||||
>,
|
||||
>
|
||||
where
|
||||
AccountIdOf<Source>: From<<Source::KeyPair as Pair>::Public>,
|
||||
AccountIdOf<Target>: From<<Target::KeyPair as Pair>::Public>,
|
||||
BalanceOf<Source>: From<u64>,
|
||||
BalanceOf<Target>: From<u64>,
|
||||
{
|
||||
// accounts that are directly controlled by participants
|
||||
let source_account_at_this_chain: AccountIdOf<Source> = source_sign.public().into();
|
||||
let target_account_at_bridged_chain: AccountIdOf<Target> = target_sign.public().into();
|
||||
|
||||
// balances that we're going to swap
|
||||
let source_balance_at_this_chain: BalanceOf<Source> = self.source_balance.cast().into();
|
||||
let target_balance_at_bridged_chain: BalanceOf<Target> = self.target_balance.cast().into();
|
||||
|
||||
// prepare token swap intention
|
||||
Ok(bp_token_swap::TokenSwap {
|
||||
swap_type: self.prepare_token_swap_type(source_client).await?,
|
||||
source_balance_at_this_chain,
|
||||
source_account_at_this_chain: source_account_at_this_chain.clone(),
|
||||
target_balance_at_bridged_chain,
|
||||
target_account_at_bridged_chain: target_account_at_bridged_chain.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Prepare token swap type.
|
||||
async fn prepare_token_swap_type<Source: Chain>(
|
||||
&self,
|
||||
source_client: &Client<Source>,
|
||||
) -> anyhow::Result<bp_token_swap::TokenSwapType<BlockNumberOf<Source>>> {
|
||||
match self.swap_type {
|
||||
TokenSwapType::NoLock =>
|
||||
Ok(bp_token_swap::TokenSwapType::TemporaryTargetAccountAtBridgedChain),
|
||||
TokenSwapType::LockUntilBlock { blocks_before_expire, ref swap_nonce } => {
|
||||
let blocks_before_expire: BlockNumberOf<Source> = blocks_before_expire.into();
|
||||
let current_source_block_number = *source_client.best_header().await?.number();
|
||||
Ok(bp_token_swap::TokenSwapType::LockClaimUntilBlock(
|
||||
current_source_block_number + blocks_before_expire,
|
||||
swap_nonce.unwrap_or_else(|| {
|
||||
U256::from(random::<u128>()).overflowing_mul(U256::from(random::<u128>())).0
|
||||
}),
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Accounts that are participating in the swap.
|
||||
#[derive(Debug)]
|
||||
struct TokenSwapAccounts<ThisAccountId, BridgedAccountId> {
|
||||
source_account_at_this_chain: ThisAccountId,
|
||||
source_account_at_bridged_chain: BridgedAccountId,
|
||||
target_account_at_bridged_chain: BridgedAccountId,
|
||||
target_account_at_this_chain: ThisAccountId,
|
||||
swap_account: ThisAccountId,
|
||||
}
|
||||
|
||||
/// Swap accounts balances.
|
||||
#[derive(Debug)]
|
||||
struct TokenSwapBalances<ThisBalance, BridgedBalance> {
|
||||
source_account_at_this_chain_balance: Option<ThisBalance>,
|
||||
source_account_at_bridged_chain_balance: Option<BridgedBalance>,
|
||||
target_account_at_bridged_chain_balance: Option<BridgedBalance>,
|
||||
target_account_at_this_chain_balance: Option<ThisBalance>,
|
||||
swap_account_balance: Option<ThisBalance>,
|
||||
}
|
||||
|
||||
/// Read swap accounts balances.
|
||||
async fn read_account_balances<Source: ChainWithBalances, Target: ChainWithBalances>(
|
||||
accounts: &TokenSwapAccounts<AccountIdOf<Source>, AccountIdOf<Target>>,
|
||||
source_client: &Client<Source>,
|
||||
target_client: &Client<Target>,
|
||||
) -> anyhow::Result<TokenSwapBalances<BalanceOf<Source>, BalanceOf<Target>>> {
|
||||
Ok(TokenSwapBalances {
|
||||
source_account_at_this_chain_balance: read_account_balance(
|
||||
source_client,
|
||||
&accounts.source_account_at_this_chain,
|
||||
)
|
||||
.await?,
|
||||
source_account_at_bridged_chain_balance: read_account_balance(
|
||||
target_client,
|
||||
&accounts.source_account_at_bridged_chain,
|
||||
)
|
||||
.await?,
|
||||
target_account_at_bridged_chain_balance: read_account_balance(
|
||||
target_client,
|
||||
&accounts.target_account_at_bridged_chain,
|
||||
)
|
||||
.await?,
|
||||
target_account_at_this_chain_balance: read_account_balance(
|
||||
source_client,
|
||||
&accounts.target_account_at_this_chain,
|
||||
)
|
||||
.await?,
|
||||
swap_account_balance: read_account_balance(source_client, &accounts.swap_account).await?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Read account balance.
|
||||
async fn read_account_balance<C: ChainWithBalances>(
|
||||
client: &Client<C>,
|
||||
account: &AccountIdOf<C>,
|
||||
) -> anyhow::Result<Option<BalanceOf<C>>> {
|
||||
match client.free_native_balance(account.clone()).await {
|
||||
Ok(balance) => Ok(Some(balance)),
|
||||
Err(SubstrateError::AccountDoesNotExist) => Ok(None),
|
||||
Err(error) => Err(anyhow::format_err!(
|
||||
"Failed to read balance of {} account {:?}: {:?}",
|
||||
C::NAME,
|
||||
account,
|
||||
error,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait until transaction is included into finalized block.
|
||||
///
|
||||
/// Returns the hash of the finalized block with transaction.
|
||||
pub(crate) async fn wait_until_transaction_is_finalized<C: Chain>(
|
||||
subscription: Subscription<TransactionStatusOf<C>>,
|
||||
) -> anyhow::Result<HashOf<C>> {
|
||||
loop {
|
||||
let transaction_status = subscription.next().await?;
|
||||
match transaction_status {
|
||||
Some(TransactionStatusOf::<C>::FinalityTimeout(_)) |
|
||||
Some(TransactionStatusOf::<C>::Usurped(_)) |
|
||||
Some(TransactionStatusOf::<C>::Dropped) |
|
||||
Some(TransactionStatusOf::<C>::Invalid) |
|
||||
None =>
|
||||
return Err(anyhow::format_err!(
|
||||
"We've been waiting for finalization of {} transaction, but it now has the {:?} status",
|
||||
C::NAME,
|
||||
transaction_status,
|
||||
)),
|
||||
Some(TransactionStatusOf::<C>::Finalized(block_hash)) => {
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"{} transaction has been finalized at block {}",
|
||||
C::NAME,
|
||||
block_hash,
|
||||
);
|
||||
return Ok(block_hash)
|
||||
},
|
||||
_ => {
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"Received intermediate status of {} transaction: {:?}",
|
||||
C::NAME,
|
||||
transaction_status,
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits until token swap state is changed from `Started` to something else.
|
||||
async fn wait_until_token_swap_state_is_changed<C: Chain>(
|
||||
client: &Client<C>,
|
||||
swap_state_storage_key: &StorageKey,
|
||||
previous_token_swap_state: bp_token_swap::TokenSwapState,
|
||||
) -> anyhow::Result<Option<bp_token_swap::TokenSwapState>> {
|
||||
log::trace!(target: "bridge", "Waiting for token swap state change");
|
||||
loop {
|
||||
async_std::task::sleep(C::AVERAGE_BLOCK_INTERVAL).await;
|
||||
|
||||
let best_block = client.best_finalized_header_number().await?;
|
||||
let best_block_hash = client.block_hash_by_number(best_block).await?;
|
||||
log::trace!(target: "bridge", "Inspecting {} block {}/{}", C::NAME, best_block, best_block_hash);
|
||||
|
||||
let token_swap_state =
|
||||
read_token_swap_state(client, best_block_hash, swap_state_storage_key).await?;
|
||||
match token_swap_state {
|
||||
Some(new_token_swap_state) if new_token_swap_state == previous_token_swap_state => {},
|
||||
_ => {
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"Token swap state has been changed from {:?} to {:?}",
|
||||
previous_token_swap_state,
|
||||
token_swap_state,
|
||||
);
|
||||
return Ok(token_swap_state)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits until swap can be claimed or canceled.
|
||||
async fn wait_until_swap_unlocked<C: Chain>(
|
||||
client: &Client<C>,
|
||||
required_block_number: BlockNumberOf<C>,
|
||||
) -> anyhow::Result<()> {
|
||||
log::trace!(target: "bridge", "Waiting for token swap unlock");
|
||||
loop {
|
||||
async_std::task::sleep(C::AVERAGE_BLOCK_INTERVAL).await;
|
||||
|
||||
let best_block = client.best_finalized_header_number().await?;
|
||||
let best_block_hash = client.block_hash_by_number(best_block).await?;
|
||||
if best_block >= required_block_number {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
log::trace!(target: "bridge", "Skipping {} block {}/{}", C::NAME, best_block, best_block_hash);
|
||||
}
|
||||
}
|
||||
|
||||
/// Read state of the active token swap.
|
||||
async fn read_token_swap_state<C: Chain>(
|
||||
client: &Client<C>,
|
||||
at_block: C::Hash,
|
||||
swap_state_storage_key: &StorageKey,
|
||||
) -> anyhow::Result<Option<bp_token_swap::TokenSwapState>> {
|
||||
Ok(client.storage_value(swap_state_storage_key.clone(), Some(at_block)).await?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams, TargetRuntimeVersionParams};
|
||||
|
||||
#[test]
|
||||
fn swap_tokens_millau_to_rialto_no_lock() {
|
||||
let swap_tokens = SwapTokens::from_iter(vec![
|
||||
"swap-tokens",
|
||||
"millau-to-rialto",
|
||||
"--source-host",
|
||||
"127.0.0.1",
|
||||
"--source-port",
|
||||
"9000",
|
||||
"--source-signer",
|
||||
"//Alice",
|
||||
"--source-balance",
|
||||
"8000000000",
|
||||
"--target-host",
|
||||
"127.0.0.1",
|
||||
"--target-port",
|
||||
"9001",
|
||||
"--target-signer",
|
||||
"//Bob",
|
||||
"--target-balance",
|
||||
"9000000000",
|
||||
"no-lock",
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
swap_tokens,
|
||||
SwapTokens {
|
||||
bridge: SwapTokensBridge::MillauToRialto,
|
||||
source: SourceConnectionParams {
|
||||
source_host: "127.0.0.1".into(),
|
||||
source_port: 9000,
|
||||
source_secure: false,
|
||||
source_runtime_version: SourceRuntimeVersionParams {
|
||||
source_version_mode: RuntimeVersionType::Bundle,
|
||||
source_spec_version: None,
|
||||
source_transaction_version: None,
|
||||
}
|
||||
},
|
||||
source_sign: SourceSigningParams {
|
||||
source_signer: Some("//Alice".into()),
|
||||
source_signer_password: None,
|
||||
source_signer_file: None,
|
||||
source_signer_password_file: None,
|
||||
source_transactions_mortality: None,
|
||||
},
|
||||
target: TargetConnectionParams {
|
||||
target_host: "127.0.0.1".into(),
|
||||
target_port: 9001,
|
||||
target_secure: false,
|
||||
target_runtime_version: TargetRuntimeVersionParams {
|
||||
target_version_mode: RuntimeVersionType::Bundle,
|
||||
target_spec_version: None,
|
||||
target_transaction_version: None,
|
||||
}
|
||||
},
|
||||
target_sign: TargetSigningParams {
|
||||
target_signer: Some("//Bob".into()),
|
||||
target_signer_password: None,
|
||||
target_signer_file: None,
|
||||
target_signer_password_file: None,
|
||||
target_transactions_mortality: None,
|
||||
},
|
||||
swap_type: TokenSwapType::NoLock,
|
||||
source_balance: Balance(8000000000),
|
||||
target_balance: Balance(9000000000),
|
||||
target_to_source_conversion_rate_override: None,
|
||||
source_to_target_conversion_rate_override: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn swap_tokens_millau_to_rialto_lock_until() {
|
||||
let swap_tokens = SwapTokens::from_iter(vec![
|
||||
"swap-tokens",
|
||||
"millau-to-rialto",
|
||||
"--source-host",
|
||||
"127.0.0.1",
|
||||
"--source-port",
|
||||
"9000",
|
||||
"--source-signer",
|
||||
"//Alice",
|
||||
"--source-balance",
|
||||
"8000000000",
|
||||
"--target-host",
|
||||
"127.0.0.1",
|
||||
"--target-port",
|
||||
"9001",
|
||||
"--target-signer",
|
||||
"//Bob",
|
||||
"--target-balance",
|
||||
"9000000000",
|
||||
"--target-to-source-conversion-rate-override",
|
||||
"metric",
|
||||
"--source-to-target-conversion-rate-override",
|
||||
"84.56",
|
||||
"lock-until-block",
|
||||
"--blocks-before-expire",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
swap_tokens,
|
||||
SwapTokens {
|
||||
bridge: SwapTokensBridge::MillauToRialto,
|
||||
source: SourceConnectionParams {
|
||||
source_host: "127.0.0.1".into(),
|
||||
source_port: 9000,
|
||||
source_secure: false,
|
||||
source_runtime_version: SourceRuntimeVersionParams {
|
||||
source_version_mode: RuntimeVersionType::Bundle,
|
||||
source_spec_version: None,
|
||||
source_transaction_version: None,
|
||||
}
|
||||
},
|
||||
source_sign: SourceSigningParams {
|
||||
source_signer: Some("//Alice".into()),
|
||||
source_signer_password: None,
|
||||
source_signer_file: None,
|
||||
source_signer_password_file: None,
|
||||
source_transactions_mortality: None,
|
||||
},
|
||||
target: TargetConnectionParams {
|
||||
target_host: "127.0.0.1".into(),
|
||||
target_port: 9001,
|
||||
target_secure: false,
|
||||
target_runtime_version: TargetRuntimeVersionParams {
|
||||
target_version_mode: RuntimeVersionType::Bundle,
|
||||
target_spec_version: None,
|
||||
target_transaction_version: None,
|
||||
}
|
||||
},
|
||||
target_sign: TargetSigningParams {
|
||||
target_signer: Some("//Bob".into()),
|
||||
target_signer_password: None,
|
||||
target_signer_file: None,
|
||||
target_signer_password_file: None,
|
||||
target_transactions_mortality: None,
|
||||
},
|
||||
swap_type: TokenSwapType::LockUntilBlock {
|
||||
blocks_before_expire: 1,
|
||||
swap_nonce: None,
|
||||
},
|
||||
source_balance: Balance(8000000000),
|
||||
target_balance: Balance(9000000000),
|
||||
target_to_source_conversion_rate_override: Some(ConversionRateOverride::Metric),
|
||||
source_to_target_conversion_rate_override: Some(ConversionRateOverride::Explicit(
|
||||
84.56
|
||||
)),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,11 @@ scale-info = { version = "2.1.1", features = ["derive"] }
|
||||
|
||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||
bp-kusama = { path = "../../primitives/chain-kusama" }
|
||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
||||
bp-messages = { path = "../../primitives/messages" }
|
||||
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
||||
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||
bp-runtime = { path = "../../primitives/runtime" }
|
||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
||||
|
||||
# Substrate Dependencies
|
||||
|
||||
|
||||
@@ -27,30 +27,6 @@ use sp_runtime::FixedU128;
|
||||
/// Unchecked Kusama extrinsic.
|
||||
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.
|
||||
///
|
||||
/// 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)]
|
||||
update_pallet_parameter(BridgePolkadotMessagesParameter),
|
||||
#[codec(index = 3)]
|
||||
send_message(
|
||||
LaneId,
|
||||
bp_message_dispatch::MessagePayload<
|
||||
bp_kusama::AccountId,
|
||||
bp_polkadot::AccountId,
|
||||
bp_polkadot::AccountPublic,
|
||||
Vec<u8>,
|
||||
>,
|
||||
bp_kusama::Balance,
|
||||
),
|
||||
send_message(LaneId, Vec<u8>, bp_kusama::Balance),
|
||||
#[codec(index = 5)]
|
||||
receive_messages_proof(
|
||||
bp_polkadot::AccountId,
|
||||
|
||||
@@ -15,13 +15,11 @@ scale-info = { version = "2.1.1", features = ["derive"] }
|
||||
|
||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||
bp-kusama = { path = "../../primitives/chain-kusama" }
|
||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
||||
bp-messages = { path = "../../primitives/messages" }
|
||||
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
||||
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||
bp-runtime = { path = "../../primitives/runtime" }
|
||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
||||
|
||||
# Substrate Dependencies
|
||||
|
||||
|
||||
@@ -27,30 +27,6 @@ use sp_runtime::FixedU128;
|
||||
/// Unchecked Polkadot extrinsic.
|
||||
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.
|
||||
///
|
||||
/// 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)]
|
||||
update_pallet_parameter(BridgeKusamaMessagesParameter),
|
||||
#[codec(index = 3)]
|
||||
send_message(
|
||||
LaneId,
|
||||
bp_message_dispatch::MessagePayload<
|
||||
bp_polkadot::AccountId,
|
||||
bp_kusama::AccountId,
|
||||
bp_kusama::AccountPublic,
|
||||
Vec<u8>,
|
||||
>,
|
||||
bp_polkadot::Balance,
|
||||
),
|
||||
send_message(LaneId, Vec<u8>, bp_polkadot::Balance),
|
||||
#[codec(index = 5)]
|
||||
receive_messages_proof(
|
||||
bp_kusama::AccountId,
|
||||
|
||||
@@ -15,13 +15,11 @@ scale-info = { version = "2.1.1", features = ["derive"] }
|
||||
|
||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
||||
bp-messages = { path = "../../primitives/messages" }
|
||||
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||
bp-rococo = { path = "../../primitives/chain-rococo" }
|
||||
bp-runtime = { path = "../../primitives/runtime" }
|
||||
bp-wococo = { path = "../../primitives/chain-wococo" }
|
||||
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
||||
pallet-bridge-messages = { path = "../../modules/messages" }
|
||||
|
||||
# Substrate Dependencies
|
||||
|
||||
@@ -26,30 +26,6 @@ use scale_info::TypeInfo;
|
||||
/// Unchecked Rococo extrinsic.
|
||||
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.
|
||||
///
|
||||
/// 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)]
|
||||
pub enum BridgeWococoMessagesCall {
|
||||
#[codec(index = 3)]
|
||||
send_message(
|
||||
LaneId,
|
||||
bp_message_dispatch::MessagePayload<
|
||||
bp_rococo::AccountId,
|
||||
bp_wococo::AccountId,
|
||||
bp_wococo::AccountPublic,
|
||||
Vec<u8>,
|
||||
>,
|
||||
bp_rococo::Balance,
|
||||
),
|
||||
send_message(LaneId, Vec<u8>, bp_rococo::Balance),
|
||||
#[codec(index = 5)]
|
||||
receive_messages_proof(
|
||||
bp_wococo::AccountId,
|
||||
|
||||
@@ -14,13 +14,11 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive"
|
||||
# Bridge dependencies
|
||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
||||
bp-messages = { path = "../../primitives/messages" }
|
||||
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||
bp-rococo = { path = "../../primitives/chain-rococo" }
|
||||
bp-runtime = { path = "../../primitives/runtime" }
|
||||
bp-wococo = { path = "../../primitives/chain-wococo" }
|
||||
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
||||
pallet-bridge-messages = { path = "../../modules/messages" }
|
||||
|
||||
# Substrate Dependencies
|
||||
|
||||
@@ -26,30 +26,6 @@ use scale_info::TypeInfo;
|
||||
/// Unchecked Wococo extrinsic.
|
||||
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.
|
||||
///
|
||||
/// 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)]
|
||||
pub enum BridgeRococoMessagesCall {
|
||||
#[codec(index = 3)]
|
||||
send_message(
|
||||
LaneId,
|
||||
bp_message_dispatch::MessagePayload<
|
||||
bp_rococo::AccountId,
|
||||
bp_wococo::AccountId,
|
||||
bp_wococo::AccountPublic,
|
||||
Vec<u8>,
|
||||
>,
|
||||
bp_rococo::Balance,
|
||||
),
|
||||
send_message(LaneId, Vec<u8>, bp_rococo::Balance),
|
||||
#[codec(index = 5)]
|
||||
receive_messages_proof(
|
||||
bp_rococo::AccountId,
|
||||
|
||||
Reference in New Issue
Block a user