Browse Source

more PrivValidator interface

pull/637/head
Ethan Buchman 7 years ago
parent
commit
944ebccfe9
11 changed files with 135 additions and 140 deletions
  1. +1
    -1
      cmd/tendermint/commands/gen_validator.go
  2. +1
    -2
      cmd/tendermint/commands/init.go
  3. +2
    -4
      cmd/tendermint/commands/reset_priv_validator.go
  4. +1
    -1
      cmd/tendermint/commands/run_node.go
  5. +1
    -1
      cmd/tendermint/commands/show_validator.go
  6. +2
    -3
      cmd/tendermint/commands/testnet.go
  7. +1
    -2
      consensus/common_test.go
  8. +2
    -2
      node/node.go
  9. +120
    -119
      types/priv_validator.go
  10. +2
    -3
      types/validator.go
  11. +2
    -2
      types/validator_set.go

+ 1
- 1
cmd/tendermint/commands/gen_validator.go View File

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


+ 1
- 2
cmd/tendermint/commands/init.go View File

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


+ 2
- 4
cmd/tendermint/commands/reset_priv_validator.go View File

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


+ 1
- 1
cmd/tendermint/commands/run_node.go View File

@ -44,7 +44,7 @@ func AddNodeFlags(cmd *cobra.Command) {
type FuncSignerAndApp func(*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())
return privValidator, clientCreator
}


+ 1
- 1
cmd/tendermint/commands/show_validator.go View File

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

+ 2
- 3
cmd/tendermint/commands/testnet.go View File

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

+ 1
- 2
consensus/common_test.go View File

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


+ 2
- 2
node/node.go View File

