|
|
@ -32,14 +32,14 @@ const ( |
|
|
|
) |
|
|
|
|
|
|
|
// A vote is either stepPrevote or stepPrecommit.
|
|
|
|
func voteToStep(vote *tmproto.Vote) int8 { |
|
|
|
func voteToStep(vote *tmproto.Vote) (int8, error) { |
|
|
|
switch vote.Type { |
|
|
|
case tmproto.PrevoteType: |
|
|
|
return stepPrevote |
|
|
|
return stepPrevote, nil |
|
|
|
case tmproto.PrecommitType: |
|
|
|
return stepPrecommit |
|
|
|
return stepPrecommit, nil |
|
|
|
default: |
|
|
|
panic(fmt.Sprintf("Unknown vote type: %v", vote.Type)) |
|
|
|
return 0, fmt.Errorf("unknown vote type: %v", vote.Type) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -55,21 +55,17 @@ type FilePVKey struct { |
|
|
|
} |
|
|
|
|
|
|
|
// Save persists the FilePVKey to its filePath.
|
|
|
|
func (pvKey FilePVKey) Save() { |
|
|
|
func (pvKey FilePVKey) Save() error { |
|
|
|
outFile := pvKey.filePath |
|
|
|
if outFile == "" { |
|
|
|
panic("cannot save PrivValidator key: filePath not set") |
|
|
|
return errors.New("cannot save PrivValidator key: filePath not set") |
|
|
|
} |
|
|
|
|
|
|
|
jsonBytes, err := tmjson.MarshalIndent(pvKey, "", " ") |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
err = tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
return tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) |
|
|
|
} |
|
|
|
|
|
|
|
//-------------------------------------------------------------------------------
|
|
|
@ -127,19 +123,16 @@ func (lss *FilePVLastSignState) CheckHRS(height int64, round int32, step int8) ( |
|
|
|
} |
|
|
|
|
|
|
|
// Save persists the FilePvLastSignState to its filePath.
|
|
|
|
func (lss *FilePVLastSignState) Save() { |
|
|
|
func (lss *FilePVLastSignState) Save() error { |
|
|
|
outFile := lss.filePath |
|
|
|
if outFile == "" { |
|
|
|
panic("cannot save FilePVLastSignState: filePath not set") |
|
|
|
return errors.New("cannot save FilePVLastSignState: filePath not set") |
|
|
|
} |
|
|
|
jsonBytes, err := tmjson.MarshalIndent(lss, "", " ") |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
err = tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
return err |
|
|
|
} |
|
|
|
return tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) |
|
|
|
} |
|
|
|
|
|
|
|
//-------------------------------------------------------------------------------
|
|
|
@ -239,17 +232,23 @@ func loadFilePV(keyFilePath, stateFilePath string, loadState bool) (*FilePV, err |
|
|
|
// LoadOrGenFilePV loads a FilePV from the given filePaths
|
|
|
|
// or else generates a new one and saves it to the filePaths.
|
|
|
|
func LoadOrGenFilePV(keyFilePath, stateFilePath string) (*FilePV, error) { |
|
|
|
var ( |
|
|
|
pv *FilePV |
|
|
|
err error |
|
|
|
) |
|
|
|
if tmos.FileExists(keyFilePath) { |
|
|
|
pv, err = LoadFilePV(keyFilePath, stateFilePath) |
|
|
|
} else { |
|
|
|
pv, err = GenFilePV(keyFilePath, stateFilePath, "") |
|
|
|
pv.Save() |
|
|
|
pv, err := LoadFilePV(keyFilePath, stateFilePath) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
return pv, nil |
|
|
|
} |
|
|
|
pv, err := GenFilePV(keyFilePath, stateFilePath, "") |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
if err := pv.Save(); err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
return pv, err |
|
|
|
|
|
|
|
return pv, nil |
|
|
|
} |
|
|
|
|
|
|
|
// GetAddress returns the address of the validator.
|
|
|
@ -283,21 +282,23 @@ func (pv *FilePV) SignProposal(ctx context.Context, chainID string, proposal *tm |
|
|
|
} |
|
|
|
|
|
|
|
// Save persists the FilePV to disk.
|
|
|
|
func (pv *FilePV) Save() { |
|
|
|
pv.Key.Save() |
|
|
|
pv.LastSignState.Save() |
|
|
|
func (pv *FilePV) Save() error { |
|
|
|
if err := pv.Key.Save(); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
return pv.LastSignState.Save() |
|
|
|
} |
|
|
|
|
|
|
|
// Reset resets all fields in the FilePV.
|
|
|
|
// NOTE: Unsafe!
|
|
|
|
func (pv *FilePV) Reset() { |
|
|
|
func (pv *FilePV) Reset() error { |
|
|
|
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() |
|
|
|
return pv.Save() |
|
|
|
} |
|
|
|
|
|
|
|
// String returns a string representation of the FilePV.
|
|
|
@ -317,8 +318,13 @@ func (pv *FilePV) String() string { |
|
|
|
// 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).
|
|
|
|
func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error { |
|
|
|
height, round, step := vote.Height, vote.Round, voteToStep(vote) |
|
|
|
step, err := voteToStep(vote) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
height := vote.Height |
|
|
|
round := vote.Round |
|
|
|
lss := pv.LastSignState |
|
|
|
|
|
|
|
sameHRS, err := lss.CheckHRS(height, round, step) |
|
|
@ -336,13 +342,19 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error { |
|
|
|
if sameHRS { |
|
|
|
if bytes.Equal(signBytes, lss.SignBytes) { |
|
|
|
vote.Signature = lss.Signature |
|
|
|
} else if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { |
|
|
|
} else { |
|
|
|
timestamp, ok, err := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
if !ok { |
|
|
|
return errors.New("conflicting data") |
|
|
|
} |
|
|
|
|
|
|
|
vote.Timestamp = timestamp |
|
|
|
vote.Signature = lss.Signature |
|
|
|
} else { |
|
|
|
err = fmt.Errorf("conflicting data") |
|
|
|
return nil |
|
|
|
} |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
// It passed the checks. Sign the vote
|
|
|
@ -350,7 +362,9 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
pv.saveSigned(height, round, step, signBytes, sig) |
|
|
|
if err := pv.saveSigned(height, round, step, signBytes, sig); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
vote.Signature = sig |
|
|
|
return nil |
|
|
|
} |
|
|
@ -378,13 +392,18 @@ func (pv *FilePV) signProposal(chainID string, proposal *tmproto.Proposal) error |
|
|
|
if sameHRS { |
|
|
|
if bytes.Equal(signBytes, lss.SignBytes) { |
|
|
|
proposal.Signature = lss.Signature |
|
|
|
} else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { |
|
|
|
} else { |
|
|
|
timestamp, ok, err := checkProposalsOnlyDifferByTimestamp(lss.SignBytes, signBytes) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
if !ok { |
|
|
|
return errors.New("conflicting data") |
|
|
|
} |
|
|
|
proposal.Timestamp = timestamp |
|
|
|
proposal.Signature = lss.Signature |
|
|
|
} else { |
|
|
|
err = fmt.Errorf("conflicting data") |
|
|
|
return nil |
|
|
|
} |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
// It passed the checks. Sign the proposal
|
|
|
@ -392,34 +411,34 @@ func (pv *FilePV) signProposal(chainID string, proposal *tmproto.Proposal) error |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
pv.saveSigned(height, round, step, signBytes, sig) |
|
|
|
if err := pv.saveSigned(height, round, step, signBytes, sig); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
proposal.Signature = sig |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
// Persist height/round/step and signature
|
|
|
|
func (pv *FilePV) saveSigned(height int64, round int32, step int8, |
|
|
|
signBytes []byte, sig []byte) { |
|
|
|
|
|
|
|
func (pv *FilePV) saveSigned(height int64, round int32, step int8, signBytes []byte, sig []byte) error { |
|
|
|
pv.LastSignState.Height = height |
|
|
|
pv.LastSignState.Round = round |
|
|
|
pv.LastSignState.Step = step |
|
|
|
pv.LastSignState.Signature = sig |
|
|
|
pv.LastSignState.SignBytes = signBytes |
|
|
|
pv.LastSignState.Save() |
|
|
|
return pv.LastSignState.Save() |
|
|
|
} |
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// returns the timestamp from the lastSignBytes.
|
|
|
|
// returns true if the only difference in the votes is their timestamp.
|
|
|
|
func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) { |
|
|
|
func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool, error) { |
|
|
|
var lastVote, newVote tmproto.CanonicalVote |
|
|
|
if err := protoio.UnmarshalDelimited(lastSignBytes, &lastVote); err != nil { |
|
|
|
panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into vote: %v", err)) |
|
|
|
return time.Time{}, false, fmt.Errorf("LastSignBytes cannot be unmarshalled into vote: %v", err) |
|
|
|
} |
|
|
|
if err := protoio.UnmarshalDelimited(newSignBytes, &newVote); err != nil { |
|
|
|
panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err)) |
|
|
|
return time.Time{}, false, fmt.Errorf("signBytes cannot be unmarshalled into vote: %v", err) |
|
|
|
} |
|
|
|
|
|
|
|
lastTime := lastVote.Timestamp |
|
|
@ -428,18 +447,18 @@ func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.T |
|
|
|
lastVote.Timestamp = now |
|
|
|
newVote.Timestamp = now |
|
|
|
|
|
|
|
return lastTime, proto.Equal(&newVote, &lastVote) |
|
|
|
return lastTime, proto.Equal(&newVote, &lastVote), nil |
|
|
|
} |
|
|
|
|
|
|
|
// returns the timestamp from the lastSignBytes.
|
|
|
|
// returns true if the only difference in the proposals is their timestamp
|
|
|
|
func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) { |
|
|
|
func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool, error) { |
|
|
|
var lastProposal, newProposal tmproto.CanonicalProposal |
|
|
|
if err := protoio.UnmarshalDelimited(lastSignBytes, &lastProposal); err != nil { |
|
|
|
panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into proposal: %v", err)) |
|
|
|
return time.Time{}, false, fmt.Errorf("LastSignBytes cannot be unmarshalled into proposal: %v", err) |
|
|
|
} |
|
|
|
if err := protoio.UnmarshalDelimited(newSignBytes, &newProposal); err != nil { |
|
|
|
panic(fmt.Sprintf("signBytes cannot be unmarshalled into proposal: %v", err)) |
|
|
|
return time.Time{}, false, fmt.Errorf("signBytes cannot be unmarshalled into proposal: %v", err) |
|
|
|
} |
|
|
|
|
|
|
|
lastTime := lastProposal.Timestamp |
|
|
@ -448,5 +467,5 @@ func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (ti |
|
|
|
lastProposal.Timestamp = now |
|
|
|
newProposal.Timestamp = now |
|
|
|
|
|
|
|
return lastTime, proto.Equal(&newProposal, &lastProposal) |
|
|
|
return lastTime, proto.Equal(&newProposal, &lastProposal), nil |
|
|
|
} |