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

assumeutxo: change getchainstates RPC to return a list of chainstates #28590

Merged
merged 1 commit into from Oct 5, 2023

Conversation

ryanofsky
Copy link
Contributor

Current getchainstates RPC returns "normal" and "snapshot" fields which are not ideal because it requires new "normal" and "snapshot" terms to be defined, and the definitions are not really consistent with internal code. (In the RPC interface, the "snapshot" chainstate becomes the "normal" chainstate after it is validated, while in internal code there is no "normal chainstate" and the "snapshot chainstate" is still called that temporarily after it is validated).

The current getchainstates RPC is also awkward to use if you to want information about the most-work chainstate, because you have to look at the "snapshot" field if it exists, and otherwise fall back to the "normal" field.

Fix these issues by having getchainstates just return a flat list of chainstates ordered by work, and adding a new chainstate "validated" field alongside the existing "snapshot_blockhash" field so it is explicit if a chainstate was originally loaded from a snapshot, and whether the snapshot has been validated.

This change was motivated by comment thread in #28562 (comment)

@DrahtBot
Copy link
Contributor

DrahtBot commented Oct 4, 2023

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

Code Coverage

For detailed information about the code coverage, see the test coverage report.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK Sjors, jamesob, achow101
Concept ACK fjahr

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

@fjahr
Copy link
Contributor

fjahr commented Oct 4, 2023

Concept ACK, would be good to have this in 26.0 so we don't change the RPC interface afterwards

@jamesob
Copy link
Member

jamesob commented Oct 4, 2023

Concept ACK, seems like a fine simplification!

@ryanofsky
Copy link
Contributor Author

Rebased 3306714 -> ff89dec (pr/getchain.1 -> pr/getchain.2, compare) due to conflict with #28589

@Sjors
Copy link
Member

Sjors commented Oct 5, 2023

Concept ACK

tACK ff89dec

Tested with a signet IBD, loading the snapshot and waiting for background sync to finish.

Copy link
Member

@jamesob jamesob left a comment

Choose a reason for hiding this comment

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

ACK ff89dec (jamesob/ackr/28590.1.ryanofsky.assumeutxo_change_getcha)

Good simplification, thanks. Built, ran tests locally.

@jamesob
Copy link
Member

jamesob commented Oct 5, 2023

Ah, just had a functional test fail while running in a loop. Investigating...

./test/functional/feature_assumeutxo.py  6.94s user 0.41s system 99% cpu 7.380 total
2023-10-05T12:58:59.161000Z TestFramework (INFO): PRNG seed is: 6200617450151020946
2023-10-05T12:58:59.161000Z TestFramework (INFO): Initializing test directory /tmp/bitcoin_func_test_smviasai
2023-10-05T12:58:59.605000Z TestFramework (INFO): -- Testing assumeutxo + some indexes + pruning
2023-10-05T12:58:59.605000Z TestFramework (INFO): Creating a UTXO snapshot at height 299
2023-10-05T12:58:59.655000Z TestFramework (INFO): Loading snapshot into second node from /tmp/bitcoin_func_test_smviasai/node0/regtest/utxos.dat
2023-10-05T12:58:59.658000Z TestFramework (INFO): Restarting node to stop at height 359
2023-10-05T12:59:00.111000Z TestFramework (INFO): Checking that blocks are segmented on disk
2023-10-05T12:59:00.111000Z TestFramework (INFO): Restarted node before snapshot validation completed, reloading...
2023-10-05T12:59:00.418000Z TestFramework (INFO): Ensuring snapshot chain syncs to tip. (399)
2023-10-05T12:59:00.470000Z TestFramework (INFO): Ensuring background validation completes
2023-10-05T13:00:00.480000Z TestFramework.utils (ERROR): wait_until() failed. Predicate: ''''
        wait_until_helper(lambda: len(n1.getchainstates()['chainstates']) != 1)
'''
2023-10-05T13:00:00.480000Z TestFramework (ERROR): Assertion failed
Traceback (most recent call last):
  File "/home/james/src/bitcoin/test/functional/test_framework/test_framework.py", line 131, in main
    self.run_test()
  File "/home/james/src/bitcoin/./test/functional/feature_assumeutxo.py", line 170, in run_test
    wait_until_helper(lambda: len(n1.getchainstates()['chainstates']) != 1)
  File "/home/james/src/bitcoin/test/functional/test_framework/util.py", line 276, in wait_until_helper
    raise AssertionError("Predicate {} not true after {} seconds".format(predicate_source, timeout))
