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

Resolved vote related comments of fast finality review #10

Merged
merged 1 commit into from
Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions core/types/vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package types
import (
"sync/atomic"

"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/crypto/bls"

"github.com/ethereum/go-ethereum/common"
)

Expand Down Expand Up @@ -61,3 +64,22 @@ func (v *VoteEnvelope) calcVoteHash() common.Hash {
}

func (b BLSPublicKey) Bytes() []byte { return b[:] }

// Verify vote using BLS.
func (vote *VoteEnvelope) Verify() error {
blsPubKey, err := bls.PublicKeyFromBytes(vote.VoteAddress[:])
if err != nil {
return errors.Wrap(err, "convert public key from bytes to bls failed")
}

sig, err := bls.SignatureFromBytes(vote.Signature[:])
if err != nil {
return errors.Wrap(err, "invalid signature")
}

voteDataHash := vote.Data.Hash()
if !sig.Verify(blsPubKey, voteDataHash[:]) {
return errors.New("verify bls signature failed.")
}
return nil
}
66 changes: 11 additions & 55 deletions core/vote/vote_manager.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
package vote

import (
"context"
"fmt"
"io/ioutil"

"github.com/prysmaticlabs/prysm/validator/accounts/iface"
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
Expand All @@ -20,7 +15,7 @@ import (
)

type (
getHighestJustifiedHeader func(chain consensus.ChainHeaderReader, header *types.Header) *types.Header
getHighestJustifiedHeaderFunc func(chain consensus.ChainHeaderReader, header *types.Header) *types.Header
)

// VoteManager will handle the vote produced by self.
Expand All @@ -39,10 +34,10 @@ type VoteManager struct {

engine consensus.PoSA

get getHighestJustifiedHeader
getHighestJustifiedHeader getHighestJustifiedHeaderFunc
}

func NewVoteManager(mux *event.TypeMux, chainconfig *params.ChainConfig, chain *core.BlockChain, pool *VotePool, journalPath, blsPasswordPath, blsWalletPath string, engine consensus.PoSA, get getHighestJustifiedHeader) (*VoteManager, error) {
func NewVoteManager(mux *event.TypeMux, chainconfig *params.ChainConfig, chain *core.BlockChain, pool *VotePool, journalPath, blsPasswordPath, blsWalletPath string, engine consensus.PoSA, getHighestJustifiedHeader getHighestJustifiedHeaderFunc) (*VoteManager, error) {
voteManager := &VoteManager{
mux: mux,

Expand All @@ -53,45 +48,11 @@ func NewVoteManager(mux *event.TypeMux, chainconfig *params.ChainConfig, chain *
pool: pool,
engine: engine,

get: get,
}

dirExists, err := wallet.Exists(blsWalletPath)
if err != nil {
log.Error("Check BLS wallet exists error: %v.", err)
return nil, err
}
if !dirExists {
log.Error("BLS wallet did not exists.")
return nil, fmt.Errorf("BLS wallet did not exists.")
}

walletPassword, err := ioutil.ReadFile(blsPasswordPath)
if err != nil {
log.Error("Read BLS wallet password error: %v.", err)
return nil, err
}
log.Info("Read BLS wallet password successfully")

w, err := wallet.OpenWallet(context.Background(), &wallet.Config{
WalletDir: blsWalletPath,
WalletPassword: string(walletPassword),
})
if err != nil {
log.Error("Open BLS wallet failed: %v.", err)
return nil, err
}
log.Info("Open BLS wallet successfully")

km, err := w.InitializeKeymanager(context.Background(), iface.InitKeymanagerConfig{ListenForChanges: false})
if err != nil {
log.Error("Initialize key manager failed: %v.", err)
return nil, err
getHighestJustifiedHeader: getHighestJustifiedHeader,
}
log.Info("Initialized keymanager successfully")

// Create voteSigner
voteSigner, err := NewVoteSigner(&km)
// Create voteSigner.
voteSigner, err := NewVoteSigner(blsPasswordPath, blsWalletPath)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -194,7 +155,7 @@ func (voteManager *VoteManager) loop() {
// 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) {
justifiedHeader := voteManager.get(voteManager.chain, header)
justifiedHeader := voteManager.getHighestJustifiedHeader(voteManager.chain, header)
if justifiedHeader == nil {
log.Error("highestJustifiedHeader is nil")
return false, 0, common.Hash{}
Expand All @@ -219,21 +180,16 @@ func (voteManager *VoteManager) UnderRules(header *types.Header) (bool, uint64,
return false, 0, common.Hash{}
}

journalLatestVote, err := journal.ReadVote(lastIndex)
if err != nil {
return false, 0, common.Hash{}
}
if journalLatestVote == nil {
// Indicate there's no vote before in local node, so it must be under rules.
return true, sourceNumber, sourceHash
}

for index := lastIndex; index >= firstIndex; index-- {
vote, err := journal.ReadVote(index)
if err != nil {
return false, 0, common.Hash{}
}
if vote == nil {
// Indicate there's no vote in local journal, so it must be under rules.
if index == 0 {
return true, sourceNumber, sourceHash
}
log.Error("vote is nil")
return false, 0, common.Hash{}
}
Expand Down
49 changes: 26 additions & 23 deletions core/vote/vote_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,6 @@ func (pool *VotePool) putIntoVotePool(vote *types.VoteEnvelope) bool {
votesPq = pool.futureVotesPq
isFutureVote = true
} else {
// Verify if the vote comes from valid validators based on voteAddress (BLSPublicKey).
if posa, ok := pool.engine.(consensus.PoSA); ok {
if !posa.VerifyVote(pool.chain, vote) {
return false
}
}
// Verify bls signature.
if err := VerifyVoteWithBLS(vote); err != nil {
log.Error("Failed to verify voteMessage", "err", err)
return false
}
votes = pool.curVotes
votesPq = pool.curVotesPq
}
Expand All @@ -159,14 +148,20 @@ func (pool *VotePool) putIntoVotePool(vote *types.VoteEnvelope) bool {
return false
}

pool.putVote(votes, votesPq, vote, voteData, voteHash, isFutureVote)

if !isFutureVote {
// Verify if the vote comes from valid validators based on voteAddress (BLSPublicKey), only verify curVotes here, will verify futureVotes in transfer process.
if posa, ok := pool.engine.(consensus.PoSA); ok {
if !posa.VerifyVote(pool.chain, vote) {
return false
}
}
// Send vote for handler usage of broadcasting to peers.
voteEv := core.NewVoteEvent{Vote: vote}
pool.votesFeed.Send(voteEv)
}

pool.putVote(votes, votesPq, vote, voteData, voteHash, isFutureVote)
NashBC marked this conversation as resolved.
Show resolved Hide resolved

return true
}

Expand Down Expand Up @@ -259,13 +254,11 @@ func (pool *VotePool) transfer(blockHash common.Hash) {
continue
}
}
// Verify the vote from futureVotes.
if err := VerifyVoteWithBLS(vote); err == nil {
// In the process of transfer, send valid vote to votes channel for handler usage
voteEv := core.NewVoteEvent{Vote: vote}
pool.votesFeed.Send(voteEv)
validVotes = append(validVotes, vote)
}

// In the process of transfer, send valid vote to votes channel for handler usage
voteEv := core.NewVoteEvent{Vote: vote}
pool.votesFeed.Send(voteEv)
validVotes = append(validVotes, vote)
NashBC marked this conversation as resolved.
Show resolved Hide resolved
}

voteData := heap.Pop(futurePq)
Expand Down Expand Up @@ -349,15 +342,25 @@ func (pool *VotePool) basicVerify(vote *types.VoteEnvelope, headNumber uint64, m
log.Warn("BlockNumber of vote is outside the range of header-256~header+13")
return false
}
// To prevent DOS attacks, make sure no more than 50 votes for the same blockHash if it's futureVotes
// No more than 21 votes per blockHash if not futureVotes and no more than 50 votes per blockHash if futureVotes

// To prevent DOS attacks, make sure no more than 21 votes per blockHash if not futureVotes
// and no more than 50 votes per blockHash if futureVotes.
maxVoteAmountPerBlock := maxCurVoteAmountPerBlock
if isFutureVote {
maxVoteAmountPerBlock = maxFutureVoteAmountPerBlock
}
if voteBox, ok := m[targetHash]; ok {
return len(voteBox.voteMessages) <= maxVoteAmountPerBlock
if len(voteBox.voteMessages) > maxVoteAmountPerBlock {
return false
}
}

// Verify bls signature.
if err := vote.Verify(); err != nil {
log.Error("Failed to verify voteMessage", "err", err)
return false
}

return true
}

Expand Down
2 changes: 1 addition & 1 deletion core/vote/vote_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func testVotePool(t *testing.T, inValidRules bool) {
file.Close()
os.Remove(journal)

var ruleFunc getHighestJustifiedHeader
var ruleFunc getHighestJustifiedHeaderFunc
if inValidRules {
ruleFunc = getHighestJustifiedHeaderForValid
} else {
Expand Down
64 changes: 42 additions & 22 deletions core/vote/vote_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ package vote
import (
"context"
"fmt"
"io/ioutil"
"time"

"github.com/pkg/errors"

"github.com/prysmaticlabs/prysm/crypto/bls"
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/validator/accounts/iface"
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/validator/keymanager"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
)

Expand All @@ -25,16 +29,51 @@ type VoteSigner struct {
pubKey [48]byte
}

func NewVoteSigner(km *keymanager.IKeymanager) (*VoteSigner, error) {
func NewVoteSigner(blsPasswordPath, blsWalletPath string) (*VoteSigner, error) {
dirExists, err := wallet.Exists(blsWalletPath)
if err != nil {
log.Error("Check BLS wallet exists error: %v.", err)
return nil, err
}
if !dirExists {
log.Error("BLS wallet did not exists.")
return nil, fmt.Errorf("BLS wallet did not exists.")
}

walletPassword, err := ioutil.ReadFile(blsPasswordPath)
if err != nil {
log.Error("Read BLS wallet password error: %v.", err)
return nil, err
}
log.Info("Read BLS wallet password successfully")

w, err := wallet.OpenWallet(context.Background(), &wallet.Config{
WalletDir: blsWalletPath,
WalletPassword: string(walletPassword),
})
if err != nil {
log.Error("Open BLS wallet failed: %v.", err)
return nil, err
}
log.Info("Open BLS wallet successfully")

km, err := w.InitializeKeymanager(context.Background(), iface.InitKeymanagerConfig{ListenForChanges: false})
if err != nil {
log.Error("Initialize key manager failed: %v.", err)
return nil, err
}
log.Info("Initialized keymanager successfully")

ctx, cancel := context.WithTimeout(context.Background(), voteSignerTimeout)
defer cancel()

pubKeys, err := (*km).FetchValidatingPublicKeys(ctx)
pubKeys, err := km.FetchValidatingPublicKeys(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not fetch validating public keys")
}

return &VoteSigner{
km: km,
km: &km,
pubKey: pubKeys[0],
}, nil
}
Expand Down Expand Up @@ -65,25 +104,6 @@ func (signer *VoteSigner) SignVote(vote *types.VoteEnvelope) error {
return nil
}

// VerifyVoteWithBLS using BLS.
func VerifyVoteWithBLS(vote *types.VoteEnvelope) error {
blsPubKey, err := bls.PublicKeyFromBytes(vote.VoteAddress[:])
if err != nil {
return errors.Wrap(err, "convert public key from bytes to bls failed")
}

sig, err := bls.SignatureFromBytes(vote.Signature[:])
if err != nil {
return errors.Wrap(err, "invalid signature")
}

voteDataHash := vote.Data.Hash()
if !sig.Verify(blsPubKey, voteDataHash[:]) {
return errors.New("verify bls signature failed.")
}
return nil
}

// Metrics to indicate if there's any failed signing.
func votesSigningErrorMetric(blockNumber uint64, blockHash common.Hash) metrics.Gauge {
return metrics.GetOrRegisterGauge(fmt.Sprintf("voteSigning/blockNumber/%d/blockHash/%s", blockNumber, blockHash), nil)
Expand Down