Skip to content

Commit 59000c6

Browse files
authored
Fix invalid batch transaction (paritytech#1957)
* fix invalid batch transaction * RaceState is now trait * clippy
1 parent 4877b30 commit 59000c6

6 files changed

Lines changed: 275 additions & 124 deletions

File tree

bridges/relays/lib-substrate-relay/src/lib.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,18 +91,21 @@ impl<AccountId> TaggedAccount<AccountId> {
9191
}
9292

9393
/// Batch call builder.
94-
pub trait BatchCallBuilder<Call>: Send {
94+
pub trait BatchCallBuilder<Call>: Clone + Send {
9595
/// Create batch call from given calls vector.
9696
fn build_batch_call(&self, _calls: Vec<Call>) -> Call;
9797
}
9898

9999
/// Batch call builder constructor.
100-
pub trait BatchCallBuilderConstructor<Call> {
100+
pub trait BatchCallBuilderConstructor<Call>: Clone {
101+
/// Call builder, used by this constructor.
102+
type CallBuilder: BatchCallBuilder<Call>;
101103
/// Create a new instance of a batch call builder.
102-
fn new_builder() -> Option<Box<dyn BatchCallBuilder<Call>>>;
104+
fn new_builder() -> Option<Self::CallBuilder>;
103105
}
104106

105107
/// Batch call builder based on `pallet-utility`.
108+
#[derive(Clone)]
106109
pub struct UtilityPalletBatchCallBuilder<C: Chain>(PhantomData<C>);
107110

108111
impl<C: Chain> BatchCallBuilder<C::Call> for UtilityPalletBatchCallBuilder<C>
@@ -118,14 +121,25 @@ impl<C: Chain> BatchCallBuilderConstructor<C::Call> for UtilityPalletBatchCallBu
118121
where
119122
C: ChainWithUtilityPallet,
120123
{
121-
fn new_builder() -> Option<Box<dyn BatchCallBuilder<C::Call>>> {
122-
Some(Box::new(Self(Default::default())))
124+
type CallBuilder = Self;
125+
126+
fn new_builder() -> Option<Self::CallBuilder> {
127+
Some(Self(Default::default()))
123128
}
124129
}
125130

126-
/// A `BatchCallBuilderConstructor` that always returns `None`.
131+
// A `BatchCallBuilderConstructor` that always returns `None`.
127132
impl<Call> BatchCallBuilderConstructor<Call> for () {
128-
fn new_builder() -> Option<Box<dyn BatchCallBuilder<Call>>> {
133+
type CallBuilder = ();
134+
fn new_builder() -> Option<Self::CallBuilder> {
129135
None
130136
}
131137
}
138+
139+
// Dummy `BatchCallBuilder` implementation that must never be used outside
140+
// of the `impl BatchCallBuilderConstructor for ()` code.
141+
impl<Call> BatchCallBuilder<Call> for () {
142+
fn build_batch_call(&self, _calls: Vec<Call>) -> Call {
143+
unreachable!("never called, because ()::new_builder() returns None; qed")
144+
}
145+
}

bridges/relays/lib-substrate-relay/src/messages_lane.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,26 @@ pub struct MessagesRelayParams<P: SubstrateMessageLane> {
111111

112112
/// Batch transaction that brings headers + and messages delivery/receiving confirmations to the
113113
/// source node.
114+
#[derive(Clone)]
114115
pub struct BatchProofTransaction<SC: Chain, TC: Chain, B: BatchCallBuilderConstructor<CallOf<SC>>> {
115-
builder: Box<dyn BatchCallBuilder<CallOf<SC>>>,
116+
builder: B::CallBuilder,
116117
proved_header: HeaderIdOf<TC>,
117118
prove_calls: Vec<CallOf<SC>>,
118119

119120
/// Using `fn() -> B` in order to avoid implementing `Send` for `B`.
120121
_phantom: PhantomData<fn() -> B>,
121122
}
122123

124+
impl<SC: Chain, TC: Chain, B: BatchCallBuilderConstructor<CallOf<SC>>> std::fmt::Debug
125+
for BatchProofTransaction<SC, TC, B>
126+
{
127+
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
128+
fmt.debug_struct("BatchProofTransaction")
129+
.field("proved_header", &self.proved_header)
130+
.finish()
131+
}
132+
}
133+
123134
impl<SC: Chain, TC: Chain, B: BatchCallBuilderConstructor<CallOf<SC>>>
124135
BatchProofTransaction<SC, TC, B>
125136
{

bridges/relays/messages/src/message_lane_loop.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ pub struct NoncesSubmitArtifacts<T> {
111111

112112
/// Batch transaction that already submit some headers and needs to be extended with
113113
/// messages/delivery proof before sending.
114-
pub trait BatchTransaction<HeaderId>: Send {
114+
pub trait BatchTransaction<HeaderId>: Debug + Send {
115115
/// Header that was required in the original call and which is bundled within this
116116
/// batch transaction.
117117
fn required_header_id(&self) -> HeaderId;
@@ -121,7 +121,7 @@ pub trait BatchTransaction<HeaderId>: Send {
121121
#[async_trait]
122122
pub trait SourceClient<P: MessageLane>: RelayClient {
123123
/// Type of batch transaction that submits finality and message receiving proof.
124-
type BatchTransaction: BatchTransaction<TargetHeaderIdOf<P>>;
124+
type BatchTransaction: BatchTransaction<TargetHeaderIdOf<P>> + Clone;
125125
/// Transaction tracker to track submitted transactions.
126126
type TransactionTracker: TransactionTracker<HeaderId = SourceHeaderIdOf<P>>;
127127

@@ -186,7 +186,7 @@ pub trait SourceClient<P: MessageLane>: RelayClient {
186186
#[async_trait]
187187
pub trait TargetClient<P: MessageLane>: RelayClient {
188188
/// Type of batch transaction that submits finality and messages proof.
189-
type BatchTransaction: BatchTransaction<SourceHeaderIdOf<P>>;
189+
type BatchTransaction: BatchTransaction<SourceHeaderIdOf<P>> + Clone;
190190
/// Transaction tracker to track submitted transactions.
191191
type TransactionTracker: TransactionTracker<HeaderId = TargetHeaderIdOf<P>>;
192192

@@ -1212,6 +1212,9 @@ pub(crate) mod tests {
12121212
original_data,
12131213
Arc::new(|_| {}),
12141214
Arc::new(move |data: &mut TestClientData| {
1215+
data.source_state.best_self =
1216+
HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1);
1217+
data.source_state.best_finalized_self = data.source_state.best_self;
12151218
if let Some(target_to_source_header_required) =
12161219
data.target_to_source_header_required.take()
12171220
{
@@ -1223,6 +1226,10 @@ pub(crate) mod tests {
12231226
}),
12241227
Arc::new(|_| {}),
12251228
Arc::new(move |data: &mut TestClientData| {
1229+
data.target_state.best_self =
1230+
HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1);
1231+
data.target_state.best_finalized_self = data.target_state.best_self;
1232+
12261233
if let Some(source_to_target_header_required) =
12271234
data.source_to_target_header_required.take()
12281235
{

bridges/relays/messages/src/message_race_delivery.rs

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,19 @@ where
322322
self.strategy.is_empty()
323323
}
324324

325-
fn required_source_header_at_target(
325+
fn required_source_header_at_target<RS: RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>>>(
326326
&self,
327327
current_best: &SourceHeaderIdOf<P>,
328+
race_state: RS,
328329
) -> Option<SourceHeaderIdOf<P>> {
330+
// we have already submitted something - let's wait until it is mined
331+
if race_state.nonces_submitted().is_some() {
332+
return None
333+
}
334+
329335
let has_nonces_to_deliver = !self.strategy.is_empty();
330336
let header_required_for_messages_delivery =
331-
self.strategy.required_source_header_at_target(current_best);
337+
self.strategy.required_source_header_at_target(current_best, race_state);
332338
let header_required_for_reward_confirmations_delivery = self
333339
.latest_confirmed_nonces_at_source
334340
.back()
@@ -381,10 +387,10 @@ where
381387
self.strategy.source_nonces_updated(at_block, nonces)
382388
}
383389

384-
fn best_target_nonces_updated(
390+
fn best_target_nonces_updated<RS: RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>>>(
385391
&mut self,
386392
nonces: TargetClientNonces<DeliveryRaceTargetNoncesData>,
387-
race_state: &mut RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::MessagesProof>,
393+
race_state: &mut RS,
388394
) {
389395
// best target nonces must always be ge than finalized target nonces
390396
let latest_nonce = nonces.latest_nonce;
@@ -396,13 +402,13 @@ where
396402
)
397403
}
398404

399-
fn finalized_target_nonces_updated(
405+
fn finalized_target_nonces_updated<RS: RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>>>(
400406
&mut self,
401407
nonces: TargetClientNonces<DeliveryRaceTargetNoncesData>,
402-
race_state: &mut RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::MessagesProof>,
408+
race_state: &mut RS,
403409
) {
404410
if let Some(ref best_finalized_source_header_id_at_best_target) =
405-
race_state.best_finalized_source_header_id_at_best_target
411+
race_state.best_finalized_source_header_id_at_best_target()
406412
{
407413
let oldest_header_number_to_keep = best_finalized_source_header_id_at_best_target.0;
408414
while self
@@ -426,13 +432,13 @@ where
426432
)
427433
}
428434

429-
async fn select_nonces_to_deliver(
435+
async fn select_nonces_to_deliver<RS: RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>>>(
430436
&self,
431-
race_state: RaceState<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::MessagesProof>,
437+
race_state: RS,
432438
) -> Option<(RangeInclusive<MessageNonce>, Self::ProofParameters)> {
433439
let best_target_nonce = self.strategy.best_at_target()?;
434440
let best_finalized_source_header_id_at_best_target =
435-
race_state.best_finalized_source_header_id_at_best_target.clone()?;
441+
race_state.best_finalized_source_header_id_at_best_target()?;
436442
let latest_confirmed_nonce_at_source = self
437443
.latest_confirmed_nonces_at_source
438444
.iter()
@@ -576,20 +582,29 @@ impl<SourceChainBalance: std::fmt::Debug> NoncesRange for MessageDetailsMap<Sour
576582

577583
#[cfg(test)]
578584
mod tests {
579-
use crate::message_lane_loop::{
580-
tests::{
581-
header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance,
582-
TestSourceClient, TestSourceHeaderId, TestTargetClient, TestTargetHeaderId,
585+
use crate::{
586+
message_lane_loop::{
587+
tests::{
588+
header_id, TestMessageLane, TestMessagesBatchTransaction, TestMessagesProof,
589+
TestSourceChainBalance, TestSourceClient, TestSourceHeaderId, TestTargetClient,
590+
TestTargetHeaderId,
591+
},
592+
MessageDetails,
583593
},
584-
MessageDetails,
594+
message_race_loop::RaceStateImpl,
585595
};
586596

587597
use super::*;
588598

589599
const DEFAULT_DISPATCH_WEIGHT: Weight = Weight::from_parts(1, 0);
590600
const DEFAULT_SIZE: u32 = 1;
591601

592-
type TestRaceState = RaceState<TestSourceHeaderId, TestTargetHeaderId, TestMessagesProof>;
602+
type TestRaceState = RaceStateImpl<
603+
TestSourceHeaderId,
604+
TestTargetHeaderId,
605+
TestMessagesProof,
606+
TestMessagesBatchTransaction,
607+
>;
593608
type TestStrategy =
594609
MessageDeliveryStrategy<TestMessageLane, TestSourceClient, TestTargetClient>;
595610

@@ -617,12 +632,13 @@ mod tests {
617632
}
618633

619634
fn prepare_strategy() -> (TestRaceState, TestStrategy) {
620-
let mut race_state = RaceState {
635+
let mut race_state = RaceStateImpl {
621636
best_finalized_source_header_id_at_source: Some(header_id(1)),
622637
best_finalized_source_header_id_at_best_target: Some(header_id(1)),
623638
best_target_header_id: Some(header_id(1)),
624639
best_finalized_target_header_id: Some(header_id(1)),
625640
nonces_to_submit: None,
641+
nonces_to_submit_batch: None,
626642
nonces_submitted: None,
627643
};
628644

@@ -964,14 +980,17 @@ mod tests {
964980
);
965981
// nothing needs to be delivered now and we don't need any new headers
966982
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
967-
assert_eq!(strategy.required_source_header_at_target(&header_id(1)), None);
983+
assert_eq!(strategy.required_source_header_at_target(&header_id(1), state.clone()), None);
968984

969985
// now let's generate two more nonces [24; 25] at the soruce;
970986
strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 19, 0));
971987
//
972988
// - so now we'll need to relay source block#2 to be able to accept messages [24; 25].
973989
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
974-
assert_eq!(strategy.required_source_header_at_target(&header_id(1)), Some(header_id(2)));
990+
assert_eq!(
991+
strategy.required_source_header_at_target(&header_id(1), state.clone()),
992+
Some(header_id(2))
993+
);
975994

976995
// let's relay source block#2
977996
state.best_finalized_source_header_id_at_source = Some(header_id(2));
@@ -982,18 +1001,18 @@ mod tests {
9821001
// and ask strategy again => still nothing to deliver, because parallel confirmations
9831002
// race need to be pushed further
9841003
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
985-
assert_eq!(strategy.required_source_header_at_target(&header_id(2)), None);
1004+
assert_eq!(strategy.required_source_header_at_target(&header_id(2), state.clone()), None);
9861005

9871006
// let's confirm messages [20; 23]
9881007
strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 23, 0));
9891008

9901009
// and ask strategy again => now we have everything required to deliver remaining
9911010
// [24; 25] nonces and proof of [20; 23] confirmation
9921011
assert_eq!(
993-
strategy.select_nonces_to_deliver(state).await,
1012+
strategy.select_nonces_to_deliver(state.clone()).await,
9941013
Some(((24..=25), proof_parameters(true, 2))),
9951014
);
996-
assert_eq!(strategy.required_source_header_at_target(&header_id(2)), None);
1015+
assert_eq!(strategy.required_source_header_at_target(&header_id(2), state), None);
9971016
}
9981017

9991018
#[async_std::test]
@@ -1025,6 +1044,7 @@ mod tests {
10251044
#[test]
10261045
#[allow(clippy::reversed_empty_ranges)]
10271046
fn no_source_headers_required_at_target_if_lanes_are_empty() {
1047+
let (state, _) = prepare_strategy();
10281048
let mut strategy = TestStrategy {
10291049
max_unrewarded_relayer_entries_at_target: 4,
10301050
max_unconfirmed_nonces_at_target: 4,
@@ -1053,7 +1073,7 @@ mod tests {
10531073
strategy.latest_confirmed_nonces_at_source,
10541074
VecDeque::from([(source_header_id, 0)])
10551075
);
1056-
assert_eq!(strategy.required_source_header_at_target(&source_header_id), None);
1076+
assert_eq!(strategy.required_source_header_at_target(&source_header_id, state), None);
10571077
}
10581078

10591079
#[async_std::test]

0 commit comments

Comments
 (0)