mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 09:51:02 +00:00
Remove message fee + message send calls (#1642)
* remove message fee * it is compiling! * fixes + fmt * more cleanup * more cleanup * restore MessageDeliveryAndDispatchPayment since we'll need relayer rewards * started rational relayer removal * more removal * removed estimate fee subcommand * remove DispatchFeePayment * more removals * removed conversion rates && some metrics * - unneeded associated type * - OutboundMessageFee * fix benchmarks compilation * fmt * test + fix benchmarks * fix send message * clippy
This commit is contained in:
committed by
Bastian Köcher
parent
1217b2cf80
commit
8c845602cf
@@ -1,439 +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/>.
|
||||
|
||||
//! Tools for updating conversion rate that is stored in the runtime storage.
|
||||
|
||||
use crate::{messages_lane::SubstrateMessageLane, TransactionParams};
|
||||
|
||||
use relay_substrate_client::{
|
||||
transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, CallOf, Chain, ChainWithTransactions,
|
||||
Client, SignParam, TransactionEra, UnsignedTransaction,
|
||||
};
|
||||
use relay_utils::metrics::F64SharedRef;
|
||||
use sp_core::Pair;
|
||||
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(Instant, f64),
|
||||
}
|
||||
|
||||
/// Different ways of building 'update conversion rate' calls.
|
||||
pub trait UpdateConversionRateCallBuilder<C: Chain> {
|
||||
/// 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<CallOf<C>>;
|
||||
}
|
||||
|
||||
impl<C: Chain> UpdateConversionRateCallBuilder<C> for () {
|
||||
fn build_update_conversion_rate_call(_conversion_rate: f64) -> anyhow::Result<CallOf<C>> {
|
||||
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<relay_substrate_client::CallOf<$source_chain>> {
|
||||
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<relay_substrate_client::CallOf<$source_chain>> {
|
||||
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<Lane>(
|
||||
client: Client<Lane::SourceChain>,
|
||||
transaction_params: TransactionParams<AccountKeyPairOf<Lane::SourceChain>>,
|
||||
left_to_right_stored_conversion_rate: F64SharedRef,
|
||||
left_to_base_conversion_rate: F64SharedRef,
|
||||
right_to_base_conversion_rate: F64SharedRef,
|
||||
max_difference_ratio: f64,
|
||||
) where
|
||||
Lane: SubstrateMessageLane,
|
||||
Lane::SourceChain: ChainWithTransactions,
|
||||
AccountIdOf<Lane::SourceChain>: From<<AccountKeyPairOf<Lane::SourceChain> 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,
|
||||
&right_to_base_conversion_rate,
|
||||
max_difference_ratio,
|
||||
)
|
||||
.await;
|
||||
if let Some((prev_conversion_rate, new_conversion_rate)) = maybe_new_conversion_rate {
|
||||
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::<Lane>(
|
||||
client.clone(),
|
||||
transaction_params.clone(),
|
||||
new_conversion_rate,
|
||||
)
|
||||
.await;
|
||||
match result {
|
||||
Ok(()) => {
|
||||
transaction_status =
|
||||
TransactionStatus::Submitted(Instant::now(), prev_conversion_rate);
|
||||
},
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
target: "bridge",
|
||||
"Failed to submit conversion rate update transaction: {:?}",
|
||||
error,
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 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,
|
||||
right_to_base_conversion_rate: &F64SharedRef,
|
||||
max_difference_ratio: f64,
|
||||
) -> Option<(f64, f64)> {
|
||||
let left_to_right_stored_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 =>
|
||||
{
|
||||
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.
|
||||
//
|
||||
// float comparison is ok here, because we compare same-origin (stored in runtime
|
||||
// storage) values and if they are different, it means that the value has actually been
|
||||
// updated
|
||||
#[allow(clippy::float_cmp)]
|
||||
if previous_left_to_right_stored_conversion_rate == left_to_right_stored_conversion_rate
|
||||
{
|
||||
// the rate has not been changed => we won't submit any transactions until it is
|
||||
// accepted, or the rate is changed by someone else
|
||||
return None
|
||||
}
|
||||
|
||||
*transaction_status = TransactionStatus::Idle;
|
||||
},
|
||||
}
|
||||
|
||||
let left_to_base_conversion_rate = (*left_to_base_conversion_rate.read().await)?;
|
||||
let right_to_base_conversion_rate = (*right_to_base_conversion_rate.read().await)?;
|
||||
let actual_left_to_right_conversion_rate =
|
||||
left_to_base_conversion_rate / right_to_base_conversion_rate;
|
||||
|
||||
let rate_difference =
|
||||
(actual_left_to_right_conversion_rate - left_to_right_stored_conversion_rate).abs();
|
||||
let rate_difference_ratio = rate_difference / left_to_right_stored_conversion_rate;
|
||||
if rate_difference_ratio < max_difference_ratio {
|
||||
return None
|
||||
}
|
||||
|
||||
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<Lane>(
|
||||
client: Client<Lane::SourceChain>,
|
||||
transaction_params: TransactionParams<AccountKeyPairOf<Lane::SourceChain>>,
|
||||
updated_rate: f64,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
Lane: SubstrateMessageLane,
|
||||
Lane::SourceChain: ChainWithTransactions,
|
||||
AccountIdOf<Lane::SourceChain>: From<<AccountKeyPairOf<Lane::SourceChain> as Pair>::Public>,
|
||||
{
|
||||
let genesis_hash = *client.genesis_hash();
|
||||
let signer_id = transaction_params.signer.public().into();
|
||||
let (spec_version, transaction_version) = client.simple_runtime_version().await?;
|
||||
let call =
|
||||
Lane::TargetToSourceChainConversionRateUpdateBuilder::build_update_conversion_rate_call(
|
||||
updated_rate,
|
||||
)?;
|
||||
client
|
||||
.submit_signed_extrinsic(
|
||||
signer_id,
|
||||
SignParam::<Lane::SourceChain> {
|
||||
spec_version,
|
||||
transaction_version,
|
||||
genesis_hash,
|
||||
signer: transaction_params.signer,
|
||||
},
|
||||
move |best_block_id, transaction_nonce| {
|
||||
Ok(UnsignedTransaction::new(call.into(), transaction_nonce)
|
||||
.era(TransactionEra::new(best_block_id, transaction_params.mortality)))
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map(drop)
|
||||
.map_err(|err| anyhow::format_err!("{:?}", err))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
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<f64>,
|
||||
left_to_base_conversion_rate: Option<f64>,
|
||||
right_to_base_conversion_rate: Option<f64>,
|
||||
max_difference_ratio: f64,
|
||||
) -> (Option<(f64, f64)>, TransactionStatus) {
|
||||
let stored_conversion_rate = Arc::new(RwLock::new(stored_conversion_rate));
|
||||
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,
|
||||
&right_to_base_conversion_rate,
|
||||
max_difference_ratio,
|
||||
));
|
||||
(result, transaction_status)
|
||||
}
|
||||
|
||||
#[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(status, Some(10.0), Some(1.0), Some(1.0), 0.0),
|
||||
(None, status),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transaction_state_is_changed_to_idle_when_stored_rate_shanges() {
|
||||
assert_eq!(
|
||||
test_maybe_select_new_conversion_rate(
|
||||
TransactionStatus::Submitted(Instant::now(), 1.0),
|
||||
Some(10.0),
|
||||
Some(1.0),
|
||||
Some(1.0),
|
||||
100.0
|
||||
),
|
||||
(None, TransactionStatus::Idle),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transaction_is_not_submitted_when_left_to_base_rate_is_unknown() {
|
||||
assert_eq!(
|
||||
test_maybe_select_new_conversion_rate(
|
||||
TransactionStatus::Idle,
|
||||
Some(10.0),
|
||||
None,
|
||||
Some(1.0),
|
||||
0.0
|
||||
),
|
||||
(None, TransactionStatus::Idle),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transaction_is_not_submitted_when_right_to_base_rate_is_unknown() {
|
||||
assert_eq!(
|
||||
test_maybe_select_new_conversion_rate(
|
||||
TransactionStatus::Idle,
|
||||
Some(10.0),
|
||||
Some(1.0),
|
||||
None,
|
||||
0.0
|
||||
),
|
||||
(None, TransactionStatus::Idle),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transaction_is_not_submitted_when_stored_rate_is_unknown() {
|
||||
assert_eq!(
|
||||
test_maybe_select_new_conversion_rate(
|
||||
TransactionStatus::Idle,
|
||||
None,
|
||||
Some(1.0),
|
||||
Some(1.0),
|
||||
0.0
|
||||
),
|
||||
(None, TransactionStatus::Idle),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transaction_is_not_submitted_when_difference_is_below_threshold() {
|
||||
assert_eq!(
|
||||
test_maybe_select_new_conversion_rate(
|
||||
TransactionStatus::Idle,
|
||||
Some(1.0),
|
||||
Some(1.0),
|
||||
Some(1.01),
|
||||
0.02
|
||||
),
|
||||
(None, TransactionStatus::Idle),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transaction_is_submitted_when_difference_is_above_threshold() {
|
||||
let left_to_right_stored_conversion_rate = 1.0;
|
||||
let left_to_base_conversion_rate = 18f64;
|
||||
let right_to_base_conversion_rate = 180f64;
|
||||
|
||||
assert!(left_to_base_conversion_rate < right_to_base_conversion_rate);
|
||||
|
||||
assert_eq!(
|
||||
test_maybe_select_new_conversion_rate(
|
||||
TransactionStatus::Idle,
|
||||
Some(left_to_right_stored_conversion_rate),
|
||||
Some(left_to_base_conversion_rate),
|
||||
Some(right_to_base_conversion_rate),
|
||||
0.02
|
||||
),
|
||||
(
|
||||
Some((
|
||||
left_to_right_stored_conversion_rate,
|
||||
left_to_base_conversion_rate / right_to_base_conversion_rate,
|
||||
)),
|
||||
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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,106 +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/>.
|
||||
|
||||
//! Substrate relay helpers
|
||||
|
||||
use relay_utils::metrics::{FloatJsonValueMetric, PrometheusError, StandaloneMetric};
|
||||
|
||||
/// Creates standalone token price metric.
|
||||
pub fn token_price_metric(token_id: &str) -> Result<FloatJsonValueMetric, PrometheusError> {
|
||||
FloatJsonValueMetric::new(
|
||||
format!("https://api.coingecko.com/api/v3/simple/price?ids={token_id}&vs_currencies=btc"),
|
||||
format!("$.{token_id}.btc"),
|
||||
format!("{}_to_base_conversion_rate", token_id.replace('-', "_")),
|
||||
format!("Rate used to convert from {} to some BASE tokens", token_id.to_uppercase()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute conversion rate between two tokens immediately, without spawning any metrics.
|
||||
///
|
||||
/// Returned rate may be used in expression: `from_tokens * rate -> to_tokens`.
|
||||
pub async fn tokens_conversion_rate_from_metrics(
|
||||
from_token_id: &str,
|
||||
to_token_id: &str,
|
||||
) -> anyhow::Result<f64> {
|
||||
let from_token_metric = token_price_metric(from_token_id)?;
|
||||
from_token_metric.update().await;
|
||||
let to_token_metric = token_price_metric(to_token_id)?;
|
||||
to_token_metric.update().await;
|
||||
|
||||
let from_token_value = *from_token_metric.shared_value_ref().read().await;
|
||||
let to_token_value = *to_token_metric.shared_value_ref().read().await;
|
||||
// `FloatJsonValueMetric` guarantees that the value is positive && normal, so no additional
|
||||
// checks required here
|
||||
match (from_token_value, to_token_value) {
|
||||
(Some(from_token_value), Some(to_token_value)) =>
|
||||
Ok(tokens_conversion_rate(from_token_value, to_token_value)),
|
||||
_ => Err(anyhow::format_err!(
|
||||
"Failed to compute conversion rate from {} to {}",
|
||||
from_token_id,
|
||||
to_token_id,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute conversion rate between two tokens, given token prices.
|
||||
///
|
||||
/// Returned rate may be used in expression: `from_tokens * rate -> to_tokens`.
|
||||
///
|
||||
/// Both prices are assumed to be normal and non-negative.
|
||||
pub fn tokens_conversion_rate(from_token_value: f64, to_token_value: f64) -> f64 {
|
||||
from_token_value / to_token_value
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn rialto_to_millau_conversion_rate_is_correct() {
|
||||
let rialto_price = 18.18;
|
||||
let millau_price = 136.35;
|
||||
assert!(rialto_price < millau_price);
|
||||
|
||||
let conversion_rate = tokens_conversion_rate(rialto_price, millau_price);
|
||||
let rialto_amount = 100.0;
|
||||
let millau_amount = rialto_amount * conversion_rate;
|
||||
assert!(
|
||||
rialto_amount > millau_amount,
|
||||
"{} RLT * {} = {} MLU",
|
||||
rialto_amount,
|
||||
conversion_rate,
|
||||
millau_amount,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn millau_to_rialto_conversion_rate_is_correct() {
|
||||
let rialto_price = 18.18;
|
||||
let millau_price = 136.35;
|
||||
assert!(rialto_price < millau_price);
|
||||
|
||||
let conversion_rate = tokens_conversion_rate(millau_price, rialto_price);
|
||||
let millau_amount = 100.0;
|
||||
let rialto_amount = millau_amount * conversion_rate;
|
||||
assert!(
|
||||
rialto_amount > millau_amount,
|
||||
"{} MLU * {} = {} RLT",
|
||||
millau_amount,
|
||||
conversion_rate,
|
||||
rialto_amount,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,8 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod conversion_rate_update;
|
||||
pub mod error;
|
||||
pub mod finality;
|
||||
pub mod helpers;
|
||||
pub mod messages_lane;
|
||||
pub mod messages_metrics;
|
||||
pub mod messages_source;
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
//! 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},
|
||||
on_demand::OnDemandRelay,
|
||||
@@ -33,49 +31,21 @@ use bridge_runtime_common::messages::{
|
||||
};
|
||||
use codec::Encode;
|
||||
use frame_support::{dispatch::GetDispatchInfo, weights::Weight};
|
||||
use messages_relay::{message_lane::MessageLane, relay_strategy::RelayStrategy};
|
||||
use messages_relay::message_lane::MessageLane;
|
||||
use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig};
|
||||
use relay_substrate_client::{
|
||||
transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain,
|
||||
ChainWithMessages, ChainWithTransactions, Client, HashOf,
|
||||
};
|
||||
use relay_utils::{metrics::MetricsParams, STALL_TIMEOUT};
|
||||
use relay_utils::{
|
||||
metrics::{GlobalMetrics, MetricsParams, StandaloneMetric},
|
||||
STALL_TIMEOUT,
|
||||
};
|
||||
use sp_core::Pair;
|
||||
use std::{convert::TryFrom, fmt::Debug, marker::PhantomData};
|
||||
|
||||
/// Substrate -> Substrate messages synchronization pipeline.
|
||||
pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync {
|
||||
/// Name of the source -> target tokens conversion rate parameter.
|
||||
///
|
||||
/// The parameter is stored at the target chain and the storage key is computed using
|
||||
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
|
||||
/// to be 1.
|
||||
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str>;
|
||||
/// Name of the target -> source tokens conversion rate parameter.
|
||||
///
|
||||
/// The parameter is stored at the source chain and the storage key is computed using
|
||||
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
|
||||
/// to be 1.
|
||||
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str>;
|
||||
|
||||
/// Name of the source chain fee multiplier parameter.
|
||||
///
|
||||
/// The parameter is stored at the target chain and the storage key is computed using
|
||||
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
|
||||
/// to be 1.
|
||||
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str>;
|
||||
/// Name of the target chain fee multiplier parameter.
|
||||
///
|
||||
/// The parameter is stored at the source chain and the storage key is computed using
|
||||
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
|
||||
/// to be 1.
|
||||
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str>;
|
||||
|
||||
/// Name of the transaction payment pallet, deployed at the source chain.
|
||||
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str>;
|
||||
/// Name of the transaction payment pallet, deployed at the target chain.
|
||||
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str>;
|
||||
|
||||
/// Messages of this chain are relayed to the `TargetChain`.
|
||||
type SourceChain: ChainWithMessages + ChainWithTransactions;
|
||||
/// Messages from the `SourceChain` are dispatched on this chain.
|
||||
@@ -85,16 +55,6 @@ pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync {
|
||||
type ReceiveMessagesProofCallBuilder: ReceiveMessagesProofCallBuilder<Self>;
|
||||
/// How receive messages delivery proof call is built?
|
||||
type ReceiveMessagesDeliveryProofCallBuilder: ReceiveMessagesDeliveryProofCallBuilder<Self>;
|
||||
|
||||
/// `TargetChain` tokens to `SourceChain` tokens conversion rate update builder.
|
||||
///
|
||||
/// If not applicable to this bridge, you may use `()` here.
|
||||
type TargetToSourceChainConversionRateUpdateBuilder: UpdateConversionRateCallBuilder<
|
||||
Self::SourceChain,
|
||||
>;
|
||||
|
||||
/// Message relay strategy.
|
||||
type RelayStrategy: RelayStrategy;
|
||||
}
|
||||
|
||||
/// Adapter that allows all `SubstrateMessageLane` to act as `MessageLane`.
|
||||
@@ -138,10 +98,6 @@ pub struct MessagesRelayParams<P: SubstrateMessageLane> {
|
||||
pub lane_id: LaneId,
|
||||
/// Metrics parameters.
|
||||
pub metrics_params: MetricsParams,
|
||||
/// Pre-registered standalone metrics.
|
||||
pub standalone_metrics: Option<StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>>,
|
||||
/// Relay strategy.
|
||||
pub relay_strategy: P::RelayStrategy,
|
||||
}
|
||||
|
||||
/// Run Substrate-to-Substrate messages sync loop.
|
||||
@@ -170,13 +126,6 @@ where
|
||||
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
|
||||
(max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2);
|
||||
|
||||
let standalone_metrics = params.standalone_metrics.map(Ok).unwrap_or_else(|| {
|
||||
crate::messages_metrics::standalone_metrics::<P>(
|
||||
source_client.clone(),
|
||||
target_client.clone(),
|
||||
)
|
||||
})?;
|
||||
|
||||
log::info!(
|
||||
target: "bridge",
|
||||
"Starting {} -> {} messages relay.\n\t\
|
||||
@@ -220,7 +169,6 @@ where
|
||||
max_messages_in_single_batch,
|
||||
max_messages_weight_in_single_batch,
|
||||
max_messages_size_in_single_batch,
|
||||
relay_strategy: params.relay_strategy,
|
||||
},
|
||||
},
|
||||
SubstrateMessagesSource::<P>::new(
|
||||
@@ -236,10 +184,12 @@ where
|
||||
params.lane_id,
|
||||
relayer_id_at_source,
|
||||
params.target_transaction_params,
|
||||
standalone_metrics.clone(),
|
||||
params.source_to_target_headers_relay,
|
||||
),
|
||||
standalone_metrics.register_and_spawn(params.metrics_params)?,
|
||||
{
|
||||
GlobalMetrics::new()?.register_and_spawn(¶ms.metrics_params.registry)?;
|
||||
params.metrics_params
|
||||
},
|
||||
futures::future::pending(),
|
||||
)
|
||||
.await
|
||||
@@ -271,7 +221,6 @@ where
|
||||
R: BridgeMessagesConfig<I, InboundRelayer = AccountIdOf<P::SourceChain>>,
|
||||
I: 'static,
|
||||
R::SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain<
|
||||
R::InboundMessageFee,
|
||||
MessagesProof = FromBridgedChainMessagesProof<HashOf<P::SourceChain>>,
|
||||
>,
|
||||
CallOf<P::TargetChain>: From<BridgeMessagesCall<R, I>> + GetDispatchInfo,
|
||||
|
||||
@@ -16,260 +16,20 @@
|
||||
|
||||
//! Tools for supporting message lanes between two Substrate-based chains.
|
||||
|
||||
use crate::{helpers::tokens_conversion_rate, messages_lane::SubstrateMessageLane, TaggedAccount};
|
||||
use crate::TaggedAccount;
|
||||
|
||||
use codec::Decode;
|
||||
use frame_system::AccountInfo;
|
||||
use pallet_balances::AccountData;
|
||||
use relay_substrate_client::{
|
||||
metrics::{
|
||||
FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric, StorageProofOverheadMetric,
|
||||
},
|
||||
metrics::{FloatStorageValue, FloatStorageValueMetric},
|
||||
AccountIdOf, BalanceOf, Chain, ChainWithBalances, Client, Error as SubstrateError, IndexOf,
|
||||
};
|
||||
use relay_utils::metrics::{
|
||||
FloatJsonValueMetric, GlobalMetrics, MetricsParams, PrometheusError, StandaloneMetric,
|
||||
};
|
||||
use relay_utils::metrics::{MetricsParams, StandaloneMetric};
|
||||
use sp_core::storage::StorageData;
|
||||
use sp_runtime::{FixedPointNumber, FixedU128};
|
||||
use std::{convert::TryFrom, fmt::Debug, marker::PhantomData};
|
||||
|
||||
/// Name of the `NextFeeMultiplier` storage value within the transaction payment pallet.
|
||||
const NEXT_FEE_MULTIPLIER_VALUE_NAME: &str = "NextFeeMultiplier";
|
||||
|
||||
/// Shared references to the standalone metrics of the message lane relay loop.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StandaloneMessagesMetrics<SC: Chain, TC: Chain> {
|
||||
/// Global metrics.
|
||||
pub global: GlobalMetrics,
|
||||
/// Storage chain proof overhead metric.
|
||||
pub source_storage_proof_overhead: StorageProofOverheadMetric<SC>,
|
||||
/// Target chain proof overhead metric.
|
||||
pub target_storage_proof_overhead: StorageProofOverheadMetric<TC>,
|
||||
/// Source tokens to base conversion rate metric.
|
||||
pub source_to_base_conversion_rate: Option<FloatJsonValueMetric>,
|
||||
/// Target tokens to base conversion rate metric.
|
||||
pub target_to_base_conversion_rate: Option<FloatJsonValueMetric>,
|
||||
/// Source tokens to target tokens conversion rate metric. This rate is stored by the target
|
||||
/// chain.
|
||||
pub source_to_target_conversion_rate: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>,
|
||||
/// Target tokens to source tokens conversion rate metric. This rate is stored by the source
|
||||
/// chain.
|
||||
pub target_to_source_conversion_rate: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>,
|
||||
|
||||
/// Actual source chain fee multiplier.
|
||||
pub source_fee_multiplier: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>,
|
||||
/// Source chain fee multiplier, stored at the target chain.
|
||||
pub source_fee_multiplier_at_target: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>,
|
||||
/// Actual target chain fee multiplier.
|
||||
pub target_fee_multiplier: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>,
|
||||
/// Target chain fee multiplier, stored at the target chain.
|
||||
pub target_fee_multiplier_at_source: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>,
|
||||
}
|
||||
|
||||
impl<SC: Chain, TC: Chain> StandaloneMessagesMetrics<SC, TC> {
|
||||
/// Swap source and target sides.
|
||||
pub fn reverse(self) -> StandaloneMessagesMetrics<TC, SC> {
|
||||
StandaloneMessagesMetrics {
|
||||
global: self.global,
|
||||
source_storage_proof_overhead: self.target_storage_proof_overhead,
|
||||
target_storage_proof_overhead: self.source_storage_proof_overhead,
|
||||
source_to_base_conversion_rate: self.target_to_base_conversion_rate,
|
||||
target_to_base_conversion_rate: self.source_to_base_conversion_rate,
|
||||
source_to_target_conversion_rate: self.target_to_source_conversion_rate,
|
||||
target_to_source_conversion_rate: self.source_to_target_conversion_rate,
|
||||
source_fee_multiplier: self.target_fee_multiplier,
|
||||
source_fee_multiplier_at_target: self.target_fee_multiplier_at_source,
|
||||
target_fee_multiplier: self.source_fee_multiplier,
|
||||
target_fee_multiplier_at_source: self.source_fee_multiplier_at_target,
|
||||
}
|
||||
}
|
||||
|
||||
/// Register all metrics in the registry.
|
||||
pub fn register_and_spawn(
|
||||
self,
|
||||
metrics: MetricsParams,
|
||||
) -> Result<MetricsParams, PrometheusError> {
|
||||
self.global.register_and_spawn(&metrics.registry)?;
|
||||
self.source_storage_proof_overhead.register_and_spawn(&metrics.registry)?;
|
||||
self.target_storage_proof_overhead.register_and_spawn(&metrics.registry)?;
|
||||
if let Some(m) = self.source_to_base_conversion_rate {
|
||||
m.register_and_spawn(&metrics.registry)?;
|
||||
}
|
||||
if let Some(m) = self.target_to_base_conversion_rate {
|
||||
m.register_and_spawn(&metrics.registry)?;
|
||||
}
|
||||
if let Some(m) = self.target_to_source_conversion_rate {
|
||||
m.register_and_spawn(&metrics.registry)?;
|
||||
}
|
||||
if let Some(m) = self.source_fee_multiplier {
|
||||
m.register_and_spawn(&metrics.registry)?;
|
||||
}
|
||||
if let Some(m) = self.source_fee_multiplier_at_target {
|
||||
m.register_and_spawn(&metrics.registry)?;
|
||||
}
|
||||
if let Some(m) = self.target_fee_multiplier {
|
||||
m.register_and_spawn(&metrics.registry)?;
|
||||
}
|
||||
if let Some(m) = self.target_fee_multiplier_at_source {
|
||||
m.register_and_spawn(&metrics.registry)?;
|
||||
}
|
||||
Ok(metrics)
|
||||
}
|
||||
|
||||
/// Return conversion rate from target to source tokens.
|
||||
pub async fn target_to_source_conversion_rate(&self) -> Option<f64> {
|
||||
let from_token_value =
|
||||
(*self.target_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await)?;
|
||||
let to_token_value =
|
||||
(*self.source_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await)?;
|
||||
Some(tokens_conversion_rate(from_token_value, to_token_value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create symmetric standalone metrics for the message lane relay loop.
|
||||
///
|
||||
/// All metrics returned by this function are exposed by loops that are serving given lane (`P`)
|
||||
/// and by loops that are serving reverse lane (`P` with swapped `TargetChain` and `SourceChain`).
|
||||
/// We assume that either conversion rate parameters have values in the storage, or they are
|
||||
/// initialized with 1:1.
|
||||
pub fn standalone_metrics<P: SubstrateMessageLane>(
|
||||
source_client: Client<P::SourceChain>,
|
||||
target_client: Client<P::TargetChain>,
|
||||
) -> anyhow::Result<StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>> {
|
||||
Ok(StandaloneMessagesMetrics {
|
||||
global: GlobalMetrics::new()?,
|
||||
source_storage_proof_overhead: StorageProofOverheadMetric::new(
|
||||
source_client.clone(),
|
||||
format!("{}_storage_proof_overhead", P::SourceChain::NAME.to_lowercase()),
|
||||
format!("{} storage proof overhead", P::SourceChain::NAME),
|
||||
)?,
|
||||
target_storage_proof_overhead: StorageProofOverheadMetric::new(
|
||||
target_client.clone(),
|
||||
format!("{}_storage_proof_overhead", P::TargetChain::NAME.to_lowercase()),
|
||||
format!("{} storage proof overhead", P::TargetChain::NAME),
|
||||
)?,
|
||||
source_to_base_conversion_rate: P::SourceChain::TOKEN_ID
|
||||
.map(|source_chain_token_id| {
|
||||
crate::helpers::token_price_metric(source_chain_token_id).map(Some)
|
||||
})
|
||||
.unwrap_or(Ok(None))?,
|
||||
target_to_base_conversion_rate: P::TargetChain::TOKEN_ID
|
||||
.map(|target_chain_token_id| {
|
||||
crate::helpers::token_price_metric(target_chain_token_id).map(Some)
|
||||
})
|
||||
.unwrap_or(Ok(None))?,
|
||||
source_to_target_conversion_rate: P::SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME
|
||||
.map(bp_runtime::storage_parameter_key)
|
||||
.map(|key| {
|
||||
FloatStorageValueMetric::new(
|
||||
FixedU128OrOne::default(),
|
||||
target_client.clone(),
|
||||
key,
|
||||
format!(
|
||||
"{}_{}_to_{}_conversion_rate",
|
||||
P::TargetChain::NAME,
|
||||
P::SourceChain::NAME,
|
||||
P::TargetChain::NAME
|
||||
),
|
||||
format!(
|
||||
"{} to {} tokens conversion rate (used by {})",
|
||||
P::SourceChain::NAME,
|
||||
P::TargetChain::NAME,
|
||||
P::TargetChain::NAME
|
||||
),
|
||||
)
|
||||
.map(Some)
|
||||
})
|
||||
.unwrap_or(Ok(None))?,
|
||||
target_to_source_conversion_rate: P::TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME
|
||||
.map(bp_runtime::storage_parameter_key)
|
||||
.map(|key| {
|
||||
FloatStorageValueMetric::new(
|
||||
FixedU128OrOne::default(),
|
||||
source_client.clone(),
|
||||
key,
|
||||
format!(
|
||||
"{}_{}_to_{}_conversion_rate",
|
||||
P::SourceChain::NAME,
|
||||
P::TargetChain::NAME,
|
||||
P::SourceChain::NAME
|
||||
),
|
||||
format!(
|
||||
"{} to {} tokens conversion rate (used by {})",
|
||||
P::TargetChain::NAME,
|
||||
P::SourceChain::NAME,
|
||||
P::SourceChain::NAME
|
||||
),
|
||||
)
|
||||
.map(Some)
|
||||
})
|
||||
.unwrap_or(Ok(None))?,
|
||||
source_fee_multiplier: P::AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME
|
||||
.map(|pallet| bp_runtime::storage_value_key(pallet, NEXT_FEE_MULTIPLIER_VALUE_NAME))
|
||||
.map(|key| {
|
||||
log::trace!(target: "bridge", "{}_fee_multiplier", P::SourceChain::NAME);
|
||||
FloatStorageValueMetric::new(
|
||||
FixedU128OrOne::default(),
|
||||
source_client.clone(),
|
||||
key,
|
||||
format!("{}_fee_multiplier", P::SourceChain::NAME,),
|
||||
format!("{} fee multiplier", P::SourceChain::NAME,),
|
||||
)
|
||||
.map(Some)
|
||||
})
|
||||
.unwrap_or(Ok(None))?,
|
||||
source_fee_multiplier_at_target: P::SOURCE_FEE_MULTIPLIER_PARAMETER_NAME
|
||||
.map(bp_runtime::storage_parameter_key)
|
||||
.map(|key| {
|
||||
FloatStorageValueMetric::new(
|
||||
FixedU128OrOne::default(),
|
||||
target_client.clone(),
|
||||
key,
|
||||
format!("{}_{}_fee_multiplier", P::TargetChain::NAME, P::SourceChain::NAME,),
|
||||
format!(
|
||||
"{} fee multiplier stored at {}",
|
||||
P::SourceChain::NAME,
|
||||
P::TargetChain::NAME,
|
||||
),
|
||||
)
|
||||
.map(Some)
|
||||
})
|
||||
.unwrap_or(Ok(None))?,
|
||||
target_fee_multiplier: P::AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME
|
||||
.map(|pallet| bp_runtime::storage_value_key(pallet, NEXT_FEE_MULTIPLIER_VALUE_NAME))
|
||||
.map(|key| {
|
||||
log::trace!(target: "bridge", "{}_fee_multiplier", P::TargetChain::NAME);
|
||||
FloatStorageValueMetric::new(
|
||||
FixedU128OrOne::default(),
|
||||
target_client,
|
||||
key,
|
||||
format!("{}_fee_multiplier", P::TargetChain::NAME,),
|
||||
format!("{} fee multiplier", P::TargetChain::NAME,),
|
||||
)
|
||||
.map(Some)
|
||||
})
|
||||
.unwrap_or(Ok(None))?,
|
||||
target_fee_multiplier_at_source: P::TARGET_FEE_MULTIPLIER_PARAMETER_NAME
|
||||
.map(bp_runtime::storage_parameter_key)
|
||||
.map(|key| {
|
||||
FloatStorageValueMetric::new(
|
||||
FixedU128OrOne::default(),
|
||||
source_client,
|
||||
key,
|
||||
format!("{}_{}_fee_multiplier", P::SourceChain::NAME, P::TargetChain::NAME,),
|
||||
format!(
|
||||
"{} fee multiplier stored at {}",
|
||||
P::TargetChain::NAME,
|
||||
P::SourceChain::NAME,
|
||||
),
|
||||
)
|
||||
.map(Some)
|
||||
})
|
||||
.unwrap_or(Ok(None))?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add relay accounts balance metrics.
|
||||
pub async fn add_relay_balances_metrics<C: ChainWithBalances>(
|
||||
client: Client<C>,
|
||||
@@ -359,9 +119,6 @@ fn convert_to_token_balance(balance: u128, token_decimals: u32) -> FixedU128 {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use frame_support::storage::generator::StorageValue;
|
||||
use sp_core::storage::StorageKey;
|
||||
|
||||
#[test]
|
||||
fn token_decimals_used_properly() {
|
||||
let plancks = 425_000_000_000;
|
||||
@@ -369,12 +126,4 @@ mod tests {
|
||||
let dots = convert_to_token_balance(plancks, token_decimals);
|
||||
assert_eq!(dots, FixedU128::saturating_from_rational(425, 10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_fee_multiplier_storage_key_is_correct() {
|
||||
assert_eq!(
|
||||
bp_runtime::storage_value_key("TransactionPayment", NEXT_FEE_MULTIPLIER_VALUE_NAME),
|
||||
StorageKey(pallet_transaction_payment::NextFeeMultiplier::<rialto_runtime::Runtime>::storage_value_final_key().to_vec()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,11 @@ use async_std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use bp_messages::{
|
||||
storage_keys::{operating_mode_key, outbound_lane_data_key},
|
||||
InboundMessageDetails, LaneId, MessageData, MessageNonce, MessagePayload,
|
||||
MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayersState,
|
||||
};
|
||||
use bp_runtime::{messages::DispatchFeePayment, BasicOperatingMode, HeaderIdProvider};
|
||||
use bridge_runtime_common::messages::{
|
||||
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
|
||||
InboundMessageDetails, LaneId, MessageNonce, MessagePayload, MessagesOperatingMode,
|
||||
OutboundLaneData, OutboundMessageDetails,
|
||||
};
|
||||
use bp_runtime::{BasicOperatingMode, HeaderIdProvider};
|
||||
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::weights::Weight;
|
||||
use messages_relay::{
|
||||
@@ -47,11 +45,11 @@ use messages_relay::{
|
||||
SourceClientState,
|
||||
},
|
||||
};
|
||||
use num_traits::{Bounded, Zero};
|
||||
use num_traits::Zero;
|
||||
use relay_substrate_client::{
|
||||
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages,
|
||||
ChainWithTransactions, Client, Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam,
|
||||
TransactionEra, TransactionTracker, UnsignedTransaction,
|
||||
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client,
|
||||
Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra,
|
||||
TransactionTracker, UnsignedTransaction,
|
||||
};
|
||||
use relay_utils::{relay_loop::Client as RelayClient, HeaderId};
|
||||
use sp_core::{Bytes, Pair};
|
||||
@@ -62,7 +60,7 @@ use std::ops::RangeInclusive;
|
||||
/// required to submit to the target node: cumulative dispatch weight of bundled messages and
|
||||
/// the proof itself.
|
||||
pub type SubstrateMessagesProof<C> = (Weight, FromBridgedChainMessagesProof<HashOf<C>>);
|
||||
type MessagesToRefine<'a, Balance> = Vec<(MessagePayload, &'a mut OutboundMessageDetails<Balance>)>;
|
||||
type MessagesToRefine<'a> = Vec<(MessagePayload, &'a mut OutboundMessageDetails)>;
|
||||
|
||||
/// Substrate client as Substrate messages source.
|
||||
pub struct SubstrateMessagesSource<P: SubstrateMessageLane> {
|
||||
@@ -207,9 +205,7 @@ where
|
||||
// prepare arguments of the inbound message details call (if we need it)
|
||||
let mut msgs_to_refine = vec![];
|
||||
for out_msg_details in out_msgs_details.iter_mut() {
|
||||
if out_msg_details.dispatch_fee_payment != DispatchFeePayment::AtTargetChain {
|
||||
continue
|
||||
}
|
||||
// in our current strategy all messages are supposed to be paid at the target chain
|
||||
|
||||
// for pay-at-target messages we may want to ask target chain for
|
||||
// refined dispatch weight
|
||||
@@ -218,7 +214,7 @@ where
|
||||
&self.lane_id,
|
||||
out_msg_details.nonce,
|
||||
);
|
||||
let msg_data: MessageData<BalanceOf<P::SourceChain>> =
|
||||
let msg_payload: MessagePayload =
|
||||
self.source_client.storage_value(msg_key, Some(id.1)).await?.ok_or_else(|| {
|
||||
SubstrateError::Custom(format!(
|
||||
"Message to {} {:?}/{} is missing from runtime the storage of {} at {:?}",
|
||||
@@ -230,7 +226,7 @@ where
|
||||
))
|
||||
})?;
|
||||
|
||||
msgs_to_refine.push((msg_data.payload, out_msg_details));
|
||||
msgs_to_refine.push((msg_payload, out_msg_details));
|
||||
}
|
||||
|
||||
for mut msgs_to_refine_batch in
|
||||
@@ -277,8 +273,7 @@ where
|
||||
MessageDetails {
|
||||
dispatch_weight: out_msg_details.dispatch_weight,
|
||||
size: out_msg_details.size as _,
|
||||
reward: out_msg_details.delivery_and_dispatch_fee,
|
||||
dispatch_fee_payment: out_msg_details.dispatch_fee_payment,
|
||||
reward: Zero::zero(),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -370,39 +365,6 @@ where
|
||||
target_to_source_headers_relay.require_more_headers(id.0).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn estimate_confirmation_transaction(
|
||||
&self,
|
||||
) -> <MessageLaneAdapter<P> as MessageLane>::SourceChainBalance {
|
||||
let runtime_version = match self.source_client.runtime_version().await {
|
||||
Ok(v) => v,
|
||||
Err(_) => return BalanceOf::<P::SourceChain>::max_value(),
|
||||
};
|
||||
async {
|
||||
let dummy_tx = P::SourceChain::sign_transaction(
|
||||
SignParam::<P::SourceChain> {
|
||||
spec_version: runtime_version.spec_version,
|
||||
transaction_version: runtime_version.transaction_version,
|
||||
genesis_hash: *self.source_client.genesis_hash(),
|
||||
signer: self.transaction_params.signer.clone(),
|
||||
},
|
||||
make_messages_delivery_proof_transaction::<P>(
|
||||
&self.transaction_params,
|
||||
HeaderId(Default::default(), Default::default()),
|
||||
Zero::zero(),
|
||||
prepare_dummy_messages_delivery_proof::<P::SourceChain, P::TargetChain>(),
|
||||
false,
|
||||
)?,
|
||||
)?
|
||||
.encode();
|
||||
self.source_client
|
||||
.estimate_extrinsic_fee(Bytes(dummy_tx))
|
||||
.await
|
||||
.map(|fee| fee.inclusion_fee())
|
||||
}
|
||||
.await
|
||||
.unwrap_or_else(|_| BalanceOf::<P::SourceChain>::max_value())
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the messages pallet at source chain is active.
|
||||
@@ -441,30 +403,6 @@ fn make_messages_delivery_proof_transaction<P: SubstrateMessageLane>(
|
||||
.era(TransactionEra::new(source_best_block_id, source_transaction_params.mortality)))
|
||||
}
|
||||
|
||||
/// Prepare 'dummy' messages delivery proof that will compose the delivery confirmation transaction.
|
||||
///
|
||||
/// We don't care about proof actually being the valid proof, because its validity doesn't
|
||||
/// affect the call weight - we only care about its size.
|
||||
fn prepare_dummy_messages_delivery_proof<SC: Chain, TC: Chain>(
|
||||
) -> SubstrateMessagesDeliveryProof<TC> {
|
||||
let single_message_confirmation_size =
|
||||
bp_messages::InboundLaneData::<()>::encoded_size_hint_u32(1, 1);
|
||||
let proof_size = TC::STORAGE_PROOF_OVERHEAD.saturating_add(single_message_confirmation_size);
|
||||
(
|
||||
UnrewardedRelayersState {
|
||||
unrewarded_relayer_entries: 1,
|
||||
messages_in_oldest_entry: 1,
|
||||
total_messages: 1,
|
||||
last_delivered_nonce: 1,
|
||||
},
|
||||
FromBridgedChainMessagesDeliveryProof {
|
||||
bridged_header_hash: Default::default(),
|
||||
storage_proof: vec![vec![0; proof_size as usize]],
|
||||
lane: Default::default(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Read best blocks from given client.
|
||||
///
|
||||
/// This function assumes that the chain that is followed by the `self_client` has
|
||||
@@ -552,7 +490,7 @@ where
|
||||
}
|
||||
|
||||
fn validate_out_msgs_details<C: Chain>(
|
||||
out_msgs_details: &[OutboundMessageDetails<C::Balance>],
|
||||
out_msgs_details: &[OutboundMessageDetails],
|
||||
nonces: RangeInclusive<MessageNonce>,
|
||||
) -> Result<(), SubstrateError> {
|
||||
let make_missing_nonce_error = |expected_nonce| {
|
||||
@@ -601,8 +539,8 @@ fn validate_out_msgs_details<C: Chain>(
|
||||
|
||||
fn split_msgs_to_refine<Source: Chain + ChainWithMessages, Target: Chain>(
|
||||
lane_id: LaneId,
|
||||
msgs_to_refine: MessagesToRefine<Source::Balance>,
|
||||
) -> Result<Vec<MessagesToRefine<Source::Balance>>, SubstrateError> {
|
||||
msgs_to_refine: MessagesToRefine,
|
||||
) -> Result<Vec<MessagesToRefine>, SubstrateError> {
|
||||
let max_batch_size = Target::max_extrinsic_size() as usize;
|
||||
let mut batches = vec![];
|
||||
|
||||
@@ -635,23 +573,20 @@ fn split_msgs_to_refine<Source: Chain + ChainWithMessages, Target: Chain>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use bp_runtime::{messages::DispatchFeePayment, Chain as ChainBase};
|
||||
use codec::MaxEncodedLen;
|
||||
use bp_runtime::Chain as ChainBase;
|
||||
use relay_rialto_client::Rialto;
|
||||
use relay_rococo_client::Rococo;
|
||||
use relay_wococo_client::Wococo;
|
||||
|
||||
fn message_details_from_rpc(
|
||||
nonces: RangeInclusive<MessageNonce>,
|
||||
) -> Vec<OutboundMessageDetails<bp_wococo::Balance>> {
|
||||
) -> Vec<OutboundMessageDetails> {
|
||||
nonces
|
||||
.into_iter()
|
||||
.map(|nonce| bp_messages::OutboundMessageDetails {
|
||||
nonce,
|
||||
dispatch_weight: Weight::zero(),
|
||||
size: 0,
|
||||
delivery_and_dispatch_fee: 0,
|
||||
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -704,31 +639,16 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_dummy_messages_delivery_proof_works() {
|
||||
let expected_minimal_size =
|
||||
bp_wococo::AccountId::max_encoded_len() as u32 + Rococo::STORAGE_PROOF_OVERHEAD;
|
||||
let dummy_proof = prepare_dummy_messages_delivery_proof::<Wococo, Rococo>();
|
||||
assert!(
|
||||
dummy_proof.1.encode().len() as u32 > expected_minimal_size,
|
||||
"Expected proof size at least {}. Got: {}",
|
||||
expected_minimal_size,
|
||||
dummy_proof.1.encode().len(),
|
||||
);
|
||||
}
|
||||
|
||||
fn check_split_msgs_to_refine(
|
||||
payload_sizes: Vec<usize>,
|
||||
expected_batches: Result<Vec<usize>, ()>,
|
||||
) {
|
||||
let mut out_msgs_details = vec![];
|
||||
for (idx, _) in payload_sizes.iter().enumerate() {
|
||||
out_msgs_details.push(OutboundMessageDetails::<BalanceOf<Rialto>> {
|
||||
out_msgs_details.push(OutboundMessageDetails {
|
||||
nonce: idx as MessageNonce,
|
||||
dispatch_weight: Weight::zero(),
|
||||
size: 0,
|
||||
delivery_and_dispatch_fee: 0,
|
||||
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
use crate::{
|
||||
messages_lane::{MessageLaneAdapter, ReceiveMessagesProofCallBuilder, SubstrateMessageLane},
|
||||
messages_metrics::StandaloneMessagesMetrics,
|
||||
messages_source::{ensure_messages_pallet_active, read_client_state, SubstrateMessagesProof},
|
||||
on_demand::OnDemandRelay,
|
||||
TransactionParams,
|
||||
@@ -32,24 +31,18 @@ use bp_messages::{
|
||||
storage_keys::inbound_lane_data_key, total_unrewarded_messages, InboundLaneData, LaneId,
|
||||
MessageNonce, UnrewardedRelayersState,
|
||||
};
|
||||
use bridge_runtime_common::messages::{
|
||||
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
|
||||
};
|
||||
use codec::Encode;
|
||||
use frame_support::weights::{Weight, WeightToFee};
|
||||
use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof;
|
||||
use messages_relay::{
|
||||
message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
|
||||
message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState},
|
||||
};
|
||||
use num_traits::{Bounded, Zero};
|
||||
use relay_substrate_client::{
|
||||
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages,
|
||||
ChainWithTransactions, Client, Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam,
|
||||
TransactionEra, TransactionTracker, UnsignedTransaction, WeightToFeeOf,
|
||||
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client,
|
||||
Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra,
|
||||
TransactionTracker, UnsignedTransaction,
|
||||
};
|
||||
use relay_utils::{relay_loop::Client as RelayClient, HeaderId};
|
||||
use sp_core::{Bytes, Pair};
|
||||
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
|
||||
use relay_utils::relay_loop::Client as RelayClient;
|
||||
use sp_core::Pair;
|
||||
use std::{collections::VecDeque, convert::TryFrom, ops::RangeInclusive};
|
||||
|
||||
/// Message receiving proof returned by the target Substrate node.
|
||||
@@ -63,7 +56,6 @@ pub struct SubstrateMessagesTarget<P: SubstrateMessageLane> {
|
||||
lane_id: LaneId,
|
||||
relayer_id_at_source: AccountIdOf<P::SourceChain>,
|
||||
transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
|
||||
metric_values: StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>,
|
||||
source_to_target_headers_relay: Option<Arc<dyn OnDemandRelay<BlockNumberOf<P::SourceChain>>>>,
|
||||
}
|
||||
|
||||
@@ -75,7 +67,6 @@ impl<P: SubstrateMessageLane> SubstrateMessagesTarget<P> {
|
||||
lane_id: LaneId,
|
||||
relayer_id_at_source: AccountIdOf<P::SourceChain>,
|
||||
transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
|
||||
metric_values: StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>,
|
||||
source_to_target_headers_relay: Option<
|
||||
Arc<dyn OnDemandRelay<BlockNumberOf<P::SourceChain>>>,
|
||||
>,
|
||||
@@ -86,7 +77,6 @@ impl<P: SubstrateMessageLane> SubstrateMessagesTarget<P> {
|
||||
lane_id,
|
||||
relayer_id_at_source,
|
||||
transaction_params,
|
||||
metric_values,
|
||||
source_to_target_headers_relay,
|
||||
}
|
||||
}
|
||||
@@ -121,7 +111,6 @@ impl<P: SubstrateMessageLane> Clone for SubstrateMessagesTarget<P> {
|
||||
lane_id: self.lane_id,
|
||||
relayer_id_at_source: self.relayer_id_at_source.clone(),
|
||||
transaction_params: self.transaction_params.clone(),
|
||||
metric_values: self.metric_values.clone(),
|
||||
source_to_target_headers_relay: self.source_to_target_headers_relay.clone(),
|
||||
}
|
||||
}
|
||||
@@ -283,142 +272,6 @@ where
|
||||
source_to_target_headers_relay.require_more_headers(id.0).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn estimate_delivery_transaction_in_source_tokens(
|
||||
&self,
|
||||
nonces: RangeInclusive<MessageNonce>,
|
||||
total_prepaid_nonces: MessageNonce,
|
||||
total_dispatch_weight: Weight,
|
||||
total_size: u32,
|
||||
) -> Result<<MessageLaneAdapter<P> as MessageLane>::SourceChainBalance, SubstrateError> {
|
||||
let conversion_rate =
|
||||
self.metric_values.target_to_source_conversion_rate().await.ok_or_else(|| {
|
||||
SubstrateError::Custom(format!(
|
||||
"Failed to compute conversion rate from {} to {}",
|
||||
P::TargetChain::NAME,
|
||||
P::SourceChain::NAME,
|
||||
))
|
||||
})?;
|
||||
|
||||
let (spec_version, transaction_version) =
|
||||
self.target_client.simple_runtime_version().await?;
|
||||
// Prepare 'dummy' delivery transaction - we only care about its length and dispatch weight.
|
||||
let delivery_tx = P::TargetChain::sign_transaction(
|
||||
SignParam {
|
||||
spec_version,
|
||||
transaction_version,
|
||||
genesis_hash: Default::default(),
|
||||
signer: self.transaction_params.signer.clone(),
|
||||
},
|
||||
make_messages_delivery_transaction::<P>(
|
||||
&self.transaction_params,
|
||||
HeaderId(Default::default(), Default::default()),
|
||||
Zero::zero(),
|
||||
self.relayer_id_at_source.clone(),
|
||||
nonces.clone(),
|
||||
prepare_dummy_messages_proof::<P::SourceChain>(
|
||||
nonces.clone(),
|
||||
total_dispatch_weight,
|
||||
total_size,
|
||||
),
|
||||
false,
|
||||
)?,
|
||||
)?
|
||||
.encode();
|
||||
let delivery_tx_fee = self.target_client.estimate_extrinsic_fee(Bytes(delivery_tx)).await?;
|
||||
let inclusion_fee_in_target_tokens = delivery_tx_fee.inclusion_fee();
|
||||
|
||||
// The pre-dispatch cost of delivery transaction includes additional fee to cover dispatch
|
||||
// fee payment (Currency::transfer in regular deployment). But if message dispatch has
|
||||
// already been paid at the Source chain, the delivery transaction will refund relayer with
|
||||
// this additional cost. But `estimate_extrinsic_fee` obviously just returns pre-dispatch
|
||||
// cost of the transaction. So if transaction delivers prepaid message, then it may happen
|
||||
// that pre-dispatch cost is larger than reward and `Rational` relayer will refuse to
|
||||
// deliver this message.
|
||||
//
|
||||
// The most obvious solution would be to deduct total weight of dispatch fee payments from
|
||||
// the `total_dispatch_weight` and use regular `estimate_extrinsic_fee` call. But what if
|
||||
// `total_dispatch_weight` is less than total dispatch fee payments weight? Weight is
|
||||
// strictly positive, so we can't use this option.
|
||||
//
|
||||
// Instead we'll be directly using `WeightToFee` and `NextFeeMultiplier` of the Target
|
||||
// chain. This requires more knowledge of the Target chain, but seems there's no better way
|
||||
// to solve this now.
|
||||
let expected_refund_in_target_tokens = if total_prepaid_nonces != 0 {
|
||||
const WEIGHT_DIFFERENCE: Weight = Weight::from_ref_time(100);
|
||||
|
||||
let (spec_version, transaction_version) =
|
||||
self.target_client.simple_runtime_version().await?;
|
||||
let larger_dispatch_weight = total_dispatch_weight.saturating_add(WEIGHT_DIFFERENCE);
|
||||
let dummy_tx = P::TargetChain::sign_transaction(
|
||||
SignParam {
|
||||
spec_version,
|
||||
transaction_version,
|
||||
genesis_hash: Default::default(),
|
||||
signer: self.transaction_params.signer.clone(),
|
||||
},
|
||||
make_messages_delivery_transaction::<P>(
|
||||
&self.transaction_params,
|
||||
HeaderId(Default::default(), Default::default()),
|
||||
Zero::zero(),
|
||||
self.relayer_id_at_source.clone(),
|
||||
nonces.clone(),
|
||||
prepare_dummy_messages_proof::<P::SourceChain>(
|
||||
nonces.clone(),
|
||||
larger_dispatch_weight,
|
||||
total_size,
|
||||
),
|
||||
false,
|
||||
)?,
|
||||
)?
|
||||
.encode();
|
||||
let larger_delivery_tx_fee =
|
||||
self.target_client.estimate_extrinsic_fee(Bytes(dummy_tx)).await?;
|
||||
|
||||
compute_prepaid_messages_refund::<P::TargetChain>(
|
||||
total_prepaid_nonces,
|
||||
compute_fee_multiplier::<P::TargetChain>(
|
||||
delivery_tx_fee.adjusted_weight_fee,
|
||||
total_dispatch_weight,
|
||||
larger_delivery_tx_fee.adjusted_weight_fee,
|
||||
larger_dispatch_weight,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
Zero::zero()
|
||||
};
|
||||
|
||||
let delivery_fee_in_source_tokens =
|
||||
convert_target_tokens_to_source_tokens::<P::SourceChain, P::TargetChain>(
|
||||
FixedU128::from_float(conversion_rate),
|
||||
inclusion_fee_in_target_tokens.saturating_sub(expected_refund_in_target_tokens),
|
||||
);
|
||||
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"Estimated {} -> {} messages delivery transaction.\n\t\
|
||||
Total nonces: {:?}\n\t\
|
||||
Prepaid messages: {}\n\t\
|
||||
Total messages size: {}\n\t\
|
||||
Total messages dispatch weight: {}\n\t\
|
||||
Inclusion fee (in {1} tokens): {:?}\n\t\
|
||||
Expected refund (in {1} tokens): {:?}\n\t\
|
||||
{1} -> {0} conversion rate: {:?}\n\t\
|
||||
Expected delivery tx fee (in {0} tokens): {:?}",
|
||||
P::SourceChain::NAME,
|
||||
P::TargetChain::NAME,
|
||||
nonces,
|
||||
total_prepaid_nonces,
|
||||
total_size,
|
||||
total_dispatch_weight,
|
||||
inclusion_fee_in_target_tokens,
|
||||
expected_refund_in_target_tokens,
|
||||
conversion_rate,
|
||||
delivery_fee_in_source_tokens,
|
||||
);
|
||||
|
||||
Ok(delivery_fee_in_source_tokens)
|
||||
}
|
||||
}
|
||||
|
||||
/// Make messages delivery transaction from given proof.
|
||||
@@ -443,153 +296,3 @@ fn make_messages_delivery_transaction<P: SubstrateMessageLane>(
|
||||
Ok(UnsignedTransaction::new(call.into(), transaction_nonce)
|
||||
.era(TransactionEra::new(target_best_block_id, target_transaction_params.mortality)))
|
||||
}
|
||||
|
||||
/// Prepare 'dummy' messages proof that will compose the delivery transaction.
|
||||
///
|
||||
/// We don't care about proof actually being the valid proof, because its validity doesn't
|
||||
/// affect the call weight - we only care about its size.
|
||||
fn prepare_dummy_messages_proof<SC: Chain>(
|
||||
nonces: RangeInclusive<MessageNonce>,
|
||||
total_dispatch_weight: Weight,
|
||||
total_size: u32,
|
||||
) -> SubstrateMessagesProof<SC> {
|
||||
(
|
||||
total_dispatch_weight,
|
||||
FromBridgedChainMessagesProof {
|
||||
bridged_header_hash: Default::default(),
|
||||
storage_proof: vec![vec![
|
||||
0;
|
||||
SC::STORAGE_PROOF_OVERHEAD.saturating_add(total_size) as usize
|
||||
]],
|
||||
lane: Default::default(),
|
||||
nonces_start: *nonces.start(),
|
||||
nonces_end: *nonces.end(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Given delivery transaction fee in target chain tokens and conversion rate to the source
|
||||
/// chain tokens, compute transaction cost in source chain tokens.
|
||||
fn convert_target_tokens_to_source_tokens<SC: Chain, TC: Chain>(
|
||||
target_to_source_conversion_rate: FixedU128,
|
||||
target_transaction_fee: TC::Balance,
|
||||
) -> SC::Balance
|
||||
where
|
||||
SC::Balance: TryFrom<TC::Balance>,
|
||||
{
|
||||
SC::Balance::try_from(
|
||||
target_to_source_conversion_rate.saturating_mul_int(target_transaction_fee),
|
||||
)
|
||||
.unwrap_or_else(|_| SC::Balance::max_value())
|
||||
}
|
||||
|
||||
/// Compute fee multiplier that is used by the chain, given a couple of fees for transactions
|
||||
/// that are only differ in dispatch weights.
|
||||
///
|
||||
/// This function assumes that standard transaction payment pallet is used by the chain.
|
||||
/// The only fee component that depends on dispatch weight is the `adjusted_weight_fee`.
|
||||
///
|
||||
/// **WARNING**: this functions will only be accurate if weight-to-fee conversion function
|
||||
/// is linear. For non-linear polynomials the error will grow with `weight_difference` growth.
|
||||
/// So better to use smaller differences.
|
||||
fn compute_fee_multiplier<C: ChainWithMessages>(
|
||||
smaller_adjusted_weight_fee: BalanceOf<C>,
|
||||
smaller_tx_weight: Weight,
|
||||
larger_adjusted_weight_fee: BalanceOf<C>,
|
||||
larger_tx_weight: Weight,
|
||||
) -> FixedU128 {
|
||||
let adjusted_weight_fee_difference =
|
||||
larger_adjusted_weight_fee.saturating_sub(smaller_adjusted_weight_fee);
|
||||
let smaller_tx_unadjusted_weight_fee = WeightToFeeOf::<C>::weight_to_fee(&smaller_tx_weight);
|
||||
let larger_tx_unadjusted_weight_fee = WeightToFeeOf::<C>::weight_to_fee(&larger_tx_weight);
|
||||
FixedU128::saturating_from_rational(
|
||||
adjusted_weight_fee_difference,
|
||||
larger_tx_unadjusted_weight_fee.saturating_sub(smaller_tx_unadjusted_weight_fee),
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute fee that will be refunded to the relayer because dispatch of `total_prepaid_nonces`
|
||||
/// messages has been paid at the source chain.
|
||||
fn compute_prepaid_messages_refund<C: ChainWithMessages>(
|
||||
total_prepaid_nonces: MessageNonce,
|
||||
fee_multiplier: FixedU128,
|
||||
) -> BalanceOf<C> {
|
||||
fee_multiplier.saturating_mul_int(WeightToFeeOf::<C>::weight_to_fee(
|
||||
&C::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN.saturating_mul(total_prepaid_nonces),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use relay_rialto_client::Rialto;
|
||||
use relay_rococo_client::Rococo;
|
||||
use relay_wococo_client::Wococo;
|
||||
|
||||
#[test]
|
||||
fn prepare_dummy_messages_proof_works() {
|
||||
const DISPATCH_WEIGHT: Weight = Weight::from_ref_time(1_000_000);
|
||||
const SIZE: u32 = 1_000;
|
||||
let dummy_proof = prepare_dummy_messages_proof::<Rococo>(1..=10, DISPATCH_WEIGHT, SIZE);
|
||||
assert_eq!(dummy_proof.0, DISPATCH_WEIGHT);
|
||||
assert!(
|
||||
dummy_proof.1.encode().len() as u32 > SIZE,
|
||||
"Expected proof size at least {}. Got: {}",
|
||||
SIZE,
|
||||
dummy_proof.1.encode().len(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_target_tokens_to_source_tokens_works() {
|
||||
assert_eq!(
|
||||
convert_target_tokens_to_source_tokens::<Rococo, Wococo>((150, 100).into(), 1_000),
|
||||
1_500
|
||||
);
|
||||
assert_eq!(
|
||||
convert_target_tokens_to_source_tokens::<Rococo, Wococo>((50, 100).into(), 1_000),
|
||||
500
|
||||
);
|
||||
assert_eq!(
|
||||
convert_target_tokens_to_source_tokens::<Rococo, Wococo>((100, 100).into(), 1_000),
|
||||
1_000
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compute_fee_multiplier_returns_sane_results() {
|
||||
let multiplier: FixedU128 =
|
||||
bp_rialto::WeightToFee::weight_to_fee(&Weight::from_ref_time(1)).into();
|
||||
|
||||
let smaller_weight = 1_000_000;
|
||||
let smaller_adjusted_weight_fee = multiplier.saturating_mul_int(
|
||||
WeightToFeeOf::<Rialto>::weight_to_fee(&Weight::from_ref_time(smaller_weight)),
|
||||
);
|
||||
|
||||
let larger_weight = smaller_weight + 200_000;
|
||||
let larger_adjusted_weight_fee = multiplier.saturating_mul_int(
|
||||
WeightToFeeOf::<Rialto>::weight_to_fee(&Weight::from_ref_time(larger_weight)),
|
||||
);
|
||||
assert_eq!(
|
||||
compute_fee_multiplier::<Rialto>(
|
||||
smaller_adjusted_weight_fee,
|
||||
Weight::from_ref_time(smaller_weight),
|
||||
larger_adjusted_weight_fee,
|
||||
Weight::from_ref_time(larger_weight),
|
||||
),
|
||||
multiplier,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compute_prepaid_messages_refund_returns_sane_results() {
|
||||
assert!(
|
||||
compute_prepaid_messages_refund::<Rialto>(
|
||||
10,
|
||||
FixedU128::saturating_from_rational(110, 100),
|
||||
) > Rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN
|
||||
.saturating_mul(10u64)
|
||||
.ref_time() as _
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user