Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Finalized block event triggers tx maintanance #12305

Merged

Conversation

michalkucharczyk
Copy link
Contributor

@michalkucharczyk michalkucharczyk commented Sep 20, 2022

tx-pool is driven by ChainEvents: NewBestBlock (NBB) and Finalized (F) which can be delivered in arbitrary order.

Existing implementation of tx-pool, handles correctly the typical sequence of events: [NBB(a), F(a)].

Maintenance of tx-pool is performed in two steps:

  1. enactment/retracment of blocks, and moving associated transaction between internal pools (enactment phase), notifying Ready/InBlock/Retracted events
  2. sending finalization event for transactions in finalized blocks

Existing implementation does not handle properly [F(a), NBB(a)] sequence of events (which was a case for instant-seal consensus): enactment phase was not executed when F was reported before NBB event.

For F events coming first both phases of maintenance should be executed. For NBB event coming after F the enactment phase should not be executed.

This PR:

  • moves the enactment phase implementation into single function (handle_enactment) which is called for both NBB and F events.
  • introduces the enactment_helper which decides if enactment phase of maintenance should be executed for incoming event.

Please note that this PR ignores tree_route provided within event, and uses tree_route function to compute retractred/enacted blocks.

Consider the following chain:

  B1-C1-D1-E1
 /
A
 \
  B2-C2-D2-E2

The following list presents some scenarios and expected behavior:

  • NBB(C1), F(C1) -> false (enactment phase was already performed in NBB(C1))
  • F(C1), NBB(C1) -> false (enactmant phase was already performed in F(C1))
  • F(E1), F(B1) -> false (enactmant phase was already performed in F(E1))
  • F(C1), NBB(D2) -> false (enactment phase was already performed in F(C1), we should not retract finalized block)
  • F(C1), F(C2) -> false (enactment phase was already performed in F(C1), we should not retract finalized block)
  • F(C1), F(C2), NBB(C1) -> false (enactmant phase was already performed in F(C1))
  • NBB(C1), NBB(C2) -> true (switching fork is OK)

Closes #10279

@github-actions github-actions bot added the A3-in_progress Pull request is in progress. No review needed at this stage. label Sep 20, 2022
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
@michalkucharczyk michalkucharczyk added B0-silent Changes should not be mentioned in any release notes C1-low PR touches the given topic and has a low impact on builders. D3-trivial 🧸 PR contains trivial changes in a runtime directory that do not require an audit labels Sep 27, 2022
@michalkucharczyk michalkucharczyk marked this pull request as ready for review September 27, 2022 17:11
@github-actions github-actions bot added A0-please_review Pull request needs code review. and removed A3-in_progress Pull request is in progress. No review needed at this stage. labels Sep 27, 2022
Copy link
Contributor

@andresilva andresilva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While reviewing this PR I was trying to come up with a way to simplify this. I also think there is another bug with the original implementation, i.e. when we retract blocks by finalizing some other fork we don't re-add the retracted transactions to the pool (since we only run that logic on NewBestBlock events). Let's try to break down the problem:

  • We want to keep track of some "canon" block that represents our internal status of tx pool "enactment"
  • This block can be updated either by processing NewBestBlock events or Finalized events and it should work properly under "inconsistent" ordering of events
  • Whenever we update this "canon" block we can retract blocks (in comparison to our previous "canon"), enact new blocks and also finalize some blocks
  • For the retracted blocks we want to extract the txs in them and re-add them to the pool
  • For enacted blocks we want to extract the txs in them and remove them from the pool
  • For finalized blocks we want to notify this to tx pool listeners

I think the overall logic should be:

  • Compute new "canon" block on new event
  • Compute enacted, retracted and finalized in relation to new event
  • Do all the actions in "maintain" regardless of event type but instead using the extracted values above

To compute "canon" block and the remaining necessary data we do as follows (roughly the stuff that's being done in update_and_check_if_new_enactment_is_valid now):

  • When we receive a NewBestBlock event we calculate the tree route between our "canon" block and the block in the event. If there are no enacted blocks then we can ignore the event (we can ignore retracted blocks since they must have been processed in an earlier event). If there are enacted blocks then we update our "canon" block to this block and we return the given enacted, retracted from the tree route we computed and no finalized blocks
  • When we receive a Finalized event we calculate the tree route between our "canon" block and the block in the event, we then return the enacted and retracted from this route, and we return the finalized blocks from the tree route that comes from the event. We update our "canon" block to the finalized block from the event

Let me know what you think about this, it's very possible my logic is faulty.

test-utils/runtime/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/graph/pool.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/api.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/tests/pool.rs Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/graph/pool.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/api.rs Outdated Show resolved Hide resolved
test-utils/runtime/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
michalkucharczyk and others added 3 commits September 30, 2022 16:33
Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
@andresilva
Copy link
Contributor

After talking to @michalkucharczyk there is something wrong in my suggestion above which is that my solution treats "conflicting" NBB events as being out-of-order. We need to make the assumption that Finalized and NewBestBlock events can arrive in any order, but within NBB events they are logically ordered (otherwise we can't distinguish whether NBB(d), NBB(b) was out-of-order or whether we actually re-orged to b). After re-reviewing and using my brain a bit I think the logic in update_and_check_if_new_enactment_is_valid is correct.

Copy link
Member

@bkchr bkchr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some last nitpicks, otherwise it looks good 👍

primitives/blockchain/src/header_metadata.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
client/transaction-pool/src/lib.rs Outdated Show resolved Hide resolved
Copy link
Member

@bkchr bkchr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you 👍

client/transaction-pool/tests/pool.rs Outdated Show resolved Hide resolved
{
let header = pool.api().push_block(5, vec![from_dave, from_bob], true);
// let header = pool.api().push_block(5, vec![from_dave, from_bob], true);
let header = pool.api().push_block_with_parent(d1, vec![from_dave, from_bob], true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DQ: Why do you changed this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I explained it in: https://github.com/paritytech/substrate/pull/12305/files#r982204082

Please cross-check if my reasoning is correct.

Copy link
Contributor Author

@michalkucharczyk michalkucharczyk Oct 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Speculating - this maybe related to the isbestblock bug in the testapi implementation. (this)

Co-authored-by: Bastian Köcher <git@kchr.de>
@andresilva
Copy link
Contributor

andresilva commented Oct 11, 2022

Just pushed a commit to address some small nits and avoid another review loop. Thanks for the PR @michalkucharczyk 👍

@andresilva
Copy link
Contributor

bot merge

@paritytech-processbot
Copy link

Waiting for commit status.

@paritytech-processbot paritytech-processbot bot merged commit 94b731c into master Oct 11, 2022
@paritytech-processbot paritytech-processbot bot deleted the mku-finalized-event-triggers-tx-maintanance branch October 11, 2022 20:20
@shunjizhan
Copy link

shunjizhan commented Oct 20, 2022

Thanks for the fix! And I have a question 🙋‍♂️

*Before* the fix, for [F(a), NBB(a)] sequence of events, enactment phase was not executed, so InBlock event is not triggered. However, does it still trigger Finalized event?

More particularly, if I have a tx like this with instal-seal node, will the console.log get executed?

  // triggers [F(a), NBB(a)] sequence
  extrinsic.signAndSend(sender, (result) => {
    if (result.status.isInBlock) {
      // this will not happen since  enactment phase was not executed
    } else if (result.status.isFinalized) {
      console.log('will this happen???')
    }
  });

@michalkucharczyk
Copy link
Contributor Author

Before the fix only Finalized event was triggered.
After the fix both events shall be triggered for discussed sequence, see this test:

let event =
ChainEvent::Finalized { hash: header.clone().hash(), tree_route: Arc::from(vec![]) };
block_on(pool.maintain(event));
block_on(pool.maintain(block_event(header.clone())));
assert_eq!(pool.status().ready, 0);
{
let mut stream = futures::executor::block_on_stream(watcher);
assert_eq!(stream.next(), Some(TransactionStatus::Ready));
assert_eq!(stream.next(), Some(TransactionStatus::InBlock((header.clone().hash(), 0))));
assert_eq!(stream.next(), Some(TransactionStatus::Finalized((header.hash(), 0))));
assert_eq!(stream.next(), None);
}

ark0f pushed a commit to gear-tech/substrate that referenced this pull request Feb 27, 2023
* finalized block event triggers tx maintanance

* tx-pool: enactment helper introduced

* tx-pool: ChainApi: added tree_route method

* enactment logic implemented + tests

Signed-off-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com>

* Some additional tests

* minor improvements

* trigger CI job

* fix compilation errors

ChainApi::tree_route return type changed to Result<Option<..>>, as some
implementations (tests) are not required to provide this tree route.

* formatting

* trait removed

* implementation slightly simplified

(thanks to @koute)

* get rid of Arc<> in EnactmentState return value

* minor improvement

* Apply suggestions from code review

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Apply suggestions from code review

* comment updated + formatting

* Apply suggestions from code review

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
Co-authored-by: Davide Galassi <davxy@datawok.net>

* formatting

* finalization notification bug fix

+ new test case
+ log::warn message when finalized block is being retracted by new event

* added error message on tree_route failure

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <git@kchr.de>

* use provided tree_route in Finalized event

* Option removed from ChainApi::tree_route

* doc added, test and logs improved

* handle_enactment aligned with original implementation

* use async-await

* Apply suggestions from code review

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <git@kchr.de>

* formatting + warn->debug

* compilation error fix

* enactment_state initializers added

* enactment_state: Option removed

* manual-seal: compilation & tests fix

* manual-seal: tests fixed

* tests cleanup

* another compilation error fixed

* TreeRoute::new added

* get rid of pub hack

* one more test added

* formatting

* TreeRoute::new doc added + formatting

* Apply suggestions from code review

Co-authored-by: Davide Galassi <davxy@datawok.net>

* (bool,Option) simplified to Option

* log message improved

* yet another review suggestions applied

* get rid of hash in handle_enactment

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <git@kchr.de>

* Update client/transaction-pool/src/lib.rs

Co-authored-by: Bastian Köcher <git@kchr.de>

* minor corrections

* EnactmentState moved to new file

* File header corrected

* error formatting aligned with codebase

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <git@kchr.de>

* remove commented code

* small nits

Signed-off-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com>
Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
Co-authored-by: Davide Galassi <davxy@datawok.net>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: André Silva <andrerfosilva@gmail.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A0-please_review Pull request needs code review. B0-silent Changes should not be mentioned in any release notes C1-low PR touches the given topic and has a low impact on builders. D3-trivial 🧸 PR contains trivial changes in a runtime directory that do not require an audit
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Instant-seal finalization event not always firing
5 participants