Read extrinsic dispatch result for mined transaction (#1582)

* read extrinsic dispatch result for mined transaction

* commit for the history

* Revert "commit for the history"

This reverts commit 99341b04750639db296172cc1432bd70e458ef4b.

* Revert "read extrinsic dispatch result for mined transaction"

This reverts commit 662b776cbf992be9f1637e52f023b782e8c441d1.

* check for successfult transaction in finality relay

* check for successful transaction in parachains relay

* TrackedTransactionStatus ->TrackedTransactionStatus<HeaderId>

* check for successful transaction in messages relay

* fix compilation

* message_lane_loop_is_able_to_recover_from_unsuccessful_transaction

* fixed too-complex-type clippy error

* aaand compilation
This commit is contained in:
Svyatoslav Nikolsky
2022-10-03 09:25:48 +03:00
committed by Bastian Köcher
parent 1abd5cb60d
commit 43afa02372
12 changed files with 460 additions and 112 deletions
+104 -44
View File
@@ -124,7 +124,7 @@ pub trait SourceClient<P: ParachainsPipeline>: RelayClient {
#[async_trait]
pub trait TargetClient<P: ParachainsPipeline>: RelayClient {
/// Transaction tracker to track submitted transactions.
type TransactionTracker: TransactionTracker;
type TransactionTracker: TransactionTracker<HeaderId = HeaderIdOf<P::TargetChain>>;
/// Get best block id.
async fn best_block(&self) -> Result<HeaderIdOf<P::TargetChain>, Self::Error>;
@@ -260,13 +260,13 @@ where
// check if our transaction has been mined
if let Some(tracker) = submitted_heads_tracker.take() {
match tracker.update(&heads_at_target).await {
match tracker.update(&best_target_block, &heads_at_target).await {
SubmittedHeadsStatus::Waiting(tracker) => {
// no news about our transaction and we shall keep waiting
submitted_heads_tracker = Some(tracker);
continue
},
SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized) => {
SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized(_)) => {
// all heads have been updated, we don't need this tracker anymore
},
SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost) => {
@@ -529,9 +529,24 @@ enum SubmittedHeadsStatus<P: ParachainsPipeline> {
/// Heads are not yet updated.
Waiting(SubmittedHeadsTracker<P>),
/// Heads transaction has either been finalized or lost (i.e. received its "final" status).
Final(TrackedTransactionStatus),
Final(TrackedTransactionStatus<HeaderIdOf<P::TargetChain>>),
}
/// Type of the transaction tracker that the `SubmittedHeadsTracker` is using.
///
/// It needs to be shared because of `poll` macro and our consuming `update` method.
type SharedTransactionTracker<P> = Shared<
Pin<
Box<
dyn Future<
Output = TrackedTransactionStatus<
HeaderIdOf<<P as ParachainsPipeline>::TargetChain>,
>,
> + Send,
>,
>,
>;
/// Submitted parachain heads transaction.
struct SubmittedHeadsTracker<P: ParachainsPipeline> {
/// Ids of parachains which heads were updated in the tracked transaction.
@@ -541,7 +556,7 @@ struct SubmittedHeadsTracker<P: ParachainsPipeline> {
/// Future that waits for submitted transaction finality or loss.
///
/// It needs to be shared because of `poll` macro and our consuming `update` method.
transaction_tracker: Shared<Pin<Box<dyn Future<Output = TrackedTransactionStatus> + Send>>>,
transaction_tracker: SharedTransactionTracker<P>,
}
impl<P: ParachainsPipeline> SubmittedHeadsTracker<P>
@@ -552,7 +567,7 @@ where
pub fn new(
awaiting_update: impl IntoIterator<Item = ParaId>,
relay_block_number: BlockNumberOf<P::SourceChain>,
transaction_tracker: impl TransactionTracker + 'static,
transaction_tracker: impl TransactionTracker<HeaderId = HeaderIdOf<P::TargetChain>> + 'static,
) -> Self {
SubmittedHeadsTracker {
awaiting_update: awaiting_update.into_iter().collect(),
@@ -564,6 +579,7 @@ where
/// Returns `None` if all submitted parachain heads have been updated.
pub async fn update(
mut self,
at_target_block: &HeaderIdOf<P::TargetChain>,
heads_at_target: &BTreeMap<ParaId, Option<BestParaHeadHash>>,
) -> SubmittedHeadsStatus<P> {
// remove all pending heads that were synced
@@ -590,14 +606,23 @@ where
// if we have synced all required heads, we are done
if self.awaiting_update.is_empty() {
return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized)
return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized(
*at_target_block,
))
}
// if underlying transaction tracker has reported that the transaction is lost, we may
// then restart our sync
let transaction_tracker = self.transaction_tracker.clone();
if let Poll::Ready(TrackedTransactionStatus::Lost) = poll!(transaction_tracker) {
return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost)
match poll!(transaction_tracker) {
Poll::Ready(TrackedTransactionStatus::Lost) =>
return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost),
Poll::Ready(TrackedTransactionStatus::Finalized(_)) => {
// so we are here and our transaction is mined+finalized, but some of heads were not
// updated => we're considering our loop as stalled
return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost)
},
_ => (),
}
SubmittedHeadsStatus::Waiting(self)
@@ -644,12 +669,17 @@ mod tests {
}
#[derive(Clone, Debug)]
struct TestTransactionTracker(TrackedTransactionStatus);
struct TestTransactionTracker(Option<TrackedTransactionStatus<HeaderIdOf<TestChain>>>);
#[async_trait]
impl TransactionTracker for TestTransactionTracker {
async fn wait(self) -> TrackedTransactionStatus {
self.0
type HeaderId = HeaderIdOf<TestChain>;
async fn wait(self) -> TrackedTransactionStatus<HeaderIdOf<TestChain>> {
match self.0 {
Some(status) => status,
None => futures::future::pending().await,
}
}
}
@@ -785,7 +815,9 @@ mod tests {
if let Some(mut exit_signal_sender) = data.exit_signal_sender.take() {
exit_signal_sender.send(()).await.unwrap();
}
Ok(TestTransactionTracker(TrackedTransactionStatus::Finalized))
Ok(TestTransactionTracker(Some(
TrackedTransactionStatus::Finalized(Default::default()),
)))
}
}
@@ -938,10 +970,31 @@ mod tests {
SubmittedHeadsTracker::new(
vec![ParaId(PARA_ID), ParaId(PARA_1_ID)],
SOURCE_BLOCK_NUMBER,
TestTransactionTracker(TrackedTransactionStatus::Finalized),
TestTransactionTracker(None),
)
}
fn all_expected_tracker_heads() -> BTreeMap<ParaId, Option<BestParaHeadHash>> {
vec![
(
ParaId(PARA_ID),
Some(BestParaHeadHash {
at_relay_block_number: SOURCE_BLOCK_NUMBER,
head_hash: PARA_0_HASH,
}),
),
(
ParaId(PARA_1_ID),
Some(BestParaHeadHash {
at_relay_block_number: SOURCE_BLOCK_NUMBER,
head_hash: PARA_0_HASH,
}),
),
]
.into_iter()
.collect()
}
impl From<SubmittedHeadsStatus<TestParachainsPipeline>> for Option<BTreeSet<ParaId>> {
fn from(status: SubmittedHeadsStatus<TestParachainsPipeline>) -> Option<BTreeSet<ParaId>> {
match status {
@@ -955,7 +1008,10 @@ mod tests {
async fn tx_tracker_update_when_nothing_is_updated() {
assert_eq!(
Some(test_tx_tracker().awaiting_update),
test_tx_tracker().update(&vec![].into_iter().collect()).await.into(),
test_tx_tracker()
.update(&HeaderId(0, Default::default()), &vec![].into_iter().collect())
.await
.into(),
);
}
@@ -965,6 +1021,7 @@ mod tests {
Some(test_tx_tracker().awaiting_update),
test_tx_tracker()
.update(
&HeaderId(0, Default::default()),
&vec![(
ParaId(PARA_ID),
Some(BestParaHeadHash {
@@ -986,6 +1043,7 @@ mod tests {
Some(vec![ParaId(PARA_1_ID)].into_iter().collect::<BTreeSet<_>>()),
test_tx_tracker()
.update(
&HeaderId(0, Default::default()),
&vec![(
ParaId(PARA_ID),
Some(BestParaHeadHash {
@@ -1006,50 +1064,52 @@ mod tests {
assert_eq!(
Option::<BTreeSet<_>>::None,
test_tx_tracker()
.update(
&vec![
(
ParaId(PARA_ID),
Some(BestParaHeadHash {
at_relay_block_number: SOURCE_BLOCK_NUMBER,
head_hash: PARA_0_HASH,
})
),
(
ParaId(PARA_1_ID),
Some(BestParaHeadHash {
at_relay_block_number: SOURCE_BLOCK_NUMBER,
head_hash: PARA_0_HASH,
})
),
]
.into_iter()
.collect()
)
.update(&HeaderId(0, Default::default()), &all_expected_tracker_heads())
.await
.into(),
);
}
#[async_std::test]
async fn tx_tracker_update_when_tx_is_stalled() {
async fn tx_tracker_update_when_tx_is_lost() {
let mut tx_tracker = test_tx_tracker();
tx_tracker.transaction_tracker =
futures::future::ready(TrackedTransactionStatus::Lost).boxed().shared();
assert_eq!(
Option::<BTreeSet<_>>::None,
tx_tracker.update(&vec![].into_iter().collect()).await.into(),
);
assert!(matches!(
tx_tracker
.update(&HeaderId(0, Default::default()), &vec![].into_iter().collect())
.await,
SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost),
));
}
#[async_std::test]
async fn tx_tracker_update_when_tx_is_finalized() {
async fn tx_tracker_update_when_tx_is_finalized_but_heads_are_not_updated() {
let mut tx_tracker = test_tx_tracker();
tx_tracker.transaction_tracker =
futures::future::ready(TrackedTransactionStatus::Finalized).boxed().shared();
futures::future::ready(TrackedTransactionStatus::Finalized(Default::default()))
.boxed()
.shared();
assert!(matches!(
tx_tracker.update(&vec![].into_iter().collect()).await,
SubmittedHeadsStatus::Waiting(_),
tx_tracker
.update(&HeaderId(0, Default::default()), &vec![].into_iter().collect())
.await,
SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost),
));
}
#[async_std::test]
async fn tx_tracker_update_when_tx_is_finalized_and_heads_are_updated() {
let mut tx_tracker = test_tx_tracker();
tx_tracker.transaction_tracker =
futures::future::ready(TrackedTransactionStatus::Finalized(Default::default()))
.boxed()
.shared();
assert!(matches!(
tx_tracker
.update(&HeaderId(0, Default::default()), &all_expected_tracker_heads())
.await,
SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized(_)),
));
}