AssertionError: Predicate ''''
        wait_until_helper(lambda: len(n1.getchainstates()['chainstates']) != 1)
''' not true after 60.0 seconds
2023-10-05T13:00:00.531000Z TestFramework (INFO): Stopping nodes
2023-10-05T13:00:00.633000Z TestFramework (WARNING): Not cleaning up dir /tmp/bitcoin_func_test_smviasai

@ryanofsky
Copy link
Contributor Author

Tangent, but @DrahtBot does not seem to pick up Sjors ACK with "Concept ACK" on the first line and "tACK" on the second line. I guess the ACK detector is overloaded

self.sync_blocks(nodes=(n0, n1))

self.log.info("Ensuring background validation completes")
# N.B.: the `snapshot` key disappears once the background validation is complete.
wait_until_helper(lambda: not n1.getchainstates().get('snapshot'))
wait_until_helper(lambda: len(n1.getchainstates()['chainstates']) != 1)
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I follow this substitution... once background validation completes, chainman.GetAll() should (at least eventually) return only one chainstate: the fully-validated snapshot chainstate. Maybe s/!=/==?

Copy link
Member

Choose a reason for hiding this comment

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

Funny, I think as-written this succeeds most of the time because the check is racily performed before validation actually completes, which essentially makes the check exit early.

Copy link
Contributor Author

@ryanofsky ryanofsky Oct 5, 2023

Choose a reason for hiding this comment

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

re: #28590 (comment)

Funny, I think as-written this succeeds most of the time because the check is racily performed before validation actually completes, which essentially makes the check exit early.

Right, this is the opposite of the condition you'd expect. Probably I just wrote the check one way, then inverted when it failed assuming I'd made a mistake. But either of these checks are racy, so it makes more sense to be direct and check the new "validated" field. Updated in new push.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

re: #28590 (comment)

Actually thinking about it more, the change you suggested is not racy, and is a little better than just checking the "validated" field because it also ensures that the background chainstate disappears from the list after it is disabled. Also check you suggested is more consistent with the check done for the n2 node below on line 218

@jamesob
Copy link
Member

jamesob commented Oct 5, 2023

I'm ACK again once this change is made: #28590 (comment)

Copy link
Contributor Author

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Updated ff89dec -> 35f6d90 (pr/getchain.2 -> pr/getchain.3, compare) fixing race condition in test

self.sync_blocks(nodes=(n0, n1))

self.log.info("Ensuring background validation completes")
# N.B.: the `snapshot` key disappears once the background validation is complete.
wait_until_helper(lambda: not n1.getchainstates().get('snapshot'))
wait_until_helper(lambda: len(n1.getchainstates()['chainstates']) != 1)
Copy link
Contributor Author

@ryanofsky ryanofsky Oct 5, 2023

Choose a reason for hiding this comment

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

re: #28590 (comment)

Funny, I think as-written this succeeds most of the time because the check is racily performed before validation actually completes, which essentially makes the check exit early.

Right, this is the opposite of the condition you'd expect. Probably I just wrote the check one way, then inverted when it failed assuming I'd made a mistake. But either of these checks are racy, so it makes more sense to be direct and check the new "validated" field. Updated in new push.

