Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor mempool spend conflict checks to increase performance #2826

Merged

Conversation

jvff
Copy link
Contributor

@jvff jvff commented Oct 5, 2021

Motivation

#2765 implemented rejection of mempool transactions that have spend conflicts (i.e., spend the same transparent outpoint or reveal the same nullifier) with other transactions already in the mempool. However, the implementation was simple and would not scale well in terms of performance as the mempool size is increased.

Solution

Replace the O(n * m) algorithm with an O(n) algorithm. Now the mempool::Storage uses HashSets to keep track of the spent outpoints and revealed nullifiers by the verified transactions. This allows it to quickly check if there are conflicts when new transactions are added.

Review

@teor2345

Reviewer Checklist

  • Code implements Specs and Designs
  • Tests for Expected Behaviour
  • Tests for Errors

Follow Up Work

Copy link
Contributor

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

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

This looks good, but I think we need to be really careful to avoid modifying anything when there's a conflict.

(I'm sorry I didn't submit this yesterday, I was a bit distracted by the release.)

zebrad/src/components/mempool/storage.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage.rs Outdated Show resolved Hide resolved
@jvff jvff force-pushed the refactor-mempool-spend-conflict-checks branch from 0ce1796 to 6af8a40 Compare October 7, 2021 03:38
@jvff jvff requested a review from teor2345 October 7, 2021 04:10
Copy link
Contributor

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

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

I really like this refactor, and we could merge it as-is.
(After we add some missing tests.)

I have a few optional suggestions for improvements.

zebrad/src/components/mempool/storage.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage/verified_set.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage/verified_set.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage/verified_set.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage/verified_set.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage/verified_set.rs Outdated Show resolved Hide resolved
jvff and others added 17 commits October 8, 2021 04:06
Keep track of the spent transparent outpoints and the revealed
nullifiers.

Clippy complained that the `ActiveState` had variants with large size
differences, but that was expected, so I disabled that lint on that
`enum`.
Clear them so that they remain consistent with the set of verified
transactions.
Store new outputs into its respective `HashSet`, and abort if a
duplicate output is found.
Restore the `HashSet` to its previous state.
Keep the mempool storage in a consistent state when a transaction is
removed.
Ensure eviction also keeps the tracked outputs consistent with the
verified transactions.
Move the code to handle the output caches into the new type. Also move
the eviction code to make things a little simpler.
Centralize the code that handles the removal of a transaction to avoid
mistakes.
Because the evicted transactions must be added to the rejected list.
Leftover from some temporary testing code.

Co-authored-by: teor <teor@riseup.net>
It is more speculation than planning, so it doesn't add much value.

Co-authored-by: teor <teor@riseup.net>
The verb should match the subject "transactions" which is plural.

Co-authored-by: teor <teor@riseup.net>
There's a subtle but important detail in the implementation that should
be made more visible to avoid mistakes in the future.

Co-authored-by: teor <teor@riseup.net>
Left-over from the attempt to move the eviction into the `VerifiedSet`.
Rewrite the comment explaining why the Clippy lint was ignored.
Refactor to avoid API misuse.
Using two transactions, perform the same test adding a conflict to both
of them to check if the second inserted transaction is properly
rejected. Then remove any conflicts from the second transaction and add
it again. That should work, because if it doesn't it means that when the
second transaction was rejected it left things it shouldn't in the
cache.
When removing multiple transactions from the mempool storage, all of the
ones requested should be removed and any other transaction should be
still be there afterwards.
@jvff jvff force-pushed the refactor-mempool-spend-conflict-checks branch from df362a1 to 8fc2832 Compare October 8, 2021 04:34
@conradoplg
Copy link
Collaborator

I reviewed this and it looks good, but I think it's better for @teor2345 to take a last look. I can make any final adjustments if needed.

If the mempool size is smaller than 4,
these tests don't fail on a trivial removal bug.
Because we need a minimum number of transactions in the mempool
to trigger the bug.

Also commit a proptest seed that fails on a trivial removal bug.
(This seed fails if we remove indexes in order,
because every index past the first removes the wrong transaction.)
And replace the very large proptest debug output with the new summary.
Copy link
Contributor

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

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

I increased the mempool size to 4, so that the new tests would actually detect multiple removal errors.

I also summarised the debug output of the tests, because it was very very large.
(It contained the entire contents of a vector of random transactions.)

Apart from the mempool size, I only changed test debug formatting.
So I think we should merge now to avoid conflicts.

Copy link
Contributor

@dconnolly dconnolly left a comment

Choose a reason for hiding this comment

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

🤩

@jvff jvff deleted the refactor-mempool-spend-conflict-checks branch November 23, 2021 22:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor spend conflict checks in the mempool storage to increase performance
4 participants