From 71a05a9c3c7df2f6886f2dd01318d4e5fe057bfc Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 17 Dec 2021 10:17:18 +0300 Subject: [PATCH 1/4] merge all similar update_conversion_rate functions --- .../src/chains/kusama_messages_to_polkadot.rs | 46 ++---- .../src/chains/millau_messages_to_rialto.rs | 45 +----- .../src/chains/polkadot_messages_to_kusama.rs | 47 ++---- .../src/chains/rialto_messages_to_millau.rs | 45 +----- .../src/chains/rococo_messages_to_wococo.rs | 2 + .../src/chains/wococo_messages_to_rococo.rs | 2 + .../src/cli/relay_headers_and_messages.rs | 86 +++-------- .../src/conversion_rate_update.rs | 146 ++++++++++++++++-- .../lib-substrate-relay/src/messages_lane.rs | 6 + 9 files changed, 203 insertions(+), 222 deletions(-) diff --git a/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs b/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs index 6a8780a34a..58827c8d34 100644 --- a/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs +++ b/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs @@ -16,14 +16,11 @@ //! Kusama-to-Polkadot messages sync entrypoint. -use codec::Encode; use frame_support::weights::Weight; -use sp_core::{Bytes, Pair}; use messages_relay::relay_strategy::MixStrategy; use relay_kusama_client::Kusama; use relay_polkadot_client::Polkadot; -use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction}; use substrate_relay_helper::messages_lane::SubstrateMessageLane; /// Description of Kusama -> Polkadot messages bridge. @@ -41,6 +38,13 @@ substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_buil relay_kusama_client::runtime::Call::BridgePolkadotMessages, relay_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_delivery_proof ); +substrate_relay_helper::generate_mocked_update_conversion_rate_call_builder!( + Kusama, + KusamaMessagesToPolkadotUpdateConversionRateCallBuilder, + relay_kusama_client::runtime::Call::BridgePolkadotMessages, + relay_kusama_client::runtime::BridgePolkadotMessagesCall::update_pallet_parameter, + relay_kusama_client::runtime::BridgePolkadotMessagesParameter::PolkadotToKusamaConversionRate +); impl SubstrateMessageLane for KusamaMessagesToPolkadot { const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = @@ -58,39 +62,7 @@ impl SubstrateMessageLane for KusamaMessagesToPolkadot { type ReceiveMessagesDeliveryProofCallBuilder = KusamaMessagesToPolkadotReceiveMessagesDeliveryProofCallBuilder; - type RelayStrategy = MixStrategy; -} + type TargetToSourceChainConversionRateUpdateBuilder = KusamaMessagesToPolkadotUpdateConversionRateCallBuilder; -/// Update Polkadot -> Kusama conversion rate, stored in Kusama runtime storage. -pub(crate) async fn update_polkadot_to_kusama_conversion_rate( - client: Client, - signer: ::AccountKeyPair, - updated_rate: f64, -) -> anyhow::Result<()> { - let genesis_hash = *client.genesis_hash(); - let signer_id = (*signer.public().as_array_ref()).into(); - client - .submit_signed_extrinsic(signer_id, move |_, transaction_nonce| { - Bytes( - Kusama::sign_transaction( - genesis_hash, - &signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - relay_kusama_client::runtime::Call::BridgePolkadotMessages( - relay_kusama_client::runtime::BridgePolkadotMessagesCall::update_pallet_parameter( - relay_kusama_client::runtime::BridgePolkadotMessagesParameter::PolkadotToKusamaConversionRate( - sp_runtime::FixedU128::from_float(updated_rate), - ) - ) - ), - transaction_nonce, - ), - ) - .encode(), - ) - }) - .await - .map(drop) - .map_err(|err| anyhow::format_err!("{:?}", err)) + type RelayStrategy = MixStrategy; } diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs b/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs index 4acc7fdd2f..db8dcc3a5d 100644 --- a/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs +++ b/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs @@ -16,13 +16,9 @@ //! Millau-to-Rialto messages sync entrypoint. -use codec::Encode; -use sp_core::{Bytes, Pair}; - use messages_relay::relay_strategy::MixStrategy; use relay_millau_client::Millau; use relay_rialto_client::Rialto; -use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction}; use substrate_relay_helper::messages_lane::{ DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, SubstrateMessageLane, @@ -31,6 +27,12 @@ use substrate_relay_helper::messages_lane::{ /// Description of Millau -> Rialto messages bridge. #[derive(Clone, Debug)] pub struct MillauMessagesToRialto; +substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!( + Millau, + MillauMessagesToRialtoUpdateConversionRateCallBuilder, + millau_runtime::Runtime, millau_runtime::WithRialtoMessagesInstance, + millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate +); impl SubstrateMessageLane for MillauMessagesToRialto { const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = @@ -55,38 +57,7 @@ impl SubstrateMessageLane for MillauMessagesToRialto { millau_runtime::WithRialtoMessagesInstance, >; - type RelayStrategy = MixStrategy; -} + type TargetToSourceChainConversionRateUpdateBuilder = MillauMessagesToRialtoUpdateConversionRateCallBuilder; -/// Update Rialto -> Millau conversion rate, stored in Millau runtime storage. -pub(crate) async fn update_rialto_to_millau_conversion_rate( - client: Client, - signer: ::AccountKeyPair, - updated_rate: f64, -) -> anyhow::Result<()> { - let genesis_hash = *client.genesis_hash(); - let signer_id = (*signer.public().as_array_ref()).into(); - client - .submit_signed_extrinsic(signer_id, move |_, transaction_nonce| { - Bytes( - Millau::sign_transaction( - genesis_hash, - &signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - millau_runtime::MessagesCall::update_pallet_parameter { - parameter: millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate( - sp_runtime::FixedU128::from_float(updated_rate), - ), - } - .into(), - transaction_nonce, - ), - ) - .encode(), - ) - }) - .await - .map(drop) - .map_err(|err| anyhow::format_err!("{:?}", err)) + type RelayStrategy = MixStrategy; } diff --git a/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs b/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs index 12ebdde15c..11e8044428 100644 --- a/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs +++ b/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs @@ -16,14 +16,10 @@ //! Polkadot-to-Kusama messages sync entrypoint. -use codec::Encode; -use sp_core::{Bytes, Pair}; - use frame_support::weights::Weight; use messages_relay::relay_strategy::MixStrategy; use relay_kusama_client::Kusama; use relay_polkadot_client::Polkadot; -use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction}; use substrate_relay_helper::messages_lane::SubstrateMessageLane; /// Description of Polkadot -> Kusama messages bridge. @@ -41,6 +37,13 @@ substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_buil relay_polkadot_client::runtime::Call::BridgeKusamaMessages, relay_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_delivery_proof ); +substrate_relay_helper::generate_mocked_update_conversion_rate_call_builder!( + Polkadot, + PolkadotMessagesToKusamaUpdateConversionRateCallBuilder, + relay_polkadot_client::runtime::Call::BridgeKusamaMessages, + relay_polkadot_client::runtime::BridgeKusamaMessagesCall::update_pallet_parameter, + relay_polkadot_client::runtime::BridgeKusamaMessagesParameter::KusamaToPolkadotConversionRate +); impl SubstrateMessageLane for PolkadotMessagesToKusama { const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = @@ -58,39 +61,7 @@ impl SubstrateMessageLane for PolkadotMessagesToKusama { type ReceiveMessagesDeliveryProofCallBuilder = PolkadotMessagesToKusamaReceiveMessagesDeliveryProofCallBuilder; - type RelayStrategy = MixStrategy; -} + type TargetToSourceChainConversionRateUpdateBuilder = PolkadotMessagesToKusamaUpdateConversionRateCallBuilder; -/// Update Kusama -> Polkadot conversion rate, stored in Polkadot runtime storage. -pub(crate) async fn update_kusama_to_polkadot_conversion_rate( - client: Client, - signer: ::AccountKeyPair, - updated_rate: f64, -) -> anyhow::Result<()> { - let genesis_hash = *client.genesis_hash(); - let signer_id = (*signer.public().as_array_ref()).into(); - client - .submit_signed_extrinsic(signer_id, move |_, transaction_nonce| { - Bytes( - Polkadot::sign_transaction( - genesis_hash, - &signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - relay_polkadot_client::runtime::Call::BridgeKusamaMessages( - relay_polkadot_client::runtime::BridgeKusamaMessagesCall::update_pallet_parameter( - relay_polkadot_client::runtime::BridgeKusamaMessagesParameter::KusamaToPolkadotConversionRate( - sp_runtime::FixedU128::from_float(updated_rate), - ) - ) - ), - transaction_nonce, - ), - ) - .encode(), - ) - }) - .await - .map(drop) - .map_err(|err| anyhow::format_err!("{:?}", err)) + type RelayStrategy = MixStrategy; } diff --git a/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs b/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs index b6e85a7dec..baa7b3545e 100644 --- a/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs +++ b/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs @@ -16,13 +16,9 @@ //! Rialto-to-Millau messages sync entrypoint. -use codec::Encode; -use sp_core::{Bytes, Pair}; - use messages_relay::relay_strategy::MixStrategy; use relay_millau_client::Millau; use relay_rialto_client::Rialto; -use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction}; use substrate_relay_helper::messages_lane::{ DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, SubstrateMessageLane, @@ -31,6 +27,12 @@ use substrate_relay_helper::messages_lane::{ /// Description of Rialto -> Millau messages bridge. #[derive(Clone, Debug)] pub struct RialtoMessagesToMillau; +substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!( + Rialto, + RialtoMessagesToMillauUpdateConversionRateCallBuilder, + rialto_runtime::Runtime, rialto_runtime::WithMillauMessagesInstance, + rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate +); impl SubstrateMessageLane for RialtoMessagesToMillau { const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = @@ -55,38 +57,7 @@ impl SubstrateMessageLane for RialtoMessagesToMillau { rialto_runtime::WithMillauMessagesInstance, >; - type RelayStrategy = MixStrategy; -} + type TargetToSourceChainConversionRateUpdateBuilder = RialtoMessagesToMillauUpdateConversionRateCallBuilder; -/// Update Millau -> Rialto conversion rate, stored in Rialto runtime storage. -pub(crate) async fn update_millau_to_rialto_conversion_rate( - client: Client, - signer: ::AccountKeyPair, - updated_rate: f64, -) -> anyhow::Result<()> { - let genesis_hash = *client.genesis_hash(); - let signer_id = (*signer.public().as_array_ref()).into(); - client - .submit_signed_extrinsic(signer_id, move |_, transaction_nonce| { - Bytes( - Rialto::sign_transaction( - genesis_hash, - &signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - rialto_runtime::MessagesCall::update_pallet_parameter { - parameter: rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate( - sp_runtime::FixedU128::from_float(updated_rate), - ), - } - .into(), - transaction_nonce, - ), - ) - .encode(), - ) - }) - .await - .map(drop) - .map_err(|err| anyhow::format_err!("{:?}", err)) + type RelayStrategy = MixStrategy; } diff --git a/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs b/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs index a85789bb1b..b9b239eb72 100644 --- a/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs +++ b/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs @@ -52,5 +52,7 @@ impl SubstrateMessageLane for RococoMessagesToWococo { type ReceiveMessagesDeliveryProofCallBuilder = RococoMessagesToWococoReceiveMessagesDeliveryProofCallBuilder; + type TargetToSourceChainConversionRateUpdateBuilder = (); + type RelayStrategy = MixStrategy; } diff --git a/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs b/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs index 7c599e4139..e1cbd98af3 100644 --- a/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs +++ b/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs @@ -53,5 +53,7 @@ impl SubstrateMessageLane for WococoMessagesToRococo { type ReceiveMessagesDeliveryProofCallBuilder = WococoMessagesToRococoReceiveMessagesDeliveryProofCallBuilder; + type TargetToSourceChainConversionRateUpdateBuilder = (); + type RelayStrategy = MixStrategy; } diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages.rs index 112d2f419b..d58cfdfa91 100644 --- a/relays/bin-substrate/src/cli/relay_headers_and_messages.rs +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages.rs @@ -138,14 +138,8 @@ macro_rules! select_bridge { bp_rialto::SESSION_LENGTH; use crate::chains::{ - millau_messages_to_rialto::{ - update_rialto_to_millau_conversion_rate as update_right_to_left_conversion_rate, - MillauMessagesToRialto as LeftToRightMessageLane, - }, - rialto_messages_to_millau::{ - update_millau_to_rialto_conversion_rate as update_left_to_right_conversion_rate, - RialtoMessagesToMillau as RightToLeftMessageLane, - }, + millau_messages_to_rialto::MillauMessagesToRialto as LeftToRightMessageLane, + rialto_messages_to_millau::RialtoMessagesToMillau as RightToLeftMessageLane, }; async fn left_create_account( @@ -190,22 +184,6 @@ macro_rules! select_bridge { wococo_messages_to_rococo::WococoMessagesToRococo as RightToLeftMessageLane, }; - async fn update_right_to_left_conversion_rate( - _client: Client, - _signer: ::AccountKeyPair, - _updated_rate: f64, - ) -> anyhow::Result<()> { - Err(anyhow::format_err!("Conversion rate is not supported by this bridge")) - } - - async fn update_left_to_right_conversion_rate( - _client: Client, - _signer: ::AccountKeyPair, - _updated_rate: f64, - ) -> anyhow::Result<()> { - Err(anyhow::format_err!("Conversion rate is not supported by this bridge")) - } - async fn left_create_account( left_client: Client, left_sign: ::AccountKeyPair, @@ -264,14 +242,8 @@ macro_rules! select_bridge { bp_polkadot::SESSION_LENGTH; use crate::chains::{ - kusama_messages_to_polkadot::{ - update_polkadot_to_kusama_conversion_rate as update_right_to_left_conversion_rate, - KusamaMessagesToPolkadot as LeftToRightMessageLane, - }, - polkadot_messages_to_kusama::{ - update_kusama_to_polkadot_conversion_rate as update_left_to_right_conversion_rate, - PolkadotMessagesToKusama as RightToLeftMessageLane, - }, + kusama_messages_to_polkadot::KusamaMessagesToPolkadot as LeftToRightMessageLane, + polkadot_messages_to_kusama::PolkadotMessagesToKusama as RightToLeftMessageLane, }; async fn left_create_account( @@ -368,7 +340,15 @@ impl RelayHeadersAndMessages { Left::NAME ) }; - substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop( + substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop::< + LeftToRightMessageLane, + Left, + >( + left_client.clone(), + TransactionParams { + signer: left_messages_pallet_owner.clone(), + mortality: left_transactions_mortality, + }, left_to_right_metrics .target_to_source_conversion_rate .as_ref() @@ -385,21 +365,6 @@ impl RelayHeadersAndMessages { .ok_or_else(format_err)? .shared_value_ref(), CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, - move |new_rate| { - log::info!( - target: "bridge", - "Going to update {} -> {} (on {}) conversion rate to {}.", - Right::NAME, - Left::NAME, - Left::NAME, - new_rate, - ); - update_right_to_left_conversion_rate( - left_client.clone(), - left_messages_pallet_owner.clone(), - new_rate, - ) - }, ); } if let Some(right_messages_pallet_owner) = right_messages_pallet_owner { @@ -411,7 +376,15 @@ impl RelayHeadersAndMessages { Right::NAME ) }; - substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop( + substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop::< + RightToLeftMessageLane, + Right, + >( + right_client.clone(), + TransactionParams { + signer: right_messages_pallet_owner.clone(), + mortality: right_transactions_mortality, + }, right_to_left_metrics .target_to_source_conversion_rate .as_ref() @@ -428,21 +401,6 @@ impl RelayHeadersAndMessages { .ok_or_else(format_err)? .shared_value_ref(), CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, - move |new_rate| { - log::info!( - target: "bridge", - "Going to update {} -> {} (on {}) conversion rate to {}.", - Left::NAME, - Right::NAME, - Right::NAME, - new_rate, - ); - update_left_to_right_conversion_rate( - right_client.clone(), - right_messages_pallet_owner.clone(), - new_rate, - ) - }, ); } diff --git a/relays/lib-substrate-relay/src/conversion_rate_update.rs b/relays/lib-substrate-relay/src/conversion_rate_update.rs index 93458457d3..2180e99e68 100644 --- a/relays/lib-substrate-relay/src/conversion_rate_update.rs +++ b/relays/lib-substrate-relay/src/conversion_rate_update.rs @@ -16,8 +16,13 @@ //! Tools for updating conversion rate that is stored in the runtime storage. +use crate::{messages_lane::SubstrateMessageLane, TransactionParams}; + +use codec::Encode; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, CallOf, Chain, Client, TransactionEra, TransactionSignScheme, UnsignedTransaction}; use relay_utils::metrics::F64SharedRef; -use std::{future::Future, time::Duration}; +use sp_core::{Bytes, Pair}; +use std::time::Duration; /// Duration between updater iterations. const SLEEP_DURATION: Duration = Duration::from_secs(60); @@ -31,19 +36,93 @@ enum TransactionStatus { Submitted(f64), } +/// Different ways of building 'update conversion rate' calls. +pub trait UpdateConversionRateCallBuilder { + /// Given conversion rate, build call that updates conversion rate in given chain runtime storage. + fn build_update_conversion_rate_call(conversion_rate: f64) -> anyhow::Result>; +} + +impl UpdateConversionRateCallBuilder for () { + fn build_update_conversion_rate_call(_conversion_rate: f64) -> anyhow::Result> { + Err(anyhow::format_err!("Conversion rate update is not supported at {}", C::NAME)) + } +} + +/// Macro that generates `UpdateConversionRateCallBuilder` implementation for the case when +/// you have a direct access to the source chain runtime. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_direct_update_conversion_rate_call_builder { + ( + $source_chain:ident, + $mocked_builder:ident, + $runtime:ty, + $instance:ty, + $parameter:path + ) => { + pub struct $mocked_builder; + + impl $crate::conversion_rate_update::UpdateConversionRateCallBuilder<$source_chain> + for $mocked_builder + { + fn build_update_conversion_rate_call( + conversion_rate: f64, + ) -> anyhow::Result> { + Ok(pallet_bridge_messages::Call::update_pallet_parameter::<$runtime, $instance> { + parameter: $parameter(sp_runtime::FixedU128::from_float(conversion_rate)), + }.into()) + } + } + }; +} + +/// Macro that generates `UpdateConversionRateCallBuilder` implementation for the case when +/// you only have an access to the mocked version of source chain runtime. In this case you +/// should provide "name" of the call variant for the bridge messages calls, the "name" of +/// the variant for the `update_pallet_parameter` call within that first option and the name +/// of the conversion rate parameter itself. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_mocked_update_conversion_rate_call_builder { + ( + $source_chain:ident, + $mocked_builder:ident, + $bridge_messages:path, + $update_pallet_parameter:path, + $parameter:path + ) => { + pub struct $mocked_builder; + + impl $crate::conversion_rate_update::UpdateConversionRateCallBuilder<$source_chain> + for $mocked_builder + { + fn build_update_conversion_rate_call( + conversion_rate: f64, + ) -> anyhow::Result> { + Ok($bridge_messages($update_pallet_parameter($parameter( + sp_runtime::FixedU128::from_float(conversion_rate), + )))) + } + } + }; +} + /// Run infinite conversion rate updater loop. /// /// The loop is maintaining the Left -> Right conversion rate, used as `RightTokens = LeftTokens * /// Rate`. -pub fn run_conversion_rate_update_loop< - SubmitConversionRateFuture: Future> + Send + 'static, ->( +pub fn run_conversion_rate_update_loop( + client: Client, + transaction_params: TransactionParams>, left_to_right_stored_conversion_rate: F64SharedRef, left_to_base_conversion_rate: F64SharedRef, right_to_base_conversion_rate: F64SharedRef, max_difference_ratio: f64, - submit_conversion_rate: impl Fn(f64) -> SubmitConversionRateFuture + Send + 'static, -) { +) where + Lane: SubstrateMessageLane, + Sign: TransactionSignScheme, + AccountIdOf: From< as Pair>::Public>, +{ async_std::task::spawn(async move { let mut transaction_status = TransactionStatus::Idle; loop { @@ -57,13 +136,30 @@ pub fn run_conversion_rate_update_loop< ) .await; if let Some((prev_conversion_rate, new_conversion_rate)) = maybe_new_conversion_rate { - let submit_conversion_rate_future = submit_conversion_rate(new_conversion_rate); - match submit_conversion_rate_future.await { + log::info!( + target: "bridge", + "Going to update {} -> {} (on {}) conversion rate to {}.", + Lane::TargetChain::NAME, + Lane::SourceChain::NAME, + Lane::SourceChain::NAME, + new_conversion_rate, + ); + + let result = update_target_to_source_conversion_rate::( + client.clone(), + transaction_params.clone(), + new_conversion_rate, + ).await; + match result { Ok(()) => { transaction_status = TransactionStatus::Submitted(prev_conversion_rate); }, Err(error) => { - log::trace!(target: "bridge", "Failed to submit conversion rate update transaction: {:?}", error); + log::trace!( + target: "bridge", + "Failed to submit conversion rate update transaction: {:?}", + error, + ); }, } } @@ -118,6 +214,38 @@ async fn maybe_select_new_conversion_rate( Some((left_to_right_stored_conversion_rate, actual_left_to_right_conversion_rate)) } +/// Update Target -> Source tokens conversion rate, stored in the Source runtime storage. +pub async fn update_target_to_source_conversion_rate( + client: Client, + transaction_params: TransactionParams>, + updated_rate: f64, +) -> anyhow::Result<()> where + Lane: SubstrateMessageLane, + Sign: TransactionSignScheme, + AccountIdOf: From< as Pair>::Public>, +{ + let genesis_hash = *client.genesis_hash(); + let signer_id = transaction_params.signer.public().into(); + let call = Lane::TargetToSourceChainConversionRateUpdateBuilder::build_update_conversion_rate_call( + updated_rate + )?; + client + .submit_signed_extrinsic(signer_id, move |best_block_id, transaction_nonce| { + Bytes( + Sign::sign_transaction( + genesis_hash, + &transaction_params.signer, + TransactionEra::new(best_block_id, transaction_params.mortality), + UnsignedTransaction::new(call, transaction_nonce), + ) + .encode(), + ) + }) + .await + .map(drop) + .map_err(|err| anyhow::format_err!("{:?}", err)) +} + #[cfg(test)] mod tests { use super::*; diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs index 4742ce6e48..f818e7858b 100644 --- a/relays/lib-substrate-relay/src/messages_lane.rs +++ b/relays/lib-substrate-relay/src/messages_lane.rs @@ -17,6 +17,7 @@ //! Tools for supporting message lanes between two Substrate-based chains. use crate::{ + conversion_rate_update::UpdateConversionRateCallBuilder, messages_metrics::StandaloneMessagesMetrics, messages_source::{SubstrateMessagesProof, SubstrateMessagesSource}, messages_target::{SubstrateMessagesDeliveryProof, SubstrateMessagesTarget}, @@ -71,6 +72,11 @@ pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { /// How receive messages delivery proof call is built? type ReceiveMessagesDeliveryProofCallBuilder: ReceiveMessagesDeliveryProofCallBuilder; + /// `TargetChain` tokens to `SourceChain` tokens conversion rate update builder. + /// + /// If not applicale to this bridge, you may use `()` here. + type TargetToSourceChainConversionRateUpdateBuilder: UpdateConversionRateCallBuilder; + /// Message relay strategy. type RelayStrategy: RelayStrategy; } From 0eabd604ff531f8a10c3c91ddc7644890e23cb40 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 17 Dec 2021 11:42:44 +0300 Subject: [PATCH 2/4] stall timeout in conversion rate update loop --- .../src/conversion_rate_update.rs | 80 ++++++++++++++++--- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/relays/lib-substrate-relay/src/conversion_rate_update.rs b/relays/lib-substrate-relay/src/conversion_rate_update.rs index 2180e99e68..2d26d50668 100644 --- a/relays/lib-substrate-relay/src/conversion_rate_update.rs +++ b/relays/lib-substrate-relay/src/conversion_rate_update.rs @@ -19,21 +19,26 @@ use crate::{messages_lane::SubstrateMessageLane, TransactionParams}; use codec::Encode; -use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, CallOf, Chain, Client, TransactionEra, TransactionSignScheme, UnsignedTransaction}; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, CallOf, Chain, Client, TransactionEra, TransactionSignScheme, UnsignedTransaction, transaction_stall_timeout}; use relay_utils::metrics::F64SharedRef; use sp_core::{Bytes, Pair}; -use std::time::Duration; +use std::time::{Duration, Instant}; /// Duration between updater iterations. const SLEEP_DURATION: Duration = Duration::from_secs(60); +/// Duration which will almost never expire. Since changing conversion rate may require manual +/// intervention (e.g. if call is made through multisig pallet), we don't want relayer to +/// resubmit transaction often. +const ALMOST_NEVER_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 30); + /// Update-conversion-rate transaction status. #[derive(Debug, Clone, Copy, PartialEq)] enum TransactionStatus { /// We have not submitted any transaction recently. Idle, /// We have recently submitted transaction that should update conversion rate. - Submitted(f64), + Submitted(Instant, f64), } /// Different ways of building 'update conversion rate' calls. @@ -123,11 +128,27 @@ pub fn run_conversion_rate_update_loop( Sign: TransactionSignScheme, AccountIdOf: From< as Pair>::Public>, { + let stall_timeout = transaction_stall_timeout( + transaction_params.mortality, + Lane::SourceChain::AVERAGE_BLOCK_INTERVAL, + ALMOST_NEVER_DURATION, + ); + + log::info!( + target: "bridge", + "Starting {} -> {} conversion rate (on {}) update loop. Stall timeout: {}s", + Lane::TargetChain::NAME, + Lane::SourceChain::NAME, + Lane::SourceChain::NAME, + stall_timeout.as_secs(), + ); + async_std::task::spawn(async move { let mut transaction_status = TransactionStatus::Idle; loop { async_std::task::sleep(SLEEP_DURATION).await; let maybe_new_conversion_rate = maybe_select_new_conversion_rate( + stall_timeout, &mut transaction_status, &left_to_right_stored_conversion_rate, &left_to_base_conversion_rate, @@ -152,10 +173,10 @@ pub fn run_conversion_rate_update_loop( ).await; match result { Ok(()) => { - transaction_status = TransactionStatus::Submitted(prev_conversion_rate); + transaction_status = TransactionStatus::Submitted(Instant::now(), prev_conversion_rate); }, Err(error) => { - log::trace!( + log::error!( target: "bridge", "Failed to submit conversion rate update transaction: {:?}", error, @@ -169,6 +190,7 @@ pub fn run_conversion_rate_update_loop( /// Select new conversion rate to submit to the node. async fn maybe_select_new_conversion_rate( + stall_timeout: Duration, transaction_status: &mut TransactionStatus, left_to_right_stored_conversion_rate: &F64SharedRef, left_to_base_conversion_rate: &F64SharedRef, @@ -179,7 +201,16 @@ async fn maybe_select_new_conversion_rate( (*left_to_right_stored_conversion_rate.read().await)?; match *transaction_status { TransactionStatus::Idle => (), - TransactionStatus::Submitted(previous_left_to_right_stored_conversion_rate) => { + TransactionStatus::Submitted(submitted_at, _) if Instant::now() - submitted_at > stall_timeout => { + log::error!( + target: "bridge", + "Conversion rate update transaction has been lost and loop stalled. Restarting", + ); + + // we assume that our transaction has been lost + *transaction_status = TransactionStatus::Idle; + } + TransactionStatus::Submitted(_, previous_left_to_right_stored_conversion_rate) => { // we can't compare float values from different sources directly, so we only care // whether the stored rate has been changed or not. If it has been changed, then we // assume that our proposal has been accepted. @@ -251,6 +282,8 @@ mod tests { use super::*; use async_std::sync::{Arc, RwLock}; + const TEST_STALL_TIMEOUT: Duration = Duration::from_secs(60); + fn test_maybe_select_new_conversion_rate( mut transaction_status: TransactionStatus, stored_conversion_rate: Option, @@ -262,6 +295,7 @@ mod tests { let left_to_base_conversion_rate = Arc::new(RwLock::new(left_to_base_conversion_rate)); let right_to_base_conversion_rate = Arc::new(RwLock::new(right_to_base_conversion_rate)); let result = async_std::task::block_on(maybe_select_new_conversion_rate( + TEST_STALL_TIMEOUT, &mut transaction_status, &stored_conversion_rate, &left_to_base_conversion_rate, @@ -273,15 +307,16 @@ mod tests { #[test] fn rate_is_not_updated_when_transaction_is_submitted() { + let status = TransactionStatus::Submitted(Instant::now(), 10.0); assert_eq!( test_maybe_select_new_conversion_rate( - TransactionStatus::Submitted(10.0), + status, Some(10.0), Some(1.0), Some(1.0), 0.0 ), - (None, TransactionStatus::Submitted(10.0)), + (None, status), ); } @@ -289,7 +324,7 @@ mod tests { fn transaction_state_is_changed_to_idle_when_stored_rate_shanges() { assert_eq!( test_maybe_select_new_conversion_rate( - TransactionStatus::Submitted(1.0), + TransactionStatus::Submitted(Instant::now(), 1.0), Some(10.0), Some(1.0), Some(1.0), @@ -368,4 +403,31 @@ mod tests { (Some((1.0, 1.03)), TransactionStatus::Idle), ); } + + #[test] + fn transaction_expires() { + let status = TransactionStatus::Submitted(Instant::now() - TEST_STALL_TIMEOUT / 2, 10.0); + assert_eq!( + test_maybe_select_new_conversion_rate( + status, + Some(10.0), + Some(1.0), + Some(1.0), + 0.0 + ), + (None, status), + ); + + let status = TransactionStatus::Submitted(Instant::now() - TEST_STALL_TIMEOUT * 2, 10.0); + assert_eq!( + test_maybe_select_new_conversion_rate( + status, + Some(10.0), + Some(1.0), + Some(1.0), + 0.0 + ), + (Some((10.0, 1.0)), TransactionStatus::Idle), + ); + } } From aca34b1b60bd7d257ccd04d43c744d1510fe5409 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 17 Dec 2021 11:42:57 +0300 Subject: [PATCH 3/4] fmt --- .../src/chains/kusama_messages_to_polkadot.rs | 3 +- .../src/chains/millau_messages_to_rialto.rs | 6 +- .../src/chains/polkadot_messages_to_kusama.rs | 3 +- .../src/chains/rialto_messages_to_millau.rs | 6 +- .../src/conversion_rate_update.rs | 56 ++++++++----------- .../lib-substrate-relay/src/messages_lane.rs | 4 +- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs b/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs index 58827c8d34..b71765ba2c 100644 --- a/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs +++ b/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs @@ -62,7 +62,8 @@ impl SubstrateMessageLane for KusamaMessagesToPolkadot { type ReceiveMessagesDeliveryProofCallBuilder = KusamaMessagesToPolkadotReceiveMessagesDeliveryProofCallBuilder; - type TargetToSourceChainConversionRateUpdateBuilder = KusamaMessagesToPolkadotUpdateConversionRateCallBuilder; + type TargetToSourceChainConversionRateUpdateBuilder = + KusamaMessagesToPolkadotUpdateConversionRateCallBuilder; type RelayStrategy = MixStrategy; } diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs b/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs index db8dcc3a5d..2db9147002 100644 --- a/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs +++ b/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs @@ -30,7 +30,8 @@ pub struct MillauMessagesToRialto; substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!( Millau, MillauMessagesToRialtoUpdateConversionRateCallBuilder, - millau_runtime::Runtime, millau_runtime::WithRialtoMessagesInstance, + millau_runtime::Runtime, + millau_runtime::WithRialtoMessagesInstance, millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate ); @@ -57,7 +58,8 @@ impl SubstrateMessageLane for MillauMessagesToRialto { millau_runtime::WithRialtoMessagesInstance, >; - type TargetToSourceChainConversionRateUpdateBuilder = MillauMessagesToRialtoUpdateConversionRateCallBuilder; + type TargetToSourceChainConversionRateUpdateBuilder = + MillauMessagesToRialtoUpdateConversionRateCallBuilder; type RelayStrategy = MixStrategy; } diff --git a/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs b/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs index 11e8044428..b284b81a0c 100644 --- a/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs +++ b/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs @@ -61,7 +61,8 @@ impl SubstrateMessageLane for PolkadotMessagesToKusama { type ReceiveMessagesDeliveryProofCallBuilder = PolkadotMessagesToKusamaReceiveMessagesDeliveryProofCallBuilder; - type TargetToSourceChainConversionRateUpdateBuilder = PolkadotMessagesToKusamaUpdateConversionRateCallBuilder; + type TargetToSourceChainConversionRateUpdateBuilder = + PolkadotMessagesToKusamaUpdateConversionRateCallBuilder; type RelayStrategy = MixStrategy; } diff --git a/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs b/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs index baa7b3545e..eb13ecc604 100644 --- a/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs +++ b/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs @@ -30,7 +30,8 @@ pub struct RialtoMessagesToMillau; substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!( Rialto, RialtoMessagesToMillauUpdateConversionRateCallBuilder, - rialto_runtime::Runtime, rialto_runtime::WithMillauMessagesInstance, + rialto_runtime::Runtime, + rialto_runtime::WithMillauMessagesInstance, rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate ); @@ -57,7 +58,8 @@ impl SubstrateMessageLane for RialtoMessagesToMillau { rialto_runtime::WithMillauMessagesInstance, >; - type TargetToSourceChainConversionRateUpdateBuilder = RialtoMessagesToMillauUpdateConversionRateCallBuilder; + type TargetToSourceChainConversionRateUpdateBuilder = + RialtoMessagesToMillauUpdateConversionRateCallBuilder; type RelayStrategy = MixStrategy; } diff --git a/relays/lib-substrate-relay/src/conversion_rate_update.rs b/relays/lib-substrate-relay/src/conversion_rate_update.rs index 2d26d50668..2f5efaf18e 100644 --- a/relays/lib-substrate-relay/src/conversion_rate_update.rs +++ b/relays/lib-substrate-relay/src/conversion_rate_update.rs @@ -19,7 +19,10 @@ use crate::{messages_lane::SubstrateMessageLane, TransactionParams}; use codec::Encode; -use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, CallOf, Chain, Client, TransactionEra, TransactionSignScheme, UnsignedTransaction, transaction_stall_timeout}; +use relay_substrate_client::{ + transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, CallOf, Chain, Client, + TransactionEra, TransactionSignScheme, UnsignedTransaction, +}; use relay_utils::metrics::F64SharedRef; use sp_core::{Bytes, Pair}; use std::time::{Duration, Instant}; @@ -43,7 +46,8 @@ enum TransactionStatus { /// Different ways of building 'update conversion rate' calls. pub trait UpdateConversionRateCallBuilder { - /// Given conversion rate, build call that updates conversion rate in given chain runtime storage. + /// Given conversion rate, build call that updates conversion rate in given chain runtime + /// storage. fn build_update_conversion_rate_call(conversion_rate: f64) -> anyhow::Result>; } @@ -170,10 +174,12 @@ pub fn run_conversion_rate_update_loop( client.clone(), transaction_params.clone(), new_conversion_rate, - ).await; + ) + .await; match result { Ok(()) => { - transaction_status = TransactionStatus::Submitted(Instant::now(), prev_conversion_rate); + transaction_status = + TransactionStatus::Submitted(Instant::now(), prev_conversion_rate); }, Err(error) => { log::error!( @@ -201,7 +207,9 @@ async fn maybe_select_new_conversion_rate( (*left_to_right_stored_conversion_rate.read().await)?; match *transaction_status { TransactionStatus::Idle => (), - TransactionStatus::Submitted(submitted_at, _) if Instant::now() - submitted_at > stall_timeout => { + TransactionStatus::Submitted(submitted_at, _) + if Instant::now() - submitted_at > stall_timeout => + { log::error!( target: "bridge", "Conversion rate update transaction has been lost and loop stalled. Restarting", @@ -209,7 +217,7 @@ async fn maybe_select_new_conversion_rate( // we assume that our transaction has been lost *transaction_status = TransactionStatus::Idle; - } + }, TransactionStatus::Submitted(_, previous_left_to_right_stored_conversion_rate) => { // we can't compare float values from different sources directly, so we only care // whether the stored rate has been changed or not. If it has been changed, then we @@ -250,16 +258,18 @@ pub async fn update_target_to_source_conversion_rate( client: Client, transaction_params: TransactionParams>, updated_rate: f64, -) -> anyhow::Result<()> where +) -> anyhow::Result<()> +where Lane: SubstrateMessageLane, Sign: TransactionSignScheme, AccountIdOf: From< as Pair>::Public>, { let genesis_hash = *client.genesis_hash(); let signer_id = transaction_params.signer.public().into(); - let call = Lane::TargetToSourceChainConversionRateUpdateBuilder::build_update_conversion_rate_call( - updated_rate - )?; + let call = + Lane::TargetToSourceChainConversionRateUpdateBuilder::build_update_conversion_rate_call( + updated_rate, + )?; client .submit_signed_extrinsic(signer_id, move |best_block_id, transaction_nonce| { Bytes( @@ -269,7 +279,7 @@ pub async fn update_target_to_source_conversion_rate( TransactionEra::new(best_block_id, transaction_params.mortality), UnsignedTransaction::new(call, transaction_nonce), ) - .encode(), + .encode(), ) }) .await @@ -309,13 +319,7 @@ mod tests { fn rate_is_not_updated_when_transaction_is_submitted() { let status = TransactionStatus::Submitted(Instant::now(), 10.0); assert_eq!( - test_maybe_select_new_conversion_rate( - status, - Some(10.0), - Some(1.0), - Some(1.0), - 0.0 - ), + test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0), (None, status), ); } @@ -408,25 +412,13 @@ mod tests { fn transaction_expires() { let status = TransactionStatus::Submitted(Instant::now() - TEST_STALL_TIMEOUT / 2, 10.0); assert_eq!( - test_maybe_select_new_conversion_rate( - status, - Some(10.0), - Some(1.0), - Some(1.0), - 0.0 - ), + test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0), (None, status), ); let status = TransactionStatus::Submitted(Instant::now() - TEST_STALL_TIMEOUT * 2, 10.0); assert_eq!( - test_maybe_select_new_conversion_rate( - status, - Some(10.0), - Some(1.0), - Some(1.0), - 0.0 - ), + test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0), (Some((10.0, 1.0)), TransactionStatus::Idle), ); } diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs index f818e7858b..0cf1e8750c 100644 --- a/relays/lib-substrate-relay/src/messages_lane.rs +++ b/relays/lib-substrate-relay/src/messages_lane.rs @@ -75,7 +75,9 @@ pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { /// `TargetChain` tokens to `SourceChain` tokens conversion rate update builder. /// /// If not applicale to this bridge, you may use `()` here. - type TargetToSourceChainConversionRateUpdateBuilder: UpdateConversionRateCallBuilder; + type TargetToSourceChainConversionRateUpdateBuilder: UpdateConversionRateCallBuilder< + Self::SourceChain, + >; /// Message relay strategy. type RelayStrategy: RelayStrategy; From ec02dd2b2c0b68d43089f0fb8a5368f60f9a5ba3 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 17 Dec 2021 11:57:05 +0300 Subject: [PATCH 4/4] fix --- relays/lib-substrate-relay/src/conversion_rate_update.rs | 6 +++--- relays/lib-substrate-relay/src/messages_lane.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/relays/lib-substrate-relay/src/conversion_rate_update.rs b/relays/lib-substrate-relay/src/conversion_rate_update.rs index 326e5f1ea1..6de56cdb9f 100644 --- a/relays/lib-substrate-relay/src/conversion_rate_update.rs +++ b/relays/lib-substrate-relay/src/conversion_rate_update.rs @@ -20,8 +20,8 @@ use crate::{messages_lane::SubstrateMessageLane, TransactionParams}; use codec::Encode; use relay_substrate_client::{ - transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, CallOf, Chain, Client, - SignParam, TransactionEra, TransactionSignScheme, UnsignedTransaction, + transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, CallOf, Chain, Client, SignParam, + TransactionEra, TransactionSignScheme, UnsignedTransaction, }; use relay_utils::metrics::F64SharedRef; use sp_core::{Bytes, Pair}; @@ -31,7 +31,7 @@ use std::time::{Duration, Instant}; const SLEEP_DURATION: Duration = Duration::from_secs(60); /// Duration which will almost never expire. Since changing conversion rate may require manual -/// intervention (e.g. if call is made through multisig pallet), we don't want relayer to +/// intervention (e.g. if call is made through `multisig` pallet), we don't want relayer to /// resubmit transaction often. const ALMOST_NEVER_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 30); diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs index 0cf1e8750c..b2f27466bd 100644 --- a/relays/lib-substrate-relay/src/messages_lane.rs +++ b/relays/lib-substrate-relay/src/messages_lane.rs @@ -74,7 +74,7 @@ pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { /// `TargetChain` tokens to `SourceChain` tokens conversion rate update builder. /// - /// If not applicale to this bridge, you may use `()` here. + /// If not applicable to this bridge, you may use `()` here. type TargetToSourceChainConversionRateUpdateBuilder: UpdateConversionRateCallBuilder< Self::SourceChain, >;