more PrivValidator interface

This commit is contained in:
Ethan Buchman
2017-09-18 22:05:33 -04:00
parent fd1b0b997a
commit 944ebccfe9
11 changed files with 138 additions and 143 deletions

View File

@ -18,7 +18,7 @@ var GenValidatorCmd = &cobra.Command{
} }
func genValidator(cmd *cobra.Command, args []string) { func genValidator(cmd *cobra.Command, args []string) {
privValidator := types.GenPrivValidator() privValidator := types.GenPrivValidatorFS("")
privValidatorJSONBytes, _ := json.MarshalIndent(privValidator, "", "\t") privValidatorJSONBytes, _ := json.MarshalIndent(privValidator, "", "\t")
fmt.Printf(`%v fmt.Printf(`%v
`, string(privValidatorJSONBytes)) `, string(privValidatorJSONBytes))

View File

@ -19,8 +19,7 @@ var InitFilesCmd = &cobra.Command{
func initFiles(cmd *cobra.Command, args []string) { func initFiles(cmd *cobra.Command, args []string) {
privValFile := config.PrivValidatorFile() privValFile := config.PrivValidatorFile()
if _, err := os.Stat(privValFile); os.IsNotExist(err) { if _, err := os.Stat(privValFile); os.IsNotExist(err) {
privValidator := types.GenPrivValidator() privValidator := types.GenPrivValidatorFS(privValFile)
privValidator.SetFile(privValFile)
privValidator.Save() privValidator.Save()
genFile := config.GenesisFile() genFile := config.GenesisFile()

View File

@ -46,14 +46,12 @@ func resetPrivValidator(cmd *cobra.Command, args []string) {
func resetPrivValidatorLocal(privValFile string, logger log.Logger) { func resetPrivValidatorLocal(privValFile string, logger log.Logger) {
// Get PrivValidator // Get PrivValidator
var privValidator types.PrivValidator
if _, err := os.Stat(privValFile); err == nil { if _, err := os.Stat(privValFile); err == nil {
privValidator = types.LoadPrivValidator(privValFile) privValidator := types.LoadPrivValidatorFS(privValFile)
privValidator.Reset() privValidator.Reset()
logger.Info("Reset PrivValidator", "file", privValFile) logger.Info("Reset PrivValidator", "file", privValFile)
} else { } else {
privValidator = types.GenPrivValidator() privValidator := types.GenPrivValidatorFS(privValFile)
privValidator.SetFile(privValFile)
privValidator.Save() privValidator.Save()
logger.Info("Generated PrivValidator", "file", privValFile) logger.Info("Generated PrivValidator", "file", privValFile)
} }

View File

@ -44,7 +44,7 @@ func AddNodeFlags(cmd *cobra.Command) {
type FuncSignerAndApp func(*cfg.Config) (types.PrivValidator, proxy.ClientCreator) type FuncSignerAndApp func(*cfg.Config) (types.PrivValidator, proxy.ClientCreator)
func DefaultSignerAndApp(config *cfg.Config) (types.PrivValidator, proxy.ClientCreator) { func DefaultSignerAndApp(config *cfg.Config) (types.PrivValidator, proxy.ClientCreator) {
privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile()) privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile())
clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir())
return privValidator, clientCreator return privValidator, clientCreator
} }

View File

@ -17,7 +17,7 @@ var ShowValidatorCmd = &cobra.Command{
} }
func showValidator(cmd *cobra.Command, args []string) { func showValidator(cmd *cobra.Command, args []string) {
privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile()) privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile())
pubKeyJSONBytes, _ := data.ToJSON(privValidator.PubKey) pubKeyJSONBytes, _ := data.ToJSON(privValidator.PubKey)
fmt.Println(string(pubKeyJSONBytes)) fmt.Println(string(pubKeyJSONBytes))
} }

View File

@ -45,7 +45,7 @@ func testnetFiles(cmd *cobra.Command, args []string) {
} }
// Read priv_validator.json to populate vals // Read priv_validator.json to populate vals
privValFile := path.Join(dataDir, mach, "priv_validator.json") privValFile := path.Join(dataDir, mach, "priv_validator.json")
privVal := types.LoadPrivValidator(privValFile) privVal := types.LoadPrivValidatorFS(privValFile)
genVals[i] = types.GenesisValidator{ genVals[i] = types.GenesisValidator{
PubKey: privVal.PubKey(), PubKey: privVal.PubKey(),
Power: 1, Power: 1,
@ -87,7 +87,6 @@ func ensurePrivValidator(file string) {
if cmn.FileExists(file) { if cmn.FileExists(file) {
return return
} }
privValidator := types.GenPrivValidator() privValidator := types.GenPrivValidatorFS(file)
privValidator.SetFile(file)
privValidator.Save() privValidator.Save()
} }

View File

@ -368,9 +368,8 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
if i < nValidators { if i < nValidators {
privVal = privVals[i] privVal = privVals[i]
} else { } else {
privVal = types.GenPrivValidator()
_, tempFilePath := Tempfile("priv_validator_") _, tempFilePath := Tempfile("priv_validator_")
privVal.SetFile(tempFilePath) privVal = types.GenPrivValidator(tempFilePath)
} }
css[i] = newConsensusStateWithConfig(thisConfig, state, privVal, appFunc()) css[i] = newConsensusStateWithConfig(thisConfig, state, privVal, appFunc())

View File

@ -60,7 +60,7 @@ type Node struct {
func NewNodeDefault(config *cfg.Config, logger log.Logger) *Node { func NewNodeDefault(config *cfg.Config, logger log.Logger) *Node {
// Get PrivValidator // Get PrivValidator
privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile()) privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile())
return NewNode(config, privValidator, return NewNode(config, privValidator,
proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger)
} }
@ -125,7 +125,7 @@ func NewNode(config *cfg.Config, privValidator types.PrivValidator, clientCreato
} }
// Log whether this node is a validator or an observer // Log whether this node is a validator or an observer
if state.Validators.HasAddress(privValidator.PubKey().Address()) { if state.Validators.HasAddress(privValidator.Address()) {
consensusLogger.Info("This node is a validator") consensusLogger.Info("This node is a validator")
} else { } else {
consensusLogger.Info("This node is not a validator") consensusLogger.Info("This node is not a validator")

View File

@ -34,37 +34,7 @@ func voteToStep(vote *Vote) int8 {
} }
} }
// Signer is an interface that defines how to sign votes. // PrivValidator defines the functionality of a local Tendermint validator.
// 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.
type Signer interface {
PubKey() crypto.PubKey
Sign(msg []byte) (crypto.Signature, error)
}
// DefaultSigner implements Signer.
// It uses a standard crypto.PrivKey.
type DefaultSigner struct {
PrivKey crypto.PrivKey `json:"priv_key"`
}
// NewDefaultSigner returns an instance of DefaultSigner.
func NewDefaultSigner(priv crypto.PrivKey) *DefaultSigner {
return &DefaultSigner{PrivKey: priv}
}
// Sign implements Signer. It signs the byte slice with a private key.
func (ds *DefaultSigner) Sign(msg []byte) (crypto.Signature, error) {
return ds.PrivKey.Sign(msg), nil
}
// PubKey implements Signer. It should return the public key that corresponds
// to the private key used for signing.
func (ds *DefaultSigner) PubKey() crypto.PubKey {
return ds.PrivKey.PubKey()
}
type PrivValidator interface { type PrivValidator interface {
Address() data.Bytes // redundant since .PubKey().Address() Address() data.Bytes // redundant since .PubKey().Address()
PubKey() crypto.PubKey PubKey() crypto.PubKey
@ -72,60 +42,45 @@ type PrivValidator interface {
SignVote(chainID string, vote *Vote) error SignVote(chainID string, vote *Vote) error
SignProposal(chainID string, proposal *Proposal) error SignProposal(chainID string, proposal *Proposal) error
SignHeartbeat(chainID string, heartbeat *Heartbeat) error SignHeartbeat(chainID string, heartbeat *Heartbeat) error
Reset()
SetFile(file string)
Save()
} }
// DefaultPrivValidator implements the functionality for signing blocks. // PrivValidatorFS implements PrivValidator using data persisted to disk
type DefaultPrivValidator struct { // to prevent double signing. The Signer itself can be mutated to use
Info PrivValidatorInfo `json:"info"` // something besides the default, for instance a hardware signer.
Signer *DefaultSigner `json:"signer"` type PrivValidatorFS struct {
ID ValidatorID `json:"id"`
Signer Signer `json:"signer"`
// mutable state to be persisted to disk
// after each signature to prevent double signing
mtx sync.Mutex
Info LastSignedInfo `json:"info"`
// For persistence. // For persistence.
// Overloaded for testing. // Overloaded for testing.
filePath string filePath string
mtx sync.Mutex
} }
func (pv *DefaultPrivValidator) Address() data.Bytes { // LoadOrGenPrivValidatorFS loads a PrivValidatorFS from the given filePath
return pv.Info.Address // or else generates a new one and saves it to the filePath.
} func LoadOrGenPrivValidatorFS(filePath string) *PrivValidatorFS {
var PrivValidatorFS *PrivValidatorFS
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
}
func LoadOrGenPrivValidator(filePath string) *DefaultPrivValidator {
var privValidator *DefaultPrivValidator
if _, err := os.Stat(filePath); err == nil { if _, err := os.Stat(filePath); err == nil {
privValidator = LoadPrivValidator(filePath) PrivValidatorFS = LoadPrivValidatorFS(filePath)
} else { } else {
privValidator = GenPrivValidator() PrivValidatorFS = GenPrivValidatorFS(filePath)
privValidator.SetFile(filePath) PrivValidatorFS.Save()
privValidator.Save()
} }
return privValidator return PrivValidatorFS
} }
func LoadPrivValidator(filePath string) *DefaultPrivValidator { // LoadPrivValidatorFS loads a PrivValidatorFS from the filePath.
func LoadPrivValidatorFS(filePath string) *PrivValidatorFS {
privValJSONBytes, err := ioutil.ReadFile(filePath) privValJSONBytes, err := ioutil.ReadFile(filePath)
if err != nil { if err != nil {
Exit(err.Error()) Exit(err.Error())
} }
privVal := DefaultPrivValidator{} privVal := PrivValidatorFS{}
err = json.Unmarshal(privValJSONBytes, &privVal) err = json.Unmarshal(privValJSONBytes, &privVal)
if err != nil { if err != nil {
Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err))
@ -135,56 +90,58 @@ func LoadPrivValidator(filePath string) *DefaultPrivValidator {
return &privVal return &privVal
} }
// Generates a new validator with private key. // GenPrivValidatorFS generates a new validator with randomly generated private key
func GenPrivValidator() *DefaultPrivValidator { // and sets the filePath, but does not call Save().
func GenPrivValidatorFS(filePath string) *PrivValidatorFS {
privKey := crypto.GenPrivKeyEd25519().Wrap() privKey := crypto.GenPrivKeyEd25519().Wrap()
pubKey := privKey.PubKey() return &PrivValidatorFS{
return &DefaultPrivValidator{ ID: ValidatorID{privKey.PubKey().Address(), privKey.PubKey()},
Info: PrivValidatorInfo{ Info: LastSignedInfo{
Address: pubKey.Address(),
PubKey: pubKey,
LastStep: stepNone, LastStep: stepNone,
}, },
Signer: NewDefaultSigner(privKey), Signer: NewDefaultSigner(privKey),
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.
func LoadPrivValidatorWithSigner(signer *DefaultSigner, filePath string) *DefaultPrivValidator {
return &DefaultPrivValidator{
Info: PrivValidatorInfo{
Address: signer.PubKey().Address(),
PubKey: signer.PubKey(),
LastStep: stepNone,
},
Signer: signer,
filePath: filePath, filePath: filePath,
} }
} }
// Overwrite address and pubkey for convenience // LoadPrivValidatorWithSigner loads a PrivValidatorFS with a custom
/*func (privVal *DefaultPrivValidator) setPubKeyAndAddress() { // signer object. The PrivValidatorFS handles double signing prevention by persisting
privVal.PubKey = privVal.Signer.PubKey() // data to the filePath, while the Signer handles the signing.
privVal.Address = privVal.PubKey.Address() // If the filePath does not exist, the PrivValidatorFS must be created manually and saved.
}*/ func LoadPrivValidatorFSWithSigner(filePath string, signerFunc func(ValidatorID) Signer) *PrivValidatorFS {
privValJSONBytes, err := ioutil.ReadFile(filePath)
if err != nil {
Exit(err.Error())
}
privVal := PrivValidatorFS{}
err = json.Unmarshal(privValJSONBytes, &privVal)
if err != nil {
Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err))
}
func (privVal *DefaultPrivValidator) SetFile(filePath string) {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
privVal.filePath = filePath privVal.filePath = filePath
privVal.Signer = signerFunc(privVal.ID)
return &privVal
} }
func (privVal *DefaultPrivValidator) Save() { // Address returns the address of the validator.
func (pv *PrivValidatorFS) Address() data.Bytes {
return pv.ID.Address
}
// PubKey returns the public key of the validator.
func (pv *PrivValidatorFS) PubKey() crypto.PubKey {
return pv.ID.PubKey
}
// Save persists the PrivValidatorFS to disk.
func (privVal *PrivValidatorFS) Save() {
privVal.mtx.Lock() privVal.mtx.Lock()
defer privVal.mtx.Unlock() defer privVal.mtx.Unlock()
privVal.save() privVal.save()
} }
func (privVal *DefaultPrivValidator) save() { func (privVal *PrivValidatorFS) save() {
if privVal.filePath == "" { if privVal.filePath == "" {
PanicSanity("Cannot save PrivValidator: filePath not set") PanicSanity("Cannot save PrivValidator: filePath not set")
} }
@ -200,8 +157,9 @@ func (privVal *DefaultPrivValidator) save() {
} }
} }
// Reset resets all fields in the PrivValidatorFS.Info.
// NOTE: Unsafe! // NOTE: Unsafe!
func (privVal *DefaultPrivValidator) Reset() { func (privVal *PrivValidatorFS) Reset() {
privVal.Info.LastHeight = 0 privVal.Info.LastHeight = 0
privVal.Info.LastRound = 0 privVal.Info.LastRound = 0
privVal.Info.LastStep = 0 privVal.Info.LastStep = 0
@ -210,11 +168,8 @@ func (privVal *DefaultPrivValidator) Reset() {
privVal.Save() privVal.Save()
} }
func (privVal *DefaultPrivValidator) GetAddress() []byte { // SignVote signs a canonical representation of the vote, along with the chainID.
return privVal.Address() func (privVal *PrivValidatorFS) SignVote(chainID string, vote *Vote) error {
}
func (privVal *DefaultPrivValidator) SignVote(chainID string, vote *Vote) error {
privVal.mtx.Lock() privVal.mtx.Lock()
defer privVal.mtx.Unlock() defer privVal.mtx.Unlock()
signature, err := privVal.signBytesHRS(vote.Height, vote.Round, voteToStep(vote), SignBytes(chainID, vote)) signature, err := privVal.signBytesHRS(vote.Height, vote.Round, voteToStep(vote), SignBytes(chainID, vote))
@ -225,7 +180,8 @@ func (privVal *DefaultPrivValidator) SignVote(chainID string, vote *Vote) error
return nil return nil
} }
func (privVal *DefaultPrivValidator) SignProposal(chainID string, proposal *Proposal) error { // SignProposal signs a canonical representation of the proposal, along with the chainID.
func (privVal *PrivValidatorFS) SignProposal(chainID string, proposal *Proposal) error {
privVal.mtx.Lock() privVal.mtx.Lock()
defer privVal.mtx.Unlock() defer privVal.mtx.Unlock()
signature, err := privVal.signBytesHRS(proposal.Height, proposal.Round, stepPropose, SignBytes(chainID, proposal)) signature, err := privVal.signBytesHRS(proposal.Height, proposal.Round, stepPropose, SignBytes(chainID, proposal))
@ -236,8 +192,17 @@ func (privVal *DefaultPrivValidator) SignProposal(chainID string, proposal *Prop
return nil return nil
} }
// SignHeartbeat signs a canonical representation of the heartbeat, along with the chainID.
func (privVal *PrivValidatorFS) SignHeartbeat(chainID string, heartbeat *Heartbeat) error {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
var err error
heartbeat.Signature, err = privVal.Signer.Sign(SignBytes(chainID, heartbeat))
return err
}
// check if there's a regression. Else sign and write the hrs+signature to disk // check if there's a regression. Else sign and write the hrs+signature to disk
func (privVal *DefaultPrivValidator) signBytesHRS(height, round int, step int8, signBytes []byte) (crypto.Signature, error) { func (privVal *PrivValidatorFS) signBytesHRS(height, round int, step int8, signBytes []byte) (crypto.Signature, error) {
sig := crypto.Signature{} sig := crypto.Signature{}
info := privVal.Info info := privVal.Info
// If height regression, err // If height regression, err
@ -287,32 +252,68 @@ func (privVal *DefaultPrivValidator) signBytesHRS(height, round int, step int8,
privVal.save() privVal.save()
return sig, nil return sig, nil
} }
func (privVal *DefaultPrivValidator) SignHeartbeat(chainID string, heartbeat *Heartbeat) error { // String returns a string representation of the PrivValidatorFS.
privVal.mtx.Lock() func (privVal *PrivValidatorFS) String() string {
defer privVal.mtx.Unlock()
var err error
heartbeat.Signature, err = privVal.Signer.Sign(SignBytes(chainID, heartbeat))
return err
}
func (privVal *DefaultPrivValidator) String() string {
info := privVal.Info info := privVal.Info
return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", info.Address, info.LastHeight, info.LastRound, info.LastStep) return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", privVal.Address(), info.LastHeight, info.LastRound, info.LastStep)
} }
//------------------------------------- //-------------------------------------
type PrivValidatorsByAddress []*DefaultPrivValidator // ValidatorID contains the identity of the validator.
type ValidatorID struct {
Address data.Bytes `json:"address"`
PubKey crypto.PubKey `json:"pub_key"`
}
// LastSignedInfo contains information about the latest
// data signed by a validator to help prevent double signing.
type LastSignedInfo struct {
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
}
// Signer is an interface that defines how to sign messages.
// It is the caller's duty to verify the msg before calling Sign,
// eg. to avoid double signing.
// Currently, the only callers are SignVote, SignProposal, and SignHeartbeat.
type Signer interface {
Sign(msg []byte) (crypto.Signature, error)
}
// DefaultSigner implements Signer.
// It uses a standard, unencrypted crypto.PrivKey.
type DefaultSigner struct {
PrivKey crypto.PrivKey `json:"priv_key"`
}
// NewDefaultSigner returns an instance of DefaultSigner.
func NewDefaultSigner(priv crypto.PrivKey) *DefaultSigner {
return &DefaultSigner{
PrivKey: priv,
}
}
// Sign implements Signer. It signs the byte slice with a private key.
func (ds *DefaultSigner) Sign(msg []byte) (crypto.Signature, error) {
return ds.PrivKey.Sign(msg), nil
}
//-------------------------------------
type PrivValidatorsByAddress []*PrivValidatorFS
func (pvs PrivValidatorsByAddress) Len() int { func (pvs PrivValidatorsByAddress) Len() int {
return len(pvs) return len(pvs)
} }
func (pvs PrivValidatorsByAddress) Less(i, j int) bool { func (pvs PrivValidatorsByAddress) Less(i, j int) bool {
return bytes.Compare(pvs[i].Info.Address, pvs[j].Info.Address) == -1 return bytes.Compare(pvs[i].Address(), pvs[j].Address()) == -1
} }
func (pvs PrivValidatorsByAddress) Swap(i, j int) { func (pvs PrivValidatorsByAddress) Swap(i, j int) {

View File

@ -106,10 +106,9 @@ func (vc validatorCodec) Compare(o1 interface{}, o2 interface{}) int {
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// For testing... // For testing...
func RandValidator(randPower bool, minPower int64) (*Validator, *DefaultPrivValidator) { func RandValidator(randPower bool, minPower int64) (*Validator, *PrivValidatorFS) {
privVal := GenPrivValidator()
_, tempFilePath := cmn.Tempfile("priv_validator_") _, tempFilePath := cmn.Tempfile("priv_validator_")
privVal.SetFile(tempFilePath) privVal := GenPrivValidatorFS(tempFilePath)
votePower := minPower votePower := minPower
if randPower { if randPower {
votePower += int64(cmn.RandUint32()) votePower += int64(cmn.RandUint32())

View File

@ -369,9 +369,9 @@ func (ac accumComparable) Less(o interface{}) bool {
// For testing // For testing
// NOTE: PrivValidator are in order. // NOTE: PrivValidator are in order.
func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []*DefaultPrivValidator) { func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []*PrivValidatorFS) {
vals := make([]*Validator, numValidators) vals := make([]*Validator, numValidators)
privValidators := make([]*DefaultPrivValidator, numValidators) privValidators := make([]*PrivValidatorFS, numValidators)
for i := 0; i < numValidators; i++ { for i := 0; i < numValidators; i++ {
val, privValidator := RandValidator(false, votingPower) val, privValidator := RandValidator(false, votingPower)
vals[i] = val vals[i] = val