tendermint/types/priv_validator.go

323 lines
8.6 KiB
Go
Raw Normal View History

package types
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"sync"
2017-05-02 00:53:32 -07:00
crypto "github.com/tendermint/go-crypto"
2017-04-21 15:13:25 -07:00
data "github.com/tendermint/go-wire/data"
2017-05-02 00:53:32 -07:00
. "github.com/tendermint/tmlibs/common"
)
2017-09-11 12:45:12 -07:00
// TODO: type ?
const (
stepNone = 0 // Used to distinguish the initial state
stepPropose = 1
stepPrevote = 2
stepPrecommit = 3
)
func voteToStep(vote *Vote) int8 {
switch vote.Type {
case VoteTypePrevote:
return stepPrevote
case VoteTypePrecommit:
return stepPrecommit
default:
2015-07-19 16:42:52 -07:00
PanicSanity("Unknown vote type")
return 0
}
}
// Signer is an interface that defines how to sign votes.
2015-11-04 16:09:43 -08:00
// It is the caller's duty to verify the msg before calling Sign,
// eg. to avoid double signing.
// Currently, the only callers are SignVote and SignProposal.
2015-11-04 16:09:43 -08:00
type Signer interface {
PubKey() crypto.PubKey
2017-09-15 22:07:04 -07:00
Sign(msg []byte) (crypto.Signature, error)
2015-11-04 16:09:43 -08:00
}
// DefaultSigner implements Signer.
// It uses a standard crypto.PrivKey.
2015-11-04 16:09:43 -08:00
type DefaultSigner struct {
2017-09-18 15:12:31 -07:00
PrivKey crypto.PrivKey `json:"priv_key"`
2015-11-04 16:09:43 -08:00
}
// NewDefaultSigner returns an instance of DefaultSigner.
func NewDefaultSigner(priv crypto.PrivKey) *DefaultSigner {
2017-09-18 15:12:31 -07:00
return &DefaultSigner{PrivKey: priv}
2015-11-04 16:09:43 -08:00
}
// Sign implements Signer. It signs the byte slice with a private key.
2017-09-15 22:07:04 -07:00
func (ds *DefaultSigner) Sign(msg []byte) (crypto.Signature, error) {
2017-09-18 15:12:31 -07:00
return ds.PrivKey.Sign(msg), nil
2015-11-04 16:09:43 -08:00
}
// PubKey implements Signer. It should return the public key that corresponds
// to the private key used for signing.
func (ds *DefaultSigner) PubKey() crypto.PubKey {
2017-09-18 15:12:31 -07:00
return ds.PrivKey.PubKey()
}
type PrivValidator interface {
Address() data.Bytes // redundant since .PubKey().Address()
PubKey() crypto.PubKey
SignVote(chainID string, vote *Vote) error
SignProposal(chainID string, proposal *Proposal) error
SignHeartbeat(chainID string, heartbeat *Heartbeat) error
Reset()
SetFile(file string)
Save()
}
// DefaultPrivValidator implements the functionality for signing blocks.
type DefaultPrivValidator struct {
Info PrivValidatorInfo `json:"info"`
Signer *DefaultSigner `json:"signer"`
// For persistence.
// Overloaded for testing.
filePath string
mtx sync.Mutex
}
func (pv *DefaultPrivValidator) Address() data.Bytes {
return pv.Info.Address
}
2017-09-18 15:12:31 -07:00
func (pv *DefaultPrivValidator) PubKey() crypto.PubKey {
return pv.Info.PubKey
}
type PrivValidatorInfo struct {
Address data.Bytes `json:"address"`
PubKey crypto.PubKey `json:"pub_key"`
LastHeight int `json:"last_height"`
LastRound int `json:"last_round"`
LastStep int8 `json:"last_step"`
LastSignature crypto.Signature `json:"last_signature,omitempty"` // so we dont lose signatures
LastSignBytes data.Bytes `json:"last_signbytes,omitempty"` // so we dont lose signatures
2015-11-04 16:09:43 -08:00
}
2017-09-18 15:12:31 -07:00
func LoadOrGenPrivValidator(filePath string) *DefaultPrivValidator {
var privValidator *DefaultPrivValidator
if _, err := os.Stat(filePath); err == nil {
privValidator = LoadPrivValidator(filePath)
} else {
privValidator = GenPrivValidator()
privValidator.SetFile(filePath)
privValidator.Save()
}
return privValidator
}
2017-09-18 15:12:31 -07:00
func LoadPrivValidator(filePath string) *DefaultPrivValidator {
privValJSONBytes, err := ioutil.ReadFile(filePath)
if err != nil {
2015-06-16 21:16:58 -07:00
Exit(err.Error())
}
2017-09-18 15:12:31 -07:00
privVal := DefaultPrivValidator{}
err = json.Unmarshal(privValJSONBytes, &privVal)
if err != nil {
Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err))
}
privVal.filePath = filePath
return &privVal
}
// Generates a new validator with private key.
2017-09-18 15:12:31 -07:00
func GenPrivValidator() *DefaultPrivValidator {
privKey := crypto.GenPrivKeyEd25519().Wrap()
pubKey := privKey.PubKey()
2017-09-18 15:12:31 -07:00
return &DefaultPrivValidator{
Info: PrivValidatorInfo{
Address: pubKey.Address(),
PubKey: pubKey,
LastStep: stepNone,
},
Signer: NewDefaultSigner(privKey),
2017-09-18 15:12:31 -07:00
filePath: "",
}
}
// LoadPrivValidatorWithSigner instantiates a private validator with a custom
// signer object. Tendermint tracks state in the PrivValidator that might be
// saved to disk. Please supply a filepath where Tendermint can save the
// private validator.
2017-09-18 15:12:31 -07:00
func LoadPrivValidatorWithSigner(signer *DefaultSigner, filePath string) *DefaultPrivValidator {
return &DefaultPrivValidator{
Info: PrivValidatorInfo{
Address: signer.PubKey().Address(),
PubKey: signer.PubKey(),
LastStep: stepNone,
},
Signer: signer,
2017-09-18 15:12:31 -07:00
filePath: filePath,
}
}
// Overwrite address and pubkey for convenience
2017-09-18 15:12:31 -07:00
/*func (privVal *DefaultPrivValidator) setPubKeyAndAddress() {
privVal.PubKey = privVal.Signer.PubKey()
privVal.Address = privVal.PubKey.Address()
2017-09-18 15:12:31 -07:00
}*/
2017-09-18 15:12:31 -07:00
func (privVal *DefaultPrivValidator) SetFile(filePath string) {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
privVal.filePath = filePath
}
2017-09-18 15:12:31 -07:00
func (privVal *DefaultPrivValidator) Save() {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
privVal.save()
}
2017-09-18 15:12:31 -07:00
func (privVal *DefaultPrivValidator) save() {
if privVal.filePath == "" {
2015-07-19 16:42:52 -07:00
PanicSanity("Cannot save PrivValidator: filePath not set")
}
jsonBytes, err := json.Marshal(privVal)
if err != nil {
// `@; BOOM!!!
PanicCrisis(err)
}
err = WriteFileAtomic(privVal.filePath, jsonBytes, 0600)
if err != nil {
// `@; BOOM!!!
2015-07-19 16:42:52 -07:00
PanicCrisis(err)
}
}
// NOTE: Unsafe!
2017-09-18 15:12:31 -07:00
func (privVal *DefaultPrivValidator) Reset() {
privVal.Info.LastHeight = 0
privVal.Info.LastRound = 0
privVal.Info.LastStep = 0
privVal.Info.LastSignature = crypto.Signature{}
privVal.Info.LastSignBytes = nil
privVal.Save()
}
2017-09-18 15:12:31 -07:00
func (privVal *DefaultPrivValidator) GetAddress() []byte {
return privVal.Address()
2016-06-26 12:33:11 -07:00
}
2017-09-18 15:12:31 -07:00
func (privVal *DefaultPrivValidator) SignVote(chainID string, vote *Vote) error {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
signature, err := privVal.signBytesHRS(vote.Height, vote.Round, voteToStep(vote), SignBytes(chainID, vote))
if err != nil {
return errors.New(Fmt("Error signing vote: %v", err))
}
2016-12-17 21:10:14 -08:00
vote.Signature = signature
return nil
}
2017-09-18 15:12:31 -07:00
func (privVal *DefaultPrivValidator) SignProposal(chainID string, proposal *Proposal) error {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
signature, err := privVal.signBytesHRS(proposal.Height, proposal.Round, stepPropose, SignBytes(chainID, proposal))
if err != nil {
2017-05-12 14:07:53 -07:00
return fmt.Errorf("Error signing proposal: %v", err)
}
2016-12-17 21:10:14 -08:00
proposal.Signature = signature
return nil
}
// check if there's a regression. Else sign and write the hrs+signature to disk
2017-09-18 15:12:31 -07:00
func (privVal *DefaultPrivValidator) signBytesHRS(height, round int, step int8, signBytes []byte) (crypto.Signature, error) {
2017-03-21 14:12:02 -07:00
sig := crypto.Signature{}
2017-09-18 15:12:31 -07:00
info := privVal.Info
// If height regression, err
2017-09-18 15:12:31 -07:00
if info.LastHeight > height {
2017-03-21 14:12:02 -07:00
return sig, errors.New("Height regression")
2014-12-17 01:37:13 -08:00
}
// More cases for when the height matches
2017-09-18 15:12:31 -07:00
if info.LastHeight == height {
// If round regression, err
2017-09-18 15:12:31 -07:00
if info.LastRound > round {
2017-03-21 14:12:02 -07:00
return sig, errors.New("Round regression")
2014-12-17 01:37:13 -08:00
}
// If step regression, err
2017-09-18 15:12:31 -07:00
if info.LastRound == round {
if info.LastStep > step {
2017-03-21 14:12:02 -07:00
return sig, errors.New("Step regression")
2017-09-18 15:12:31 -07:00
} else if info.LastStep == step {
if info.LastSignBytes != nil {
if info.LastSignature.Empty() {
PanicSanity("privVal: LastSignature is nil but LastSignBytes is not!")
}
// so we dont sign a conflicting vote or proposal
// NOTE: proposals are non-deterministic (include time),
// so we can actually lose them, but will still never sign conflicting ones
2017-09-18 15:12:31 -07:00
if bytes.Equal(info.LastSignBytes, signBytes) {
// log.Notice("Using info.LastSignature", "sig", info.LastSignature)
return info.LastSignature, nil
}
}
2017-03-21 14:12:02 -07:00
return sig, errors.New("Step regression")
}
2014-12-17 01:37:13 -08:00
}
}
2014-12-17 01:37:13 -08:00
// Sign
2017-09-18 15:12:31 -07:00
sig, err := privVal.Signer.Sign(signBytes)
2017-09-15 22:07:04 -07:00
if err != nil {
return sig, err
}
2014-12-17 01:37:13 -08:00
// Persist height/round/step
2017-09-18 15:12:31 -07:00
privVal.Info.LastHeight = height
privVal.Info.LastRound = round
privVal.Info.LastStep = step
privVal.Info.LastSignature = sig
privVal.Info.LastSignBytes = signBytes
privVal.save()
2014-12-17 01:37:13 -08:00
2017-03-21 14:12:02 -07:00
return sig, nil
2016-06-26 12:33:11 -07:00
}
2017-09-18 15:12:31 -07:00
func (privVal *DefaultPrivValidator) SignHeartbeat(chainID string, heartbeat *Heartbeat) error {
2017-07-29 11:15:10 -07:00
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
2017-09-15 22:07:04 -07:00
var err error
2017-09-18 15:12:31 -07:00
heartbeat.Signature, err = privVal.Signer.Sign(SignBytes(chainID, heartbeat))
2017-09-15 22:07:04 -07:00
return err
2017-07-29 11:15:10 -07:00
}
2017-09-18 15:12:31 -07:00
func (privVal *DefaultPrivValidator) String() string {
info := privVal.Info
return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", info.Address, info.LastHeight, info.LastRound, info.LastStep)
2014-12-17 01:37:13 -08:00
}
//-------------------------------------
2017-09-18 15:12:31 -07:00
type PrivValidatorsByAddress []*DefaultPrivValidator
func (pvs PrivValidatorsByAddress) Len() int {
return len(pvs)
}
func (pvs PrivValidatorsByAddress) Less(i, j int) bool {
2017-09-18 15:12:31 -07:00
return bytes.Compare(pvs[i].Info.Address, pvs[j].Info.Address) == -1
}
func (pvs PrivValidatorsByAddress) Swap(i, j int) {
it := pvs[i]
pvs[i] = pvs[j]
pvs[j] = it
}