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

Default to NODE_WITNESS in nLocalServices #21090

Merged
merged 5 commits into from
Jul 22, 2021

Conversation

dhruv
Copy link
Contributor

@dhruv dhruv commented Feb 5, 2021

Builds on #21009 and makes progress on remaining items in #17862

Removing RewindBlockIndex() in #21009 allows the following:

  • removal of tests using segwitheight=-1 in p2p_segwit.py.
  • move test_upgrade_after_activation() out of p2p_segwit.py reducing runtime
  • in turn, that allows us to drop support for -segwitheight=-1, which is only supported for that test.
  • that allows us to always set NODE_WITNESS in our local services. The only reason we don't do that is to support -segwitheight=-1.
  • that in turn allows us to drop all of the GetLocalServices() & NODE_WITNESS checks inside net_processing.cpp, since our local services would always include NODE_WITNESS

@DrahtBot
Copy link
Contributor

DrahtBot commented Feb 6, 2021

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

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.

laanwj added a commit that referenced this pull request Apr 27, 2021
d831e71 [validation] RewindBlockIndex no longer needed (Dhruv Mehta)

Pull request description:

  Closes #17862

  Context from [original comment](#17862 (comment)) (minor edits):

  `RewindBlockIndex()` is a mechanism to allow nodes to be upgraded after segwit activation, while still keeping their chainstate/datadir in a consistent state. It works as follows:

  - A pre-segwit (i.e. v0.13.0 or older) node is running.
  -  Segwit activates. The pre-segwit node remains sync'ed to the tip, but is not enforcing the new segwit rules.
  - The user upgrades the node to a segwit-aware version (v0.13.1 or newer).
  - On startup, in `AppInitMain()`, `RewindBlockIndex()` is called. This walks the chain backwards from the tip, disconnecting and erasing blocks that from after segwit activation that weren't validated with segwit rules.
  - those blocks are then redownloaded (with witness data) and validated with segwit rules.

  This logic probably isn't required any more since:

  - Segwit activated at height 481824, when the block chain was 130GB and the total number of txs was 250 million. Today, we're at height 667704, the blockchain is over 315GB and the total number of txs is over 600 million. Even if 20% of that added data is witness data (a high estimate), then around 150GB of transactions would need to be rewound to get back to segwit activation height. It'd probably be faster to simply validate from genesis, especially since we won't be validating any scripts before the assumevalid block. It's also unclear whether rewinding 150GB of transactions would even work. It's certainly never been tested.
  - Bitcoin Core v0.13 is hardly used any more. https://luke.dashjr.org/programs/bitcoin/files/charts/software.html shows less than 50 nodes running it. The software was EOL on Aug 1st 2018. It's very unlikely that anyone is running 0.13 and will want to upgrade to 0.22.

  This PR introduces `NeedsRedownload()` which merely checks for insufficiently validated segwit blocks and requests that the user restarts the node with `-reindex`. Reindexing the block files upon restart will make the node rebuild chain state and block index from the `blk*.dat` files on disk. The node won't be able to index the blocks with `BLOCK_OPT_WITNESS`, so they will be missing from the chain and be re-downloaded, with witness data.

  Removing this code allows the following (done in follow-up #21090):

  - removal of tests using `segwitheight=-1` in `p2p_segwit.py`.
  - in turn, that allows us to drop support for `-segwitheight=-1`, which is only supported for that test.
  - that allows us to always set `NODE_WITNESS` in our local services. The only reason we don't do that is to support `-segwitheight=-1`.
  - that in turn allows us to drop all of the `GetLocalServices() & NODE_WITNESS` checks inside `net_processing.cpp`, since our local services would always include `NODE_WITNESS`

ACKs for top commit:
  jnewbery:
    utACK d831e71
  jamesob:
    ACK d831e71
  laanwj:
    Cursory code review ACK d831e71. Agree with the direction of the change, thanks for simplifying the logic here.
  glozow:
    utACK d831e71

Tree-SHA512: 3eddf5121ccd081ad7f15a5c6478ef867083edc8ba0bf1ee759e87bc070ee3d2f0698a3feba8db8dc087987c8452887b6f72cff05b3e178f41cb10a515fb8053
@jnewbery
Copy link
Contributor

This needs rebase now that #21009 is merged. I did my own rebase here: https://github.com/jnewbery/bitcoin/tree/pr21090.1 since I need it for #20799. Feel free to take that if it's helpful.

I think it might make sense to remove the test_upgrade_after_activation() subtest from p2p_segwit.py and place it in its own file. It's not anything to do with p2p and doesn't have any dependencies on the rest of that test.

sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Apr 27, 2021
d831e71 [validation] RewindBlockIndex no longer needed (Dhruv Mehta)

Pull request description:

  Closes bitcoin#17862

  Context from [original comment](bitcoin#17862 (comment)) (minor edits):

  `RewindBlockIndex()` is a mechanism to allow nodes to be upgraded after segwit activation, while still keeping their chainstate/datadir in a consistent state. It works as follows:

  - A pre-segwit (i.e. v0.13.0 or older) node is running.
  -  Segwit activates. The pre-segwit node remains sync'ed to the tip, but is not enforcing the new segwit rules.
  - The user upgrades the node to a segwit-aware version (v0.13.1 or newer).
  - On startup, in `AppInitMain()`, `RewindBlockIndex()` is called. This walks the chain backwards from the tip, disconnecting and erasing blocks that from after segwit activation that weren't validated with segwit rules.
  - those blocks are then redownloaded (with witness data) and validated with segwit rules.

  This logic probably isn't required any more since:

  - Segwit activated at height 481824, when the block chain was 130GB and the total number of txs was 250 million. Today, we're at height 667704, the blockchain is over 315GB and the total number of txs is over 600 million. Even if 20% of that added data is witness data (a high estimate), then around 150GB of transactions would need to be rewound to get back to segwit activation height. It'd probably be faster to simply validate from genesis, especially since we won't be validating any scripts before the assumevalid block. It's also unclear whether rewinding 150GB of transactions would even work. It's certainly never been tested.
  - Bitcoin Core v0.13 is hardly used any more. https://luke.dashjr.org/programs/bitcoin/files/charts/software.html shows less than 50 nodes running it. The software was EOL on Aug 1st 2018. It's very unlikely that anyone is running 0.13 and will want to upgrade to 0.22.

  This PR introduces `NeedsRedownload()` which merely checks for insufficiently validated segwit blocks and requests that the user restarts the node with `-reindex`. Reindexing the block files upon restart will make the node rebuild chain state and block index from the `blk*.dat` files on disk. The node won't be able to index the blocks with `BLOCK_OPT_WITNESS`, so they will be missing from the chain and be re-downloaded, with witness data.

  Removing this code allows the following (done in follow-up bitcoin#21090):

  - removal of tests using `segwitheight=-1` in `p2p_segwit.py`.
  - in turn, that allows us to drop support for `-segwitheight=-1`, which is only supported for that test.
  - that allows us to always set `NODE_WITNESS` in our local services. The only reason we don't do that is to support `-segwitheight=-1`.
  - that in turn allows us to drop all of the `GetLocalServices() & NODE_WITNESS` checks inside `net_processing.cpp`, since our local services would always include `NODE_WITNESS`

ACKs for top commit:
  jnewbery:
    utACK d831e71
  jamesob:
    ACK bitcoin@d831e71
  laanwj:
    Cursory code review ACK d831e71. Agree with the direction of the change, thanks for simplifying the logic here.
  glozow:
    utACK d831e71

Tree-SHA512: 3eddf5121ccd081ad7f15a5c6478ef867083edc8ba0bf1ee759e87bc070ee3d2f0698a3feba8db8dc087987c8452887b6f72cff05b3e178f41cb10a515fb8053
@dhruv dhruv force-pushed the default-to-node-witness-2021 branch 2 times, most recently from e889ad4 to 117ae3f Compare April 29, 2021 18:10
@dhruv dhruv marked this pull request as ready for review April 29, 2021 18:12
@dhruv
Copy link
Contributor Author

dhruv commented Apr 29, 2021

Rebased against master, ready for review.

@jnewbery it does make a ton of sense to extract test_upgrade_after_activation() into another file. Once -reindex has the expected effect, the rest of the test merely verifies that the node syncs on to the strongest chain, and that is in covered in other tests. I've gone ahead and done that refactor. This also simplified p2p_segwit.py since it now only needs two nodes for the rest of the test cases.

src/chainparams.cpp Outdated Show resolved Hide resolved
@dhruv dhruv force-pushed the default-to-node-witness-2021 branch 2 times, most recently from bf325dd to f43a127 Compare April 30, 2021 00:58
Copy link
Contributor Author

@dhruv dhruv left a comment

Choose a reason for hiding this comment

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

Rebased with master. Weirdly DrahtBot didn't mention it was needed (but the CI did).

Ready for further review with one question below.

src/net_processing.cpp Show resolved Hide resolved
test/functional/p2p_segwit.py Outdated Show resolved Hide resolved
test/functional/feature_presegwit_node_upgrade.py Outdated Show resolved Hide resolved
test/functional/feature_presegwit_node_upgrade.py Outdated Show resolved Hide resolved
test/functional/feature_presegwit_node_upgrade.py Outdated Show resolved Hide resolved
@dhruv dhruv force-pushed the default-to-node-witness-2021 branch from f43a127 to 86608da Compare April 30, 2021 14:11
@dhruv
Copy link
Contributor Author

dhruv commented Apr 30, 2021

Comments addressed. Ready for further review.

@DrahtBot
Copy link
Contributor

DrahtBot commented May 3, 2021

🕵️ @sipa has been requested to review this pull request as specified in the REVIEWERS file.

Copy link
Contributor

@mzumsande mzumsande left a comment

Choose a reason for hiding this comment

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

Concept ACK.

src/chainparams.cpp Show resolved Hide resolved
@dhruv
Copy link
Contributor Author

dhruv commented Jun 20, 2021

Addressed comments from @mzumsande. Ready for further review.

@jnewbery
Copy link
Contributor

utACK c44bac1

witness_root = CBlock.get_merkle_root([ser_uint256(0),
ser_uint256(txid)])
script = get_witness_script(witness_root, 0)
assert_equal(witness_commitment, script.hex())
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 the test for default_witness_commitment removed?

Copy link
Contributor

Choose a reason for hiding this comment

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

Segwit is a buried deployment. There are no miners calling getblocktemplate before the segwit activation is locked in.

Copy link
Member

Choose a reason for hiding this comment

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

The commitment is produced regardless of whether segwit locked in or not.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I see now that default_witness_commitment is not tested anywhere else. Perhaps it should be tested in mining_basic.py?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the catch, @MarcoFalke ! Test is now moved into mining_basic.py

@jnewbery
Copy link
Contributor

jnewbery commented Jul 6, 2021

@dhruv - do you plan to rebase this?

@dhruv
Copy link
Contributor Author

dhruv commented Jul 6, 2021

@jnewbery I do. Been on break for a long weekend where I live.

@theStack
Copy link
Contributor

theStack commented Jul 7, 2021

Concept ACK

@dhruv dhruv force-pushed the default-to-node-witness-2021 branch from c44bac1 to a806647 Compare July 8, 2021 05:16
@dhruv
Copy link
Contributor Author

dhruv commented Jul 8, 2021

Rebased. Addressed comments. Ready for further review.

Copy link
Contributor

@jnewbery jnewbery left a comment

Choose a reason for hiding this comment

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

utACK a806647

Thanks for rebasing!

Comment on lines +54 to +55
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
Copy link
Contributor

Choose a reason for hiding this comment

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

This is ok for now, but it'd be nice to rework this test so that it doesn't require a wallet.

We could also update the test to verify that the witness commitment commits to the wtxids of the transactions in the block rather than the txids. Currently the block being tested contains only the coinbase transaction and one transaction that spends from a P2PKH output, so the txid is the same as wtxid. It could be updated to include transactions that spend segwit outputs so that the txid and wtxid are different.

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 can do those in a follow-up PR. Can you tell me more about how I could remove the wallet dependency? My understanding is that to have getblocktemplate include a transaction that the witness root commits to, I need a valid transaction in the mempool, and to create that valid tx, I need a wallet. I feel like I am missing something about the test framework perhaps?

Copy link
Contributor

Choose a reason for hiding this comment

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

@dhruv: Luckily we have a minimalistic wallet implementation for the functional test framework (first introduced by MarcoFalke last year, see #19800). Look for modules importing MiniWallet for example usage, see also issue #20078. Also, feel free to tag me in your follow-up PR, will be happy to review.

Copy link
Member

Choose a reason for hiding this comment

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

Agree

Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

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

ACK a806647

Thanks to the PR review club log the changes were straight-forward to review. Built and ran tests locally, also checked via git grep DeploymentEnabled.*SEGWIT and git grep GetLocalServices.*WITNESS that all occurences of now-superfluous is-segwit-enabled checks were tackled.

@mzumsande
Copy link
Contributor

Code-Review ACK a806647

@laanwj
Copy link
Member

laanwj commented Jul 22, 2021

Code review ACK a806647, nice cleanup

@laanwj laanwj merged commit 5d83e7d into bitcoin:master Jul 22, 2021
witness_root = CBlock.get_merkle_root([ser_uint256(0),
ser_uint256(txid)])
script = get_witness_script(witness_root, 0)
assert_equal(witness_commitment, script.hex())
Copy link
Member

Choose a reason for hiding this comment

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

Python will throw key-error, so you don't need the "# Check that default_witness_commitment is present." section.

See:

>>> {'b':1}['a']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'a'

Suggestion:

         assert_equal(tmpl['default_witness_commitment'], script.hex())

Comment on lines +54 to +55
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
Copy link
Member

Choose a reason for hiding this comment

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

Agree

sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jul 23, 2021
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 18, 2022
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.

7 participants