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

Reject conflicting mempool transactions #2765

Merged

Conversation

jvff
Copy link
Contributor

@jvff jvff commented Sep 16, 2021

Motivation

The mempool should ideally provide a set of transactions that can be included in a block to be mined. This means that it should not contain any conflicting transactions, i.e., transactions that spend the same UTXOs or reveal the same nullifiers.

This closes #2682.

Specifications

Solution

Change the mempool::Storage to check if transactions to be included don't conflict with any existing transactions. Reject transactions that conflict with any existing transactions in the mempool.

Review

Reviewer Checklist

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

Follow Up Work

  • Consider replacing existing transactions in the mempool with a transaction that has conflicts but has a higher fee?

@jvff jvff requested a review from a team September 16, 2021 13:53
@jvff jvff self-assigned this Sep 16, 2021
@conradoplg
Copy link
Collaborator

I posted this on Discord, but for the record:

  1. I think you need to check if transactions have the same input, not the same output
  2. Terminology seems correct to me
  3. Maybe it's a bit convoluted, but it's easy to understand what is going on since the code is so small. It seems better than having four separate functions... (It's also a bit unfortunate that it needs so many explicit lifetimes, but removing them leads to the error you posted, right?)

@jvff jvff force-pushed the reject-conflicting-mempool-transactions branch from 3832ea4 to 22d8ffd Compare September 16, 2021 20:05
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 PR looks good.

There's a non-blocking performance issue that we will probably want to fix later, once more mempool components are in place.

I'm looking forward to seeing some tests, when the testing framework all comes together.

zebrad/src/components/mempool/storage.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/error.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage.rs Show resolved Hide resolved
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 missed some Conflict to SpendConflict tweaks.

zebrad/src/components/mempool/storage.rs Outdated Show resolved Hide resolved
zebrad/src/components/mempool/storage.rs Outdated Show resolved Hide resolved
Copy link
Contributor Author

@jvff jvff left a comment

Choose a reason for hiding this comment

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

Updated. Changes can be seen here.

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.

Looks great, just one documentation clarification

zebra-chain/src/transaction.rs Outdated Show resolved Hide resolved
teor2345
teor2345 previously approved these changes Sep 21, 2021
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 great!

I opened #2787 to check for duplicate spends within the same mempool transaction.
(Currently, that consensus rule is checked in zebra-state, so it doesn't apply to mempool transactions.)

@jvff
Copy link
Contributor Author

jvff commented Sep 22, 2021

I've implemented a test case, but it's currently blocked by depending on #2771. If that gets merged, I can update this PR and remove the draft status. In the meantime, here's the test code in case you want to take an initial look.

I thought about implementing the same test case using nullifiers for the different pools, but I'm not 100% sure how to create mock transactions with them 😅

@teor2345
Copy link
Contributor

teor2345 commented Sep 23, 2021

I thought about implementing the same test case using nullifiers for the different pools, but I'm not 100% sure how to create mock transactions with them 😅

It's so complicated, and it works differently for every shielded pool.
So I just used the Arbitrary impl to do it:

#[test]
fn reject_duplicate_sapling_nullifiers_in_block(
spend1 in TypeNameToDebug::<sapling::Spend<PerSpendAnchor>>::arbitrary(),
mut spend2 in TypeNameToDebug::<sapling::Spend<PerSpendAnchor>>::arbitrary(),
sapling_shielded_data1 in TypeNameToDebug::<sapling::ShieldedData<PerSpendAnchor>>::arbitrary(),
sapling_shielded_data2 in TypeNameToDebug::<sapling::ShieldedData<PerSpendAnchor>>::arbitrary(),
) {
zebra_test::init();
let mut block1 = zebra_test::vectors::BLOCK_MAINNET_1_BYTES
.zcash_deserialize_into::<Block>()
.expect("block should deserialize");
// create a double-spend across two transactions
let duplicate_nullifier = spend1.nullifier;
spend2.nullifier = duplicate_nullifier;

Feel free to copy and paste the 3 similar tests for each pool.

@jvff jvff force-pushed the reject-conflicting-mempool-transactions branch from 8df91d9 to 0af839d Compare September 24, 2021 01:22
teor2345
teor2345 previously approved these changes Sep 24, 2021
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.

Looks good, feel free to merge once you've resolved the conflicts.

(As long as the tests run for a reasonable amount of time.)

jvff and others added 10 commits September 24, 2021 11:53
Returns an iterator over the UTXO `OutPoint`s spent by the transaction.
An error representing that a transaction was rejected because it
conflicts with another transaction that's already in the mempool.
Reject including a transaction in the mempool if it spends outputs
already spent by, or reveals nullifiers already revealed by another
transaction in the mempool.
Remove the `r` that was incorrectly added.

Co-authored-by: teor <teor@riseup.net>
Make the situation clearer, because there are other types of conflict.

Co-authored-by: teor <teor@riseup.net>
Because otherwise it could lead to confusion because it could also mean
the outputs of the transaction represented as `OutPoint` references.

Co-authored-by: teor <teor@riseup.net>
Refactor to follow the convention used for other tests.
A getter to allow changing the first element.
Allow appending elements to the collection.
This is just to make the code that generates arbitrary anchors a bit
simpler.
Generate two transactions (either V4 or V5) and insert a conflicting
spend, which can be either a transparent UTXO, or a nullifier for one of
the shielded pools. Check that any attempt to insert both transactions
causes one to be accepted and the other to be rejected.
@jvff jvff force-pushed the reject-conflicting-mempool-transactions branch from 0af839d to c87b067 Compare September 24, 2021 12:05
@jvff jvff marked this pull request as ready for review September 24, 2021 15:09
@jvff
Copy link
Contributor Author

jvff commented Sep 26, 2021

I've updated the PR to fix the merge conflict, fix a clippy lint and add some more documentation. I know you said I could merge this, but I thought the documentation might want another quick round of check. Feel free to change anything and merge it afterwards 👍

The isolated changes can be seen here.

@jvff jvff requested a review from teor2345 September 27, 2021 10:32
@teor2345 teor2345 enabled auto-merge (squash) September 28, 2021 00:24
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.

Looks good to me, I just removed an outdated TODO comment

@teor2345 teor2345 merged commit a0d45c3 into ZcashFoundation:main Sep 28, 2021
@jvff jvff deleted the reject-conflicting-mempool-transactions branch September 28, 2021 12:01
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.

Reject duplicate UTXO spends and nullifier reveals across mempool transactions
3 participants