mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 19:51:02 +00:00
Merge commit '392447f5c8f986ded2559a78457f4cd87942f393' into update-bridges-subtree-r/w
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
//! data. Message lane allows sending arbitrary messages between bridged chains. This
|
||||
//! module provides entrypoint that starts reading messages from given message lane
|
||||
//! of source chain and submits proof-of-message-at-source-chain transactions to the
|
||||
//! target chain. Additionaly, proofs-of-messages-delivery are sent back from the
|
||||
//! target chain. Additionally, proofs-of-messages-delivery are sent back from the
|
||||
//! target chain to the source chain.
|
||||
|
||||
// required for futures::select!
|
||||
@@ -29,6 +29,7 @@ mod metrics;
|
||||
|
||||
pub mod message_lane;
|
||||
pub mod message_lane_loop;
|
||||
pub mod relay_strategy;
|
||||
|
||||
mod message_race_delivery;
|
||||
mod message_race_loop;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
use num_traits::{SaturatingAdd, Zero};
|
||||
use relay_utils::{BlockNumberBase, HeaderId};
|
||||
use std::fmt::Debug;
|
||||
use std::{fmt::Debug, ops::Sub};
|
||||
|
||||
/// One-way message lane.
|
||||
pub trait MessageLane: 'static + Clone + Send + Sync {
|
||||
@@ -40,7 +40,15 @@ pub trait MessageLane: 'static + Clone + Send + Sync {
|
||||
/// 1) pay transaction fees;
|
||||
/// 2) pay message delivery and dispatch fee;
|
||||
/// 3) pay relayer rewards.
|
||||
type SourceChainBalance: Clone + Copy + Debug + PartialOrd + SaturatingAdd + Zero + Send + Sync;
|
||||
type SourceChainBalance: Clone
|
||||
+ Copy
|
||||
+ Debug
|
||||
+ PartialOrd
|
||||
+ Sub<Output = Self::SourceChainBalance>
|
||||
+ SaturatingAdd
|
||||
+ Zero
|
||||
+ Send
|
||||
+ Sync;
|
||||
/// Number of the source header.
|
||||
type SourceHeaderNumber: BlockNumberBase;
|
||||
/// Hash of the source header.
|
||||
@@ -53,7 +61,9 @@ pub trait MessageLane: 'static + Clone + Send + Sync {
|
||||
}
|
||||
|
||||
/// Source header id within given one-way message lane.
|
||||
pub type SourceHeaderIdOf<P> = HeaderId<<P as MessageLane>::SourceHeaderHash, <P as MessageLane>::SourceHeaderNumber>;
|
||||
pub type SourceHeaderIdOf<P> =
|
||||
HeaderId<<P as MessageLane>::SourceHeaderHash, <P as MessageLane>::SourceHeaderNumber>;
|
||||
|
||||
/// Target header id within given one-way message lane.
|
||||
pub type TargetHeaderIdOf<P> = HeaderId<<P as MessageLane>::TargetHeaderHash, <P as MessageLane>::TargetHeaderNumber>;
|
||||
pub type TargetHeaderIdOf<P> =
|
||||
HeaderId<<P as MessageLane>::TargetHeaderHash, <P as MessageLane>::TargetHeaderNumber>;
|
||||
|
||||
@@ -24,15 +24,13 @@
|
||||
//! finalized header. I.e. when talking about headers in lane context, we
|
||||
//! only care about finalized headers.
|
||||
|
||||
use crate::message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf};
|
||||
use crate::message_race_delivery::run as run_message_delivery_race;
|
||||
use crate::message_race_receiving::run as run_message_receiving_race;
|
||||
use crate::metrics::MessageLaneLoopMetrics;
|
||||
use std::{collections::BTreeMap, fmt::Debug, future::Future, ops::RangeInclusive, time::Duration};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt};
|
||||
|
||||
use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight};
|
||||
use bp_runtime::messages::DispatchFeePayment;
|
||||
use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt};
|
||||
use relay_utils::{
|
||||
interval,
|
||||
metrics::{GlobalMetrics, MetricsParams},
|
||||
@@ -40,11 +38,18 @@ use relay_utils::{
|
||||
relay_loop::Client as RelayClient,
|
||||
retry_backoff, FailedClient,
|
||||
};
|
||||
use std::{collections::BTreeMap, fmt::Debug, future::Future, ops::RangeInclusive, time::Duration};
|
||||
|
||||
use crate::{
|
||||
message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
|
||||
message_race_delivery::run as run_message_delivery_race,
|
||||
message_race_receiving::run as run_message_receiving_race,
|
||||
metrics::MessageLaneLoopMetrics,
|
||||
relay_strategy::RelayStrategy,
|
||||
};
|
||||
|
||||
/// Message lane loop configuration params.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Params {
|
||||
pub struct Params<Strategy: RelayStrategy> {
|
||||
/// Id of lane this loop is servicing.
|
||||
pub lane: LaneId,
|
||||
/// Interval at which we ask target node about its updates.
|
||||
@@ -56,7 +61,7 @@ pub struct Params {
|
||||
/// The loop will auto-restart if there has been no updates during this period.
|
||||
pub stall_timeout: Duration,
|
||||
/// Message delivery race parameters.
|
||||
pub delivery_params: MessageDeliveryParams,
|
||||
pub delivery_params: MessageDeliveryParams<Strategy>,
|
||||
}
|
||||
|
||||
/// Relayer operating mode.
|
||||
@@ -64,20 +69,22 @@ pub struct Params {
|
||||
pub enum RelayerMode {
|
||||
/// The relayer doesn't care about rewards.
|
||||
Altruistic,
|
||||
/// The relayer will deliver all messages and confirmations as long as he's not losing any funds.
|
||||
NoLosses,
|
||||
/// The relayer will deliver all messages and confirmations as long as he's not losing any
|
||||
/// funds.
|
||||
Rational,
|
||||
}
|
||||
|
||||
/// Message delivery race parameters.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MessageDeliveryParams {
|
||||
/// Maximal number of unconfirmed relayer entries at the inbound lane. If there's that number of entries
|
||||
/// in the `InboundLaneData::relayers` set, all new messages will be rejected until reward payment will
|
||||
/// be proved (by including outbound lane state to the message delivery transaction).
|
||||
pub struct MessageDeliveryParams<Strategy: RelayStrategy> {
|
||||
/// Maximal number of unconfirmed relayer entries at the inbound lane. If there's that number
|
||||
/// of entries in the `InboundLaneData::relayers` set, all new messages will be rejected until
|
||||
/// reward payment will be proved (by including outbound lane state to the message delivery
|
||||
/// transaction).
|
||||
pub max_unrewarded_relayer_entries_at_target: MessageNonce,
|
||||
/// Message delivery race will stop delivering messages if there are `max_unconfirmed_nonces_at_target`
|
||||
/// unconfirmed nonces on the target node. The race would continue once they're confirmed by the
|
||||
/// receiving race.
|
||||
/// Message delivery race will stop delivering messages if there are
|
||||
/// `max_unconfirmed_nonces_at_target` unconfirmed nonces on the target node. The race would
|
||||
/// continue once they're confirmed by the receiving race.
|
||||
pub max_unconfirmed_nonces_at_target: MessageNonce,
|
||||
/// Maximal number of relayed messages in single delivery transaction.
|
||||
pub max_messages_in_single_batch: MessageNonce,
|
||||
@@ -85,8 +92,8 @@ pub struct MessageDeliveryParams {
|
||||
pub max_messages_weight_in_single_batch: Weight,
|
||||
/// Maximal cumulative size of relayed messages in single delivery transaction.
|
||||
pub max_messages_size_in_single_batch: u32,
|
||||
/// Relayer operating mode.
|
||||
pub relayer_mode: RelayerMode,
|
||||
/// Relay strategy
|
||||
pub relay_strategy: Strategy,
|
||||
}
|
||||
|
||||
/// Message details.
|
||||
@@ -103,7 +110,8 @@ pub struct MessageDetails<SourceChainBalance> {
|
||||
}
|
||||
|
||||
/// Messages details map.
|
||||
pub type MessageDetailsMap<SourceChainBalance> = BTreeMap<MessageNonce, MessageDetails<SourceChainBalance>>;
|
||||
pub type MessageDetailsMap<SourceChainBalance> =
|
||||
BTreeMap<MessageNonce, MessageDetails<SourceChainBalance>>;
|
||||
|
||||
/// Message delivery race proof parameters.
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -125,6 +133,7 @@ pub trait SourceClient<P: MessageLane>: RelayClient {
|
||||
&self,
|
||||
id: SourceHeaderIdOf<P>,
|
||||
) -> Result<(SourceHeaderIdOf<P>, MessageNonce), Self::Error>;
|
||||
|
||||
/// Get nonce of the latest message, which receiving has been confirmed by the target chain.
|
||||
async fn latest_confirmed_received_nonce(
|
||||
&self,
|
||||
@@ -175,11 +184,12 @@ pub trait TargetClient<P: MessageLane>: RelayClient {
|
||||
id: TargetHeaderIdOf<P>,
|
||||
) -> Result<(TargetHeaderIdOf<P>, MessageNonce), Self::Error>;
|
||||
|
||||
/// Get nonce of latest confirmed message.
|
||||
/// Get nonce of the latest confirmed message.
|
||||
async fn latest_confirmed_received_nonce(
|
||||
&self,
|
||||
id: TargetHeaderIdOf<P>,
|
||||
) -> Result<(TargetHeaderIdOf<P>, MessageNonce), Self::Error>;
|
||||
|
||||
/// Get state of unrewarded relayers set at the inbound lane.
|
||||
async fn unrewarded_relayers_state(
|
||||
&self,
|
||||
@@ -210,19 +220,21 @@ pub trait TargetClient<P: MessageLane>: RelayClient {
|
||||
async fn estimate_delivery_transaction_in_source_tokens(
|
||||
&self,
|
||||
nonces: RangeInclusive<MessageNonce>,
|
||||
total_prepaid_nonces: MessageNonce,
|
||||
total_dispatch_weight: Weight,
|
||||
total_size: u32,
|
||||
) -> P::SourceChainBalance;
|
||||
) -> Result<P::SourceChainBalance, Self::Error>;
|
||||
}
|
||||
|
||||
/// State of the client.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct ClientState<SelfHeaderId, PeerHeaderId> {
|
||||
/// Best header id of this chain.
|
||||
/// The best header id of this chain.
|
||||
pub best_self: SelfHeaderId,
|
||||
/// Best finalized header id of this chain.
|
||||
pub best_finalized_self: SelfHeaderId,
|
||||
/// Best finalized header id of the peer chain read at the best block of this chain (at `best_finalized_self`).
|
||||
/// Best finalized header id of the peer chain read at the best block of this chain (at
|
||||
/// `best_finalized_self`).
|
||||
pub best_finalized_peer_at_best_self: PeerHeaderId,
|
||||
}
|
||||
|
||||
@@ -241,50 +253,49 @@ pub struct ClientsState<P: MessageLane> {
|
||||
pub target: Option<TargetClientState<P>>,
|
||||
}
|
||||
|
||||
/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs sync loop.
|
||||
/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs
|
||||
/// sync loop.
|
||||
pub fn metrics_prefix<P: MessageLane>(lane: &LaneId) -> String {
|
||||
format!(
|
||||
"{}_to_{}_MessageLane_{}",
|
||||
P::SOURCE_NAME,
|
||||
P::TARGET_NAME,
|
||||
hex::encode(lane)
|
||||
)
|
||||
format!("{}_to_{}_MessageLane_{}", P::SOURCE_NAME, P::TARGET_NAME, hex::encode(lane))
|
||||
}
|
||||
|
||||
/// Run message lane service loop.
|
||||
pub async fn run<P: MessageLane>(
|
||||
params: Params,
|
||||
pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
|
||||
params: Params<Strategy>,
|
||||
source_client: impl SourceClient<P>,
|
||||
target_client: impl TargetClient<P>,
|
||||
metrics_params: MetricsParams,
|
||||
exit_signal: impl Future<Output = ()> + Send + 'static,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), relay_utils::Error> {
|
||||
let exit_signal = exit_signal.shared();
|
||||
relay_utils::relay_loop(source_client, target_client)
|
||||
.reconnect_delay(params.reconnect_delay)
|
||||
.with_metrics(Some(metrics_prefix::<P>(¶ms.lane)), metrics_params)
|
||||
.loop_metric(|registry, prefix| MessageLaneLoopMetrics::new(registry, prefix))?
|
||||
.standalone_metric(|registry, prefix| GlobalMetrics::new(registry, prefix))?
|
||||
.loop_metric(MessageLaneLoopMetrics::new)?
|
||||
.standalone_metric(GlobalMetrics::new)?
|
||||
.expose()
|
||||
.await?
|
||||
.run(
|
||||
metrics_prefix::<P>(¶ms.lane),
|
||||
move |source_client, target_client, metrics| {
|
||||
run_until_connection_lost(
|
||||
params.clone(),
|
||||
source_client,
|
||||
target_client,
|
||||
metrics,
|
||||
exit_signal.clone(),
|
||||
)
|
||||
},
|
||||
)
|
||||
.run(metrics_prefix::<P>(¶ms.lane), move |source_client, target_client, metrics| {
|
||||
run_until_connection_lost(
|
||||
params.clone(),
|
||||
source_client,
|
||||
target_client,
|
||||
metrics,
|
||||
exit_signal.clone(),
|
||||
)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Run one-way message delivery loop until connection with target or source node is lost, or exit signal is received.
|
||||
async fn run_until_connection_lost<P: MessageLane, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
params: Params,
|
||||
/// Run one-way message delivery loop until connection with target or source node is lost, or exit
|
||||
/// signal is received.
|
||||
async fn run_until_connection_lost<
|
||||
P: MessageLane,
|
||||
Strategy: RelayStrategy,
|
||||
SC: SourceClient<P>,
|
||||
TC: TargetClient<P>,
|
||||
>(
|
||||
params: Params<Strategy>,
|
||||
source_client: SC,
|
||||
target_client: TC,
|
||||
metrics_msg: Option<MessageLaneLoopMetrics>,
|
||||
@@ -446,11 +457,16 @@ async fn run_until_connection_lost<P: MessageLane, SC: SourceClient<P>, TC: Targ
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::stream::StreamExt;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use relay_utils::{HeaderId, MaybeConnectionError};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::relay_strategy::AltruisticStrategy;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub fn header_id(number: TestSourceHeaderNumber) -> TestSourceHeaderId {
|
||||
HeaderId(number, number)
|
||||
@@ -554,7 +570,7 @@ pub(crate) mod tests {
|
||||
let mut data = self.data.lock();
|
||||
(self.tick)(&mut *data);
|
||||
if data.is_source_fails {
|
||||
return Err(TestError);
|
||||
return Err(TestError)
|
||||
}
|
||||
Ok(data.source_state.clone())
|
||||
}
|
||||
@@ -566,7 +582,7 @@ pub(crate) mod tests {
|
||||
let mut data = self.data.lock();
|
||||
(self.tick)(&mut *data);
|
||||
if data.is_source_fails {
|
||||
return Err(TestError);
|
||||
return Err(TestError)
|
||||
}
|
||||
Ok((id, data.source_latest_generated_nonce))
|
||||
}
|
||||
@@ -606,11 +622,7 @@ pub(crate) mod tests {
|
||||
nonces: RangeInclusive<MessageNonce>,
|
||||
proof_parameters: MessageProofParameters,
|
||||
) -> Result<
|
||||
(
|
||||
SourceHeaderIdOf<TestMessageLane>,
|
||||
RangeInclusive<MessageNonce>,
|
||||
TestMessagesProof,
|
||||
),
|
||||
(SourceHeaderIdOf<TestMessageLane>, RangeInclusive<MessageNonce>, TestMessagesProof),
|
||||
TestError,
|
||||
> {
|
||||
let mut data = self.data.lock();
|
||||
@@ -691,7 +703,7 @@ pub(crate) mod tests {
|
||||
let mut data = self.data.lock();
|
||||
(self.tick)(&mut *data);
|
||||
if data.is_target_fails {
|
||||
return Err(TestError);
|
||||
return Err(TestError)
|
||||
}
|
||||
Ok(data.target_state.clone())
|
||||
}
|
||||
@@ -703,7 +715,7 @@ pub(crate) mod tests {
|
||||
let mut data = self.data.lock();
|
||||
(self.tick)(&mut *data);
|
||||
if data.is_target_fails {
|
||||
return Err(TestError);
|
||||
return Err(TestError)
|
||||
}
|
||||
Ok((id, data.target_latest_received_nonce))
|
||||
}
|
||||
@@ -729,7 +741,7 @@ pub(crate) mod tests {
|
||||
let mut data = self.data.lock();
|
||||
(self.tick)(&mut *data);
|
||||
if data.is_target_fails {
|
||||
return Err(TestError);
|
||||
return Err(TestError)
|
||||
}
|
||||
Ok((id, data.target_latest_confirmed_received_nonce))
|
||||
}
|
||||
@@ -750,14 +762,15 @@ pub(crate) mod tests {
|
||||
let mut data = self.data.lock();
|
||||
(self.tick)(&mut *data);
|
||||
if data.is_target_fails {
|
||||
return Err(TestError);
|
||||
return Err(TestError)
|
||||
}
|
||||
data.target_state.best_self =
|
||||
HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1);
|
||||
data.target_state.best_finalized_self = data.target_state.best_self;
|
||||
data.target_latest_received_nonce = *proof.0.end();
|
||||
if let Some(target_latest_confirmed_received_nonce) = proof.1 {
|
||||
data.target_latest_confirmed_received_nonce = target_latest_confirmed_received_nonce;
|
||||
data.target_latest_confirmed_received_nonce =
|
||||
target_latest_confirmed_received_nonce;
|
||||
}
|
||||
data.submitted_messages_proofs.push(proof);
|
||||
Ok(nonces)
|
||||
@@ -773,12 +786,13 @@ pub(crate) mod tests {
|
||||
async fn estimate_delivery_transaction_in_source_tokens(
|
||||
&self,
|
||||
nonces: RangeInclusive<MessageNonce>,
|
||||
_total_prepaid_nonces: MessageNonce,
|
||||
total_dispatch_weight: Weight,
|
||||
total_size: u32,
|
||||
) -> TestSourceChainBalance {
|
||||
BASE_MESSAGE_DELIVERY_TRANSACTION_COST * (nonces.end() - nonces.start() + 1)
|
||||
+ total_dispatch_weight
|
||||
+ total_size as TestSourceChainBalance
|
||||
) -> Result<TestSourceChainBalance, TestError> {
|
||||
Ok(BASE_MESSAGE_DELIVERY_TRANSACTION_COST * (nonces.end() - nonces.start() + 1) +
|
||||
total_dispatch_weight +
|
||||
total_size as TestSourceChainBalance)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,14 +805,8 @@ pub(crate) mod tests {
|
||||
async_std::task::block_on(async {
|
||||
let data = Arc::new(Mutex::new(data));
|
||||
|
||||
let source_client = TestSourceClient {
|
||||
data: data.clone(),
|
||||
tick: source_tick,
|
||||
};
|
||||
let target_client = TestTargetClient {
|
||||
data: data.clone(),
|
||||
tick: target_tick,
|
||||
};
|
||||
let source_client = TestSourceClient { data: data.clone(), tick: source_tick };
|
||||
let target_client = TestTargetClient { data: data.clone(), tick: target_tick };
|
||||
let _ = run(
|
||||
Params {
|
||||
lane: [0, 0, 0, 0],
|
||||
@@ -812,7 +820,7 @@ pub(crate) mod tests {
|
||||
max_messages_in_single_batch: 4,
|
||||
max_messages_weight_in_single_batch: 4,
|
||||
max_messages_size_in_single_batch: 4,
|
||||
relayer_mode: RelayerMode::Altruistic,
|
||||
relay_strategy: AltruisticStrategy,
|
||||
},
|
||||
},
|
||||
source_client,
|
||||
@@ -901,7 +909,10 @@ pub(crate) mod tests {
|
||||
data.source_state.best_finalized_self = data.source_state.best_self;
|
||||
// headers relay must only be started when we need new target headers at source node
|
||||
if data.target_to_source_header_required.is_some() {
|
||||
assert!(data.source_state.best_finalized_peer_at_best_self.0 < data.target_state.best_self.0);
|
||||
assert!(
|
||||
data.source_state.best_finalized_peer_at_best_self.0 <
|
||||
data.target_state.best_self.0
|
||||
);
|
||||
data.target_to_source_header_required = None;
|
||||
}
|
||||
// syncing target headers -> source chain
|
||||
@@ -918,7 +929,10 @@ pub(crate) mod tests {
|
||||
data.target_state.best_finalized_self = data.target_state.best_self;
|
||||
// headers relay must only be started when we need new source headers at target node
|
||||
if data.source_to_target_header_required.is_some() {
|
||||
assert!(data.target_state.best_finalized_peer_at_best_self.0 < data.source_state.best_self.0);
|
||||
assert!(
|
||||
data.target_state.best_finalized_peer_at_best_self.0 <
|
||||
data.source_state.best_self.0
|
||||
);
|
||||
data.source_to_target_header_required = None;
|
||||
}
|
||||
// syncing source headers -> target chain
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,10 +54,12 @@ pub trait MessageRace {
|
||||
}
|
||||
|
||||
/// State of race source client.
|
||||
type SourceClientState<P> = ClientState<<P as MessageRace>::SourceHeaderId, <P as MessageRace>::TargetHeaderId>;
|
||||
type SourceClientState<P> =
|
||||
ClientState<<P as MessageRace>::SourceHeaderId, <P as MessageRace>::TargetHeaderId>;
|
||||
|
||||
/// State of race target client.
|
||||
type TargetClientState<P> = ClientState<<P as MessageRace>::TargetHeaderId, <P as MessageRace>::SourceHeaderId>;
|
||||
type TargetClientState<P> =
|
||||
ClientState<<P as MessageRace>::TargetHeaderId, <P as MessageRace>::SourceHeaderId>;
|
||||
|
||||
/// Inclusive nonces range.
|
||||
pub trait NoncesRange: Debug + Sized {
|
||||
@@ -76,7 +78,7 @@ pub struct SourceClientNonces<NoncesRange> {
|
||||
/// New nonces range known to the client. `New` here means all nonces generated after
|
||||
/// `prev_latest_nonce` passed to the `SourceClient::nonces` method.
|
||||
pub new_nonces: NoncesRange,
|
||||
/// Latest nonce that is confirmed to the bridged client. This nonce only makes
|
||||
/// The latest nonce that is confirmed to the bridged client. This nonce only makes
|
||||
/// sense in some races. In other races it is `None`.
|
||||
pub confirmed_nonce: Option<MessageNonce>,
|
||||
}
|
||||
@@ -84,7 +86,7 @@ pub struct SourceClientNonces<NoncesRange> {
|
||||
/// Nonces on the race target client.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TargetClientNonces<TargetNoncesData> {
|
||||
/// Latest nonce that is known to the target client.
|
||||
/// The latest nonce that is known to the target client.
|
||||
pub latest_nonce: MessageNonce,
|
||||
/// Additional data from target node that may be used by the race.
|
||||
pub nonces_data: TargetNoncesData,
|
||||
@@ -93,7 +95,7 @@ pub struct TargetClientNonces<TargetNoncesData> {
|
||||
/// One of message lane clients, which is source client for the race.
|
||||
#[async_trait]
|
||||
pub trait SourceClient<P: MessageRace> {
|
||||
/// Type of error this clients returns.
|
||||
/// Type of error these clients returns.
|
||||
type Error: std::fmt::Debug + MaybeConnectionError;
|
||||
/// Type of nonces range returned by the source client.
|
||||
type NoncesRange: NoncesRange;
|
||||
@@ -118,7 +120,7 @@ pub trait SourceClient<P: MessageRace> {
|
||||
/// One of message lane clients, which is target client for the race.
|
||||
#[async_trait]
|
||||
pub trait TargetClient<P: MessageRace> {
|
||||
/// Type of error this clients returns.
|
||||
/// Type of error these clients returns.
|
||||
type Error: std::fmt::Debug + MaybeConnectionError;
|
||||
/// Type of the additional data from the target client, used by the race.
|
||||
type TargetNoncesData: std::fmt::Debug;
|
||||
@@ -155,19 +157,26 @@ pub trait RaceStrategy<SourceHeaderId, TargetHeaderId, Proof>: Debug {
|
||||
/// Should return true if nothing has to be synced.
|
||||
fn is_empty(&self) -> bool;
|
||||
/// Return id of source header that is required to be on target to continue synchronization.
|
||||
fn required_source_header_at_target(&self, current_best: &SourceHeaderId) -> Option<SourceHeaderId>;
|
||||
/// Return best nonce at source node.
|
||||
fn required_source_header_at_target(
|
||||
&self,
|
||||
current_best: &SourceHeaderId,
|
||||
) -> Option<SourceHeaderId>;
|
||||
/// Return the best nonce at source node.
|
||||
///
|
||||
/// `Some` is returned only if we are sure that the value is greater or equal
|
||||
/// than the result of `best_at_target`.
|
||||
fn best_at_source(&self) -> Option<MessageNonce>;
|
||||
/// Return best nonce at target node.
|
||||
/// Return the best nonce at target node.
|
||||
///
|
||||
/// May return `None` if value is yet unknown.
|
||||
fn best_at_target(&self) -> Option<MessageNonce>;
|
||||
|
||||
/// Called when nonces are updated at source node of the race.
|
||||
fn source_nonces_updated(&mut self, at_block: SourceHeaderId, nonces: SourceClientNonces<Self::SourceNoncesRange>);
|
||||
fn source_nonces_updated(
|
||||
&mut self,
|
||||
at_block: SourceHeaderId,
|
||||
nonces: SourceClientNonces<Self::SourceNoncesRange>,
|
||||
);
|
||||
/// Called when best nonces are updated at target node of the race.
|
||||
fn best_target_nonces_updated(
|
||||
&mut self,
|
||||
@@ -197,7 +206,7 @@ pub struct RaceState<SourceHeaderId, TargetHeaderId, Proof> {
|
||||
/// Best finalized source header id at the best block on the target
|
||||
/// client (at the `best_finalized_source_header_id_at_best_target`).
|
||||
pub best_finalized_source_header_id_at_best_target: Option<SourceHeaderId>,
|
||||
/// Best header id at the target client.
|
||||
/// The best header id at the target client.
|
||||
pub best_target_header_id: Option<TargetHeaderId>,
|
||||
/// Best finalized header id at the target client.
|
||||
pub best_finalized_target_header_id: Option<TargetHeaderId>,
|
||||
@@ -430,8 +439,10 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
strategy,
|
||||
);
|
||||
|
||||
return Err(FailedClient::Both);
|
||||
} else if race_state.nonces_to_submit.is_none() && race_state.nonces_submitted.is_none() && strategy.is_empty()
|
||||
return Err(FailedClient::Both)
|
||||
} else if race_state.nonces_to_submit.is_none() &&
|
||||
race_state.nonces_submitted.is_none() &&
|
||||
strategy.is_empty()
|
||||
{
|
||||
stall_countdown = Instant::now();
|
||||
}
|
||||
@@ -439,7 +450,8 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
if source_client_is_online {
|
||||
source_client_is_online = false;
|
||||
|
||||
let nonces_to_deliver = select_nonces_to_deliver(race_state.clone(), &mut strategy).await;
|
||||
let nonces_to_deliver =
|
||||
select_nonces_to_deliver(race_state.clone(), &mut strategy).await;
|
||||
let best_at_source = strategy.best_at_source();
|
||||
|
||||
if let Some((at_block, nonces_range, proof_parameters)) = nonces_to_deliver {
|
||||
@@ -451,9 +463,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
at_block,
|
||||
);
|
||||
source_generate_proof.set(
|
||||
race_source
|
||||
.generate_proof(at_block, nonces_range, proof_parameters)
|
||||
.fuse(),
|
||||
race_source.generate_proof(at_block, nonces_range, proof_parameters).fuse(),
|
||||
);
|
||||
} else if source_nonces_required && best_at_source.is_some() {
|
||||
log::debug!(target: "bridge", "Asking {} about message nonces", P::source_name());
|
||||
@@ -516,7 +526,9 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
}
|
||||
}
|
||||
|
||||
impl<SourceHeaderId, TargetHeaderId, Proof> Default for RaceState<SourceHeaderId, TargetHeaderId, Proof> {
|
||||
impl<SourceHeaderId, TargetHeaderId, Proof> Default
|
||||
for RaceState<SourceHeaderId, TargetHeaderId, Proof>
|
||||
{
|
||||
fn default() -> Self {
|
||||
RaceState {
|
||||
best_finalized_source_header_id_at_source: None,
|
||||
@@ -539,7 +551,7 @@ where
|
||||
|
||||
let need_update = now_time.saturating_duration_since(prev_time) > Duration::from_secs(10);
|
||||
if !need_update {
|
||||
return prev_time;
|
||||
return prev_time
|
||||
}
|
||||
|
||||
let now_best_nonce_at_source = strategy.best_at_source();
|
||||
@@ -569,11 +581,7 @@ where
|
||||
.select_nonces_to_deliver(race_state)
|
||||
.await
|
||||
.map(|(nonces_range, proof_parameters)| {
|
||||
(
|
||||
best_finalized_source_header_id_at_best_target,
|
||||
nonces_range,
|
||||
proof_parameters,
|
||||
)
|
||||
(best_finalized_source_header_id_at_best_target, nonces_range, proof_parameters)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -592,8 +600,14 @@ mod tests {
|
||||
// target node only knows about source' BEST_AT_TARGET block
|
||||
// source node has BEST_AT_SOURCE > BEST_AT_TARGET block
|
||||
let mut race_state = RaceState::<_, _, ()> {
|
||||
best_finalized_source_header_id_at_source: Some(HeaderId(BEST_AT_SOURCE, BEST_AT_SOURCE)),
|
||||
best_finalized_source_header_id_at_best_target: Some(HeaderId(BEST_AT_TARGET, BEST_AT_TARGET)),
|
||||
best_finalized_source_header_id_at_source: Some(HeaderId(
|
||||
BEST_AT_SOURCE,
|
||||
BEST_AT_SOURCE,
|
||||
)),
|
||||
best_finalized_source_header_id_at_best_target: Some(HeaderId(
|
||||
BEST_AT_TARGET,
|
||||
BEST_AT_TARGET,
|
||||
)),
|
||||
best_target_header_id: Some(HeaderId(0, 0)),
|
||||
best_finalized_target_header_id: Some(HeaderId(0, 0)),
|
||||
nonces_to_submit: None,
|
||||
@@ -604,16 +618,10 @@ mod tests {
|
||||
let mut strategy = BasicStrategy::new();
|
||||
strategy.source_nonces_updated(
|
||||
HeaderId(GENERATED_AT, GENERATED_AT),
|
||||
SourceClientNonces {
|
||||
new_nonces: 0..=10,
|
||||
confirmed_nonce: None,
|
||||
},
|
||||
SourceClientNonces { new_nonces: 0..=10, confirmed_nonce: None },
|
||||
);
|
||||
strategy.best_target_nonces_updated(
|
||||
TargetClientNonces {
|
||||
latest_nonce: 5u64,
|
||||
nonces_data: (),
|
||||
},
|
||||
TargetClientNonces { latest_nonce: 5u64, nonces_data: () },
|
||||
&mut race_state,
|
||||
);
|
||||
|
||||
|
||||
@@ -11,18 +11,21 @@
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
//! Message receiving race delivers proof-of-messages-delivery from lane.target to lane.source.
|
||||
//! Message receiving race delivers proof-of-messages-delivery from "lane.target" to "lane.source".
|
||||
|
||||
use crate::message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf};
|
||||
use crate::message_lane_loop::{
|
||||
SourceClient as MessageLaneSourceClient, SourceClientState, TargetClient as MessageLaneTargetClient,
|
||||
TargetClientState,
|
||||
use crate::{
|
||||
message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
|
||||
message_lane_loop::{
|
||||
SourceClient as MessageLaneSourceClient, SourceClientState,
|
||||
TargetClient as MessageLaneTargetClient, TargetClientState,
|
||||
},
|
||||
message_race_loop::{
|
||||
MessageRace, NoncesRange, SourceClient, SourceClientNonces, TargetClient,
|
||||
TargetClientNonces,
|
||||
},
|
||||
message_race_strategy::BasicStrategy,
|
||||
metrics::MessageLaneLoopMetrics,
|
||||
};
|
||||
use crate::message_race_loop::{
|
||||
MessageRace, NoncesRange, SourceClient, SourceClientNonces, TargetClient, TargetClientNonces,
|
||||
};
|
||||
use crate::message_race_strategy::BasicStrategy;
|
||||
use crate::metrics::MessageLaneLoopMetrics;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bp_messages::MessageNonce;
|
||||
@@ -129,11 +132,7 @@ where
|
||||
nonces: RangeInclusive<MessageNonce>,
|
||||
_proof_parameters: Self::ProofParameters,
|
||||
) -> Result<
|
||||
(
|
||||
TargetHeaderIdOf<P>,
|
||||
RangeInclusive<MessageNonce>,
|
||||
P::MessagesReceivingProof,
|
||||
),
|
||||
(TargetHeaderIdOf<P>, RangeInclusive<MessageNonce>, P::MessagesReceivingProof),
|
||||
Self::Error,
|
||||
> {
|
||||
self.client
|
||||
@@ -168,19 +167,14 @@ where
|
||||
at_block: SourceHeaderIdOf<P>,
|
||||
update_metrics: bool,
|
||||
) -> Result<(SourceHeaderIdOf<P>, TargetClientNonces<()>), Self::Error> {
|
||||
let (at_block, latest_confirmed_nonce) = self.client.latest_confirmed_received_nonce(at_block).await?;
|
||||
let (at_block, latest_confirmed_nonce) =
|
||||
self.client.latest_confirmed_received_nonce(at_block).await?;
|
||||
if update_metrics {
|
||||
if let Some(metrics_msg) = self.metrics_msg.as_ref() {
|
||||
metrics_msg.update_source_latest_confirmed_nonce::<P>(latest_confirmed_nonce);
|
||||
}
|
||||
}
|
||||
Ok((
|
||||
at_block,
|
||||
TargetClientNonces {
|
||||
latest_nonce: latest_confirmed_nonce,
|
||||
nonces_data: (),
|
||||
},
|
||||
))
|
||||
Ok((at_block, TargetClientNonces { latest_nonce: latest_confirmed_nonce, nonces_data: () }))
|
||||
}
|
||||
|
||||
async fn submit_proof(
|
||||
@@ -189,9 +183,7 @@ where
|
||||
nonces: RangeInclusive<MessageNonce>,
|
||||
proof: P::MessagesReceivingProof,
|
||||
) -> Result<RangeInclusive<MessageNonce>, Self::Error> {
|
||||
self.client
|
||||
.submit_messages_receiving_proof(generated_at_block, proof)
|
||||
.await?;
|
||||
self.client.submit_messages_receiving_proof(generated_at_block, proof).await?;
|
||||
Ok(nonces)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
//! 2) new nonces may be proved to target node (i.e. they have appeared at the
|
||||
//! block, which is known to the target node).
|
||||
|
||||
use crate::message_race_loop::{NoncesRange, RaceState, RaceStrategy, SourceClientNonces, TargetClientNonces};
|
||||
use crate::message_race_loop::{
|
||||
NoncesRange, RaceState, RaceStrategy, SourceClientNonces, TargetClientNonces,
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bp_messages::MessageNonce;
|
||||
@@ -40,15 +42,29 @@ pub struct BasicStrategy<
|
||||
> {
|
||||
/// All queued nonces.
|
||||
source_queue: SourceRangesQueue<SourceHeaderHash, SourceHeaderNumber, SourceNoncesRange>,
|
||||
/// Best nonce known to target node (at its best block). `None` if it has not been received yet.
|
||||
/// The best nonce known to target node (at its best block). `None` if it has not been received
|
||||
/// yet.
|
||||
best_target_nonce: Option<MessageNonce>,
|
||||
/// Unused generic types dump.
|
||||
_phantom: PhantomData<(TargetHeaderNumber, TargetHeaderHash, Proof)>,
|
||||
}
|
||||
|
||||
impl<SourceHeaderNumber, SourceHeaderHash, TargetHeaderNumber, TargetHeaderHash, SourceNoncesRange, Proof>
|
||||
BasicStrategy<SourceHeaderNumber, SourceHeaderHash, TargetHeaderNumber, TargetHeaderHash, SourceNoncesRange, Proof>
|
||||
where
|
||||
impl<
|
||||
SourceHeaderNumber,
|
||||
SourceHeaderHash,
|
||||
TargetHeaderNumber,
|
||||
TargetHeaderHash,
|
||||
SourceNoncesRange,
|
||||
Proof,
|
||||
>
|
||||
BasicStrategy<
|
||||
SourceHeaderNumber,
|
||||
SourceHeaderHash,
|
||||
TargetHeaderNumber,
|
||||
TargetHeaderHash,
|
||||
SourceNoncesRange,
|
||||
Proof,
|
||||
> where
|
||||
SourceHeaderHash: Clone,
|
||||
SourceHeaderNumber: Clone + Ord,
|
||||
SourceNoncesRange: NoncesRange,
|
||||
@@ -79,9 +95,9 @@ where
|
||||
|
||||
/// Returns index of the latest source queue entry, that may be delivered to the target node.
|
||||
///
|
||||
/// Returns `None` if no entries may be delivered. All entries before and including the `Some(_)`
|
||||
/// index are guaranteed to be witnessed at source blocks that are known to be finalized at the
|
||||
/// target node.
|
||||
/// Returns `None` if no entries may be delivered. All entries before and including the
|
||||
/// `Some(_)` index are guaranteed to be witnessed at source blocks that are known to be
|
||||
/// finalized at the target node.
|
||||
pub fn maximal_available_source_queue_index(
|
||||
&self,
|
||||
race_state: RaceState<
|
||||
@@ -95,12 +111,12 @@ where
|
||||
|
||||
// if we have already selected nonces that we want to submit, do nothing
|
||||
if race_state.nonces_to_submit.is_some() {
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
// if we already submitted some nonces, do nothing
|
||||
if race_state.nonces_submitted.is_some() {
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
// 1) we want to deliver all nonces, starting from `target_nonce + 1`
|
||||
@@ -124,17 +140,34 @@ where
|
||||
while let Some((queued_at, queued_range)) = self.source_queue.pop_front() {
|
||||
if let Some(range_to_requeue) = queued_range.greater_than(nonce) {
|
||||
self.source_queue.push_front((queued_at, range_to_requeue));
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<SourceHeaderNumber, SourceHeaderHash, TargetHeaderNumber, TargetHeaderHash, SourceNoncesRange, Proof>
|
||||
RaceStrategy<HeaderId<SourceHeaderHash, SourceHeaderNumber>, HeaderId<TargetHeaderHash, TargetHeaderNumber>, Proof>
|
||||
for BasicStrategy<SourceHeaderNumber, SourceHeaderHash, TargetHeaderNumber, TargetHeaderHash, SourceNoncesRange, Proof>
|
||||
where
|
||||
impl<
|
||||
SourceHeaderNumber,
|
||||
SourceHeaderHash,
|
||||
TargetHeaderNumber,
|
||||
TargetHeaderHash,
|
||||
SourceNoncesRange,
|
||||
Proof,
|
||||
>
|
||||
RaceStrategy<
|
||||
HeaderId<SourceHeaderHash, SourceHeaderNumber>,
|
||||
HeaderId<TargetHeaderHash, TargetHeaderNumber>,
|
||||
Proof,
|
||||
>
|
||||
for BasicStrategy<
|
||||
SourceHeaderNumber,
|
||||
SourceHeaderHash,
|
||||
TargetHeaderNumber,
|
||||
TargetHeaderHash,
|
||||
SourceNoncesRange,
|
||||
Proof,
|
||||
> where
|
||||
SourceHeaderHash: Clone + Debug + Send,
|
||||
SourceHeaderNumber: Clone + Ord + Debug + Send,
|
||||
SourceNoncesRange: NoncesRange + Debug + Send,
|
||||
@@ -162,7 +195,8 @@ where
|
||||
fn best_at_source(&self) -> Option<MessageNonce> {
|
||||
let best_in_queue = self.source_queue.back().map(|(_, range)| range.end());
|
||||
match (best_in_queue, self.best_target_nonce) {
|
||||
(Some(best_in_queue), Some(best_target_nonce)) if best_in_queue > best_target_nonce => Some(best_in_queue),
|
||||
(Some(best_in_queue), Some(best_target_nonce)) if best_in_queue > best_target_nonce =>
|
||||
Some(best_in_queue),
|
||||
(_, Some(best_target_nonce)) => Some(best_target_nonce),
|
||||
(_, None) => None,
|
||||
}
|
||||
@@ -205,18 +239,17 @@ where
|
||||
|
||||
if let Some(best_target_nonce) = self.best_target_nonce {
|
||||
if nonce < best_target_nonce {
|
||||
return;
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(true) = self.source_queue.front().map(|(_, range)| range.begin() <= nonce) {
|
||||
let maybe_subrange = self
|
||||
.source_queue
|
||||
.pop_front()
|
||||
.and_then(|(at_block, range)| range.greater_than(nonce).map(|subrange| (at_block, subrange)));
|
||||
let maybe_subrange = self.source_queue.pop_front().and_then(|(at_block, range)| {
|
||||
range.greater_than(nonce).map(|subrange| (at_block, subrange))
|
||||
});
|
||||
if let Some((at_block, subrange)) = maybe_subrange {
|
||||
self.source_queue.push_front((at_block, subrange));
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,10 +271,8 @@ where
|
||||
race_state.nonces_submitted = None;
|
||||
}
|
||||
|
||||
self.best_target_nonce = Some(std::cmp::max(
|
||||
self.best_target_nonce.unwrap_or(nonces.latest_nonce),
|
||||
nonce,
|
||||
));
|
||||
self.best_target_nonce =
|
||||
Some(std::cmp::max(self.best_target_nonce.unwrap_or(nonces.latest_nonce), nonce));
|
||||
}
|
||||
|
||||
fn finalized_target_nonces_updated(
|
||||
@@ -278,9 +309,12 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::message_lane::MessageLane;
|
||||
use crate::message_lane_loop::tests::{
|
||||
header_id, TestMessageLane, TestMessagesProof, TestSourceHeaderHash, TestSourceHeaderNumber,
|
||||
use crate::{
|
||||
message_lane::MessageLane,
|
||||
message_lane_loop::tests::{
|
||||
header_id, TestMessageLane, TestMessagesProof, TestSourceHeaderHash,
|
||||
TestSourceHeaderNumber,
|
||||
},
|
||||
};
|
||||
|
||||
type SourceNoncesRange = RangeInclusive<MessageNonce>;
|
||||
@@ -295,17 +329,11 @@ mod tests {
|
||||
>;
|
||||
|
||||
fn source_nonces(new_nonces: SourceNoncesRange) -> SourceClientNonces<SourceNoncesRange> {
|
||||
SourceClientNonces {
|
||||
new_nonces,
|
||||
confirmed_nonce: None,
|
||||
}
|
||||
SourceClientNonces { new_nonces, confirmed_nonce: None }
|
||||
}
|
||||
|
||||
fn target_nonces(latest_nonce: MessageNonce) -> TargetClientNonces<()> {
|
||||
TargetClientNonces {
|
||||
latest_nonce,
|
||||
nonces_data: (),
|
||||
}
|
||||
TargetClientNonces { latest_nonce, nonces_data: () }
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -420,18 +448,12 @@ mod tests {
|
||||
strategy.source_nonces_updated(header_id(5), source_nonces(7..=8));
|
||||
|
||||
state.best_finalized_source_header_id_at_best_target = Some(header_id(4));
|
||||
assert_eq!(
|
||||
strategy.select_nonces_to_deliver(state.clone()).await,
|
||||
Some((1..=6, ()))
|
||||
);
|
||||
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=6, ())));
|
||||
strategy.best_target_nonces_updated(target_nonces(6), &mut state);
|
||||
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
|
||||
|
||||
state.best_finalized_source_header_id_at_best_target = Some(header_id(5));
|
||||
assert_eq!(
|
||||
strategy.select_nonces_to_deliver(state.clone()).await,
|
||||
Some((7..=8, ()))
|
||||
);
|
||||
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((7..=8, ())));
|
||||
strategy.best_target_nonces_updated(target_nonces(8), &mut state);
|
||||
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
|
||||
}
|
||||
@@ -471,16 +493,17 @@ mod tests {
|
||||
strategy.source_nonces_updated(header_id(3), source_nonces(7..=9));
|
||||
|
||||
fn source_queue_nonces(
|
||||
source_queue: &SourceRangesQueue<TestSourceHeaderHash, TestSourceHeaderNumber, SourceNoncesRange>,
|
||||
source_queue: &SourceRangesQueue<
|
||||
TestSourceHeaderHash,
|
||||
TestSourceHeaderNumber,
|
||||
SourceNoncesRange,
|
||||
>,
|
||||
) -> Vec<MessageNonce> {
|
||||
source_queue.iter().flat_map(|(_, range)| range.clone()).collect()
|
||||
}
|
||||
|
||||
strategy.remove_le_nonces_from_source_queue(1);
|
||||
assert_eq!(
|
||||
source_queue_nonces(&strategy.source_queue),
|
||||
vec![2, 3, 4, 5, 6, 7, 8, 9],
|
||||
);
|
||||
assert_eq!(source_queue_nonces(&strategy.source_queue), vec![2, 3, 4, 5, 6, 7, 8, 9],);
|
||||
|
||||
strategy.remove_le_nonces_from_source_queue(5);
|
||||
assert_eq!(source_queue_nonces(&strategy.source_queue), vec![6, 7, 8, 9]);
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
//! Metrics for message lane relay loop.
|
||||
|
||||
use crate::message_lane::MessageLane;
|
||||
use crate::message_lane_loop::{SourceClientState, TargetClientState};
|
||||
use crate::{
|
||||
message_lane::MessageLane,
|
||||
message_lane_loop::{SourceClientState, TargetClientState},
|
||||
};
|
||||
|
||||
use bp_messages::MessageNonce;
|
||||
use relay_utils::metrics::{metric_name, register, GaugeVec, Opts, PrometheusError, Registry, U64};
|
||||
@@ -81,28 +83,40 @@ impl MessageLaneLoopMetrics {
|
||||
}
|
||||
|
||||
/// Update latest generated nonce at source.
|
||||
pub fn update_source_latest_generated_nonce<P: MessageLane>(&self, source_latest_generated_nonce: MessageNonce) {
|
||||
pub fn update_source_latest_generated_nonce<P: MessageLane>(
|
||||
&self,
|
||||
source_latest_generated_nonce: MessageNonce,
|
||||
) {
|
||||
self.lane_state_nonces
|
||||
.with_label_values(&["source_latest_generated"])
|
||||
.set(source_latest_generated_nonce);
|
||||
}
|
||||
|
||||
/// Update latest confirmed nonce at source.
|
||||
pub fn update_source_latest_confirmed_nonce<P: MessageLane>(&self, source_latest_confirmed_nonce: MessageNonce) {
|
||||
/// Update the latest confirmed nonce at source.
|
||||
pub fn update_source_latest_confirmed_nonce<P: MessageLane>(
|
||||
&self,
|
||||
source_latest_confirmed_nonce: MessageNonce,
|
||||
) {
|
||||
self.lane_state_nonces
|
||||
.with_label_values(&["source_latest_confirmed"])
|
||||
.set(source_latest_confirmed_nonce);
|
||||
}
|
||||
|
||||
/// Update latest received nonce at target.
|
||||
pub fn update_target_latest_received_nonce<P: MessageLane>(&self, target_latest_generated_nonce: MessageNonce) {
|
||||
/// Update the latest received nonce at target.
|
||||
pub fn update_target_latest_received_nonce<P: MessageLane>(
|
||||
&self,
|
||||
target_latest_generated_nonce: MessageNonce,
|
||||
) {
|
||||
self.lane_state_nonces
|
||||
.with_label_values(&["target_latest_received"])
|
||||
.set(target_latest_generated_nonce);
|
||||
}
|
||||
|
||||
/// Update latest confirmed nonce at target.
|
||||
pub fn update_target_latest_confirmed_nonce<P: MessageLane>(&self, target_latest_confirmed_nonce: MessageNonce) {
|
||||
/// Update the latest confirmed nonce at target.
|
||||
pub fn update_target_latest_confirmed_nonce<P: MessageLane>(
|
||||
&self,
|
||||
target_latest_confirmed_nonce: MessageNonce,
|
||||
) {
|
||||
self.lane_state_nonces
|
||||
.with_label_values(&["target_latest_confirmed"])
|
||||
.set(target_latest_confirmed_nonce);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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/>.
|
||||
|
||||
//! Altruistic relay strategy
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
message_lane::MessageLane,
|
||||
message_lane_loop::{
|
||||
SourceClient as MessageLaneSourceClient, TargetClient as MessageLaneTargetClient,
|
||||
},
|
||||
relay_strategy::{RelayReference, RelayStrategy},
|
||||
};
|
||||
|
||||
/// The relayer doesn't care about rewards.
|
||||
#[derive(Clone)]
|
||||
pub struct AltruisticStrategy;
|
||||
|
||||
#[async_trait]
|
||||
impl RelayStrategy for AltruisticStrategy {
|
||||
async fn decide<
|
||||
P: MessageLane,
|
||||
SourceClient: MessageLaneSourceClient<P>,
|
||||
TargetClient: MessageLaneTargetClient<P>,
|
||||
>(
|
||||
&self,
|
||||
_reference: &mut RelayReference<P, SourceClient, TargetClient>,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
// 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/>.
|
||||
|
||||
//! enforcement strategy
|
||||
|
||||
use num_traits::Zero;
|
||||
|
||||
use bp_messages::{MessageNonce, Weight};
|
||||
use bp_runtime::messages::DispatchFeePayment;
|
||||
|
||||
use crate::{
|
||||
message_lane::MessageLane,
|
||||
message_lane_loop::{
|
||||
MessageDetails, SourceClient as MessageLaneSourceClient,
|
||||
TargetClient as MessageLaneTargetClient,
|
||||
},
|
||||
message_race_loop::NoncesRange,
|
||||
relay_strategy::{RelayMessagesBatchReference, RelayReference, RelayStrategy},
|
||||
};
|
||||
|
||||
/// Do hard check and run soft check strategy
|
||||
#[derive(Clone)]
|
||||
pub struct EnforcementStrategy<Strategy: RelayStrategy> {
|
||||
strategy: Strategy,
|
||||
}
|
||||
|
||||
impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
|
||||
pub fn new(strategy: Strategy) -> Self {
|
||||
Self { strategy }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
|
||||
pub async fn decide<
|
||||
P: MessageLane,
|
||||
SourceClient: MessageLaneSourceClient<P>,
|
||||
TargetClient: MessageLaneTargetClient<P>,
|
||||
>(
|
||||
&self,
|
||||
reference: RelayMessagesBatchReference<P, SourceClient, TargetClient>,
|
||||
) -> Option<MessageNonce> {
|
||||
let mut hard_selected_count = 0;
|
||||
let mut soft_selected_count = 0;
|
||||
|
||||
let mut selected_weight: Weight = 0;
|
||||
let mut selected_count: MessageNonce = 0;
|
||||
|
||||
let hard_selected_begin_nonce =
|
||||
reference.nonces_queue[reference.nonces_queue_range.start].1.begin();
|
||||
|
||||
// relay reference
|
||||
let mut relay_reference = RelayReference {
|
||||
lane_source_client: reference.lane_source_client.clone(),
|
||||
lane_target_client: reference.lane_target_client.clone(),
|
||||
|
||||
selected_reward: P::SourceChainBalance::zero(),
|
||||
selected_cost: P::SourceChainBalance::zero(),
|
||||
selected_size: 0,
|
||||
|
||||
total_reward: P::SourceChainBalance::zero(),
|
||||
total_confirmations_cost: P::SourceChainBalance::zero(),
|
||||
total_cost: P::SourceChainBalance::zero(),
|
||||
|
||||
hard_selected_begin_nonce,
|
||||
selected_prepaid_nonces: 0,
|
||||
selected_unpaid_weight: 0,
|
||||
|
||||
index: 0,
|
||||
nonce: 0,
|
||||
details: MessageDetails {
|
||||
dispatch_weight: 0,
|
||||
size: 0,
|
||||
reward: P::SourceChainBalance::zero(),
|
||||
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
|
||||
},
|
||||
};
|
||||
|
||||
let all_ready_nonces = reference
|
||||
.nonces_queue
|
||||
.range(reference.nonces_queue_range.clone())
|
||||
.flat_map(|(_, ready_nonces)| ready_nonces.iter())
|
||||
.enumerate();
|
||||
for (index, (nonce, details)) in all_ready_nonces {
|
||||
relay_reference.index = index;
|
||||
relay_reference.nonce = *nonce;
|
||||
relay_reference.details = *details;
|
||||
|
||||
// Since we (hopefully) have some reserves in `max_messages_weight_in_single_batch`
|
||||
// and `max_messages_size_in_single_batch`, we may still try to submit transaction
|
||||
// with single message if message overflows these limits. The worst case would be if
|
||||
// transaction will be rejected by the target runtime, but at least we have tried.
|
||||
|
||||
// limit messages in the batch by weight
|
||||
let new_selected_weight = match selected_weight.checked_add(details.dispatch_weight) {
|
||||
Some(new_selected_weight)
|
||||
if new_selected_weight <= reference.max_messages_weight_in_single_batch =>
|
||||
new_selected_weight,
|
||||
new_selected_weight if selected_count == 0 => {
|
||||
log::warn!(
|
||||
target: "bridge",
|
||||
"Going to submit message delivery transaction with declared dispatch \
|
||||
weight {:?} that overflows maximal configured weight {}",
|
||||
new_selected_weight,
|
||||
reference.max_messages_weight_in_single_batch,
|
||||
);
|
||||
new_selected_weight.unwrap_or(Weight::MAX)
|
||||
},
|
||||
_ => break,
|
||||
};
|
||||
|
||||
// limit messages in the batch by size
|
||||
let new_selected_size = match relay_reference.selected_size.checked_add(details.size) {
|
||||
Some(new_selected_size)
|
||||
if new_selected_size <= reference.max_messages_size_in_single_batch =>
|
||||
new_selected_size,
|
||||
new_selected_size if selected_count == 0 => {
|
||||
log::warn!(
|
||||
target: "bridge",
|
||||
"Going to submit message delivery transaction with message \
|
||||
size {:?} that overflows maximal configured size {}",
|
||||
new_selected_size,
|
||||
reference.max_messages_size_in_single_batch,
|
||||
);
|
||||
new_selected_size.unwrap_or(u32::MAX)
|
||||
},
|
||||
_ => break,
|
||||
};
|
||||
|
||||
// limit number of messages in the batch
|
||||
let new_selected_count = selected_count + 1;
|
||||
if new_selected_count > reference.max_messages_in_this_batch {
|
||||
break
|
||||
}
|
||||
relay_reference.selected_size = new_selected_size;
|
||||
|
||||
// If dispatch fee has been paid at the source chain, it means that it is **relayer**
|
||||
// who's paying for dispatch at the target chain AND reward must cover this dispatch
|
||||
// fee.
|
||||
//
|
||||
// If dispatch fee is paid at the target chain, it means that it'll be withdrawn from
|
||||
// the dispatch origin account AND reward is not covering this fee.
|
||||
//
|
||||
// So in the latter case we're not adding the dispatch weight to the delivery
|
||||
// transaction weight.
|
||||
let mut new_selected_prepaid_nonces = relay_reference.selected_prepaid_nonces;
|
||||
let new_selected_unpaid_weight = match details.dispatch_fee_payment {
|
||||
DispatchFeePayment::AtSourceChain => {
|
||||
new_selected_prepaid_nonces += 1;
|
||||
relay_reference.selected_unpaid_weight.saturating_add(details.dispatch_weight)
|
||||
},
|
||||
DispatchFeePayment::AtTargetChain => relay_reference.selected_unpaid_weight,
|
||||
};
|
||||
relay_reference.selected_prepaid_nonces = new_selected_prepaid_nonces;
|
||||
relay_reference.selected_unpaid_weight = new_selected_unpaid_weight;
|
||||
|
||||
// now the message has passed all 'strong' checks, and we CAN deliver it. But do we WANT
|
||||
// to deliver it? It depends on the relayer strategy.
|
||||
if self.strategy.decide(&mut relay_reference).await {
|
||||
soft_selected_count = index + 1;
|
||||
}
|
||||
|
||||
hard_selected_count = index + 1;
|
||||
selected_weight = new_selected_weight;
|
||||
selected_count = new_selected_count;
|
||||
}
|
||||
|
||||
if hard_selected_count != soft_selected_count {
|
||||
let hard_selected_end_nonce =
|
||||
hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1;
|
||||
let soft_selected_begin_nonce = hard_selected_begin_nonce;
|
||||
let soft_selected_end_nonce =
|
||||
soft_selected_begin_nonce + soft_selected_count as MessageNonce - 1;
|
||||
log::warn!(
|
||||
target: "bridge",
|
||||
"Relayer may deliver nonces [{:?}; {:?}], but because of its strategy it has selected \
|
||||
nonces [{:?}; {:?}].",
|
||||
hard_selected_begin_nonce,
|
||||
hard_selected_end_nonce,
|
||||
soft_selected_begin_nonce,
|
||||
soft_selected_end_nonce,
|
||||
);
|
||||
|
||||
hard_selected_count = soft_selected_count;
|
||||
}
|
||||
|
||||
if hard_selected_count != 0 {
|
||||
if relay_reference.selected_reward != P::SourceChainBalance::zero() &&
|
||||
relay_reference.selected_cost != P::SourceChainBalance::zero()
|
||||
{
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"Expected reward from delivering nonces [{:?}; {:?}] is: {:?} - {:?} = {:?}",
|
||||
hard_selected_begin_nonce,
|
||||
hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1,
|
||||
&relay_reference.selected_reward,
|
||||
&relay_reference.selected_cost,
|
||||
relay_reference.selected_reward - relay_reference.selected_cost,
|
||||
);
|
||||
}
|
||||
|
||||
Some(hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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/>.
|
||||
|
||||
//! Adapter for using `enum RelayerMode` in a context which requires `RelayStrategy`.
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
message_lane::MessageLane,
|
||||
message_lane_loop::{
|
||||
RelayerMode, SourceClient as MessageLaneSourceClient,
|
||||
TargetClient as MessageLaneTargetClient,
|
||||
},
|
||||
relay_strategy::{AltruisticStrategy, RationalStrategy, RelayReference, RelayStrategy},
|
||||
};
|
||||
|
||||
/// `RelayerMode` adapter.
|
||||
#[derive(Clone)]
|
||||
pub struct MixStrategy {
|
||||
relayer_mode: RelayerMode,
|
||||
}
|
||||
|
||||
impl MixStrategy {
|
||||
/// Create mix strategy instance
|
||||
pub fn new(relayer_mode: RelayerMode) -> Self {
|
||||
Self { relayer_mode }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl RelayStrategy for MixStrategy {
|
||||
async fn decide<
|
||||
P: MessageLane,
|
||||
SourceClient: MessageLaneSourceClient<P>,
|
||||
TargetClient: MessageLaneTargetClient<P>,
|
||||
>(
|
||||
&self,
|
||||
reference: &mut RelayReference<P, SourceClient, TargetClient>,
|
||||
) -> bool {
|
||||
match self.relayer_mode {
|
||||
RelayerMode::Altruistic => AltruisticStrategy.decide(reference).await,
|
||||
RelayerMode::Rational => RationalStrategy.decide(reference).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// 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/>.
|
||||
|
||||
//! Relayer strategy
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use bp_messages::{MessageNonce, Weight};
|
||||
|
||||
use crate::{
|
||||
message_lane::MessageLane,
|
||||
message_lane_loop::{
|
||||
MessageDetails, MessageDetailsMap, SourceClient as MessageLaneSourceClient,
|
||||
TargetClient as MessageLaneTargetClient,
|
||||
},
|
||||
message_race_strategy::SourceRangesQueue,
|
||||
};
|
||||
|
||||
pub(crate) use self::enforcement_strategy::*;
|
||||
pub use self::{altruistic_strategy::*, mix_strategy::*, rational_strategy::*};
|
||||
|
||||
mod altruistic_strategy;
|
||||
mod enforcement_strategy;
|
||||
mod mix_strategy;
|
||||
mod rational_strategy;
|
||||
|
||||
/// Relayer strategy trait
|
||||
#[async_trait]
|
||||
pub trait RelayStrategy: 'static + Clone + Send + Sync {
|
||||
/// The relayer decide how to process nonce by reference.
|
||||
/// From given set of source nonces, that are ready to be delivered, select nonces
|
||||
/// to fit into single delivery transaction.
|
||||
///
|
||||
/// The function returns last nonce that must be delivered to the target chain.
|
||||
async fn decide<
|
||||
P: MessageLane,
|
||||
SourceClient: MessageLaneSourceClient<P>,
|
||||
TargetClient: MessageLaneTargetClient<P>,
|
||||
>(
|
||||
&self,
|
||||
reference: &mut RelayReference<P, SourceClient, TargetClient>,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
/// Reference data for participating in relay
|
||||
pub struct RelayReference<
|
||||
P: MessageLane,
|
||||
SourceClient: MessageLaneSourceClient<P>,
|
||||
TargetClient: MessageLaneTargetClient<P>,
|
||||
> {
|
||||
/// The client that is connected to the message lane source node.
|
||||
pub lane_source_client: SourceClient,
|
||||
/// The client that is connected to the message lane target node.
|
||||
pub lane_target_client: TargetClient,
|
||||
/// Current block reward summary
|
||||
pub selected_reward: P::SourceChainBalance,
|
||||
/// Current block cost summary
|
||||
pub selected_cost: P::SourceChainBalance,
|
||||
/// Messages size summary
|
||||
pub selected_size: u32,
|
||||
|
||||
/// Current block reward summary
|
||||
pub total_reward: P::SourceChainBalance,
|
||||
/// All confirmations cost
|
||||
pub total_confirmations_cost: P::SourceChainBalance,
|
||||
/// Current block cost summary
|
||||
pub total_cost: P::SourceChainBalance,
|
||||
|
||||
/// Hard check begin nonce
|
||||
pub hard_selected_begin_nonce: MessageNonce,
|
||||
/// Count prepaid nonces
|
||||
pub selected_prepaid_nonces: MessageNonce,
|
||||
/// Unpaid nonces weight summary
|
||||
pub selected_unpaid_weight: Weight,
|
||||
|
||||
/// Index by all ready nonces
|
||||
pub index: usize,
|
||||
/// Current nonce
|
||||
pub nonce: MessageNonce,
|
||||
/// Current nonce details
|
||||
pub details: MessageDetails<P::SourceChainBalance>,
|
||||
}
|
||||
|
||||
/// Relay reference data
|
||||
pub struct RelayMessagesBatchReference<
|
||||
P: MessageLane,
|
||||
SourceClient: MessageLaneSourceClient<P>,
|
||||
TargetClient: MessageLaneTargetClient<P>,
|
||||
> {
|
||||
/// Maximal number of relayed messages in single delivery transaction.
|
||||
pub max_messages_in_this_batch: MessageNonce,
|
||||
/// Maximal cumulative dispatch weight of relayed messages in single delivery transaction.
|
||||
pub max_messages_weight_in_single_batch: Weight,
|
||||
/// Maximal cumulative size of relayed messages in single delivery transaction.
|
||||
pub max_messages_size_in_single_batch: u32,
|
||||
/// The client that is connected to the message lane source node.
|
||||
pub lane_source_client: SourceClient,
|
||||
/// The client that is connected to the message lane target node.
|
||||
pub lane_target_client: TargetClient,
|
||||
/// Source queue.
|
||||
pub nonces_queue: SourceRangesQueue<
|
||||
P::SourceHeaderHash,
|
||||
P::SourceHeaderNumber,
|
||||
MessageDetailsMap<P::SourceChainBalance>,
|
||||
>,
|
||||
/// Source queue range
|
||||
pub nonces_queue_range: Range<usize>,
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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/>.
|
||||
|
||||
//! Rational relay strategy
|
||||
|
||||
use async_trait::async_trait;
|
||||
use num_traits::SaturatingAdd;
|
||||
|
||||
use bp_messages::MessageNonce;
|
||||
|
||||
use crate::{
|
||||
message_lane::MessageLane,
|
||||
message_lane_loop::{
|
||||
SourceClient as MessageLaneSourceClient, TargetClient as MessageLaneTargetClient,
|
||||
},
|
||||
relay_strategy::{RelayReference, RelayStrategy},
|
||||
};
|
||||
|
||||
/// The relayer will deliver all messages and confirmations as long as he's not losing any
|
||||
/// funds.
|
||||
#[derive(Clone)]
|
||||
pub struct RationalStrategy;
|
||||
|
||||
#[async_trait]
|
||||
impl RelayStrategy for RationalStrategy {
|
||||
async fn decide<
|
||||
P: MessageLane,
|
||||
SourceClient: MessageLaneSourceClient<P>,
|
||||
TargetClient: MessageLaneTargetClient<P>,
|
||||
>(
|
||||
&self,
|
||||
reference: &mut RelayReference<P, SourceClient, TargetClient>,
|
||||
) -> bool {
|
||||
// technically, multiple confirmations will be delivered in a single transaction,
|
||||
// meaning less loses for relayer. But here we don't know the final relayer yet, so
|
||||
// we're adding a separate transaction for every message. Normally, this cost is covered
|
||||
// by the message sender. Probably reconsider this?
|
||||
let confirmation_transaction_cost =
|
||||
reference.lane_source_client.estimate_confirmation_transaction().await;
|
||||
|
||||
let delivery_transaction_cost = match reference
|
||||
.lane_target_client
|
||||
.estimate_delivery_transaction_in_source_tokens(
|
||||
reference.hard_selected_begin_nonce..=
|
||||
(reference.hard_selected_begin_nonce + reference.index as MessageNonce),
|
||||
reference.selected_prepaid_nonces,
|
||||
reference.selected_unpaid_weight,
|
||||
reference.selected_size as u32,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
log::debug!(
|
||||
target: "bridge",
|
||||
"Failed to estimate delivery transaction cost: {:?}. No nonces selected for delivery",
|
||||
err,
|
||||
);
|
||||
return false
|
||||
},
|
||||
};
|
||||
|
||||
// if it is the first message that makes reward less than cost, let's log it
|
||||
// if this message makes batch profitable again, let's log it
|
||||
let is_total_reward_less_than_cost = reference.total_reward < reference.total_cost;
|
||||
let prev_total_cost = reference.total_cost;
|
||||
let prev_total_reward = reference.total_reward;
|
||||
reference.total_confirmations_cost = reference
|
||||
.total_confirmations_cost
|
||||
.saturating_add(&confirmation_transaction_cost);
|
||||
reference.total_reward = reference.total_reward.saturating_add(&reference.details.reward);
|
||||
reference.total_cost =
|
||||
reference.total_confirmations_cost.saturating_add(&delivery_transaction_cost);
|
||||
if !is_total_reward_less_than_cost && reference.total_reward < reference.total_cost {
|
||||
log::debug!(
|
||||
target: "bridge",
|
||||
"Message with nonce {} (reward = {:?}) changes total cost {:?}->{:?} and makes it larger than \
|
||||
total reward {:?}->{:?}",
|
||||
reference.nonce,
|
||||
reference.details.reward,
|
||||
prev_total_cost,
|
||||
reference.total_cost,
|
||||
prev_total_reward,
|
||||
reference.total_reward,
|
||||
);
|
||||
} else if is_total_reward_less_than_cost && reference.total_reward >= reference.total_cost {
|
||||
log::debug!(
|
||||
target: "bridge",
|
||||
"Message with nonce {} (reward = {:?}) changes total cost {:?}->{:?} and makes it less than or \
|
||||
equal to the total reward {:?}->{:?} (again)",
|
||||
reference.nonce,
|
||||
reference.details.reward,
|
||||
prev_total_cost,
|
||||
reference.total_cost,
|
||||
prev_total_reward,
|
||||
reference.total_reward,
|
||||
);
|
||||
}
|
||||
|
||||
// Rational relayer never want to lose his funds
|
||||
if reference.total_reward >= reference.total_cost {
|
||||
reference.selected_reward = reference.total_reward;
|
||||
reference.selected_cost = reference.total_cost;
|
||||
return true
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user