mirror of
https://github.com/tendermint/tendermint.git
synced 2026-08-28 11:56:15 +00:00
consensus: only call privValidator.GetPubKey once per block (#5143)
Closes #4865
This commit is contained in:
@@ -25,6 +25,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [consensus] \#5143 Only call `privValidator.GetPubKey` once per block (@melekes)
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
|
||||
+58
-30
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/libs/fail"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
@@ -32,6 +33,8 @@ var (
|
||||
ErrInvalidProposalPOLRound = errors.New("error invalid proposal POL round")
|
||||
ErrAddingVote = errors.New("error adding vote")
|
||||
ErrVoteHeightMismatch = errors.New("error vote height mismatch")
|
||||
|
||||
errPubKeyIsNotSet = errors.New("pubkey is not set. Look for \"Can't get private validator pubkey\" errors")
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -96,6 +99,9 @@ type State struct {
|
||||
mtx sync.RWMutex
|
||||
cstypes.RoundState
|
||||
state sm.State // State until height-1.
|
||||
// privValidator pubkey, memoized for the duration of one block
|
||||
// to avoid extra requests to HSM
|
||||
privValidatorPubKey crypto.PubKey
|
||||
|
||||
// state changes may be triggered by: msgs from peers,
|
||||
// msgs from ourself, or by timeouts
|
||||
@@ -252,11 +258,17 @@ func (cs *State) GetValidators() (int64, []*types.Validator) {
|
||||
return cs.state.LastBlockHeight, cs.state.Validators.Copy().Validators
|
||||
}
|
||||
|
||||
// SetPrivValidator sets the private validator account for signing votes.
|
||||
// SetPrivValidator sets the private validator account for signing votes. It
|
||||
// immediately requests pubkey and caches it.
|
||||
func (cs *State) SetPrivValidator(priv types.PrivValidator) {
|
||||
cs.mtx.Lock()
|
||||
defer cs.mtx.Unlock()
|
||||
|
||||
cs.privValidator = priv
|
||||
cs.mtx.Unlock()
|
||||
|
||||
if err := cs.updatePrivValidatorPubKey(); err != nil {
|
||||
cs.Logger.Error("Can't get private validator pubkey", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SetTimeoutTicker sets the local timer. It may be useful to overwrite for testing.
|
||||
@@ -930,14 +942,13 @@ func (cs *State) enterPropose(height int64, round int) {
|
||||
}
|
||||
logger.Debug("This node is a validator")
|
||||
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
if cs.privValidatorPubKey == nil {
|
||||
// If this node is a validator & proposer in the current round, it will
|
||||
// miss the opportunity to create a block.
|
||||
logger.Error("Error on retrival of pubkey", "err", err)
|
||||
logger.Error(fmt.Sprintf("enterPropose: %v", errPubKeyIsNotSet))
|
||||
return
|
||||
}
|
||||
address := pubKey.Address()
|
||||
address := cs.privValidatorPubKey.Address()
|
||||
|
||||
// if not a validator, we're done
|
||||
if !cs.Validators.HasAddress(address) {
|
||||
@@ -1027,6 +1038,10 @@ func (cs *State) isProposalComplete() bool {
|
||||
// NOTE: keep it side-effect free for clarity.
|
||||
// CONTRACT: cs.privValidator is not nil.
|
||||
func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.PartSet) {
|
||||
if cs.privValidator == nil {
|
||||
panic("entered createProposalBlock with privValidator being nil")
|
||||
}
|
||||
|
||||
var commit *types.Commit
|
||||
switch {
|
||||
case cs.Height == 1:
|
||||
@@ -1041,17 +1056,13 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa
|
||||
return
|
||||
}
|
||||
|
||||
if cs.privValidator == nil {
|
||||
panic("entered createProposalBlock with privValidator being nil")
|
||||
}
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
if cs.privValidatorPubKey == nil {
|
||||
// If this node is a validator & proposer in the current round, it will
|
||||
// miss the opportunity to create a block.
|
||||
cs.Logger.Error("Error on retrival of pubkey", "err", err)
|
||||
cs.Logger.Error(fmt.Sprintf("enterPropose: %v", errPubKeyIsNotSet))
|
||||
return
|
||||
}
|
||||
proposerAddr := pubKey.Address()
|
||||
proposerAddr := cs.privValidatorPubKey.Address()
|
||||
|
||||
return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr)
|
||||
}
|
||||
@@ -1489,6 +1500,11 @@ func (cs *State) finalizeCommit(height int64) {
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
// Private validator might have changed it's key pair => refetch pubkey.
|
||||
if err := cs.updatePrivValidatorPubKey(); err != nil {
|
||||
cs.Logger.Error("Can't get private validator pubkey", "err", err)
|
||||
}
|
||||
|
||||
// cs.StartTime is already set.
|
||||
// Schedule Round0 to start soon.
|
||||
cs.scheduleRound0(&cs.RoundState)
|
||||
@@ -1540,12 +1556,11 @@ func (cs *State) recordMetrics(height int64, block *types.Block) {
|
||||
}
|
||||
|
||||
if cs.privValidator != nil {
|
||||
pubkey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
if cs.privValidatorPubKey == nil {
|
||||
// Metrics won't be updated, but it's not critical.
|
||||
cs.Logger.Error("Error on retrieval of pubkey", "err", err)
|
||||
cs.Logger.Error(fmt.Sprintf("recordMetrics: %v", errPubKeyIsNotSet))
|
||||
} else {
|
||||
address = pubkey.Address()
|
||||
address = cs.privValidatorPubKey.Address()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1716,12 +1731,11 @@ func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) {
|
||||
if err == ErrVoteHeightMismatch {
|
||||
return added, err
|
||||
} else if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok {
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "can't get pubkey")
|
||||
if cs.privValidatorPubKey == nil {
|
||||
return false, errPubKeyIsNotSet
|
||||
}
|
||||
|
||||
if bytes.Equal(vote.ValidatorAddress, pubKey.Address()) {
|
||||
if bytes.Equal(vote.ValidatorAddress, cs.privValidatorPubKey.Address()) {
|
||||
cs.Logger.Error(
|
||||
"Found conflicting vote from ourselves. Did you unsafe_reset a validator?",
|
||||
"height",
|
||||
@@ -1920,11 +1934,10 @@ func (cs *State) signVote(
|
||||
// and the privValidator will refuse to sign anything.
|
||||
cs.wal.FlushAndSync()
|
||||
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't get pubkey")
|
||||
if cs.privValidatorPubKey == nil {
|
||||
return nil, errPubKeyIsNotSet
|
||||
}
|
||||
addr := pubKey.Address()
|
||||
addr := cs.privValidatorPubKey.Address()
|
||||
valIdx, _ := cs.Validators.GetByAddress(addr)
|
||||
|
||||
vote := &types.Vote{
|
||||
@@ -1937,7 +1950,7 @@ func (cs *State) signVote(
|
||||
BlockID: types.BlockID{Hash: hash, PartsHeader: header},
|
||||
}
|
||||
|
||||
err = cs.privValidator.SignVote(cs.state.ChainID, vote)
|
||||
err := cs.privValidator.SignVote(cs.state.ChainID, vote)
|
||||
return vote, err
|
||||
}
|
||||
|
||||
@@ -1966,15 +1979,14 @@ func (cs *State) signAddVote(msgType types.SignedMsgType, hash []byte, header ty
|
||||
return nil
|
||||
}
|
||||
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
if cs.privValidatorPubKey == nil {
|
||||
// Vote won't be signed, but it's not critical.
|
||||
cs.Logger.Error("Error on retrival of pubkey", "err", err)
|
||||
cs.Logger.Error(fmt.Sprintf("signAddVote: %v", errPubKeyIsNotSet))
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the node not in the validator set, do nothing.
|
||||
if !cs.Validators.HasAddress(pubKey.Address()) {
|
||||
if !cs.Validators.HasAddress(cs.privValidatorPubKey.Address()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1991,6 +2003,22 @@ func (cs *State) signAddVote(msgType types.SignedMsgType, hash []byte, header ty
|
||||
return nil
|
||||
}
|
||||
|
||||
// updatePrivValidatorPubKey get's the private validator public key and
|
||||
// memoizes it. This func returns an error if the private validator is not
|
||||
// responding or responds with an error.
|
||||
func (cs *State) updatePrivValidatorPubKey() error {
|
||||
if cs.privValidator == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs.privValidatorPubKey = pubKey
|
||||
return nil
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
||||
func CompareHRS(h1 int64, r1 int, s1 cstypes.RoundStepType, h2 int64, r2 int, s2 cstypes.RoundStepType) int {
|
||||
|
||||
Reference in New Issue
Block a user