Current getchainstates RPC returns "normal" and "snapshot" fields which are not
ideal because it requires new "normal" and "snapshot" terms to be defined, and
the definitions are not really consistent with internal code. (In the RPC
interface, the "snapshot" chainstate becomes the "normal" chainstate after it
is validated, while in internal code there is no "normal chainstate" and the
"snapshot chainstate" is still called that temporarily after it is validated).

The current getchainstatees RPC is also awkward to use if you to want
information about the most-work chainstate because you have to look at the
"snapshot" field if it exists, and otherwise fall back to the "normal" field.

Fix these issues by having getchainstates just return a flat list of
chainstates ordered by work, and adding new chainstate "validated" field
alongside the existing "snapshot_blockhash" so it is explicit if a chainstate
was originally loaded from a snapshot, and whether the snapshot has been
validated.
Copy link
Contributor Author

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Updated 35f6d90 -> a9ef702 (pr/getchain.3 -> pr/getchain.4, compare) taking James suggestion and making test a little stricter and more internally consistent

self.sync_blocks(nodes=(n0, n1))

self.log.info("Ensuring background validation completes")
# N.B.: the `snapshot` key disappears once the background validation is complete.
wait_until_helper(lambda: not n1.getchainstates().get('snapshot'))
wait_until_helper(lambda: len(n1.getchainstates()['chainstates']) != 1)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

re: #28590 (comment)

Actually thinking about it more, the change you suggested is not racy, and is a little better than just checking the "validated" field because it also ensures that the background chainstate disappears from the list after it is disabled. Also check you suggested is more consistent with the check done for the n2 node below on line 218

@Sjors
Copy link
Member

Sjors commented Oct 5, 2023

re-ACK a9ef702

Only a test change since I last reviewed, which I didn't study closely.

@DrahtBot DrahtBot requested a review from jamesob October 5, 2023 15:15
@jamesob
Copy link
Member

jamesob commented Oct 5, 2023

re-ACK a9ef702

@DrahtBot DrahtBot removed the request for review from jamesob October 5, 2023 15:17
@achow101
Copy link
Member

achow101 commented Oct 5, 2023

ACK a9ef702

@achow101 achow101 merged commit 0b2c93b into bitcoin:master Oct 5, 2023
13 of 16 checks passed
@fjahr fjahr mentioned this pull request Oct 6, 2023
Frank-GER pushed a commit to syscoin/syscoin that referenced this pull request Oct 13, 2023
…a list of chainstates

a9ef702 assumeutxo: change getchainstates RPC to return a list of chainstates (Ryan Ofsky)

Pull request description:

  Current `getchainstates` RPC returns "normal" and "snapshot" fields which are not ideal because it requires new "normal" and "snapshot" terms to be defined, and the definitions are not really consistent with internal code. (In the RPC interface, the "snapshot" chainstate becomes the "normal" chainstate after it is validated, while in internal code there is no "normal chainstate" and the "snapshot chainstate" is still called that temporarily after it is validated).

  The current `getchainstates` RPC is also awkward to use if you to want information about the most-work chainstate, because you have to look at the "snapshot" field if it exists, and otherwise fall back to the "normal" field.

  Fix these issues by having `getchainstates` just return a flat list of chainstates ordered by work, and adding a new chainstate "validated" field alongside the existing "snapshot_blockhash" field so it is explicit if a chainstate was originally loaded from a snapshot, and whether the snapshot has been validated.

  This change was motivated by comment thread in bitcoin#28562 (comment)

ACKs for top commit:
  Sjors:
    re-ACK a9ef702
  jamesob:
    re-ACK a9ef702
  achow101:
    ACK a9ef702

Tree-SHA512: b364e2e96675fb7beaaee60c4dff4b69e6bc2d8a30dea1ba094265633d1cddf9dbf1c5ce20c07d6e23222cf1e92a195acf6227e4901f3962e81a1e53a43490aa
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

7 participants