mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 21:41:12 +00:00
Fix invalid batch transaction (#1957)
* fix invalid batch transaction * RaceState is now trait * clippy
This commit is contained in:
committed by
Bastian Köcher
parent
ce89a2edca
commit
2a848fd836
@@ -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]
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user