Stored conversion rate updater (#1005)

* update conversion rate: initial commit

* Rialto=Polkadot && Millau=Kusama + actually update conversion rates

* update deployment scripts and readme

* allow non-zero difference between stored and real rates

* dummy commit

* Revert "dummy commit"

This reverts commit a438198180a8385feeaaca60c9d2da0950465215.

* clippy

* #[allow(clippy::float_cmp)] in conversion rate update

* dummy

* Revert "dummy"

This reverts commit 90cd6e47cda56f655e94dbef76138e6cc58d664a.

* spell

* shared_value_ref() -> get()

* Revert "shared_value_ref() -> get()"

This reverts commit 20aa30de6a59b2099cfba3e9676e71200b7bb468.
This commit is contained in:
Svyatoslav Nikolsky
2021-09-01 11:51:57 +03:00
committed by Bastian Köcher
parent fc9363619a
commit 3ef4574594
13 changed files with 464 additions and 24 deletions
@@ -245,3 +245,33 @@ pub(crate) fn add_standalone_metrics(
)),
)
}
/// Update Rialto -> Millau conversion rate, stored in Millau runtime storage.
pub(crate) async fn update_rialto_to_millau_conversion_rate(
client: Client<Millau>,
signer: <Millau as TransactionSignScheme>::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,
transaction_nonce,
millau_runtime::MessagesCall::update_pallet_parameter(
millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate(
sp_runtime::FixedU128::from_float(updated_rate),
),
)
.into(),
)
.encode(),
)
})
.await
.map(drop)
.map_err(|err| anyhow::format_err!("{:?}", err))
}
@@ -37,9 +37,9 @@ mod wococo;
// Rialto as BTC and Millau as wBTC (only in relayer).
/// The identifier of token, which value is associated with Rialto token value by relayer.
pub(crate) const RIALTO_ASSOCIATED_TOKEN_ID: &str = "bitcoin";
pub(crate) const RIALTO_ASSOCIATED_TOKEN_ID: &str = "polkadot";
/// The identifier of token, which value is associated with Millau token value by relayer.
pub(crate) const MILLAU_ASSOCIATED_TOKEN_ID: &str = "wrapped-bitcoin";
pub(crate) const MILLAU_ASSOCIATED_TOKEN_ID: &str = "kusama";
use relay_utils::metrics::MetricsParams;
@@ -244,3 +244,33 @@ pub(crate) fn add_standalone_metrics(
)),
)
}
/// Update Millau -> Rialto conversion rate, stored in Rialto runtime storage.
pub(crate) async fn update_millau_to_rialto_conversion_rate(
client: Client<Rialto>,
signer: <Rialto as TransactionSignScheme>::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,
transaction_nonce,
rialto_runtime::MessagesCall::update_pallet_parameter(
rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate(
sp_runtime::FixedU128::from_float(updated_rate),
),
)
.into(),
)
.encode(),
)
})
.await
.map(drop)
.map_err(|err| anyhow::format_err!("{:?}", err))
}
@@ -391,6 +391,17 @@ macro_rules! declare_chain_options {
pub [<$chain_prefix _signer_password_file>]: Option<std::path::PathBuf>,
}
#[doc = "Parameters required to sign transaction on behalf of owner of the messages pallet at " $chain "."]
#[derive(StructOpt, Debug, PartialEq, Eq)]
pub struct [<$chain MessagesPalletOwnerSigningParams>] {
#[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."]
#[structopt(long)]
pub [<$chain_prefix _messages_pallet_owner>]: Option<String>,
#[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."]
#[structopt(long)]
pub [<$chain_prefix _messages_pallet_owner_password>]: Option<String>,
}
impl [<$chain SigningParams>] {
/// Parse signing params into chain-specific KeyPair.
pub fn to_keypair<Chain: CliChain>(&self) -> anyhow::Result<Chain::KeyPair> {
@@ -433,6 +444,23 @@ macro_rules! declare_chain_options {
}
}
#[allow(dead_code)]
impl [<$chain MessagesPalletOwnerSigningParams>] {
/// Parse signing params into chain-specific KeyPair.
pub fn to_keypair<Chain: CliChain>(&self) -> anyhow::Result<Option<Chain::KeyPair>> {
use sp_core::crypto::Pair;
let [<$chain_prefix _messages_pallet_owner>] = match self.[<$chain_prefix _messages_pallet_owner>] {
Some(ref messages_pallet_owner) => messages_pallet_owner,
None => return Ok(None),
};
Chain::KeyPair::from_string(
[<$chain_prefix _messages_pallet_owner>],
self.[<$chain_prefix _messages_pallet_owner_password>].as_deref()
).map_err(|e| anyhow::format_err!("{:?}", e)).map(Some)
}
}
impl [<$chain ConnectionParams>] {
/// Convert connection params into Substrate client.
pub async fn to_client<Chain: CliChain>(
@@ -26,6 +26,7 @@ use futures::{FutureExt, TryFutureExt};
use structopt::StructOpt;
use strum::VariantNames;
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
use relay_utils::metrics::MetricsParams;
use substrate_relay_helper::messages_lane::{MessagesRelayParams, SubstrateMessageLane};
use substrate_relay_helper::on_demand_headers::OnDemandHeadersRelay;
@@ -33,6 +34,14 @@ use substrate_relay_helper::on_demand_headers::OnDemandHeadersRelay;
use crate::cli::{relay_messages::RelayerMode, CliChain, HexLaneId, PrometheusParams};
use crate::declare_chain_options;
/// Maximal allowed conversion rate error ratio (abs(real - stored) / stored) that we allow.
///
/// If it is zero, then transaction will be submitted every time we see difference between
/// stored and real conversion rates. If it is large enough (e.g. > than 10 percents, which is 0.1),
/// then rational relayers may stop relaying messages because they were submitted using
/// lesser conversion rate.
const CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO: f64 = 0.05;
/// Start headers+messages relayer process.
#[derive(StructOpt)]
pub enum RelayHeadersAndMessages {
@@ -68,9 +77,13 @@ macro_rules! declare_bridge_options {
#[structopt(flatten)]
left_sign: [<$chain1 SigningParams>],
#[structopt(flatten)]
left_messages_pallet_owner: [<$chain1 MessagesPalletOwnerSigningParams>],
#[structopt(flatten)]
right: [<$chain2 ConnectionParams>],
#[structopt(flatten)]
right_sign: [<$chain2 SigningParams>],
#[structopt(flatten)]
right_messages_pallet_owner: [<$chain2 MessagesPalletOwnerSigningParams>],
}
#[allow(unreachable_patterns)]
@@ -106,9 +119,11 @@ macro_rules! select_bridge {
use crate::chains::millau_messages_to_rialto::{
add_standalone_metrics as add_left_to_right_standalone_metrics, run as left_to_right_messages,
update_rialto_to_millau_conversion_rate as update_right_to_left_conversion_rate,
};
use crate::chains::rialto_messages_to_millau::{
add_standalone_metrics as add_right_to_left_standalone_metrics, run as right_to_left_messages,
update_millau_to_rialto_conversion_rate as update_left_to_right_conversion_rate,
};
$generic
@@ -135,6 +150,22 @@ macro_rules! select_bridge {
add_standalone_metrics as add_right_to_left_standalone_metrics, run as right_to_left_messages,
};
async fn update_right_to_left_conversion_rate(
_client: Client<Left>,
_signer: <Left as TransactionSignScheme>::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<Right>,
_signer: <Right as TransactionSignScheme>::AccountKeyPair,
_updated_rate: f64,
) -> anyhow::Result<()> {
Err(anyhow::format_err!("Conversion rate is not supported by this bridge"))
}
$generic
}
}
@@ -158,16 +189,86 @@ impl RelayHeadersAndMessages {
let left_client = params.left.to_client::<Left>().await?;
let left_sign = params.left_sign.to_keypair::<Left>()?;
let left_messages_pallet_owner = params.left_messages_pallet_owner.to_keypair::<Left>()?;
let right_client = params.right.to_client::<Right>().await?;
let right_sign = params.right_sign.to_keypair::<Right>()?;
let right_messages_pallet_owner = params.right_messages_pallet_owner.to_keypair::<Right>()?;
let lanes = params.shared.lane;
let relayer_mode = params.shared.relayer_mode.into();
const METRIC_IS_SOME_PROOF: &str = "it is `None` when metric has been already registered; \
this is the command entrypoint, so nothing has been registered yet; \
qed";
let metrics_params: MetricsParams = params.shared.prometheus_params.into();
let metrics_params = relay_utils::relay_metrics(None, metrics_params).into_params();
let (metrics_params, _) = add_left_to_right_standalone_metrics(None, metrics_params, left_client.clone())?;
let (metrics_params, _) = add_right_to_left_standalone_metrics(None, metrics_params, right_client.clone())?;
let (metrics_params, left_to_right_metrics) =
add_left_to_right_standalone_metrics(None, metrics_params, left_client.clone())?;
let (metrics_params, right_to_left_metrics) =
add_right_to_left_standalone_metrics(None, metrics_params, right_client.clone())?;
if let Some(left_messages_pallet_owner) = left_messages_pallet_owner {
let left_client = left_client.clone();
substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop(
left_to_right_metrics
.target_to_source_conversion_rate
.expect(METRIC_IS_SOME_PROOF),
left_to_right_metrics
.target_to_base_conversion_rate
.clone()
.expect(METRIC_IS_SOME_PROOF),
left_to_right_metrics
.source_to_base_conversion_rate
.clone()
.expect(METRIC_IS_SOME_PROOF),
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 {
let right_client = right_client.clone();
substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop(
right_to_left_metrics
.target_to_source_conversion_rate
.expect(METRIC_IS_SOME_PROOF),
left_to_right_metrics
.source_to_base_conversion_rate
.expect(METRIC_IS_SOME_PROOF),
left_to_right_metrics
.target_to_base_conversion_rate
.expect(METRIC_IS_SOME_PROOF),
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,
)
},
);
}
let left_to_right_on_demand_headers = OnDemandHeadersRelay::new(
left_client.clone(),