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

allow checkpoint or genesis origin; refactoring #9976

Merged
merged 8 commits into from
Dec 9, 2021

Conversation

kasey
Copy link
Contributor

@kasey kasey commented Dec 2, 2021

This branch includes the last bit of refactoring to the blockchain service initialization. The blockchain service initialization code now checks the database to look for a checkpoint sync (aka weak subjectivity sync) block root. If one is found, it uses that root in place of the genesis root for code paths that need a block root value to fall back to in the case of a zero hash. This branch also includes much of the db initialization code, which is not used until the final of 3 branches for this feature, which adds a flag to specify the block+state for a checkpoint sync. This code saves the block and state to the db and marks the block as finalized.

What type of PR is this?

Uncomment one line below and remove others.

Bug fix
Feature
Documentation
Other

What does this PR do? Why is it needed?

First of 3 branches to introduce a minimal weak subjectivity sync implementation.

@kasey kasey requested a review from a team as a code owner December 2, 2021 23:39
@kasey kasey force-pushed the cp-sync-origin-root branch 2 times, most recently from 058d0bf to 5edcfbd Compare December 2, 2021 23:53
@kasey kasey force-pushed the cp-sync-origin-root branch 2 times, most recently from 0ae7a12 to 6dcc6fc Compare December 4, 2021 00:05
@kasey kasey changed the title WIP: allow checkpoint or genesis origin; refactoring allow checkpoint or genesis origin; refactoring Dec 6, 2021
beacon-chain/db/kv/blocks.go Outdated Show resolved Hide resolved

var root [32]byte
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blocksBucket)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to use the blocks bucket? This is the kind of bucket that could undergo migrations and would require other developers to filter out other values in this bucket that aren't blocks, adding some burden down the line. Probably best to use a separate bucket for this and keep blocks bucket pure in its schema

Copy link
Member

Choose a reason for hiding this comment

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

I think it is fine to do this, other special blocks(genesis,etc) are also saved the same way. Adding another bucket seems excessive here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Like Nishant says, I wound up using this bucket because that's where the equivalent key for the genesis root is stored. I wouldn't mind creating a new bucket to hold singleton keys like this, with an eye towards moving other keys the next time the main object of their bucket undergoes a migration.

beacon-chain/blockchain/receive_attestation.go Outdated Show resolved Hide resolved
}
}

// DBError implements the Error method so that it can be asserted as an error.
Copy link
Contributor

Choose a reason for hiding this comment

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

Really nice pattern! I mentioned it in #9454

beacon-chain/db/kv/wss.go Show resolved Hide resolved
beacon-chain/db/kv/wss.go Show resolved Hide resolved
beacon-chain/blockchain/service.go Outdated Show resolved Hide resolved
beacon-chain/blockchain/service.go Outdated Show resolved Hide resolved
beacon-chain/blockchain/service.go Show resolved Hide resolved
beacon-chain/blockchain/service.go Outdated Show resolved Hide resolved
s.finalizedCheckpt = ethpb.CopyCheckpoint(finalizedCheckpoint)
s.prevFinalizedCheckpt = ethpb.CopyCheckpoint(finalizedCheckpoint)
s.resumeForkChoice(justifiedCheckpoint, finalizedCheckpoint)
finalizedState, err = s.cfg.StateGen.Resume(ctx, s.cfg.FinalizedStateAtStartUp)
Copy link
Member

Choose a reason for hiding this comment

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

There is a bit of an edge case here:
https://github.com/prysmaticlabs/prysm/blob/develop/beacon-chain/state/stategen/service.go#L88
https://github.com/prysmaticlabs/prysm/blob/develop/beacon-chain/state/stategen/service.go#L98

If the root in the db is a zero hash, would we be fine with returning this function returning a genesis state ? The reason I am asking, is because in the whole service, genesis root has been replaced with origin root. I am assuming the same logic also applies to stategen.

Or are we assuming here, that in the case of WSS, the checkpoints are already saved into the db, and this edge case isnt really valid/possible ?

Copy link
Contributor Author

@kasey kasey Dec 7, 2021

Choose a reason for hiding this comment

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

are we assuming here, that in the case of WSS, the checkpoints are already saved into the db, and this edge case isnt really valid/possible

