Fix invalid batch transaction (#1957)

* fix invalid batch transaction

* RaceState is now trait

* clippy
This commit is contained in:
Svyatoslav Nikolsky
2023-03-14 17:11:03 +03:00
committed by Bastian Köcher
parent ce89a2edca
commit 2a848fd836
6 changed files with 275 additions and 124 deletions
+21 -7
View File
@@ -91,18 +91,21 @@ impl<AccountId> TaggedAccount<AccountId> {
} }
/// Batch call builder. /// Batch call builder.
pub trait BatchCallBuilder<Call>: Send { pub trait BatchCallBuilder<Call>: Clone + Send {
/// Create batch call from given calls vector. /// Create batch call from given calls vector.
fn build_batch_call(&self, _calls: Vec<Call>) -> Call; fn build_batch_call(&self, _calls: Vec<Call>) -> Call;
} }
/// Batch call builder constructor. /// Batch call builder constructor.
pub trait BatchCallBuilderConstructor<Call> { pub trait BatchCallBuilderConstructor<Call>: Clone {
/// Call builder, used by this constructor.
type CallBuilder: BatchCallBuilder<Call>;
/// Create a new instance of a batch call builder. /// Create a new instance of a batch call builder.
fn new_builder() -> Option<Box<dyn BatchCallBuilder<Call>>>; fn new_builder() -> Option<Self::CallBuilder>;
} }
/// Batch call builder based on `pallet-utility`. /// Batch call builder based on `pallet-utility`.
#[derive(Clone)]
pub struct UtilityPalletBatchCallBuilder<C: Chain>(PhantomData<C>); pub struct UtilityPalletBatchCallBuilder<C: Chain>(PhantomData<C>);
impl<C: Chain> BatchCallBuilder<C::Call> for UtilityPalletBatchCallBuilder<C> impl<C: Chain> BatchCallBuilder<C::Call> for UtilityPalletBatchCallBuilder<C>
@@ -118,14 +121,25 @@ impl<C: Chain> BatchCallBuilderConstructor<C::Call> for UtilityPalletBatchCallBu
where where
C: ChainWithUtilityPallet, C: ChainWithUtilityPallet,
{ {
fn new_builder() -> Option<Box<dyn BatchCallBuilder<C::Call>>> { type CallBuilder = Self;
Some(Box::new(Self(Default::default())))
fn new_builder() -> Option<Self::CallBuilder> {
Some(Self(Default::default()))
} }
} }
/// A `BatchCallBuilderConstructor` that always returns `None`. // A `BatchCallBuilderConstructor` that always returns `None`.
impl<Call> BatchCallBuilderConstructor<Call> for () { impl<Call> BatchCallBuilderConstructor<Call> for () {
fn new_builder() -> Option<Box<dyn BatchCallBuilder<Call>>> { type CallBuilder = ();
fn new_builder() -> Option<Self::CallBuilder> {
None None
} }
} }
// Dummy `BatchCallBuilder` implementation that must never be used outside
// of the `impl BatchCallBuilderConstructor for ()` code.
impl<Call> BatchCallBuilder<Call> for () {
fn build_batch_call(&self, _calls: Vec<Call>) -> Call {
unreachable!("never called, because ()::new_builder() returns None; qed")
}
}
@@ -111,8 +111,9 @@ pub struct MessagesRelayParams<P: SubstrateMessageLane> {
/// Batch transaction that brings headers + and messages delivery/receiving confirmations to the /// Batch transaction that brings headers + and messages delivery/receiving confirmations to the
/// source node. /// source node.
#[derive(Clone)]
pub struct BatchProofTransaction<SC: Chain, TC: Chain, B: BatchCallBuilderConstructor<CallOf<SC>>> { pub struct BatchProofTransaction<SC: Chain, TC: Chain, B: BatchCallBuilderConstructor<CallOf<SC>>> {
builder: Box<dyn BatchCallBuilder<CallOf<SC>>>, builder: B::CallBuilder,
proved_header: HeaderIdOf<TC>, proved_header: HeaderIdOf<TC>,
prove_calls: Vec<CallOf<SC>>, prove_calls: Vec<CallOf<SC>>,
@@ -120,6 +121,16 @@ pub struct BatchProofTransaction<SC: Chain, TC: Chain, B: BatchCallBuilderConstr
_phantom: PhantomData<fn() -> B>, _phantom: PhantomData<fn() -> B>,
} }
impl<SC: Chain, TC: Chain, B: BatchCallBuilderConstructor<CallOf<SC>>> std::fmt::Debug
for BatchProofTransaction<SC, TC, B>
{
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct("BatchProofTransaction")
.field("proved_header", &self.proved_header)
.finish()
}
}
impl<SC: Chain, TC: Chain, B: BatchCallBuilderConstructor<CallOf<SC>>> impl<SC: Chain, TC: Chain, B: BatchCallBuilderConstructor<CallOf<SC>>>
BatchProofTransaction<SC, TC, B> BatchProofTransaction<SC, TC, B>
{ {
@@ -111,7 +111,7 @@ pub struct NoncesSubmitArtifacts<T> {
/// Batch transaction that already submit some headers and needs to be extended with /// Batch transaction that already submit some headers and needs to be extended with
/// messages/delivery proof before sending. /// messages/delivery proof before sending.
pub trait BatchTransaction<HeaderId>: Send { pub trait BatchTransaction<HeaderId>: Debug + Send {
/// Header that was required in the original call and which is bundled within this /// Header that was required in the original call and which is bundled within this
/// batch transaction. /// batch transaction.
fn required_header_id(&self) -> HeaderId; fn required_header_id(&self) -> HeaderId;
@@ -121,7 +121,7 @@ pub trait BatchTransaction<HeaderId>: Send {
#[async_trait] #[async_trait]
pub trait SourceClient<P: MessageLane>: RelayClient { pub trait SourceClient<P: MessageLane>: RelayClient {
/// Type of batch transaction that submits finality and message receiving proof. /// Type of batch transaction that submits finality and message receiving proof.
type BatchTransaction: BatchTransaction<TargetHeaderIdOf<P>>; type BatchTransaction: BatchTransaction<TargetHeaderIdOf<P>> + Clone;
/// Transaction tracker to track submitted transactions. /// Transaction tracker to track submitted transactions.
type TransactionTracker: TransactionTracker<HeaderId = SourceHeaderIdOf<P>>; type TransactionTracker: TransactionTracker<HeaderId = SourceHeaderIdOf<P>>;
@@ -186,7 +186,7 @@ pub trait SourceClient<P: MessageLane>: RelayClient {
#[async_trait] #[async_trait]
pub trait TargetClient<P: MessageLane>: RelayClient { pub trait TargetClient<P: MessageLane>: RelayClient {
/// Type of batch transaction that submits finality and messages proof. /// Type of batch transaction that submits finality and messages proof.
type BatchTransaction: BatchTransaction<SourceHeaderIdOf<P>>; type BatchTransaction: BatchTransaction<SourceHeaderIdOf<P>> + Clone;
/// Transaction tracker to track submitted transactions. /// Transaction tracker to track submitted transactions.
type TransactionTracker: TransactionTracker<HeaderId = TargetHeaderIdOf<P>>; type TransactionTracker: TransactionTracker<HeaderId = TargetHeaderIdOf<P>>;
@@ -1212,6 +1212,9 @@ pub(crate) mod tests {
original_data, original_data,
Arc::new(|_| {}), Arc::new(|_| {}),
Arc::new(move |data: &mut TestClientData| { Arc::new(move |data: &mut TestClientData| {
data.source_state.best_self =
HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1);
data.source_state.best_finalized_self = data.source_state.best_self;
if let Some(target_to_source_header_required) = if let Some(target_to_source_header_required) =
data.target_to_source_header_required.take() data.target_to_source_header_required.take()
{ {
@@ -1223,6 +1226,10 @@ pub(crate) mod tests {
}), }),
Arc::new(|_| {}), Arc::new(|_| {}),
Arc::new(move |data: &mut TestClientData| { Arc::new(move |data: &mut TestClientData| {
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;
if let Some(source_to_target_header_required) = if let Some(source_to_target_header_required) =
data.source_to_target_header_required.take() data.source_to_target_header_required.take()
{ {
@@ -322,13 +322,19 @@ where
self.strategy.is_empty() self.strategy.is_empty()
} }
fn required_source_header_at_target( fn required_source_header_at_target<RS: RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>>>(
&self, &self,
current_best: &SourceHeaderIdOf<P>, current_best: &SourceHeaderIdOf<P>,
race_state: RS,
) -> Option<SourceHeaderIdOf<P>> { ) -> Option<SourceHeaderIdOf<P>> {
// we have already submitted something - let's wait until it is mined
if race_state.nonces_submitted().is_some() {
return None
}
let has_nonces_to_deliver = !self.strategy.is_empty(); let has_nonces_to_deliver = !self.strategy.is_empty();
let header_required_for_messages_delivery = let header_required_for_messages_delivery =
self.strategy.required_source_header_at_target(current_best); self.strategy.required_source_header_at_target(current_best, race_state);
let header_required_for_reward_confirmations_delivery = self let header_required_for_reward_confirmations_delivery = self
.latest_confirmed_nonces_at_source .latest_confirmed_nonces_at_source
.back() .back()
@@ -381,10 +387,10 @@ where
self.strategy.source_nonces_updated(at_block, nonces) self.strategy.source_nonces_updated(at_block, nonces)
} }
fn best_target_nonces_updated( fn best_target_nonces_updated<RS: RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>>>(
&mut self, &mut self,
nonces: TargetClientNonces<DeliveryRaceTargetNoncesData>, nonces: TargetClientNonces<DeliveryRaceTargetNoncesData>,
race_state: &mut RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::MessagesProof>, race_state: &mut RS,
) { ) {
// best target nonces must always be ge than finalized target nonces // best target nonces must always be ge than finalized target nonces
let latest_nonce = nonces.latest_nonce; let latest_nonce = nonces.latest_nonce;
@@ -396,13 +402,13 @@ where
) )
} }
fn finalized_target_nonces_updated( fn finalized_target_nonces_updated<RS: RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>>>(
&mut self, &mut self,
nonces: TargetClientNonces<DeliveryRaceTargetNoncesData>, nonces: TargetClientNonces<DeliveryRaceTargetNoncesData>,
race_state: &mut RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::MessagesProof>, race_state: &mut RS,
) { ) {
if let Some(ref best_finalized_source_header_id_at_best_target) = if let Some(ref best_finalized_source_header_id_at_best_target) =
race_state.best_finalized_source_header_id_at_best_target race_state.best_finalized_source_header_id_at_best_target()
{ {
let oldest_header_number_to_keep = best_finalized_source_header_id_at_best_target.0; let oldest_header_number_to_keep = best_finalized_source_header_id_at_best_target.0;
while self while self
@@ -426,13 +432,13 @@ where
) )
} }
async fn select_nonces_to_deliver( async fn select_nonces_to_deliver<RS: RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>>>(
&self, &self,
race_state: RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::MessagesProof>, race_state: RS,
) -> Option<(RangeInclusive<MessageNonce>, Self::ProofParameters)> { ) -> Option<(RangeInclusive<MessageNonce>, Self::ProofParameters)> {
let best_target_nonce = self.strategy.best_at_target()?; let best_target_nonce = self.strategy.best_at_target()?;
let best_finalized_source_header_id_at_best_target = let best_finalized_source_header_id_at_best_target =
race_state.best_finalized_source_header_id_at_best_target.clone()?; race_state.best_finalized_source_header_id_at_best_target()?;
let latest_confirmed_nonce_at_source = self let latest_confirmed_nonce_at_source = self
.latest_confirmed_nonces_at_source .latest_confirmed_nonces_at_source
.iter() .iter()
@@ -576,12 +582,16 @@ impl<SourceChainBalance: std::fmt::Debug> NoncesRange for MessageDetailsMap<Sour
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::message_lane_loop::{ use crate::{
tests::{ message_lane_loop::{
header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance, tests::{
TestSourceClient, TestSourceHeaderId, TestTargetClient, TestTargetHeaderId, header_id, TestMessageLane, TestMessagesBatchTransaction, TestMessagesProof,
TestSourceChainBalance, TestSourceClient, TestSourceHeaderId, TestTargetClient,
TestTargetHeaderId,
},
MessageDetails,
}, },
MessageDetails, message_race_loop::RaceStateImpl,
}; };
use super::*; use super::*;
@@ -589,7 +599,12 @@ mod tests {
const DEFAULT_DISPATCH_WEIGHT: Weight = Weight::from_parts(1, 0); const DEFAULT_DISPATCH_WEIGHT: Weight = Weight::from_parts(1, 0);
const DEFAULT_SIZE: u32 = 1; const DEFAULT_SIZE: u32 = 1;
type TestRaceState = RaceState<TestSourceHeaderId, TestTargetHeaderId, TestMessagesProof>; type TestRaceState = RaceStateImpl<
TestSourceHeaderId,
TestTargetHeaderId,
TestMessagesProof,
TestMessagesBatchTransaction,
>;
type TestStrategy = type TestStrategy =
MessageDeliveryStrategy<TestMessageLane, TestSourceClient, TestTargetClient>; MessageDeliveryStrategy<TestMessageLane, TestSourceClient, TestTargetClient>;
@@ -617,12 +632,13 @@ mod tests {
} }
fn prepare_strategy() -> (TestRaceState, TestStrategy) { fn prepare_strategy() -> (TestRaceState, TestStrategy) {
let mut race_state = RaceState { let mut race_state = RaceStateImpl {
best_finalized_source_header_id_at_source: Some(header_id(1)), best_finalized_source_header_id_at_source: Some(header_id(1)),
best_finalized_source_header_id_at_best_target: Some(header_id(1)), best_finalized_source_header_id_at_best_target: Some(header_id(1)),
best_target_header_id: Some(header_id(1)), best_target_header_id: Some(header_id(1)),
best_finalized_target_header_id: Some(header_id(1)), best_finalized_target_header_id: Some(header_id(1)),
nonces_to_submit: None, nonces_to_submit: None,
nonces_to_submit_batch: None,
nonces_submitted: None, nonces_submitted: None,
}; };
@@ -964,14 +980,17 @@ mod tests {
); );
// nothing needs to be delivered now and we don't need any new headers // nothing needs to be delivered now and we don't need any new headers
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
assert_eq!(strategy.required_source_header_at_target(&header_id(1)), None); assert_eq!(strategy.required_source_header_at_target(&header_id(1), state.clone()), None);
// now let's generate two more nonces [24; 25] at the soruce; // now let's generate two more nonces [24; 25] at the soruce;
strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 19, 0)); strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 19, 0));
// //
// - so now we'll need to relay source block#2 to be able to accept messages [24; 25]. // - so now we'll need to relay source block#2 to be able to accept messages [24; 25].
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
assert_eq!(strategy.required_source_header_at_target(&header_id(1)), Some(header_id(2))); assert_eq!(
strategy.required_source_header_at_target(&header_id(1), state.clone()),
Some(header_id(2))
);
// let's relay source block#2 // let's relay source block#2
state.best_finalized_source_header_id_at_source = Some(header_id(2)); state.best_finalized_source_header_id_at_source = Some(header_id(2));
@@ -982,7 +1001,7 @@ mod tests {
// and ask strategy again => still nothing to deliver, because parallel confirmations // and ask strategy again => still nothing to deliver, because parallel confirmations
// race need to be pushed further // race need to be pushed further
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
assert_eq!(strategy.required_source_header_at_target(&header_id(2)), None); assert_eq!(strategy.required_source_header_at_target(&header_id(2), state.clone()), None);
// let's confirm messages [20; 23] // let's confirm messages [20; 23]
strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 23, 0)); strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 23, 0));
@@ -990,10 +1009,10 @@ mod tests {
// and ask strategy again => now we have everything required to deliver remaining // and ask strategy again => now we have everything required to deliver remaining
// [24; 25] nonces and proof of [20; 23] confirmation // [24; 25] nonces and proof of [20; 23] confirmation
assert_eq!( assert_eq!(
strategy.select_nonces_to_deliver(state).await, strategy.select_nonces_to_deliver(state.clone()).await,
Some(((24..=25), proof_parameters(true, 2))), Some(((24..=25), proof_parameters(true, 2))),
); );
assert_eq!(strategy.required_source_header_at_target(&header_id(2)), None); assert_eq!(strategy.required_source_header_at_target(&header_id(2), state), None);
} }
#[async_std::test] #[async_std::test]
@@ -1025,6 +1044,7 @@ mod tests {
#[test] #[test]
#[allow(clippy::reversed_empty_ranges)] #[allow(clippy::reversed_empty_ranges)]
fn no_source_headers_required_at_target_if_lanes_are_empty() { fn no_source_headers_required_at_target_if_lanes_are_empty() {
let (state, _) = prepare_strategy();
let mut strategy = TestStrategy { let mut strategy = TestStrategy {
max_unrewarded_relayer_entries_at_target: 4, max_unrewarded_relayer_entries_at_target: 4,
max_unconfirmed_nonces_at_target: 4, max_unconfirmed_nonces_at_target: 4,
@@ -1053,7 +1073,7 @@ mod tests {
strategy.latest_confirmed_nonces_at_source, strategy.latest_confirmed_nonces_at_source,
VecDeque::from([(source_header_id, 0)]) VecDeque::from([(source_header_id, 0)])
); );
assert_eq!(strategy.required_source_header_at_target(&source_header_id), None); assert_eq!(strategy.required_source_header_at_target(&source_header_id, state), None);
} }
#[async_std::test] #[async_std::test]
+128 -48
View File
@@ -25,7 +25,7 @@ use crate::message_lane_loop::{BatchTransaction, ClientState, NoncesSubmitArtifa
use async_trait::async_trait; use async_trait::async_trait;
use bp_messages::MessageNonce; use bp_messages::MessageNonce;
use futures::{ use futures::{
future::FutureExt, future::{FutureExt, TryFutureExt},
stream::{FusedStream, StreamExt}, stream::{FusedStream, StreamExt},
}; };
use relay_utils::{ use relay_utils::{
@@ -41,14 +41,14 @@ use std::{
/// One of races within lane. /// One of races within lane.
pub trait MessageRace { pub trait MessageRace {
/// Header id of the race source. /// Header id of the race source.
type SourceHeaderId: Debug + Clone + PartialEq; type SourceHeaderId: Debug + Clone + PartialEq + Send;
/// Header id of the race source. /// Header id of the race source.
type TargetHeaderId: Debug + Clone + PartialEq; type TargetHeaderId: Debug + Clone + PartialEq + Send;
/// Message nonce used in the race. /// Message nonce used in the race.
type MessageNonce: Debug + Clone; type MessageNonce: Debug + Clone;
/// Proof that is generated and delivered in this race. /// Proof that is generated and delivered in this race.
type Proof: Debug + Clone; type Proof: Debug + Clone + Send;
/// Name of the race source. /// Name of the race source.
fn source_name() -> String; fn source_name() -> String;
@@ -128,7 +128,7 @@ pub trait TargetClient<P: MessageRace> {
/// Type of the additional data from the target client, used by the race. /// Type of the additional data from the target client, used by the race.
type TargetNoncesData: std::fmt::Debug; type TargetNoncesData: std::fmt::Debug;
/// Type of batch transaction that submits finality and proof to the target node. /// Type of batch transaction that submits finality and proof to the target node.
type BatchTransaction: BatchTransaction<P::SourceHeaderId>; type BatchTransaction: BatchTransaction<P::SourceHeaderId> + Clone;
/// Transaction tracker to track submitted transactions. /// Transaction tracker to track submitted transactions.
type TransactionTracker: TransactionTracker<HeaderId = P::TargetHeaderId>; type TransactionTracker: TransactionTracker<HeaderId = P::TargetHeaderId>;
@@ -175,9 +175,10 @@ pub trait RaceStrategy<SourceHeaderId, TargetHeaderId, Proof>: Debug {
/// Should return true if nothing has to be synced. /// Should return true if nothing has to be synced.
fn is_empty(&self) -> bool; fn is_empty(&self) -> bool;
/// Return id of source header that is required to be on target to continue synchronization. /// Return id of source header that is required to be on target to continue synchronization.
fn required_source_header_at_target( fn required_source_header_at_target<RS: RaceState<SourceHeaderId, TargetHeaderId>>(
&self, &self,
current_best: &SourceHeaderId, current_best: &SourceHeaderId,
race_state: RS,
) -> Option<SourceHeaderId>; ) -> Option<SourceHeaderId>;
/// Return the best nonce at source node. /// Return the best nonce at source node.
/// ///
@@ -196,29 +197,53 @@ pub trait RaceStrategy<SourceHeaderId, TargetHeaderId, Proof>: Debug {
nonces: SourceClientNonces<Self::SourceNoncesRange>, nonces: SourceClientNonces<Self::SourceNoncesRange>,
); );
/// Called when best nonces are updated at target node of the race. /// Called when best nonces are updated at target node of the race.
fn best_target_nonces_updated( fn best_target_nonces_updated<RS: RaceState<SourceHeaderId, TargetHeaderId>>(
&mut self, &mut self,
nonces: TargetClientNonces<Self::TargetNoncesData>, nonces: TargetClientNonces<Self::TargetNoncesData>,
race_state: &mut RaceState<SourceHeaderId, TargetHeaderId, Proof>, race_state: &mut RS,
); );
/// Called when finalized nonces are updated at target node of the race. /// Called when finalized nonces are updated at target node of the race.
fn finalized_target_nonces_updated( fn finalized_target_nonces_updated<RS: RaceState<SourceHeaderId, TargetHeaderId>>(
&mut self, &mut self,
nonces: TargetClientNonces<Self::TargetNoncesData>, nonces: TargetClientNonces<Self::TargetNoncesData>,
race_state: &mut RaceState<SourceHeaderId, TargetHeaderId, Proof>, race_state: &mut RS,
); );
/// Should return `Some(nonces)` if we need to deliver proof of `nonces` (and associated /// Should return `Some(nonces)` if we need to deliver proof of `nonces` (and associated
/// data) from source to target node. /// data) from source to target node.
/// Additionally, parameters required to generate proof are returned. /// Additionally, parameters required to generate proof are returned.
async fn select_nonces_to_deliver( async fn select_nonces_to_deliver<RS: RaceState<SourceHeaderId, TargetHeaderId>>(
&self, &self,
race_state: RaceState<SourceHeaderId, TargetHeaderId, Proof>, race_state: RS,
) -> Option<(RangeInclusive<MessageNonce>, Self::ProofParameters)>; ) -> Option<(RangeInclusive<MessageNonce>, Self::ProofParameters)>;
} }
/// State of the race. /// State of the race.
pub trait RaceState<SourceHeaderId, TargetHeaderId>: Send {
/// Best finalized source header id at the source client.
fn best_finalized_source_header_id_at_source(&self) -> Option<SourceHeaderId>;
/// Best finalized source header id at the best block on the target
/// client (at the `best_finalized_source_header_id_at_best_target`).
fn best_finalized_source_header_id_at_best_target(&self) -> Option<SourceHeaderId>;
/// The best header id at the target client.
fn best_target_header_id(&self) -> Option<TargetHeaderId>;
/// Best finalized header id at the target client.
fn best_finalized_target_header_id(&self) -> Option<TargetHeaderId>;
/// Returns `true` if we have selected nonces to submit to the target node.
fn nonces_to_submit(&self) -> Option<RangeInclusive<MessageNonce>>;
/// Reset our nonces selection.
fn reset_nonces_to_submit(&mut self);
/// Returns `true` if we have submitted some nonces to the target node and are
/// waiting for them to appear there.
fn nonces_submitted(&self) -> Option<RangeInclusive<MessageNonce>>;
/// Reset our nonces submission.
fn reset_nonces_submitted(&mut self);
}
/// State of the race and prepared batch transaction (if available).
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RaceState<SourceHeaderId, TargetHeaderId, Proof> { pub(crate) struct RaceStateImpl<SourceHeaderId, TargetHeaderId, Proof, BatchTx> {
/// Best finalized source header id at the source client. /// Best finalized source header id at the source client.
pub best_finalized_source_header_id_at_source: Option<SourceHeaderId>, pub best_finalized_source_header_id_at_source: Option<SourceHeaderId>,
/// Best finalized source header id at the best block on the target /// Best finalized source header id at the best block on the target
@@ -230,13 +255,67 @@ pub struct RaceState<SourceHeaderId, TargetHeaderId, Proof> {
pub best_finalized_target_header_id: Option<TargetHeaderId>, pub best_finalized_target_header_id: Option<TargetHeaderId>,
/// Range of nonces that we have selected to submit. /// Range of nonces that we have selected to submit.
pub nonces_to_submit: Option<(SourceHeaderId, RangeInclusive<MessageNonce>, Proof)>, pub nonces_to_submit: Option<(SourceHeaderId, RangeInclusive<MessageNonce>, Proof)>,
/// Batch transaction ready to include and deliver selected `nonces_to_submit` from the
/// `state`.
pub nonces_to_submit_batch: Option<BatchTx>,
/// Range of nonces that is currently submitted. /// Range of nonces that is currently submitted.
pub nonces_submitted: Option<RangeInclusive<MessageNonce>>, pub nonces_submitted: Option<RangeInclusive<MessageNonce>>,
} }
impl<SourceHeaderId, TargetHeaderId, Proof> RaceState<SourceHeaderId, TargetHeaderId, Proof> { impl<SourceHeaderId, TargetHeaderId, Proof, BatchTx> Default
/// Reset `nonces_submitted` to `None`. for RaceStateImpl<SourceHeaderId, TargetHeaderId, Proof, BatchTx>
fn reset_submitted(&mut self) { {
fn default() -> Self {
RaceStateImpl {
best_finalized_source_header_id_at_source: None,
best_finalized_source_header_id_at_best_target: None,
best_target_header_id: None,
best_finalized_target_header_id: None,
nonces_to_submit: None,
nonces_to_submit_batch: None,
nonces_submitted: None,
}
}
}
impl<SourceHeaderId, TargetHeaderId, Proof, BatchTx> RaceState<SourceHeaderId, TargetHeaderId>
for RaceStateImpl<SourceHeaderId, TargetHeaderId, Proof, BatchTx>
where
SourceHeaderId: Clone + Send,
TargetHeaderId: Clone + Send,
Proof: Clone + Send,
BatchTx: Clone + Send,
{
fn best_finalized_source_header_id_at_source(&self) -> Option<SourceHeaderId> {
self.best_finalized_source_header_id_at_source.clone()
}
fn best_finalized_source_header_id_at_best_target(&self) -> Option<SourceHeaderId> {
self.best_finalized_source_header_id_at_best_target.clone()
}
fn best_target_header_id(&self) -> Option<TargetHeaderId> {
self.best_target_header_id.clone()
}
fn best_finalized_target_header_id(&self) -> Option<TargetHeaderId> {
self.best_finalized_target_header_id.clone()
}
fn nonces_to_submit(&self) -> Option<RangeInclusive<MessageNonce>> {
self.nonces_to_submit.as_ref().map(|(_, nonces, _)| nonces.clone())
}
fn reset_nonces_to_submit(&mut self) {
self.nonces_to_submit = None;
self.nonces_to_submit_batch = None;
}
fn nonces_submitted(&self) -> Option<RangeInclusive<MessageNonce>> {
self.nonces_submitted.clone()
}
fn reset_nonces_submitted(&mut self) {
self.nonces_submitted = None; self.nonces_submitted = None;
} }
} }
@@ -257,7 +336,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
>, >,
) -> Result<(), FailedClient> { ) -> Result<(), FailedClient> {
let mut progress_context = Instant::now(); let mut progress_context = Instant::now();
let mut race_state = RaceState::default(); let mut race_state = RaceStateImpl::default();
let mut source_retry_backoff = retry_backoff(); let mut source_retry_backoff = retry_backoff();
let mut source_client_is_online = true; let mut source_client_is_online = true;
@@ -302,7 +381,8 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
!= Some(&source_state.best_finalized_self); != Some(&source_state.best_finalized_self);
if is_source_state_updated { if is_source_state_updated {
source_nonces_required = true; source_nonces_required = true;
race_state.best_finalized_source_header_id_at_source = Some(source_state.best_finalized_self); race_state.best_finalized_source_header_id_at_source
= Some(source_state.best_finalized_self);
} }
} }
}, },
@@ -353,7 +433,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
source_required_header = race_state source_required_header = race_state
.best_finalized_source_header_id_at_best_target .best_finalized_source_header_id_at_best_target
.as_ref() .as_ref()
.and_then(|best| strategy.required_source_header_at_target(best)); .and_then(|best| strategy.required_source_header_at_target(best, race_state.clone()));
}, },
nonces = target_best_nonces => { nonces = target_best_nonces => {
target_best_nonces_required = false; target_best_nonces_required = false;
@@ -408,10 +488,13 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|maybe_batch_transaction: Option<TC::BatchTransaction>| { |maybe_batch_transaction: Option<TC::BatchTransaction>| {
log::debug!( log::debug!(
target: "bridge", target: "bridge",
"Target {} client has been asked for more {} headers. Batch tx: {:?}", "Target {} client has been asked for more {} headers. Batch tx: {}",
P::target_name(), P::target_name(),
P::source_name(), P::source_name(),
maybe_batch_transaction.is_some(), maybe_batch_transaction
.as_ref()
.map(|bt| format!("yes ({:?})", bt.required_header_id()))
.unwrap_or_else(|| "no".into()),
); );
target_batch_transaction = maybe_batch_transaction; target_batch_transaction = maybe_batch_transaction;
@@ -425,7 +508,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
source_client_is_online = process_future_result( source_client_is_online = process_future_result(
proof, proof,
&mut source_retry_backoff, &mut source_retry_backoff,
|(at_block, nonces_range, proof)| { |(at_block, nonces_range, proof, batch_transaction)| {
log::debug!( log::debug!(
target: "bridge", target: "bridge",
"Received proof for nonces in range {:?} from {}", "Received proof for nonces in range {:?} from {}",
@@ -434,6 +517,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
); );
race_state.nonces_to_submit = Some((at_block, nonces_range, proof)); race_state.nonces_to_submit = Some((at_block, nonces_range, proof));
race_state.nonces_to_submit_batch = batch_transaction;
}, },
&mut source_go_offline_future, &mut source_go_offline_future,
async_std::task::sleep, async_std::task::sleep,
@@ -452,8 +536,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
P::target_name(), P::target_name(),
); );
target_batch_transaction = None; race_state.reset_nonces_to_submit();
race_state.nonces_to_submit = None;
race_state.nonces_submitted = Some(artifacts.nonces); race_state.nonces_submitted = Some(artifacts.nonces);
target_tx_tracker.set(artifacts.tx_tracker.wait().fuse()); target_tx_tracker.set(artifacts.tx_tracker.wait().fuse());
}, },
@@ -490,7 +573,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
e, e,
); );
race_state.reset_submitted(); race_state.reset_nonces_submitted();
}); });
}, },
(TrackedTransactionStatus::Lost, _) => { (TrackedTransactionStatus::Lost, _) => {
@@ -503,7 +586,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
strategy, strategy,
); );
race_state.reset_submitted(); race_state.reset_nonces_submitted();
}, },
_ => (), _ => (),
} }
@@ -524,6 +607,12 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
source_client_is_online = false; source_client_is_online = false;
// if we've started to submit batch transaction, let's prioritize it // if we've started to submit batch transaction, let's prioritize it
//
// we're using `take` here, because we don't need batch transaction (i.e. some
// underlying finality proof) anymore for our future calls - we were unable to
// use it for our current state, so why would we need to keep an obsolete proof
// for the future?
let target_batch_transaction = target_batch_transaction.take();
let expected_race_state = let expected_race_state =
if let Some(ref target_batch_transaction) = target_batch_transaction { if let Some(ref target_batch_transaction) = target_batch_transaction {
// when selecting nonces for the batch transaction, we assume that the required // when selecting nonces for the batch transaction, we assume that the required
@@ -551,7 +640,12 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
); );
source_generate_proof.set( 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)
.and_then(|(at_source_block, nonces, proof)| async {
Ok((at_source_block, nonces, proof, target_batch_transaction))
})
.fuse(),
); );
} else if source_nonces_required && best_at_source.is_some() { } else if source_nonces_required && best_at_source.is_some() {
log::debug!(target: "bridge", "Asking {} about message nonces", P::source_name()); log::debug!(target: "bridge", "Asking {} about message nonces", P::source_name());
@@ -582,7 +676,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
"Going to submit proof of messages in range {:?} to {} node{}", "Going to submit proof of messages in range {:?} to {} node{}",
nonces_range, nonces_range,
P::target_name(), P::target_name(),
target_batch_transaction.as_ref().map(|tx| format!( race_state.nonces_to_submit_batch.as_ref().map(|tx| format!(
". This transaction is batched with sending the proof for header {:?}.", ". This transaction is batched with sending the proof for header {:?}.",
tx.required_header_id()) tx.required_header_id())
).unwrap_or_default(), ).unwrap_or_default(),
@@ -591,7 +685,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
target_submit_proof.set( target_submit_proof.set(
race_target race_target
.submit_proof( .submit_proof(
target_batch_transaction.take(), race_state.nonces_to_submit_batch.clone(),
at_block.clone(), at_block.clone(),
nonces_range.clone(), nonces_range.clone(),
proof.clone(), proof.clone(),
@@ -628,21 +722,6 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
} }
} }
impl<SourceHeaderId, TargetHeaderId, Proof> Default
for RaceState<SourceHeaderId, TargetHeaderId, Proof>
{
fn default() -> Self {
RaceState {
best_finalized_source_header_id_at_source: None,
best_finalized_source_header_id_at_best_target: None,
best_target_header_id: None,
best_finalized_target_header_id: None,
nonces_to_submit: None,
nonces_submitted: None,
}
}
}
/// Print race progress. /// Print race progress.
fn print_race_progress<P, S>(prev_time: Instant, strategy: &S) -> Instant fn print_race_progress<P, S>(prev_time: Instant, strategy: &S) -> Instant
where where
@@ -670,7 +749,7 @@ where
} }
async fn select_nonces_to_deliver<SourceHeaderId, TargetHeaderId, Proof, Strategy>( async fn select_nonces_to_deliver<SourceHeaderId, TargetHeaderId, Proof, Strategy>(
race_state: RaceState<SourceHeaderId, TargetHeaderId, Proof>, race_state: impl RaceState<SourceHeaderId, TargetHeaderId>,
strategy: &Strategy, strategy: &Strategy,
) -> Option<(SourceHeaderId, RangeInclusive<MessageNonce>, Strategy::ProofParameters)> ) -> Option<(SourceHeaderId, RangeInclusive<MessageNonce>, Strategy::ProofParameters)>
where where
@@ -678,7 +757,7 @@ where
Strategy: RaceStrategy<SourceHeaderId, TargetHeaderId, Proof>, Strategy: RaceStrategy<SourceHeaderId, TargetHeaderId, Proof>,
{ {
let best_finalized_source_header_id_at_best_target = let best_finalized_source_header_id_at_best_target =
race_state.best_finalized_source_header_id_at_best_target.clone()?; race_state.best_finalized_source_header_id_at_best_target()?;
strategy strategy
.select_nonces_to_deliver(race_state) .select_nonces_to_deliver(race_state)
.await .await
@@ -701,7 +780,7 @@ mod tests {
// target node only knows about source' BEST_AT_TARGET block // target node only knows about source' BEST_AT_TARGET block
// source node has BEST_AT_SOURCE > BEST_AT_TARGET block // source node has BEST_AT_SOURCE > BEST_AT_TARGET block
let mut race_state = RaceState::<_, _, ()> { let mut race_state = RaceStateImpl::<_, _, (), ()> {
best_finalized_source_header_id_at_source: Some(HeaderId( best_finalized_source_header_id_at_source: Some(HeaderId(
BEST_AT_SOURCE, BEST_AT_SOURCE,
BEST_AT_SOURCE, BEST_AT_SOURCE,
@@ -713,11 +792,12 @@ mod tests {
best_target_header_id: Some(HeaderId(0, 0)), best_target_header_id: Some(HeaderId(0, 0)),
best_finalized_target_header_id: Some(HeaderId(0, 0)), best_finalized_target_header_id: Some(HeaderId(0, 0)),
nonces_to_submit: None, nonces_to_submit: None,
nonces_to_submit_batch: None,
nonces_submitted: None, nonces_submitted: None,
}; };
// we have some nonces to deliver and they're generated at GENERATED_AT < BEST_AT_SOURCE // we have some nonces to deliver and they're generated at GENERATED_AT < BEST_AT_SOURCE
let mut strategy = BasicStrategy::new(); let mut strategy = BasicStrategy::<_, _, _, _, _, ()>::new();
strategy.source_nonces_updated( strategy.source_nonces_updated(
HeaderId(GENERATED_AT, GENERATED_AT), HeaderId(GENERATED_AT, GENERATED_AT),
SourceClientNonces { new_nonces: 0..=10, confirmed_nonce: None }, SourceClientNonces { new_nonces: 0..=10, confirmed_nonce: None },
@@ -106,24 +106,25 @@ impl<
/// at source blocks that are known to be finalized at the target node. /// at source blocks that are known to be finalized at the target node.
/// ///
/// Returns `None` if no entries may be delivered. /// Returns `None` if no entries may be delivered.
pub fn available_source_queue_indices( pub fn available_source_queue_indices<
&self, RS: RaceState<
race_state: RaceState<
HeaderId<SourceHeaderHash, SourceHeaderNumber>, HeaderId<SourceHeaderHash, SourceHeaderNumber>,
HeaderId<TargetHeaderHash, TargetHeaderNumber>, HeaderId<TargetHeaderHash, TargetHeaderNumber>,
Proof,
>, >,
>(
&self,
race_state: RS,
) -> Option<RangeInclusive<usize>> { ) -> Option<RangeInclusive<usize>> {
// if we do not know best nonce at target node, we can't select anything // if we do not know best nonce at target node, we can't select anything
let best_target_nonce = self.best_target_nonce?; let best_target_nonce = self.best_target_nonce?;
// if we have already selected nonces that we want to submit, do nothing // if we have already selected nonces that we want to submit, do nothing
if race_state.nonces_to_submit.is_some() { if race_state.nonces_to_submit().is_some() {
return None return None
} }
// if we already submitted some nonces, do nothing // if we already submitted some nonces, do nothing
if race_state.nonces_submitted.is_some() { if race_state.nonces_submitted().is_some() {
return None return None
} }
@@ -143,7 +144,7 @@ impl<
// //
// => let's first select range of entries inside deque that are already finalized at // => let's first select range of entries inside deque that are already finalized at
// the target client and pass this range to the selector // the target client and pass this range to the selector
let best_header_at_target = race_state.best_finalized_source_header_id_at_best_target?; let best_header_at_target = race_state.best_finalized_source_header_id_at_best_target()?;
let end_index = self let end_index = self
.source_queue .source_queue
.iter() .iter()
@@ -204,9 +205,15 @@ impl<
self.source_queue.is_empty() self.source_queue.is_empty()
} }
fn required_source_header_at_target( fn required_source_header_at_target<
RS: RaceState<
HeaderId<SourceHeaderHash, SourceHeaderNumber>,
HeaderId<TargetHeaderHash, TargetHeaderNumber>,
>,
>(
&self, &self,
current_best: &HeaderId<SourceHeaderHash, SourceHeaderNumber>, current_best: &HeaderId<SourceHeaderHash, SourceHeaderNumber>,
_race_state: RS,
) -> Option<HeaderId<SourceHeaderHash, SourceHeaderNumber>> { ) -> Option<HeaderId<SourceHeaderHash, SourceHeaderNumber>> {
self.source_queue self.source_queue
.back() .back()
@@ -247,46 +254,46 @@ impl<
) )
} }
fn best_target_nonces_updated( fn best_target_nonces_updated<
&mut self, RS: RaceState<
nonces: TargetClientNonces<()>,
race_state: &mut RaceState<
HeaderId<SourceHeaderHash, SourceHeaderNumber>, HeaderId<SourceHeaderHash, SourceHeaderNumber>,
HeaderId<TargetHeaderHash, TargetHeaderNumber>, HeaderId<TargetHeaderHash, TargetHeaderNumber>,
Proof,
>, >,
>(
&mut self,
nonces: TargetClientNonces<()>,
race_state: &mut RS,
) { ) {
let nonce = nonces.latest_nonce; let nonce = nonces.latest_nonce;
let need_to_select_new_nonces = race_state let need_to_select_new_nonces = race_state
.nonces_to_submit .nonces_to_submit()
.as_ref() .map(|nonces| *nonces.end() <= nonce)
.map(|(_, nonces, _)| *nonces.end() <= nonce)
.unwrap_or(false); .unwrap_or(false);
if need_to_select_new_nonces { if need_to_select_new_nonces {
race_state.nonces_to_submit = None; race_state.reset_nonces_to_submit();
} }
let need_new_nonces_to_submit = race_state let need_new_nonces_to_submit = race_state
.nonces_submitted .nonces_submitted()
.as_ref()
.map(|nonces| *nonces.end() <= nonce) .map(|nonces| *nonces.end() <= nonce)
.unwrap_or(false); .unwrap_or(false);
if need_new_nonces_to_submit { if need_new_nonces_to_submit {
race_state.nonces_submitted = None; race_state.reset_nonces_submitted();
} }
self.best_target_nonce = Some(nonce); self.best_target_nonce = Some(nonce);
} }
fn finalized_target_nonces_updated( fn finalized_target_nonces_updated<
&mut self, RS: RaceState<
nonces: TargetClientNonces<()>,
_race_state: &mut RaceState<
HeaderId<SourceHeaderHash, SourceHeaderNumber>, HeaderId<SourceHeaderHash, SourceHeaderNumber>,
HeaderId<TargetHeaderHash, TargetHeaderNumber>, HeaderId<TargetHeaderHash, TargetHeaderNumber>,
Proof,
>, >,
>(
&mut self,
nonces: TargetClientNonces<()>,
_race_state: &mut RS,
) { ) {
self.remove_le_nonces_from_source_queue(nonces.latest_nonce); self.remove_le_nonces_from_source_queue(nonces.latest_nonce);
self.best_target_nonce = Some(std::cmp::max( self.best_target_nonce = Some(std::cmp::max(
@@ -295,13 +302,14 @@ impl<
)); ));
} }
async fn select_nonces_to_deliver( async fn select_nonces_to_deliver<
&self, RS: RaceState<
race_state: RaceState<
HeaderId<SourceHeaderHash, SourceHeaderNumber>, HeaderId<SourceHeaderHash, SourceHeaderNumber>,
HeaderId<TargetHeaderHash, TargetHeaderNumber>, HeaderId<TargetHeaderHash, TargetHeaderNumber>,
Proof,
>, >,
>(
&self,
race_state: RS,
) -> Option<(RangeInclusive<MessageNonce>, Self::ProofParameters)> { ) -> Option<(RangeInclusive<MessageNonce>, Self::ProofParameters)> {
let available_indices = self.available_source_queue_indices(race_state)?; let available_indices = self.available_source_queue_indices(race_state)?;
let range_begin = std::cmp::max( let range_begin = std::cmp::max(
@@ -317,15 +325,23 @@ impl<
mod tests { mod tests {
use super::*; use super::*;
use crate::{ use crate::{
message_lane::MessageLane, message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
message_lane_loop::tests::{ message_lane_loop::tests::{
header_id, TestMessageLane, TestMessagesProof, TestSourceHeaderHash, header_id, TestMessageLane, TestMessagesProof, TestSourceHeaderHash,
TestSourceHeaderNumber, TestSourceHeaderNumber,
}, },
message_race_loop::RaceStateImpl,
}; };
type SourceNoncesRange = RangeInclusive<MessageNonce>; type SourceNoncesRange = RangeInclusive<MessageNonce>;
type TestRaceStateImpl = RaceStateImpl<
SourceHeaderIdOf<TestMessageLane>,
TargetHeaderIdOf<TestMessageLane>,
TestMessagesProof,
(),
>;
type BasicStrategy<P> = super::BasicStrategy< type BasicStrategy<P> = super::BasicStrategy<
<P as MessageLane>::SourceHeaderNumber, <P as MessageLane>::SourceHeaderNumber,
<P as MessageLane>::SourceHeaderHash, <P as MessageLane>::SourceHeaderHash,
@@ -357,7 +373,7 @@ mod tests {
assert_eq!(strategy.best_at_source(), None); assert_eq!(strategy.best_at_source(), None);
strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); strategy.source_nonces_updated(header_id(1), source_nonces(1..=5));
assert_eq!(strategy.best_at_source(), None); assert_eq!(strategy.best_at_source(), None);
strategy.best_target_nonces_updated(target_nonces(10), &mut Default::default()); strategy.best_target_nonces_updated(target_nonces(10), &mut TestRaceStateImpl::default());
assert_eq!(strategy.source_queue, vec![(header_id(1), 1..=5)]); assert_eq!(strategy.source_queue, vec![(header_id(1), 1..=5)]);
assert_eq!(strategy.best_at_source(), Some(10)); assert_eq!(strategy.best_at_source(), Some(10));
} }
@@ -365,7 +381,7 @@ mod tests {
#[test] #[test]
fn source_nonce_is_never_lower_than_known_target_nonce() { fn source_nonce_is_never_lower_than_known_target_nonce() {
let mut strategy = BasicStrategy::<TestMessageLane>::new(); let mut strategy = BasicStrategy::<TestMessageLane>::new();
strategy.best_target_nonces_updated(target_nonces(10), &mut Default::default()); strategy.best_target_nonces_updated(target_nonces(10), &mut TestRaceStateImpl::default());
strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); strategy.source_nonces_updated(header_id(1), source_nonces(1..=5));
assert_eq!(strategy.source_queue, vec![]); assert_eq!(strategy.source_queue, vec![]);
} }
@@ -386,15 +402,17 @@ mod tests {
strategy.source_nonces_updated(header_id(2), source_nonces(6..=10)); strategy.source_nonces_updated(header_id(2), source_nonces(6..=10));
strategy.source_nonces_updated(header_id(3), source_nonces(11..=15)); strategy.source_nonces_updated(header_id(3), source_nonces(11..=15));
strategy.source_nonces_updated(header_id(4), source_nonces(16..=20)); strategy.source_nonces_updated(header_id(4), source_nonces(16..=20));
strategy.finalized_target_nonces_updated(target_nonces(15), &mut Default::default()); strategy
.finalized_target_nonces_updated(target_nonces(15), &mut TestRaceStateImpl::default());
assert_eq!(strategy.source_queue, vec![(header_id(4), 16..=20)]); assert_eq!(strategy.source_queue, vec![(header_id(4), 16..=20)]);
strategy.finalized_target_nonces_updated(target_nonces(17), &mut Default::default()); strategy
.finalized_target_nonces_updated(target_nonces(17), &mut TestRaceStateImpl::default());
assert_eq!(strategy.source_queue, vec![(header_id(4), 18..=20)]); assert_eq!(strategy.source_queue, vec![(header_id(4), 18..=20)]);
} }
#[test] #[test]
fn selected_nonces_are_dropped_on_target_nonce_update() { fn selected_nonces_are_dropped_on_target_nonce_update() {
let mut state = RaceState::default(); let mut state = TestRaceStateImpl::default();
let mut strategy = BasicStrategy::<TestMessageLane>::new(); let mut strategy = BasicStrategy::<TestMessageLane>::new();
state.nonces_to_submit = Some((header_id(1), 5..=10, (5..=10, None))); state.nonces_to_submit = Some((header_id(1), 5..=10, (5..=10, None)));
strategy.best_target_nonces_updated(target_nonces(7), &mut state); strategy.best_target_nonces_updated(target_nonces(7), &mut state);
@@ -405,7 +423,7 @@ mod tests {
#[test] #[test]
fn submitted_nonces_are_dropped_on_target_nonce_update() { fn submitted_nonces_are_dropped_on_target_nonce_update() {
let mut state = RaceState::default(); let mut state = TestRaceStateImpl::default();
let mut strategy = BasicStrategy::<TestMessageLane>::new(); let mut strategy = BasicStrategy::<TestMessageLane>::new();
state.nonces_submitted = Some(5..=10); state.nonces_submitted = Some(5..=10);
strategy.best_target_nonces_updated(target_nonces(7), &mut state); strategy.best_target_nonces_updated(target_nonces(7), &mut state);
@@ -416,7 +434,7 @@ mod tests {
#[async_std::test] #[async_std::test]
async fn nothing_is_selected_if_something_is_already_selected() { async fn nothing_is_selected_if_something_is_already_selected() {
let mut state = RaceState::default(); let mut state = TestRaceStateImpl::default();
let mut strategy = BasicStrategy::<TestMessageLane>::new(); let mut strategy = BasicStrategy::<TestMessageLane>::new();
state.nonces_to_submit = Some((header_id(1), 1..=10, (1..=10, None))); state.nonces_to_submit = Some((header_id(1), 1..=10, (1..=10, None)));
strategy.best_target_nonces_updated(target_nonces(0), &mut state); strategy.best_target_nonces_updated(target_nonces(0), &mut state);
@@ -426,7 +444,7 @@ mod tests {
#[async_std::test] #[async_std::test]
async fn nothing_is_selected_if_something_is_already_submitted() { async fn nothing_is_selected_if_something_is_already_submitted() {
let mut state = RaceState::default(); let mut state = TestRaceStateImpl::default();
let mut strategy = BasicStrategy::<TestMessageLane>::new(); let mut strategy = BasicStrategy::<TestMessageLane>::new();
state.nonces_submitted = Some(1..=10); state.nonces_submitted = Some(1..=10);
strategy.best_target_nonces_updated(target_nonces(0), &mut state); strategy.best_target_nonces_updated(target_nonces(0), &mut state);
@@ -436,7 +454,7 @@ mod tests {
#[async_std::test] #[async_std::test]
async fn select_nonces_to_deliver_works() { async fn select_nonces_to_deliver_works() {
let mut state = RaceState::<_, _, TestMessagesProof>::default(); let mut state = TestRaceStateImpl::default();
let mut strategy = BasicStrategy::<TestMessageLane>::new(); let mut strategy = BasicStrategy::<TestMessageLane>::new();
strategy.best_target_nonces_updated(target_nonces(0), &mut state); strategy.best_target_nonces_updated(target_nonces(0), &mut state);
strategy.source_nonces_updated(header_id(1), source_nonces(1..=1)); strategy.source_nonces_updated(header_id(1), source_nonces(1..=1));
@@ -457,7 +475,7 @@ mod tests {
#[test] #[test]
fn available_source_queue_indices_works() { fn available_source_queue_indices_works() {
let mut state = RaceState::<_, _, TestMessagesProof>::default(); let mut state = TestRaceStateImpl::default();
let mut strategy = BasicStrategy::<TestMessageLane>::new(); let mut strategy = BasicStrategy::<TestMessageLane>::new();
strategy.best_target_nonces_updated(target_nonces(0), &mut state); strategy.best_target_nonces_updated(target_nonces(0), &mut state);
strategy.source_nonces_updated(header_id(1), source_nonces(1..=3)); strategy.source_nonces_updated(header_id(1), source_nonces(1..=3));
@@ -482,7 +500,7 @@ mod tests {
#[test] #[test]
fn remove_le_nonces_from_source_queue_works() { fn remove_le_nonces_from_source_queue_works() {
let mut state = RaceState::<_, _, TestMessagesProof>::default(); let mut state = TestRaceStateImpl::default();
let mut strategy = BasicStrategy::<TestMessageLane>::new(); let mut strategy = BasicStrategy::<TestMessageLane>::new();
strategy.best_target_nonces_updated(target_nonces(0), &mut state); strategy.best_target_nonces_updated(target_nonces(0), &mut state);
strategy.source_nonces_updated(header_id(1), source_nonces(1..=3)); strategy.source_nonces_updated(header_id(1), source_nonces(1..=3));
@@ -518,12 +536,13 @@ mod tests {
let target_header_1 = header_id(1); let target_header_1 = header_id(1);
// we start in perfec sync state - all headers are synced and finalized on both ends // we start in perfec sync state - all headers are synced and finalized on both ends
let mut state = RaceState::<_, _, TestMessagesProof> { let mut state = TestRaceStateImpl {
best_finalized_source_header_id_at_source: Some(source_header_1), best_finalized_source_header_id_at_source: Some(source_header_1),
best_finalized_source_header_id_at_best_target: Some(source_header_1), best_finalized_source_header_id_at_best_target: Some(source_header_1),
best_target_header_id: Some(target_header_1), best_target_header_id: Some(target_header_1),
best_finalized_target_header_id: Some(target_header_1), best_finalized_target_header_id: Some(target_header_1),
nonces_to_submit: None, nonces_to_submit: None,
nonces_to_submit_batch: None,
nonces_submitted: None, nonces_submitted: None,
}; };