Skip to content

Conversation

@pg156
Copy link

@pg156 pg156 commented Jan 28, 2022

This PR enables a non-wallet functional test mempool_updatefromblock.py to be run without wallet compiled, by using the MiniWallet proposed in #20078. This picks up from where #21999 left off.

Commit 1 of 2: MiniWallet changes only to minimize diffs for easier reviewing
Commit 2 of 2: Refactor (renaming a function and variables)

The test run time decreases from 16.5s to 4.3s.

@fanquake fanquake added the Tests label Jan 28, 2022
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a whitespace here, you have to remove it to pass the linter.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks. Fixed. Don't know why the linter now complains about the spelling errors in unchanged files.

^---- failure generated from test/lint/lint-python.sh
src/random.h:92: occuring ==> occurring
src/rpc/blockchain.cpp:794: nWe ==> new
src/util/syscall_sandbox.cpp:128: creat ==> create
test/functional/data/rpc_decodescript.json:81: ba ==> by, be
test/functional/data/rpc_decodescript.json:84: ba ==> by, be
^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in test/lint/lint-spelling.ignore-words.txt

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Author

Choose a reason for hiding this comment

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

Thanks. Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem of the linter now is "test/functional/mempool_updatefromblock.py:12:1: F401 'decimal.Decimal' imported but unused" - this PR removes all uses.

@pg156 pg156 force-pushed the mempool-updatefromblock-miniwallet branch from 47e8d66 to 934d09e Compare January 29, 2022 22:32
@maflcko
Copy link
Member

maflcko commented Feb 22, 2022

Are you still working on this?

@pg156
Copy link
Author

pg156 commented Feb 27, 2022

Are you still working on this?

Yes. Read mzumsande's comment too quickly and didn't realize it was a request to change. Will fix soon.

@bitcoin bitcoin deleted a comment from Ferrydh Feb 27, 2022
@pg156 pg156 force-pushed the mempool-updatefromblock-miniwallet branch from 934d09e to 4b8eb3c Compare February 27, 2022 17:16
@pg156
Copy link
Author

pg156 commented Feb 28, 2022

The linter problem is fixed.

Copy link
Contributor

@aureleoules aureleoules left a comment

Choose a reason for hiding this comment

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

tACK 203a3e065ecd0ec8a9fb84f4deb512ef2b66cdf7.
Tested on NixOS 22.05 64 bits with --disable-wallet.

I verified that the migration to MiniWallet does not alter what is being tested.
It takes 3s to run on my machine 🚀

Copy link
Member

Choose a reason for hiding this comment

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

Why is this changed?

Copy link
Author

Choose a reason for hiding this comment

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

Changed back to getmempoolentry.

I chose getrawmempool over getmempoolentry to follow some existing functional test files. After a closer look at those two RPCs, I agree the existing getmempoolentry here is better as it indicates the "query using a transaction id" intention.

Copy link
Member

Choose a reason for hiding this comment

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

Why does this need SEQUENCE_FINAL?

Copy link
Author

Choose a reason for hiding this comment

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

Without SEQUENCE_FINAL, the test broke at assert_equal(len(node.getrawmempool()), size) after the first block is invalidated, as the mempool has 26 transactions instead of the expected 100.

In a simpler example with fewer transactions without SEQUENCE_FINAL, I can see after the block is invalidated, transactions are removed from the mempool because of this place:

bool fEnforceBIP68 = static_cast<uint32_t>(tx.nVersion) >= 2

Copy link
Member

@maflcko maflcko Mar 22, 2022

Choose a reason for hiding this comment

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

Why is this changed? (from getmempoolentry to getrawmempool)?

Copy link
Author

Choose a reason for hiding this comment

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

Changed back to getmempoolentry.

@pg156 pg156 force-pushed the mempool-updatefromblock-miniwallet branch from 4b8eb3c to 7be943f Compare March 23, 2022 01:42
@maflcko
Copy link
Member

maflcko commented Mar 23, 2022

You'll need to do the changes from yesterday in the first commit, not in the second.

@pg156 pg156 force-pushed the mempool-updatefromblock-miniwallet branch from 7be943f to 72ad8ad Compare March 25, 2022 16:06
@pg156
Copy link
Author

pg156 commented Mar 25, 2022

Fixed. Thank you @MarcoFalke.

@maflcko
Copy link
Member

maflcko commented Mar 30, 2022

Can you explain why it is ok to "flatten" the tournament into a linked list?

Copy link
Contributor

@mjdietzx mjdietzx left a comment

Choose a reason for hiding this comment

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

I think this PR introduces an unwanted behavior change. It looks like now you have a simple straight line of spends tx0->tx1->...->txN.

Before this test created a huge, complicated "cyclic tournament" graph of transactions where:

  • Transaction tx[K] is a child of each of previous transactions tx[0]..tx[K-1] at their output K-1
  • Transaction tx[K] is an ancestor of each of subsequent transactions tx[K+1]..tx[N-1].

That's kind of hard to visualize. But the first transaction tx0 has 99 outputs, and tx1, ... tx100 all spend one of tx0's outputs. And tx100 has 99 inputs, an input from tx1, ... tx99. Looking at L51-L80 (before this PRs changes) you can try to imagine what tx4 looks like and that's kinda hard!

It's easy to see by the runtime of this test. Before this PR running this test in our CI took ~5 minutes. Now this test runs in ~1s.


I've found this test to be extremely valuable bc it highlights a possible denial of service attack vector. Imagine someone creates a graph of transactions like this and gets them all mined into a single block (let's just say they mine the block or collude w/ a miner). Even though the mempool has DEFAULT_ANCESTOR_LIMIT and DEFAULT_DESCENDANT_LIMIT of 25, this test just shows how large and complicated a web of dependent transactions you can make within those limits.

Now imagine that huge graph takes up a single block that's mined. And then somehow (maybe on purpose) that block gets reorged. All of a sudden this entire block of transactions enters the mempool again. And all the nodes need to validate every single transaction in this graph before adding them back to their mempool. Imagine if we did something stupid (or a lot of extra looping work) in the mempool validation logic. When that block gets reorged it could potentially crash or stall a bunch of nodes.

So I think this test is a really good stress test as-is. If you do any looping or weird stuff in the mempool validation logic this test can very easily timeout. I learned this the hard way when trying out some RBF changes, where I saw this test timing out. And I started to really appreciate the value of this specific test at that point

Comment on lines 32 to 36
If all of the N created transactions tx[0]..tx[N-1] reside in the mempool,
the following holds:
the tx[K] transaction:
- has N-K descendants (including this one), and
- has K+1 ancestors (including this one)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think these conditions hold true after this PR

@mjdietzx
Copy link
Contributor

mjdietzx commented Apr 3, 2022

Concept ACK - I think this is a valuable test and it'll be good to update it so that it's not skipped when the wallet isn't compiled. My suggestion would be to minimize changes as much as possible to make it work w/ MiniWallet - and implementing this test as-is w/ MiniWallet will probably be difficult! But I think it'll be a good exercise, and I think MiniWallet can probably support what this test needs without any internal code changes to it (although I'm not totally sure bc I haven't looked at that code in a while)

@pg156
Copy link
Author

pg156 commented Apr 7, 2022

Thank you so much @mjdietzx for the review with the context and insights. I am changing this to draft status, to explore how to keep the graph structure.

@DrahtBot
Copy link
Contributor

DrahtBot commented Jul 2, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #25525 (test: remove wallet dependency from mempool_updatefromblock.py by ayush933)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@maflcko maflcko closed this Jul 7, 2022
@bitcoin bitcoin locked and limited conversation to collaborators Jul 7, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants