2023-04-10 13:36:45 +03:00
package vote
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
)
2023-05-05 09:15:43 +03:00
var votesManagerCounter = metrics . NewRegisteredCounter ( "votesManager/local" , nil )
2023-04-10 13:36:45 +03:00
// VoteManager will handle the vote produced by self.
type VoteManager struct {
mux * event . TypeMux
chain * core . BlockChain
chainconfig * params . ChainConfig
chainHeadCh chan core . ChainHeadEvent
chainHeadSub event . Subscription
pool * VotePool
signer * VoteSigner
journal * VoteJournal
engine consensus . PoSA
}
func NewVoteManager ( mux * event . TypeMux , chainconfig * params . ChainConfig , chain * core . BlockChain , pool * VotePool , journalPath , blsPasswordPath , blsWalletPath string , engine consensus . PoSA ) ( * VoteManager , error ) {
voteManager := & VoteManager {
mux : mux ,
chain : chain ,
chainconfig : chainconfig ,
chainHeadCh : make ( chan core . ChainHeadEvent , chainHeadChanSize ) ,
pool : pool ,
engine : engine ,
}
// Create voteSigner.
voteSigner , err := NewVoteSigner ( blsPasswordPath , blsWalletPath )
if err != nil {
return nil , err
}
log . Info ( "Create voteSigner successfully" )
voteManager . signer = voteSigner
// Create voteJournal
voteJournal , err := NewVoteJournal ( journalPath )
if err != nil {
return nil , err
}
log . Info ( "Create voteJournal successfully" )
voteManager . journal = voteJournal
// Subscribe to chain head event.
voteManager . chainHeadSub = voteManager . chain . SubscribeChainHeadEvent ( voteManager . chainHeadCh )
go voteManager . loop ( )
return voteManager , nil
}
func ( voteManager * VoteManager ) loop ( ) {
log . Debug ( "vote manager routine loop started" )
events := voteManager . mux . Subscribe ( downloader . StartEvent { } , downloader . DoneEvent { } , downloader . FailedEvent { } )
defer func ( ) {
log . Debug ( "vote manager loop defer func occur" )
if ! events . Closed ( ) {
log . Debug ( "event not closed, unsubscribed by vote manager loop" )
events . Unsubscribe ( )
}
} ( )
dlEventCh := events . Chan ( )
startVote := true
for {
select {
case ev := <- dlEventCh :
if ev == nil {
log . Debug ( "dlEvent is nil, continue" )
continue
}
switch ev . Data . ( type ) {
case downloader . StartEvent :
log . Debug ( "downloader is in startEvent mode, will not startVote" )
startVote = false
case downloader . FailedEvent :
log . Debug ( "downloader is in FailedEvent mode, set startVote flag as true" )
startVote = true
case downloader . DoneEvent :
log . Debug ( "downloader is in DoneEvent mode, set the startVote flag to true" )
startVote = true
}
case cHead := <- voteManager . chainHeadCh :
if ! startVote {
log . Debug ( "startVote flag is false, continue" )
continue
}
if cHead . Block == nil {
log . Debug ( "cHead.Block is nil, continue" )
continue
}
curHead := cHead . Block . Header ( )
// Check if cur validator is within the validatorSet at curHead
if ! voteManager . engine . IsActiveValidatorAt ( voteManager . chain , curHead ) {
log . Debug ( "cur validator is not within the validatorSet at curHead" )
continue
}
// Vote for curBlockHeader block.
vote := & types . VoteData {
TargetNumber : curHead . Number . Uint64 ( ) ,
TargetHash : curHead . Hash ( ) ,
}
voteMessage := & types . VoteEnvelope {
Data : vote ,
}
// Put Vote into journal and VotesPool if we are active validator and allow to sign it.
if ok , sourceNumber , sourceHash := voteManager . UnderRules ( curHead ) ; ok {
log . Debug ( "curHead is underRules for voting" )
if sourceHash == ( common . Hash { } ) {
log . Debug ( "sourceHash is empty" )
continue
}
voteMessage . Data . SourceNumber = sourceNumber
voteMessage . Data . SourceHash = sourceHash
if err := voteManager . signer . SignVote ( voteMessage ) ; err != nil {
2023-05-05 09:15:43 +03:00
log . Error ( "Failed to sign vote" , "err" , err , "votedBlockNumber" , voteMessage . Data . TargetNumber , "votedBlockHash" , voteMessage . Data . TargetHash , "voteMessageHash" , voteMessage . Hash ( ) )
votesSigningErrorCounter . Inc ( 1 )
2023-04-10 13:36:45 +03:00
continue
}
if err := voteManager . journal . WriteVote ( voteMessage ) ; err != nil {
log . Error ( "Failed to write vote into journal" , "err" , err )
2023-05-05 09:15:43 +03:00
voteJournalErrorCounter . Inc ( 1 )
2023-04-10 13:36:45 +03:00
continue
}
log . Debug ( "vote manager produced vote" , "votedBlockNumber" , voteMessage . Data . TargetNumber , "votedBlockHash" , voteMessage . Data . TargetHash , "voteMessageHash" , voteMessage . Hash ( ) )
voteManager . pool . PutVote ( voteMessage )
2023-05-05 09:15:43 +03:00
votesManagerCounter . Inc ( 1 )
2023-04-10 13:36:45 +03:00
}
case <- voteManager . chainHeadSub . Err ( ) :
log . Debug ( "voteManager subscribed chainHead failed" )
return
}
}
}
// UnderRules checks if the produced header under the following rules:
// A validator must not publish two distinct votes for the same height. (Rule 1)
// A validator must not vote within the span of its other votes . (Rule 2)
// Validators always vote for their canonical chain’ s latest block. (Rule 3)
func ( voteManager * VoteManager ) UnderRules ( header * types . Header ) ( bool , uint64 , common . Hash ) {
sourceNumber , sourceHash , err := voteManager . engine . GetJustifiedNumberAndHash ( voteManager . chain , header )
if err != nil {
log . Error ( "failed to get the highest justified number and hash at cur header" , "curHeader's BlockNumber" , header . Number , "curHeader's BlockHash" , header . Hash ( ) )
return false , 0 , common . Hash { }
}
targetNumber := header . Number . Uint64 ( )
voteDataBuffer := voteManager . journal . voteDataBuffer
//Rule 1: A validator must not publish two distinct votes for the same height.
if voteDataBuffer . Contains ( targetNumber ) {
log . Debug ( "err: A validator must not publish two distinct votes for the same height." )
return false , 0 , common . Hash { }
}
//Rule 2: A validator must not vote within the span of its other votes.
2023-04-24 09:28:28 +03:00
blockNumber := sourceNumber + 1
if blockNumber + maliciousVoteSlashScope < targetNumber {
blockNumber = targetNumber - maliciousVoteSlashScope
}
for ; blockNumber < targetNumber ; blockNumber ++ {
2023-04-10 13:36:45 +03:00
if voteDataBuffer . Contains ( blockNumber ) {
voteData , ok := voteDataBuffer . Get ( blockNumber )
if ! ok {
log . Error ( "Failed to get voteData info from LRU cache." )
continue
}
if voteData . ( * types . VoteData ) . SourceNumber > sourceNumber {
2023-05-11 09:45:15 +03:00
log . Debug ( fmt . Sprintf ( "error: cur vote %d-->%d is across the span of other votes %d-->%d" ,
2023-04-24 09:28:28 +03:00
sourceNumber , targetNumber , voteData . ( * types . VoteData ) . SourceNumber , voteData . ( * types . VoteData ) . TargetNumber ) )
2023-04-10 13:36:45 +03:00
return false , 0 , common . Hash { }
}
}
}
for blockNumber := targetNumber + 1 ; blockNumber <= targetNumber + upperLimitOfVoteBlockNumber ; blockNumber ++ {
if voteDataBuffer . Contains ( blockNumber ) {
voteData , ok := voteDataBuffer . Get ( blockNumber )
if ! ok {
log . Error ( "Failed to get voteData info from LRU cache." )
continue
}
if voteData . ( * types . VoteData ) . SourceNumber < sourceNumber {
2023-05-11 09:45:15 +03:00
log . Debug ( fmt . Sprintf ( "error: cur vote %d-->%d is within the span of other votes %d-->%d" ,
sourceNumber , targetNumber , voteData . ( * types . VoteData ) . SourceNumber , voteData . ( * types . VoteData ) . TargetNumber ) )
2023-04-10 13:36:45 +03:00
return false , 0 , common . Hash { }
}
}
}
// Rule 3: Validators always vote for their canonical chain’ s latest block.
2023-05-11 09:45:15 +03:00
// Since the header subscribed to is the canonical chain, so this rule is satisfied by default.
2023-04-10 13:36:45 +03:00
log . Debug ( "All three rules check passed" )
return true , sourceNumber , sourceHash
}