-
Notifications
You must be signed in to change notification settings - Fork 922
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
Conversation
058d0bf
to
5edcfbd
Compare
0ae7a12
to
6dcc6fc
Compare
|
||
var root [32]byte | ||
err := s.db.View(func(tx *bolt.Tx) error { | ||
bkt := tx.Bucket(blocksBucket) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
} | ||
} | ||
|
||
// DBError implements the Error method so that it can be asserted as an error. |
There was a problem hiding this comment.
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
s.finalizedCheckpt = ethpb.CopyCheckpoint(finalizedCheckpoint) | ||
s.prevFinalizedCheckpt = ethpb.CopyCheckpoint(finalizedCheckpoint) | ||
s.resumeForkChoice(justifiedCheckpoint, finalizedCheckpoint) | ||
finalizedState, err = s.cfg.StateGen.Resume(ctx, s.cfg.FinalizedStateAtStartUp) |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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:
prysm/beacon-chain/node/node.go
Line 453 in 790bf03
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
|
||
ss, err := slots.EpochStart(s.finalizedCheckpt.Epoch) | ||
if flags.Get().HeadSync { |
There was a problem hiding this comment.
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
5617741
to
26a957b
Compare
There was a problem hiding this 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>
use log.WithError for aggregation friendliness Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
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?
What does this PR do? Why is it needed?
First of 3 branches to introduce a minimal weak subjectivity sync implementation.