@ -60,7 +60,7 @@ type Node struct {
func NewNodeDefault(config *cfg.Config, logger log.Logger) *Node {
// Get PrivValidator
privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile())
privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile())
return NewNode(config, privValidator,
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
if state.Validators.HasAddress(privValidator.PubKey().Address()) {
if state.Validators.HasAddress(privValidator.Address()) {
consensusLogger.Info("This node is a validator")
} else {
consensusLogger.Info("This node is not a validator")


+ 120
- 119
types/priv_validator.go View File

@ -34,37 +34,7 @@ func voteToStep(vote *Vote) int8 {
}
}
// Signer is an interface that defines how to sign votes.
// 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()
}
// PrivValidator defines the functionality of a local Tendermint validator.
type PrivValidator interface {
Address() data.Bytes // redundant since .PubKey().Address()
PubKey() crypto.PubKey
@ -72,60 +42,45 @@ type PrivValidator interface {
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"`
// PrivValidatorFS implements PrivValidator using data persisted to disk
// to prevent double signing. The Signer itself can be mutated to use
// something besides the default, for instance a hardware 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.
// Overloaded for testing.
filePath string
mtx sync.Mutex
}
func (pv *DefaultPrivValidator) Address() data.Bytes {
return pv.Info.Address
}
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
// LoadOrGenPrivValidatorFS loads a PrivValidatorFS from the given filePath
// or else generates a new one and saves it to the filePath.
func LoadOrGenPrivValidatorFS(filePath string) *PrivValidatorFS {
var PrivValidatorFS *PrivValidatorFS
if _, err := os.Stat(filePath); err == nil {
privValidator = LoadPrivValidator(filePath)
PrivValidatorFS = LoadPrivValidatorFS(filePath)
} else {
privValidator = GenPrivValidator()
privValidator.SetFile(filePath)
privValidator.Save()
PrivValidatorFS = GenPrivValidatorFS(filePath)
PrivValidatorFS.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)
if err != nil {
Exit(err.Error())
}
privVal := DefaultPrivValidator{}
privVal := PrivValidatorFS{}
err = json.Unmarshal(privValJSONBytes, &privVal)
if err != nil {
Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err))
@ -135,56 +90,58 @@ func LoadPrivValidator(filePath string) *DefaultPrivValidator {
return &privVal
}
// Generates a new validator with private key.
func GenPrivValidator() *DefaultPrivValidator {
// GenPrivValidatorFS generates a new validator with randomly generated private key
// and sets the filePath, but does not call Save().
func GenPrivValidatorFS(filePath string) *PrivValidatorFS {
privKey := crypto.GenPrivKeyEd25519().Wrap()
pubKey := privKey.PubKey()
return &DefaultPrivValidator{
Info: PrivValidatorInfo{
Address: pubKey.Address(),
PubKey: pubKey,
return &PrivValidatorFS{
ID: ValidatorID{privKey.PubKey().Address(), privKey.PubKey()},
Info: LastSignedInfo{
LastStep: stepNone,
},
Signer: NewDefaultSigner(privKey),
filePath: "",
filePath: 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,
// LoadPrivValidatorWithSigner loads a PrivValidatorFS with a custom
// signer object. The PrivValidatorFS handles double signing prevention by persisting
// data to the filePath, while the Signer handles the signing.
// 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))
}
privVal.filePath = filePath
privVal.Signer = signerFunc(privVal.ID)
return &privVal
}
// Overwrite address and pubkey for convenience
/*func (privVal *DefaultPrivValidator) setPubKeyAndAddress() {
privVal.PubKey = privVal.Signer.PubKey()
privVal.Address = privVal.PubKey.Address()
}*/
// Address returns the address of the validator.
func (pv *PrivValidatorFS) Address() data.Bytes {
return pv.ID.Address
}
func (privVal *DefaultPrivValidator) SetFile(filePath string) {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
privVal.filePath = filePath
// PubKey returns the public key of the validator.
func (pv *PrivValidatorFS) PubKey() crypto.PubKey {
return pv.ID.PubKey
}
func (privVal *DefaultPrivValidator) Save() {
// Save persists the PrivValidatorFS to disk.
func (privVal *PrivValidatorFS) Save() {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
privVal.save()
}
func (privVal *DefaultPrivValidator) save() {
func (privVal *PrivValidatorFS) save() {
if privVal.filePath == "" {
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!
func (privVal *DefaultPrivValidator) Reset() {
func (privVal *PrivValidatorFS) Reset() {
privVal.Info.LastHeight = 0
privVal.Info.LastRound = 0
privVal.Info.LastStep = 0
@ -210,11 +168,8 @@ func (privVal *DefaultPrivValidator) Reset() {
privVal.Save()
}
func (privVal *DefaultPrivValidator) GetAddress() []byte {
return privVal.Address()
}
func (privVal *DefaultPrivValidator) SignVote(chainID string, vote *Vote) error {
// SignVote signs a canonical representation of the vote, along with the chainID.
func (privVal *PrivValidatorFS) 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))
@ -225,7 +180,8 @@ func (privVal *DefaultPrivValidator) SignVote(chainID string, vote *Vote) error
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()
defer privVal.mtx.Unlock()
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
}
// 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
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{}
info := privVal.Info
// If height regression, err
@ -287,32 +252,68 @@ func (privVal *DefaultPrivValidator) signBytesHRS(height, round int, step int8,
privVal.save()
return sig, nil
}
// String returns a string representation of the PrivValidatorFS.
func (privVal *PrivValidatorFS) String() string {
info := privVal.Info
return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", privVal.Address(), info.LastHeight, info.LastRound, info.LastStep)
}
func (privVal *DefaultPrivValidator) 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
//-------------------------------------
// ValidatorID contains the identity of the validator.
type ValidatorID struct {
Address data.Bytes `json:"address"`
PubKey crypto.PubKey `json:"pub_key"`
}
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)
// 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 []*DefaultPrivValidator
type PrivValidatorsByAddress []*PrivValidatorFS
func (pvs PrivValidatorsByAddress) Len() int {
return len(pvs)
}
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) {


+ 2
- 3
types/validator.go View File

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


+ 2
- 2
types/validator_set.go View File

@ -369,9 +369,9 @@ func (ac accumComparable) Less(o interface{}) bool {
// For testing
// 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)
privValidators := make([]*DefaultPrivValidator, numValidators)
privValidators := make([]*PrivValidatorFS, numValidators)
for i := 0; i < numValidators; i++ {
val, privValidator := RandValidator(false, votingPower)
vals[i] = val


Loading…
Cancel
Save