-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Align eth1data Majority Vote with the spec #7200
Merged
+579
−479
Merged
Changes from 22 commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
144df31
align voting with the spec
rkapka cc7291d
Merge branch 'master' into eth1vote-spec-align
rkapka be196f9
remove redundant else statements
rkapka d8673e2
add comment to exported variable
rkapka 05f8636
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] a630b17
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] dccdcb9
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 245b486
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] ca93bf5
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 0b48fcb
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 65fb301
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 8857cae
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 3112536
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 06468dd
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 1ba0b07
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 0f0201c
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 01f4fb0
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] c25d200
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 6d42b7a
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 93d22e7
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] add26cc
Merge branch 'master' into eth1vote-spec-align
rkapka 069f0c1
refactor tests
rkapka e8e5546
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] f773bff
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 49d6980
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] bed040d
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 5665953
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] de6370a
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 5b4efef
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] fe30f01
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 75278db
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] b233e32
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 63721c7
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 4d077d4
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] ac8b47e
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 273276c
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 79b2469
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] da7eabb
fix mock POWChain
rkapka e787e4c
move last block's time check and add more test cases
rkapka 6f2e887
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] a1280cb
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 49d2cd5
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] e314009
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] 07209c6
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] c8456f1
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] adf9d70
Merge refs/heads/master into eth1vote-spec-align
prylabs-bulldozer[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -221,22 +221,25 @@ func (vs *Server) eth1Data(ctx context.Context, slot uint64) (*ethpb.Eth1Data, e | |
return eth1Data, nil | ||
} | ||
|
||
// eth1DataMajorityVote determines the appropriate eth1data for a block proposal using an extended | ||
// simple voting algorithm - voting with the majority. The algorithm for this method is as follows: | ||
// - Determine the timestamp for the start slot for the current eth1 voting period. | ||
// - Determine the timestamp for the start slot for the previous eth1 voting period. | ||
// - Determine the most recent eth1 block before each timestamp. | ||
// - Subtract the current period's eth1block.number by ETH1_FOLLOW_DISTANCE to determine the voting upper bound. | ||
// - Subtract the previous period's eth1block.number by ETH1_FOLLOW_DISTANCE to determine the voting lower bound. | ||
// eth1DataMajorityVote determines the appropriate eth1data for a block proposal using | ||
// an algorithm called Voting with the Majority. The algorithm works as follows: | ||
// - Determine the timestamp for the start slot for the eth1 voting period. | ||
// - Determine the earliest and latest timestamps that a valid block can have. | ||
// - Determine the first block not before the earliest timestamp. This block is the lower bound. | ||
// - Determine the last block not after the latest timestamp. This block is the upper bound. | ||
// - Filter out votes on unknown blocks and blocks which are outside of the range determined by the lower and upper bounds. | ||
// - If no blocks are left after filtering, use the current period's most recent eth1 block for proposal. | ||
// - Determine the vote with the highest count. Prefer the vote with the highest eth1 block height in the event of a tie. | ||
// - This vote's block is the eth1block to use for the block proposal. | ||
// - If no blocks are left after filtering votes: | ||
// - Use eth1data from the latest valid block. | ||
// - If there are no valid blocks, use the current eth1data from the beacon state. | ||
// - Otherwise: | ||
// - Determine the vote with the highest count. Prefer the vote with the highest eth1 block height in the event of a tie. | ||
// - This vote's block is the eth1 block to use for the block proposal. | ||
func (vs *Server) eth1DataMajorityVote(ctx context.Context, beaconState *stateTrie.BeaconState) (*ethpb.Eth1Data, error) { | ||
ctx, cancel := context.WithTimeout(ctx, eth1dataTimeout) | ||
defer cancel() | ||
|
||
slot := beaconState.Slot() | ||
votingPeriodStartTime := vs.slotStartTime(slot) | ||
|
||
if vs.MockEth1Votes { | ||
return vs.mockETH1DataVote(ctx, slot) | ||
|
@@ -246,51 +249,66 @@ func (vs *Server) eth1DataMajorityVote(ctx context.Context, beaconState *stateTr | |
} | ||
eth1DataNotification = false | ||
|
||
slotsPerVotingPeriod := params.BeaconConfig().EpochsPerEth1VotingPeriod * params.BeaconConfig().SlotsPerEpoch | ||
currentPeriodVotingStartTime := vs.slotStartTime(slot) | ||
// Can't use slotStartTime function because slot would be negative in the initial voting period. | ||
previousPeriodVotingStartTime := currentPeriodVotingStartTime - | ||
slotsPerVotingPeriod*params.BeaconConfig().SecondsPerSlot | ||
eth1FollowDistance := int64(params.BeaconConfig().Eth1FollowDistance) | ||
earliestValidTime := votingPeriodStartTime - 2*params.BeaconConfig().SecondsPerETH1Block*uint64(eth1FollowDistance) | ||
latestValidTime := votingPeriodStartTime - params.BeaconConfig().SecondsPerETH1Block*uint64(eth1FollowDistance) | ||
|
||
currentPeriodBlockNumber, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, currentPeriodVotingStartTime) | ||
lastBlockByEarliestValidTime, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, earliestValidTime) | ||
if err != nil { | ||
log.WithError(err).Error("Failed to get block number for current voting period") | ||
log.WithError(err).Error("Failed to get last block by earliest valid time") | ||
return vs.randomETH1DataVote(ctx) | ||
} | ||
previousPeriodBlockNumber, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, previousPeriodVotingStartTime) | ||
timeOfLastBlockByEarliestValidTime, err := vs.Eth1BlockFetcher.BlockTimeByHeight(ctx, lastBlockByEarliestValidTime) | ||
if err != nil { | ||
log.WithError(err).Error("Failed to get block number for previous voting period") | ||
log.WithError(err).Error("Failed to get time of last block by earliest valid time") | ||
return vs.randomETH1DataVote(ctx) | ||
} | ||
eth1FollowDistance := int64(params.BeaconConfig().Eth1FollowDistance) | ||
currentPeriodInitialBlock := big.NewInt(0).Sub(currentPeriodBlockNumber, big.NewInt(eth1FollowDistance)) | ||
previousPeriodInitialBlock := big.NewInt(0).Sub(previousPeriodBlockNumber, big.NewInt(eth1FollowDistance)) | ||
// Increment the earliest block if the original block's time is before valid time. | ||
// This is very likely to happen because BlockTimeByHeight returns the last block AT OR BEFORE the specified time. | ||
if timeOfLastBlockByEarliestValidTime < earliestValidTime { | ||
lastBlockByEarliestValidTime = big.NewInt(0).Add(lastBlockByEarliestValidTime, big.NewInt(1)) | ||
} | ||
|
||
currentDepositCount, _ := vs.DepositFetcher.DepositsNumberAndRootAtHeight(ctx, currentPeriodInitialBlock) | ||
if currentDepositCount == 0 { | ||
return vs.ChainStartFetcher.ChainStartEth1Data(), nil | ||
lastBlockByLatestValidTime, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, latestValidTime) | ||
if err != nil { | ||
log.WithError(err).Error("Failed to get last block by latest valid time") | ||
return vs.randomETH1DataVote(ctx) | ||
} | ||
timeOfLastBlockByLatestValidTime, err := vs.Eth1BlockFetcher.BlockTimeByHeight(ctx, lastBlockByLatestValidTime) | ||
if err != nil { | ||
log.WithError(err).Error("Failed to get time of last block by latest valid time") | ||
return vs.randomETH1DataVote(ctx) | ||
} | ||
|
||
if len(beaconState.Eth1DataVotes()) == 0 { | ||
eth1Data, err := vs.defaultEth1DataResponse(ctx, currentPeriodBlockNumber) | ||
if err != nil { | ||
log.WithError(err).Error("Failed to get eth1 data from current period block number") | ||
return vs.randomETH1DataVote(ctx) | ||
} | ||
return eth1Data, nil | ||
lastBlockDepositCount, lastBlockDepositRoot := vs.DepositFetcher.DepositsNumberAndRootAtHeight(ctx, lastBlockByLatestValidTime) | ||
if lastBlockDepositCount == 0 { | ||
return vs.ChainStartFetcher.ChainStartEth1Data(), nil | ||
} | ||
|
||
inRangeVotes, err := vs.inRangeVotes(ctx, beaconState, currentPeriodInitialBlock, previousPeriodInitialBlock) | ||
inRangeVotes, err := vs.inRangeVotes(ctx, beaconState, lastBlockByEarliestValidTime, lastBlockByLatestValidTime) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if len(inRangeVotes) == 0 { | ||
eth1Data, err := vs.defaultEth1DataResponse(ctx, currentPeriodBlockNumber) | ||
if err != nil { | ||
log.WithError(err).Error("Failed to get eth1 data from current period block number") | ||
return vs.randomETH1DataVote(ctx) | ||
// If latest block is in range and it doesn't undo deposits, then choose that block. | ||
// Otherwise take the current eth1data. | ||
currentETH1Data := vs.HeadFetcher.HeadETH1Data() | ||
if timeOfLastBlockByLatestValidTime >= earliestValidTime { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this already a given ? earliestValidTime := votingPeriodStartTime - 2*params.BeaconConfig().SecondsPerETH1Block*uint64(eth1FollowDistance)
latestValidTime := votingPeriodStartTime - params.BeaconConfig().SecondsPerETH1Block*uint64(eth1FollowDistance) Unless the assumption is |
||
if lastBlockDepositCount >= currentETH1Data.DepositCount { | ||
hash, err := vs.Eth1BlockFetcher.BlockHashByHeight(ctx, lastBlockByLatestValidTime) | ||
if err != nil { | ||
log.WithError(err).Error("Failed to get hash of last block by latest valid time") | ||
return vs.randomETH1DataVote(ctx) | ||
} | ||
return ðpb.Eth1Data{ | ||
BlockHash: hash.Bytes(), | ||
DepositCount: lastBlockDepositCount, | ||
DepositRoot: lastBlockDepositRoot[:], | ||
}, nil | ||
} | ||
return vs.HeadFetcher.HeadETH1Data(), nil | ||
} | ||
return eth1Data, nil | ||
return vs.HeadFetcher.HeadETH1Data(), nil | ||
} | ||
|
||
chosenVote := chosenEth1DataMajorityVote(inRangeVotes) | ||
|
@@ -308,8 +326,8 @@ func (vs *Server) slotStartTime(slot uint64) uint64 { | |
|
||
func (vs *Server) inRangeVotes(ctx context.Context, | ||
beaconState *stateTrie.BeaconState, | ||
currentPeriodInitialBlock *big.Int, | ||
previousPeriodInitialBlock *big.Int) ([]eth1DataSingleVote, error) { | ||
firstValidBlockNumber *big.Int, | ||
lastValidBlockNumber *big.Int) ([]eth1DataSingleVote, error) { | ||
|
||
currentETH1Data := vs.HeadFetcher.HeadETH1Data() | ||
|
||
|
@@ -323,10 +341,10 @@ func (vs *Server) inRangeVotes(ctx context.Context, | |
if eth1Data.DepositCount < currentETH1Data.DepositCount { | ||
continue | ||
} | ||
// previousPeriodInitialBlock.Cmp(height) < 1 filters out all blocks before previousPeriodInitialBlock | ||
// currentPeriodInitialBlock.Cmp(height) > -1 filters out all blocks after currentPeriodInitialBlock | ||
// These filters result in the range [previousPeriodInitialBlock, currentPeriodInitialBlock] | ||
if ok && previousPeriodInitialBlock.Cmp(height) < 1 && currentPeriodInitialBlock.Cmp(height) > -1 { | ||
// firstValidBlockNumber.Cmp(height) < 1 filters out all blocks before firstValidBlockNumber | ||
// lastValidBlockNumber.Cmp(height) > -1 filters out all blocks after lastValidBlockNumber | ||
// These filters result in the range [firstValidBlockNumber, lastValidBlockNumber] | ||
if ok && firstValidBlockNumber.Cmp(height) < 1 && lastValidBlockNumber.Cmp(height) > -1 { | ||
inRangeVotes = append(inRangeVotes, eth1DataSingleVote{eth1Data: *eth1Data, blockHeight: height}) | ||
} | ||
} | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
chosenTime
is always 0, so the conditiont > chosenTime
is almost always true( except when t = 0), the time retrieved from this is not accurate.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 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 actually had another bug here. The check
chosenNumber == nil
caused non-deterministic test results. At the beginning of the loop this condition is always true, but there is no guaranteed order for map retrieval, so a block with incorrect time sometimes got assigned tochosenNumber
.