mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-26 19:21:44 +00:00
rearrange priv_validator.go
This commit is contained in:
@ -34,15 +34,7 @@ func voteToStep(vote *types.Vote) int8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilePV implements PrivValidator using data persisted to disk
|
//-------------------------------------------------------------------------------
|
||||||
// to prevent double signing.
|
|
||||||
// NOTE: the directories containing pv.Key.filePath and pv.LastSignState.filePath must already exist.
|
|
||||||
// It includes the LastSignature and LastSignBytes so we don't lose the signature
|
|
||||||
// if the process crashes after signing but before the resulting consensus message is processed.
|
|
||||||
type FilePV struct {
|
|
||||||
Key FilePVKey
|
|
||||||
LastSignState FilePVLastSignState
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilePVKey stores the immutable part of PrivValidator.
|
// FilePVKey stores the immutable part of PrivValidator.
|
||||||
type FilePVKey struct {
|
type FilePVKey struct {
|
||||||
@ -53,6 +45,26 @@ type FilePVKey struct {
|
|||||||
filePath string
|
filePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save persists the FilePVKey to its filePath.
|
||||||
|
func (pvKey FilePVKey) Save() {
|
||||||
|
outFile := pvKey.filePath
|
||||||
|
if outFile == "" {
|
||||||
|
panic("Cannot save PrivValidator key: filePath not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, err := cdc.MarshalJSONIndent(pvKey, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = cmn.WriteFileAtomic(outFile, jsonBytes, 0600)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------
|
||||||
|
|
||||||
// FilePVLastSignState stores the mutable part of PrivValidator.
|
// FilePVLastSignState stores the mutable part of PrivValidator.
|
||||||
type FilePVLastSignState struct {
|
type FilePVLastSignState struct {
|
||||||
Height int64 `json:"height"`
|
Height int64 `json:"height"`
|
||||||
@ -64,16 +76,67 @@ type FilePVLastSignState struct {
|
|||||||
filePath string
|
filePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAddress returns the address of the validator.
|
// CheckHRS checks the given height, round, step (HRS) against that of the
|
||||||
// Implements PrivValidator.
|
// FilePVLastSignState. It returns an error if the arguments constitute a regression,
|
||||||
func (pv *FilePV) GetAddress() types.Address {
|
// or if they match but the SignBytes are empty.
|
||||||
return pv.Key.Address
|
// The returned boolean indicates whether the last Signature should be reused -
|
||||||
|
// it returns true if the HRS matches the arguments and the SignBytes are not empty (indicating
|
||||||
|
// we have already signed for this HRS, and can reuse the existing signature).
|
||||||
|
// It panics if the HRS matches the arguments, there's a SignBytes, but no Signature.
|
||||||
|
func (lss *FilePVLastSignState) CheckHRS(height int64, round int, step int8) (bool, error) {
|
||||||
|
|
||||||
|
if lss.Height > height {
|
||||||
|
return false, errors.New("Height regression")
|
||||||
|
}
|
||||||
|
|
||||||
|
if lss.Height == height {
|
||||||
|
if lss.Round > round {
|
||||||
|
return false, errors.New("Round regression")
|
||||||
|
}
|
||||||
|
|
||||||
|
if lss.Round == round {
|
||||||
|
if lss.Step > step {
|
||||||
|
return false, errors.New("Step regression")
|
||||||
|
} else if lss.Step == step {
|
||||||
|
if lss.SignBytes != nil {
|
||||||
|
if lss.Signature == nil {
|
||||||
|
panic("pv: Signature is nil but SignBytes is not!")
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, errors.New("No SignBytes found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPubKey returns the public key of the validator.
|
// Save persists the FilePvLastSignState to its filePath.
|
||||||
// Implements PrivValidator.
|
func (lss *FilePVLastSignState) Save() {
|
||||||
func (pv *FilePV) GetPubKey() crypto.PubKey {
|
outFile := lss.filePath
|
||||||
return pv.Key.PubKey
|
if outFile == "" {
|
||||||
|
panic("Cannot save FilePVLastSignState: filePath not set")
|
||||||
|
}
|
||||||
|
jsonBytes, err := cdc.MarshalJSONIndent(lss, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = cmn.WriteFileAtomic(outFile, jsonBytes, 0600)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// FilePV implements PrivValidator using data persisted to disk
|
||||||
|
// to prevent double signing.
|
||||||
|
// NOTE: the directories containing pv.Key.filePath and pv.LastSignState.filePath must already exist.
|
||||||
|
// It includes the LastSignature and LastSignBytes so we don't lose the signature
|
||||||
|
// if the process crashes after signing but before the resulting consensus message is processed.
|
||||||
|
type FilePV struct {
|
||||||
|
Key FilePVKey
|
||||||
|
LastSignState FilePVLastSignState
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenFilePV generates a new validator with randomly generated private key
|
// GenFilePV generates a new validator with randomly generated private key
|
||||||
@ -145,54 +208,16 @@ func LoadOrGenFilePV(keyFilePath string, stateFilePath string) *FilePV {
|
|||||||
return pv
|
return pv
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save persists the FilePV to disk.
|
// GetAddress returns the address of the validator.
|
||||||
func (pv *FilePV) Save() {
|
// Implements PrivValidator.
|
||||||
pv.saveKey()
|
func (pv *FilePV) GetAddress() types.Address {
|
||||||
pv.saveState()
|
return pv.Key.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pv *FilePV) saveKey() {
|
// GetPubKey returns the public key of the validator.
|
||||||
outFile := pv.Key.filePath
|
// Implements PrivValidator.
|
||||||
if outFile == "" {
|
func (pv *FilePV) GetPubKey() crypto.PubKey {
|
||||||
panic("Cannot save PrivValidator key: filePath not set")
|
return pv.Key.PubKey
|
||||||
}
|
|
||||||
|
|
||||||
jsonBytes, err := cdc.MarshalJSONIndent(pv.Key, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
err = cmn.WriteFileAtomic(outFile, jsonBytes, 0600)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pv *FilePV) saveState() {
|
|
||||||
outFile := pv.LastSignState.filePath
|
|
||||||
if outFile == "" {
|
|
||||||
panic("Cannot save PrivValidator state: filePath not set")
|
|
||||||
}
|
|
||||||
jsonBytes, err := cdc.MarshalJSONIndent(pv.LastSignState, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
err = cmn.WriteFileAtomic(outFile, jsonBytes, 0600)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset resets all fields in the FilePV.
|
|
||||||
// NOTE: Unsafe!
|
|
||||||
func (pv *FilePV) Reset() {
|
|
||||||
var sig []byte
|
|
||||||
pv.LastSignState.Height = 0
|
|
||||||
pv.LastSignState.Round = 0
|
|
||||||
pv.LastSignState.Step = 0
|
|
||||||
pv.LastSignState.Signature = sig
|
|
||||||
pv.LastSignState.SignBytes = nil
|
|
||||||
pv.Save()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignVote signs a canonical representation of the vote, along with the
|
// SignVote signs a canonical representation of the vote, along with the
|
||||||
@ -213,59 +238,57 @@ func (pv *FilePV) SignProposal(chainID string, proposal *types.Proposal) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns error if HRS regression or no LastSignBytes. returns true if HRS is unchanged
|
// Save persists the FilePV to disk.
|
||||||
func (pv *FilePV) checkHRS(height int64, round int, step int8) (bool, error) {
|
func (pv *FilePV) Save() {
|
||||||
lss := pv.LastSignState
|
pv.Key.Save()
|
||||||
|
pv.LastSignState.Save()
|
||||||
if lss.Height > height {
|
|
||||||
return false, errors.New("Height regression")
|
|
||||||
}
|
|
||||||
|
|
||||||
if lss.Height == height {
|
|
||||||
if lss.Round > round {
|
|
||||||
return false, errors.New("Round regression")
|
|
||||||
}
|
|
||||||
|
|
||||||
if lss.Round == round {
|
|
||||||
if lss.Step > step {
|
|
||||||
return false, errors.New("Step regression")
|
|
||||||
} else if lss.Step == step {
|
|
||||||
if lss.SignBytes != nil {
|
|
||||||
if lss.Signature == nil {
|
|
||||||
panic("pv: LastSignature is nil but LastSignBytes is not!")
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, errors.New("No LastSignature found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset resets all fields in the FilePV.
|
||||||
|
// NOTE: Unsafe!
|
||||||
|
func (pv *FilePV) Reset() {
|
||||||
|
var sig []byte
|
||||||
|
pv.LastSignState.Height = 0
|
||||||
|
pv.LastSignState.Round = 0
|
||||||
|
pv.LastSignState.Step = 0
|
||||||
|
pv.LastSignState.Signature = sig
|
||||||
|
pv.LastSignState.SignBytes = nil
|
||||||
|
pv.Save()
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the FilePV.
|
||||||
|
func (pv *FilePV) String() string {
|
||||||
|
return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastSignState.Height, pv.LastSignState.Round, pv.LastSignState.Step)
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// signVote checks if the vote is good to sign and sets the vote signature.
|
// signVote checks if the vote is good to sign and sets the vote signature.
|
||||||
// It may need to set the timestamp as well if the vote is otherwise the same as
|
// It may need to set the timestamp as well if the vote is otherwise the same as
|
||||||
// a previously signed vote (ie. we crashed after signing but before the vote hit the WAL).
|
// a previously signed vote (ie. we crashed after signing but before the vote hit the WAL).
|
||||||
func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
|
func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
|
||||||
height, round, step := vote.Height, vote.Round, voteToStep(vote)
|
height, round, step := vote.Height, vote.Round, voteToStep(vote)
|
||||||
signBytes := vote.SignBytes(chainID)
|
|
||||||
|
|
||||||
sameHRS, err := pv.checkHRS(height, round, step)
|
lss := pv.LastSignState
|
||||||
|
|
||||||
|
sameHRS, err := lss.CheckHRS(height, round, step)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signBytes := vote.SignBytes(chainID)
|
||||||
|
|
||||||
// We might crash before writing to the wal,
|
// We might crash before writing to the wal,
|
||||||
// causing us to try to re-sign for the same HRS.
|
// causing us to try to re-sign for the same HRS.
|
||||||
// If signbytes are the same, use the last signature.
|
// If signbytes are the same, use the last signature.
|
||||||
// If they only differ by timestamp, use last timestamp and signature
|
// If they only differ by timestamp, use last timestamp and signature
|
||||||
// Otherwise, return error
|
// Otherwise, return error
|
||||||
if sameHRS {
|
if sameHRS {
|
||||||
if bytes.Equal(signBytes, pv.LastSignState.SignBytes) {
|
if bytes.Equal(signBytes, lss.SignBytes) {
|
||||||
vote.Signature = pv.LastSignState.Signature
|
vote.Signature = lss.Signature
|
||||||
} else if timestamp, ok := checkVotesOnlyDifferByTimestamp(pv.LastSignState.SignBytes, signBytes); ok {
|
} else if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok {
|
||||||
vote.Timestamp = timestamp
|
vote.Timestamp = timestamp
|
||||||
vote.Signature = pv.LastSignState.Signature
|
vote.Signature = lss.Signature
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("Conflicting data")
|
err = fmt.Errorf("Conflicting data")
|
||||||
}
|
}
|
||||||
@ -287,24 +310,27 @@ func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
|
|||||||
// a previously signed proposal ie. we crashed after signing but before the proposal hit the WAL).
|
// a previously signed proposal ie. we crashed after signing but before the proposal hit the WAL).
|
||||||
func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error {
|
func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error {
|
||||||
height, round, step := proposal.Height, proposal.Round, stepPropose
|
height, round, step := proposal.Height, proposal.Round, stepPropose
|
||||||
signBytes := proposal.SignBytes(chainID)
|
|
||||||
|
|
||||||
sameHRS, err := pv.checkHRS(height, round, step)
|
lss := pv.LastSignState
|
||||||
|
|
||||||
|
sameHRS, err := lss.CheckHRS(height, round, step)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signBytes := proposal.SignBytes(chainID)
|
||||||
|
|
||||||
// We might crash before writing to the wal,
|
// We might crash before writing to the wal,
|
||||||
// causing us to try to re-sign for the same HRS.
|
// causing us to try to re-sign for the same HRS.
|
||||||
// If signbytes are the same, use the last signature.
|
// If signbytes are the same, use the last signature.
|
||||||
// If they only differ by timestamp, use last timestamp and signature
|
// If they only differ by timestamp, use last timestamp and signature
|
||||||
// Otherwise, return error
|
// Otherwise, return error
|
||||||
if sameHRS {
|
if sameHRS {
|
||||||
if bytes.Equal(signBytes, pv.LastSignState.SignBytes) {
|
if bytes.Equal(signBytes, lss.SignBytes) {
|
||||||
proposal.Signature = pv.LastSignState.Signature
|
proposal.Signature = lss.Signature
|
||||||
} else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(pv.LastSignState.SignBytes, signBytes); ok {
|
} else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok {
|
||||||
proposal.Timestamp = timestamp
|
proposal.Timestamp = timestamp
|
||||||
proposal.Signature = pv.LastSignState.Signature
|
proposal.Signature = lss.Signature
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("Conflicting data")
|
err = fmt.Errorf("Conflicting data")
|
||||||
}
|
}
|
||||||
@ -330,15 +356,10 @@ func (pv *FilePV) saveSigned(height int64, round int, step int8,
|
|||||||
pv.LastSignState.Step = step
|
pv.LastSignState.Step = step
|
||||||
pv.LastSignState.Signature = sig
|
pv.LastSignState.Signature = sig
|
||||||
pv.LastSignState.SignBytes = signBytes
|
pv.LastSignState.SignBytes = signBytes
|
||||||
pv.saveState()
|
pv.LastSignState.Save()
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a string representation of the FilePV.
|
//-----------------------------------------------------------------------------------------
|
||||||
func (pv *FilePV) String() string {
|
|
||||||
return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastSignState.Height, pv.LastSignState.Round, pv.LastSignState.Step)
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------
|
|
||||||
|
|
||||||
// returns the timestamp from the lastSignBytes.
|
// returns the timestamp from the lastSignBytes.
|
||||||
// returns true if the only difference in the votes is their timestamp.
|
// returns true if the only difference in the votes is their timestamp.
|
||||||
|
Reference in New Issue
Block a user