yes, good point, I am making that assumption wrt stategen. I almost undertook a bigger refactor to split the block chain service into separate genesis/checkpoint types, but my instinct was that this zero hash logic is so prevalent and subtle that I didn't want to make huge and confusing change all at once. I also had a branch where I implemented an "origin store" that would get either the checkpoint sync block or genesis, but it started growing in scope so I fell back to this simpler option.

This change came in while I was working on the branch and is a little out of step with the concepts in this PR:

if r == params.BeaconConfig().ZeroHash {

As an extra defensive check, in the code path linked above, where we detect the zero hash and fall back to genesis, we could first try to look up the origin block root via OriginBlockRoot, and fail if one exists (because that indicates that somehow we tried to start up with an origin checkpoint, but something went wrong with the finalization db entry). If we feel the need to do this check in multiple places I might try out the origin store idea again.

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense, thanks for the explanation ! I think having the check at only one place is fine now, as the assumption is always that the new weak subjectivity checkpoints are baked in.


ss, err := slots.EpochStart(s.finalizedCheckpt.Epoch)
if flags.Get().HeadSync {
Copy link
Member

Choose a reason for hiding this comment

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

Unrelated to your PR, but we should remove this whole HeadSync chunk later, no one really uses it and it is not functional afaik even if anyone decided to use it.

Copy link
Member

Choose a reason for hiding this comment

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

I know no one uses it but maybe better to wait for the v3 release? We certainly missed this for v2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

noted, we'll come back to this when we're ready to make breaking changes.

@@ -0,0 +1,46 @@
package kv
Copy link
Member

Choose a reason for hiding this comment

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

This is great

// (ex: an open file) prepares the database so that the beacon node can begin
// syncing, using the provided values as their point of origin. This is an alternative
// to syncing from genesis, and should only be run on an empty database.
func (s *Store) SaveOrigin(ctx context.Context, stateReader, blockReader io.Reader) error {
Copy link
Member

Choose a reason for hiding this comment

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

I guess one thing that is tricky and that we need to be aware of would be what if a user provides a phase0/altair/merge WSS state ? Currently the code assumes that the user only provides an altair block/state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, copy/pasting from my response on this to Raul:
Working on making this more future proof in cp-sync-sniff-origin-init 65912a9
Still need to do a bit of testing but i think that work will decouple this code from relying on a particular proto struct.

// InitializeFromSSZReader can be used when the source for a serialized BeaconState object
// is an io.Reader. This allows client code to remain agnostic about whether the data comes
// from the network or a file without needing to read the entire state into mem as a large byte slice.
func InitializeFromSSZReader(r io.Reader) (*BeaconState, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Curious how we can make this more forward-compatible say when BeaconStateMerge comes around. Do we duplicate code into v3 pkg?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Everyone landed on the same (totally valid) concern haha, copying one more time ;)
Working on making this more future proof in cp-sync-sniff-origin-init 65912a9
Still need to do a bit of testing but i think that work will decouple this code from relying on a particular proto struct.

beacon-chain/db/kv/wss.go Show resolved Hide resolved

ss, err := slots.EpochStart(s.finalizedCheckpt.Epoch)
if flags.Get().HeadSync {
Copy link
Member

Choose a reason for hiding this comment

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

I know no one uses it but maybe better to wait for the v3 release? We certainly missed this for v2

some quick readability improvements and simplifying the logic enforcing
the startup ordering of the attestation processing routine
rauljordan
rauljordan previously approved these changes Dec 8, 2021
nisdas
nisdas previously approved these changes Dec 9, 2021
Copy link
Member

@nisdas nisdas left a comment

Choose a reason for hiding this comment

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

LGTM

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
@kasey kasey dismissed stale reviews from nisdas and rauljordan via 06c1866 December 9, 2021 15:35
kasey and others added 2 commits December 9, 2021 09:37
use log.WithError for aggregation friendliness

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
@rauljordan rauljordan added OK to merge Ready For Review A pull request ready for code review labels Dec 9, 2021
@prylabs-bulldozer prylabs-bulldozer bot merged commit 3c61cc7 into develop Dec 9, 2021
@delete-merged-branch delete-merged-branch bot deleted the cp-sync-origin-root branch December 9, 2021 22:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Ready For Review A pull request ready for code review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants