From 28ec3d48fafc88805710c77fb70074fdce61be37 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 5 Nov 2015 02:09:43 +0200 Subject: [PATCH] signer interface for validators --- node/node.go | 19 ++++++++++----- node/node_test.go | 2 +- types/priv_validator.go | 51 +++++++++++++++++++++++++++++++---------- types/vote_set_test.go | 2 +- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/node/node.go b/node/node.go index 539e315dc..0a11074e5 100644 --- a/node/node.go +++ b/node/node.go @@ -41,7 +41,15 @@ type Node struct { privKey crypto.PrivKeyEd25519 } -func NewNode() *Node { +func NewNodeDefaultPrivVal() *Node { + // Get PrivValidator + privValidatorFile := config.GetString("priv_validator_file") + privValidator := types.LoadOrGenPrivValidator(privValidatorFile) + + return NewNode(privValidator) +} + +func NewNode(privValidator *types.PrivValidator) *Node { // Get BlockStore blockStoreDB := dbm.GetDB("blockstore") blockStore := bc.NewBlockStore(blockStoreDB) @@ -58,10 +66,6 @@ func NewNode() *Node { // add the chainid to the global config config.Set("chain_id", state.ChainID) - // Get PrivValidator - privValidatorFile := config.GetString("priv_validator_file") - privValidator := types.LoadOrGenPrivValidator(privValidatorFile) - // Generate node PrivKey privKey := crypto.GenPrivKeyEd25519() @@ -249,6 +253,9 @@ func makeNodeInfo(sw *p2p.Switch, privKey crypto.PrivKeyEd25519) *p2p.NodeInfo { //------------------------------------------------------------------------------ +// Users wishing to use an external signer for their validators +// should fork tendermint/tendermint and implement RunNode to +// load their custom priv validator and call NewNode(privVal) func RunNode() { // Wait until the genesis doc becomes available @@ -274,7 +281,7 @@ func RunNode() { } // Create & start node - n := NewNode() + n := NewNodeDefaultPrivVal() l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp")) n.AddListener(l) err := n.Start() diff --git a/node/node_test.go b/node/node_test.go index 49e7d3ddf..c6a9c1c26 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -24,7 +24,7 @@ func TestNodeStartStop(t *testing.T) { time.Sleep(time.Second * 2) // Create & start node - n := NewNode() + n := NewNodeDefaultPrivVal() l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp")) n.AddListener(l) n.Start() diff --git a/types/priv_validator.go b/types/priv_validator.go index e2536e47d..4ad1de3e1 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -35,12 +35,15 @@ func voteToStep(vote *Vote) int8 { } type PrivValidator struct { - Address []byte `json:"address"` - PubKey crypto.PubKeyEd25519 `json:"pub_key"` - PrivKey crypto.PrivKeyEd25519 `json:"priv_key"` - LastHeight int `json:"last_height"` - LastRound int `json:"last_round"` - LastStep int8 `json:"last_step"` + Address []byte `json:"address"` + PubKey crypto.PubKeyEd25519 `json:"pub_key"` + LastHeight int `json:"last_height"` + LastRound int `json:"last_round"` + LastStep int8 `json:"last_step"` + + // PrivKey should be empty if a Signer other than the default is being used. + PrivKey crypto.PrivKeyEd25519 `json:"priv_key"` + signer Signer // For persistence. // Overloaded for testing. @@ -48,6 +51,32 @@ type PrivValidator struct { mtx sync.Mutex } +// XXX: This is used 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 { + Sign(msg []byte) crypto.SignatureEd25519 +} + +// Implements Signer +type DefaultSigner struct { + priv crypto.PrivKeyEd25519 +} + +func NewDefaultSigner(priv crypto.PrivKeyEd25519) *DefaultSigner { + return &DefaultSigner{priv: priv} +} + +// Implements Signer +func (ds *DefaultSigner) Sign(msg []byte) crypto.SignatureEd25519 { + return ds.priv.Sign(msg).(crypto.SignatureEd25519) +} + +func (privVal *PrivValidator) SetSigner(s Signer) { + privVal.signer = s +} + // Generates a new validator with private key. func GenPrivValidator() *PrivValidator { privKeyBytes := new([64]byte) @@ -63,6 +92,7 @@ func GenPrivValidator() *PrivValidator { LastRound: 0, LastStep: stepNone, filePath: "", + signer: NewDefaultSigner(privKey), } } @@ -76,6 +106,7 @@ func LoadPrivValidator(filePath string) *PrivValidator { Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) } privVal.filePath = filePath + privVal.signer = NewDefaultSigner(privVal.PrivKey) return privVal } @@ -145,14 +176,10 @@ func (privVal *PrivValidator) SignVote(chainID string, vote *Vote) error { privVal.save() // Sign - privVal.SignVoteUnsafe(chainID, vote) + vote.Signature = privVal.signer.Sign(SignBytes(chainID, vote)) return nil } -func (privVal *PrivValidator) SignVoteUnsafe(chainID string, vote *Vote) { - vote.Signature = privVal.PrivKey.Sign(SignBytes(chainID, vote)).(crypto.SignatureEd25519) -} - func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) error { privVal.mtx.Lock() defer privVal.mtx.Unlock() @@ -167,7 +194,7 @@ func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) e privVal.save() // Sign - proposal.Signature = privVal.PrivKey.Sign(SignBytes(chainID, proposal)).(crypto.SignatureEd25519) + proposal.Signature = privVal.signer.Sign(SignBytes(chainID, proposal)) return nil } else { return errors.New(fmt.Sprintf("Attempt of duplicate signing of proposal: Height %v, Round %v", proposal.Height, proposal.Round)) diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 92686c91e..629a54e94 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -60,7 +60,7 @@ func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote { } func signAddVote(privVal *PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) { - privVal.SignVoteUnsafe(config.GetString("chain_id"), vote) + vote.Signature = privVal.signer.Sign(SignBytes(config.GetString("chain_id"), vote)) added, _, err := voteSet.AddByAddress(privVal.Address, vote) return added, err }