diff --git a/cmd/tendermint/commands/gen_validator.go b/cmd/tendermint/commands/gen_validator.go index 97c583c22..984176d2a 100644 --- a/cmd/tendermint/commands/gen_validator.go +++ b/cmd/tendermint/commands/gen_validator.go @@ -9,18 +9,16 @@ import ( "github.com/tendermint/tendermint/types" ) -var genValidatorCmd = &cobra.Command{ +// GenValidatorCmd allows the generation of a keypair for a +// validator. +var GenValidatorCmd = &cobra.Command{ Use: "gen_validator", Short: "Generate new validator keypair", Run: genValidator, } -func init() { - RootCmd.AddCommand(genValidatorCmd) -} - func genValidator(cmd *cobra.Command, args []string) { - privValidator := types.GenPrivValidator() + privValidator := types.GenPrivValidatorFS("") privValidatorJSONBytes, _ := json.MarshalIndent(privValidator, "", "\t") fmt.Printf(`%v `, string(privValidatorJSONBytes)) diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index ce900defa..cbafac3ef 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -9,21 +9,17 @@ import ( cmn "github.com/tendermint/tmlibs/common" ) -var initFilesCmd = &cobra.Command{ +// InitFilesCmd initialises a fresh Tendermint Core instance. +var InitFilesCmd = &cobra.Command{ Use: "init", Short: "Initialize Tendermint", Run: initFiles, } -func init() { - RootCmd.AddCommand(initFilesCmd) -} - 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() @@ -33,7 +29,7 @@ func initFiles(cmd *cobra.Command, args []string) { ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)), } genDoc.Validators = []types.GenesisValidator{types.GenesisValidator{ - PubKey: privValidator.PubKey, + PubKey: privValidator.GetPubKey(), Power: 10, }} diff --git a/cmd/tendermint/commands/probe_upnp.go b/cmd/tendermint/commands/probe_upnp.go index e23e48973..643b7713f 100644 --- a/cmd/tendermint/commands/probe_upnp.go +++ b/cmd/tendermint/commands/probe_upnp.go @@ -9,16 +9,13 @@ import ( "github.com/tendermint/tendermint/p2p/upnp" ) -var probeUpnpCmd = &cobra.Command{ +// ProbeUpnpCmd adds capabilities to test the UPnP functionality. +var ProbeUpnpCmd = &cobra.Command{ Use: "probe_upnp", Short: "Test UPnP functionality", RunE: probeUpnp, } -func init() { - RootCmd.AddCommand(probeUpnpCmd) -} - func probeUpnp(cmd *cobra.Command, args []string) error { capabilities, err := upnp.Probe(logger) if err != nil { diff --git a/cmd/tendermint/commands/replay.go b/cmd/tendermint/commands/replay.go index 0c88b2443..303ccba6b 100644 --- a/cmd/tendermint/commands/replay.go +++ b/cmd/tendermint/commands/replay.go @@ -6,7 +6,8 @@ import ( "github.com/tendermint/tendermint/consensus" ) -var replayCmd = &cobra.Command{ +// ReplayCmd allows replaying of messages from the WAL. +var ReplayCmd = &cobra.Command{ Use: "replay", Short: "Replay messages from WAL", Run: func(cmd *cobra.Command, args []string) { @@ -14,15 +15,12 @@ var replayCmd = &cobra.Command{ }, } -var replayConsoleCmd = &cobra.Command{ +// ReplayConsoleCmd allows replaying of messages from the WAL in a +// console. +var ReplayConsoleCmd = &cobra.Command{ Use: "replay_console", Short: "Replay messages from WAL in a console", Run: func(cmd *cobra.Command, args []string) { consensus.RunReplayFile(config.BaseConfig, config.Consensus, true) }, } - -func init() { - RootCmd.AddCommand(replayCmd) - RootCmd.AddCommand(replayConsoleCmd) -} diff --git a/cmd/tendermint/commands/reset_priv_validator.go b/cmd/tendermint/commands/reset_priv_validator.go index fa12be4ea..b9c08715f 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -9,21 +9,27 @@ import ( "github.com/tendermint/tmlibs/log" ) -var resetAllCmd = &cobra.Command{ +// ResetAllCmd removes the database of this Tendermint core +// instance. +var ResetAllCmd = &cobra.Command{ Use: "unsafe_reset_all", Short: "(unsafe) Remove all the data and WAL, reset this node's validator", Run: resetAll, } -var resetPrivValidatorCmd = &cobra.Command{ +// ResetPrivValidatorCmd resets the private validator files. +var ResetPrivValidatorCmd = &cobra.Command{ Use: "unsafe_reset_priv_validator", Short: "(unsafe) Reset this node's validator", Run: resetPrivValidator, } -func init() { - RootCmd.AddCommand(resetAllCmd) - RootCmd.AddCommand(resetPrivValidatorCmd) +// ResetAll removes the privValidator files. +// Exported so other CLI tools can use it +func ResetAll(dbDir, privValFile string, logger log.Logger) { + resetPrivValidatorFS(privValFile, logger) + os.RemoveAll(dbDir) + logger.Info("Removed all data", "dir", dbDir) } // XXX: this is totally unsafe. @@ -35,26 +41,17 @@ func resetAll(cmd *cobra.Command, args []string) { // XXX: this is totally unsafe. // it's only suitable for testnets. func resetPrivValidator(cmd *cobra.Command, args []string) { - resetPrivValidatorLocal(config.PrivValidatorFile(), logger) + resetPrivValidatorFS(config.PrivValidatorFile(), logger) } -// Exported so other CLI tools can use it -func ResetAll(dbDir, privValFile string, logger log.Logger) { - resetPrivValidatorLocal(privValFile, logger) - os.RemoveAll(dbDir) - logger.Info("Removed all data", "dir", dbDir) -} - -func resetPrivValidatorLocal(privValFile string, logger log.Logger) { +func resetPrivValidatorFS(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) } diff --git a/cmd/tendermint/commands/root.go b/cmd/tendermint/commands/root.go index 6cef5daa9..a54b50069 100644 --- a/cmd/tendermint/commands/root.go +++ b/cmd/tendermint/commands/root.go @@ -34,11 +34,12 @@ func ParseConfig() (*cfg.Config, error) { return conf, err } +// RootCmd is the root command for Tendermint core. var RootCmd = &cobra.Command{ Use: "tendermint", Short: "Tendermint Core (BFT Consensus) in Go", PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { - if cmd.Name() == versionCmd.Name() { + if cmd.Name() == VersionCmd.Name() { return nil } config, err = ParseConfig() diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 6740df289..f0a1eede2 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -5,21 +5,9 @@ import ( "github.com/spf13/cobra" - "github.com/tendermint/tendermint/node" - "github.com/tendermint/tendermint/types" + nm "github.com/tendermint/tendermint/node" ) -var runNodeCmd = &cobra.Command{ - Use: "node", - Short: "Run the tendermint node", - RunE: runNode, -} - -func init() { - AddNodeFlags(runNodeCmd) - RootCmd.AddCommand(runNodeCmd) -} - // AddNodeFlags exposes some common configuration options on the command-line // These are exposed for convenience of commands embedding a tendermint node func AddNodeFlags(cmd *cobra.Command) { @@ -48,31 +36,32 @@ func AddNodeFlags(cmd *cobra.Command) { cmd.Flags().Bool("consensus.create_empty_blocks", config.Consensus.CreateEmptyBlocks, "Set this to false to only produce blocks when there are txs or when the AppHash changes") } -// Users wishing to: -// * Use an external signer for their validators -// * Supply an in-proc abci app -// should import github.com/tendermint/tendermint/node and implement -// their own run_node to call node.NewNode (instead of node.NewNodeDefault) -// with their custom priv validator and/or custom proxy.ClientCreator -func runNode(cmd *cobra.Command, args []string) error { +// NewRunNodeCmd returns the command that allows the CLI to start a +// node. It can be used with a custom PrivValidator and in-process ABCI application. +func NewRunNodeCmd(nodeProvider nm.NodeProvider) *cobra.Command { + cmd := &cobra.Command{ + Use: "node", + Short: "Run the tendermint node", + RunE: func(cmd *cobra.Command, args []string) error { + // Create & start node + n, err := nodeProvider(config, logger) + if err != nil { + return fmt.Errorf("Failed to create node: %v", err) + } - genDocFile := config.GenesisFile() - genDoc, err := types.GenesisDocFromFile(genDocFile) - if err != nil { - return err - } - config.ChainID = genDoc.ChainID + if _, err := n.Start(); err != nil { + return fmt.Errorf("Failed to start node: %v", err) + } else { + logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo()) + } - // Create & start node - n := node.NewNodeDefault(config, logger.With("module", "node")) - if _, err := n.Start(); err != nil { - return fmt.Errorf("Failed to start node: %v", err) - } else { - logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo()) + // Trap signal, run forever. + n.RunForever() + + return nil + }, } - // Trap signal, run forever. - n.RunForever() - - return nil + AddNodeFlags(cmd) + return cmd } diff --git a/cmd/tendermint/commands/show_validator.go b/cmd/tendermint/commands/show_validator.go index 53a687c6d..458f11c2d 100644 --- a/cmd/tendermint/commands/show_validator.go +++ b/cmd/tendermint/commands/show_validator.go @@ -9,18 +9,15 @@ import ( "github.com/tendermint/tendermint/types" ) -var showValidatorCmd = &cobra.Command{ +// ShowValidatorCmd adds capabilities for showing the validator info. +var ShowValidatorCmd = &cobra.Command{ Use: "show_validator", Short: "Show this node's validator info", Run: showValidator, } -func init() { - RootCmd.AddCommand(showValidatorCmd) -} - func showValidator(cmd *cobra.Command, args []string) { - privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile(), logger) + privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()) pubKeyJSONBytes, _ := data.ToJSON(privValidator.PubKey) fmt.Println(string(pubKeyJSONBytes)) } diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index 315f3b498..ac6f337a9 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -11,12 +11,6 @@ import ( cmn "github.com/tendermint/tmlibs/common" ) -var testnetFilesCmd = &cobra.Command{ - Use: "testnet", - Short: "Initialize files for a Tendermint testnet", - Run: testnetFiles, -} - //flags var ( nValidators int @@ -24,12 +18,18 @@ var ( ) func init() { - testnetFilesCmd.Flags().IntVar(&nValidators, "n", 4, + TestnetFilesCmd.Flags().IntVar(&nValidators, "n", 4, "Number of validators to initialize the testnet with") - testnetFilesCmd.Flags().StringVar(&dataDir, "dir", "mytestnet", + TestnetFilesCmd.Flags().StringVar(&dataDir, "dir", "mytestnet", "Directory to store initialization data for the testnet") +} - RootCmd.AddCommand(testnetFilesCmd) +// TestnetFilesCmd allows initialisation of files for a +// Tendermint testnet. +var TestnetFilesCmd = &cobra.Command{ + Use: "testnet", + Short: "Initialize files for a Tendermint testnet", + Run: testnetFiles, } func testnetFiles(cmd *cobra.Command, args []string) { @@ -45,9 +45,9 @@ 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, + PubKey: privVal.GetPubKey(), Power: 1, Name: mach, } @@ -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() } diff --git a/cmd/tendermint/commands/version.go b/cmd/tendermint/commands/version.go index 5c92160ea..f9f545e59 100644 --- a/cmd/tendermint/commands/version.go +++ b/cmd/tendermint/commands/version.go @@ -8,14 +8,11 @@ import ( "github.com/tendermint/tendermint/version" ) -var versionCmd = &cobra.Command{ +// VersionCmd ... +var VersionCmd = &cobra.Command{ Use: "version", Short: "Show version info", Run: func(cmd *cobra.Command, args []string) { fmt.Println(version.Version) }, } - -func init() { - RootCmd.AddCommand(versionCmd) -} diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index 5493e4f2f..86ca1531d 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -3,11 +3,39 @@ package main import ( "os" - "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tmlibs/cli" + + cmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + nm "github.com/tendermint/tendermint/node" ) func main() { - cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) + rootCmd := cmd.RootCmd + rootCmd.AddCommand( + cmd.GenValidatorCmd, + cmd.InitFilesCmd, + cmd.ProbeUpnpCmd, + cmd.ReplayCmd, + cmd.ReplayConsoleCmd, + cmd.ResetAllCmd, + cmd.ResetPrivValidatorCmd, + cmd.ShowValidatorCmd, + cmd.TestnetFilesCmd, + cmd.VersionCmd) + + // NOTE: + // Users wishing to: + // * Use an external signer for their validators + // * Supply an in-proc abci app + // * Supply a genesis doc file from another source + // * Provide their own DB implementation + // can copy this file and use something other than the + // DefaultNewNode function + nodeFunc := nm.DefaultNewNode + + // Create & start node + rootCmd.AddCommand(cmd.NewRunNodeCmd(nodeFunc)) + + cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) cmd.Execute() } diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 81a4ab42c..0f2d7b040 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -5,6 +5,8 @@ import ( "testing" "time" + crypto "github.com/tendermint/go-crypto" + data "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" . "github.com/tendermint/tmlibs/common" @@ -53,7 +55,7 @@ func TestByzantine(t *testing.T) { eventLogger := logger.With("module", "events") for i := 0; i < N; i++ { if i == 0 { - css[i].privValidator = NewByzantinePrivValidator(css[i].privValidator.(*types.PrivValidator)) + css[i].privValidator = NewByzantinePrivValidator(css[i].privValidator) // make byzantine css[i].decideProposal = func(j int) func(int, int) { return func(height, round int) { @@ -257,51 +259,42 @@ func (br *ByzantineReactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) { // byzantine privValidator type ByzantinePrivValidator struct { - Address []byte `json:"address"` - types.Signer `json:"-"` + types.Signer - mtx sync.Mutex + pv types.PrivValidator } // Return a priv validator that will sign anything -func NewByzantinePrivValidator(pv *types.PrivValidator) *ByzantinePrivValidator { +func NewByzantinePrivValidator(pv types.PrivValidator) *ByzantinePrivValidator { return &ByzantinePrivValidator{ - Address: pv.Address, - Signer: pv.Signer, + Signer: pv.(*types.PrivValidatorFS).Signer, + pv: pv, } } -func (privVal *ByzantinePrivValidator) GetAddress() []byte { - return privVal.Address +func (privVal *ByzantinePrivValidator) GetAddress() data.Bytes { + return privVal.pv.GetAddress() +} + +func (privVal *ByzantinePrivValidator) GetPubKey() crypto.PubKey { + return privVal.pv.GetPubKey() } func (privVal *ByzantinePrivValidator) SignVote(chainID string, vote *types.Vote) (err error) { - privVal.mtx.Lock() - defer privVal.mtx.Unlock() - - // Sign vote.Signature, err = privVal.Sign(types.SignBytes(chainID, vote)) return err } func (privVal *ByzantinePrivValidator) SignProposal(chainID string, proposal *types.Proposal) (err error) { - privVal.mtx.Lock() - defer privVal.mtx.Unlock() - - // Sign proposal.Signature, err = privVal.Sign(types.SignBytes(chainID, proposal)) return nil } func (privVal *ByzantinePrivValidator) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) (err error) { - privVal.mtx.Lock() - defer privVal.mtx.Unlock() - - // Sign heartbeat.Signature, err = privVal.Sign(types.SignBytes(chainID, heartbeat)) return nil } func (privVal *ByzantinePrivValidator) String() string { - return Fmt("PrivValidator{%X}", privVal.Address) + return Fmt("PrivValidator{%X}", privVal.GetAddress()) } diff --git a/consensus/common_test.go b/consensus/common_test.go index 6a05b74e3..b16afc3d0 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -50,12 +50,12 @@ type validatorStub struct { Index int // Validator index. NOTE: we don't assume validator set changes. Height int Round int - *types.PrivValidator + types.PrivValidator } var testMinPower = 10 -func NewValidatorStub(privValidator *types.PrivValidator, valIndex int) *validatorStub { +func NewValidatorStub(privValidator types.PrivValidator, valIndex int) *validatorStub { return &validatorStub{ Index: valIndex, PrivValidator: privValidator, @@ -65,7 +65,7 @@ func NewValidatorStub(privValidator *types.PrivValidator, valIndex int) *validat func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) { vote := &types.Vote{ ValidatorIndex: vs.Index, - ValidatorAddress: vs.PrivValidator.Address, + ValidatorAddress: vs.PrivValidator.GetAddress(), Height: vs.Height, Round: vs.Round, Type: voteType, @@ -142,7 +142,7 @@ func signAddVotes(to *ConsensusState, voteType byte, hash []byte, header types.P func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *validatorStub, blockHash []byte) { prevotes := cs.Votes.Prevotes(round) var vote *types.Vote - if vote = prevotes.GetByAddress(privVal.Address); vote == nil { + if vote = prevotes.GetByAddress(privVal.GetAddress()); vote == nil { panic("Failed to find prevote from validator") } if blockHash == nil { @@ -159,7 +159,7 @@ func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *valid func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorStub, blockHash []byte) { votes := cs.LastCommit var vote *types.Vote - if vote = votes.GetByAddress(privVal.Address); vote == nil { + if vote = votes.GetByAddress(privVal.GetAddress()); vote == nil { panic("Failed to find precommit from validator") } if !bytes.Equal(vote.BlockID.Hash, blockHash) { @@ -170,7 +170,7 @@ func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorS func validatePrecommit(t *testing.T, cs *ConsensusState, thisRound, lockRound int, privVal *validatorStub, votedBlockHash, lockedBlockHash []byte) { precommits := cs.Votes.Precommits(thisRound) var vote *types.Vote - if vote = precommits.GetByAddress(privVal.Address); vote == nil { + if vote = precommits.GetByAddress(privVal.GetAddress()); vote == nil { panic("Failed to find precommit from validator") } @@ -225,11 +225,11 @@ func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} { //------------------------------------------------------------------------------- // consensus states -func newConsensusState(state *sm.State, pv *types.PrivValidator, app abci.Application) *ConsensusState { +func newConsensusState(state *sm.State, pv types.PrivValidator, app abci.Application) *ConsensusState { return newConsensusStateWithConfig(config, state, pv, app) } -func newConsensusStateWithConfig(thisConfig *cfg.Config, state *sm.State, pv *types.PrivValidator, app abci.Application) *ConsensusState { +func newConsensusStateWithConfig(thisConfig *cfg.Config, state *sm.State, pv types.PrivValidator, app abci.Application) *ConsensusState { // Get BlockStore blockDB := dbm.NewMemDB() blockStore := bc.NewBlockStore(blockDB) @@ -258,17 +258,17 @@ func newConsensusStateWithConfig(thisConfig *cfg.Config, state *sm.State, pv *ty return cs } -func loadPrivValidator(config *cfg.Config) *types.PrivValidator { +func loadPrivValidator(config *cfg.Config) *types.PrivValidatorFS { privValidatorFile := config.PrivValidatorFile() ensureDir(path.Dir(privValidatorFile), 0700) - privValidator := types.LoadOrGenPrivValidator(privValidatorFile, log.TestingLogger()) + privValidator := types.LoadOrGenPrivValidatorFS(privValidatorFile) privValidator.Reset() return privValidator } func fixedConsensusStateDummy() *ConsensusState { stateDB := dbm.NewMemDB() - state := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) + state, _ := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) state.SetLogger(log.TestingLogger().With("module", "state")) privValidator := loadPrivValidator(config) cs := newConsensusState(state, privValidator, dummy.NewDummyApplication()) @@ -338,7 +338,7 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou logger := consensusLogger() for i := 0; i < nValidators; i++ { db := dbm.NewMemDB() // each state needs its own db - state := sm.MakeGenesisState(db, genDoc) + state, _ := sm.MakeGenesisState(db, genDoc) state.SetLogger(logger.With("module", "state", "validator", i)) state.Save() thisConfig := ResetConfig(Fmt("%s_%d", testName, i)) @@ -359,18 +359,17 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF css := make([]*ConsensusState, nPeers) for i := 0; i < nPeers; i++ { db := dbm.NewMemDB() // each state needs its own db - state := sm.MakeGenesisState(db, genDoc) + state, _ := sm.MakeGenesisState(db, genDoc) state.SetLogger(log.TestingLogger().With("module", "state")) state.Save() thisConfig := ResetConfig(Fmt("%s_%d", testName, i)) ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal - var privVal *types.PrivValidator + var privVal types.PrivValidator if i < nValidators { privVal = privVals[i] } else { - privVal = types.GenPrivValidator() _, tempFilePath := Tempfile("priv_validator_") - privVal.SetFile(tempFilePath) + privVal = types.GenPrivValidatorFS(tempFilePath) } css[i] = newConsensusStateWithConfig(thisConfig, state, privVal, appFunc()) @@ -393,9 +392,9 @@ func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int { //------------------------------------------------------------------------------- // genesis -func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []*types.PrivValidator) { +func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []*types.PrivValidatorFS) { validators := make([]types.GenesisValidator, numValidators) - privValidators := make([]*types.PrivValidator, numValidators) + privValidators := make([]*types.PrivValidatorFS, numValidators) for i := 0; i < numValidators; i++ { val, privVal := types.RandValidator(randPower, minPower) validators[i] = types.GenesisValidator{ @@ -412,10 +411,10 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G }, privValidators } -func randGenesisState(numValidators int, randPower bool, minPower int64) (*sm.State, []*types.PrivValidator) { +func randGenesisState(numValidators int, randPower bool, minPower int64) (*sm.State, []*types.PrivValidatorFS) { genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower) db := dbm.NewMemDB() - s0 := sm.MakeGenesisState(db, genDoc) + s0, _ := sm.MakeGenesisState(db, genDoc) s0.SetLogger(log.TestingLogger().With("module", "state")) s0.Save() return s0, privValidators diff --git a/consensus/height_vote_set_test.go b/consensus/height_vote_set_test.go index 29751b40a..7e03e40f5 100644 --- a/consensus/height_vote_set_test.go +++ b/consensus/height_vote_set_test.go @@ -44,10 +44,10 @@ func TestPeerCatchupRounds(t *testing.T) { } -func makeVoteHR(t *testing.T, height, round int, privVals []*types.PrivValidator, valIndex int) *types.Vote { +func makeVoteHR(t *testing.T, height, round int, privVals []*types.PrivValidatorFS, valIndex int) *types.Vote { privVal := privVals[valIndex] vote := &types.Vote{ - ValidatorAddress: privVal.Address, + ValidatorAddress: privVal.GetAddress(), ValidatorIndex: valIndex, Height: height, Round: round, diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index b1f9a0a5b..623a65413 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -132,7 +132,7 @@ func TestVotingPowerChange(t *testing.T) { //--------------------------------------------------------------------------- t.Log("---------------------------- Testing changing the voting power of one validator a few times") - val1PubKey := css[0].privValidator.(*types.PrivValidator).PubKey + val1PubKey := css[0].privValidator.GetPubKey() updateValidatorTx := dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 25) previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower() @@ -193,7 +193,7 @@ func TestValidatorSetChanges(t *testing.T) { //--------------------------------------------------------------------------- t.Log("---------------------------- Testing adding one validator") - newValidatorPubKey1 := css[nVals].privValidator.(*types.PrivValidator).PubKey + newValidatorPubKey1 := css[nVals].privValidator.GetPubKey() newValidatorTx1 := dummy.MakeValSetChangeTx(newValidatorPubKey1.Bytes(), uint64(testMinPower)) // wait till everyone makes block 2 @@ -219,7 +219,7 @@ func TestValidatorSetChanges(t *testing.T) { //--------------------------------------------------------------------------- t.Log("---------------------------- Testing changing the voting power of one validator") - updateValidatorPubKey1 := css[nVals].privValidator.(*types.PrivValidator).PubKey + updateValidatorPubKey1 := css[nVals].privValidator.GetPubKey() updateValidatorTx1 := dummy.MakeValSetChangeTx(updateValidatorPubKey1.Bytes(), 25) previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower() @@ -235,10 +235,10 @@ func TestValidatorSetChanges(t *testing.T) { //--------------------------------------------------------------------------- t.Log("---------------------------- Testing adding two validators at once") - newValidatorPubKey2 := css[nVals+1].privValidator.(*types.PrivValidator).PubKey + newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey() newValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), uint64(testMinPower)) - newValidatorPubKey3 := css[nVals+2].privValidator.(*types.PrivValidator).PubKey + newValidatorPubKey3 := css[nVals+2].privValidator.GetPubKey() newValidatorTx3 := dummy.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), uint64(testMinPower)) waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3) diff --git a/consensus/replay_file.go b/consensus/replay_file.go index 39ce47c5e..1182aaf04 100644 --- a/consensus/replay_file.go +++ b/consensus/replay_file.go @@ -241,12 +241,15 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo // Get State stateDB := dbm.NewDB("state", config.DBBackend, config.DBDir()) - state := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) + state, err := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) + if err != nil { + cmn.Exit(err.Error()) + } // Create proxyAppConn connection (consensus, mempool, query) clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) proxyApp := proxy.NewAppConns(clientCreator, NewHandshaker(state, blockStore)) - _, err := proxyApp.Start() + _, err = proxyApp.Start() if err != nil { cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err)) } diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 03ca6c8d7..765434677 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -162,8 +162,8 @@ LOOP: cs.Wait() } -func toPV(pv PrivValidator) *types.PrivValidator { - return pv.(*types.PrivValidator) +func toPV(pv types.PrivValidator) *types.PrivValidatorFS { + return pv.(*types.PrivValidatorFS) } func setupReplayTest(t *testing.T, thisCase *testCase, nLines int, crashAfter bool) (*ConsensusState, chan interface{}, string, string) { @@ -317,7 +317,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) { walFile := writeWAL(string(walBody)) config.Consensus.SetWalFile(walFile) - privVal := types.LoadPrivValidator(config.PrivValidatorFile()) + privVal := types.LoadPrivValidatorFS(config.PrivValidatorFile()) wal, err := NewWAL(walFile, false) if err != nil { @@ -332,7 +332,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) { t.Fatalf(err.Error()) } - state, store := stateAndStore(config, privVal.PubKey) + state, store := stateAndStore(config, privVal.GetPubKey()) store.chain = chain store.commits = commits @@ -346,7 +346,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) { // run nBlocks against a new client to build up the app state. // use a throwaway tendermint state proxyApp := proxy.NewAppConns(clientCreator2, nil) - state, _ := stateAndStore(config, privVal.PubKey) + state, _ := stateAndStore(config, privVal.GetPubKey()) buildAppStateFromChain(proxyApp, state, chain, nBlocks, mode) } @@ -558,7 +558,7 @@ func readPieceFromWAL(msgBytes []byte) (interface{}, error) { // fresh state and mock store func stateAndStore(config *cfg.Config, pubKey crypto.PubKey) (*sm.State, *mockBlockStore) { stateDB := dbm.NewMemDB() - state := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) + state, _ := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) state.SetLogger(log.TestingLogger().With("module", "state")) store := NewMockBlockStore(config, state.Params()) diff --git a/consensus/state.go b/consensus/state.go index ac1656209..648fc0559 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -180,14 +180,6 @@ func (ti *timeoutInfo) String() string { return fmt.Sprintf("%v ; %d/%d %v", ti.Duration, ti.Height, ti.Round, ti.Step) } -// PrivValidator is a validator that can sign votes and proposals. -type PrivValidator interface { - GetAddress() []byte - SignVote(chainID string, vote *types.Vote) error - SignProposal(chainID string, proposal *types.Proposal) error - SignHeartbeat(chainID string, heartbeat *types.Heartbeat) error -} - // ConsensusState handles execution of the consensus algorithm. // It processes votes and proposals, and upon reaching agreement, // commits blocks to the chain and executes them against the application. @@ -197,7 +189,7 @@ type ConsensusState struct { // config details config *cfg.ConsensusConfig - privValidator PrivValidator // for signing votes + privValidator types.PrivValidator // for signing votes // services for creating and executing blocks proxyAppConn proxy.AppConnConsensus @@ -308,7 +300,7 @@ func (cs *ConsensusState) GetValidators() (int, []*types.Validator) { } // SetPrivValidator sets the private validator account for signing votes. -func (cs *ConsensusState) SetPrivValidator(priv PrivValidator) { +func (cs *ConsensusState) SetPrivValidator(priv types.PrivValidator) { cs.mtx.Lock() defer cs.mtx.Unlock() cs.privValidator = priv diff --git a/consensus/state_test.go b/consensus/state_test.go index 9ae052033..c4a6769e9 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -79,7 +79,7 @@ func TestProposerSelection0(t *testing.T) { <-newRoundCh prop = cs1.GetRoundState().Validators.GetProposer() - if !bytes.Equal(prop.Address, vss[1].Address) { + if !bytes.Equal(prop.Address, vss[1].GetAddress()) { panic(Fmt("expected proposer to be validator %d. Got %X", 1, prop.Address)) } } @@ -100,7 +100,7 @@ func TestProposerSelection2(t *testing.T) { // everyone just votes nil. we get a new proposer each round for i := 0; i < len(vss); i++ { prop := cs1.GetRoundState().Validators.GetProposer() - if !bytes.Equal(prop.Address, vss[(i+2)%len(vss)].Address) { + if !bytes.Equal(prop.Address, vss[(i+2)%len(vss)].GetAddress()) { panic(Fmt("expected proposer to be validator %d. Got %X", (i+2)%len(vss), prop.Address)) } @@ -502,8 +502,6 @@ func TestLockPOLRelock(t *testing.T) { newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1) newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlockHeader(), 1) - t.Logf("vs2 last round %v", vs2.PrivValidator.LastRound) - // everything done from perspective of cs1 /* diff --git a/node/node.go b/node/node.go index e0ddeb5d4..4286fece2 100644 --- a/node/node.go +++ b/node/node.go @@ -3,6 +3,7 @@ package node import ( "bytes" "errors" + "fmt" "net" "net/http" "strings" @@ -10,11 +11,15 @@ import ( abci "github.com/tendermint/abci/types" crypto "github.com/tendermint/go-crypto" wire "github.com/tendermint/go-wire" + cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" + bc "github.com/tendermint/tendermint/blockchain" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/consensus" mempl "github.com/tendermint/tendermint/mempool" - p2p "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/proxy" rpccore "github.com/tendermint/tendermint/rpc/core" grpccore "github.com/tendermint/tendermint/rpc/grpc" @@ -26,20 +31,66 @@ import ( "github.com/tendermint/tendermint/state/txindex/null" "github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/version" - cmn "github.com/tendermint/tmlibs/common" - dbm "github.com/tendermint/tmlibs/db" - "github.com/tendermint/tmlibs/log" _ "net/http/pprof" ) +//------------------------------------------------------------------------------ + +// DBContext specifies config information for loading a new DB. +type DBContext struct { + ID string + Config *cfg.Config +} + +// DBProvider takes a DBContext and returns an instantiated DB. +type DBProvider func(*DBContext) (dbm.DB, error) + +// DefaultDBProvider returns a database using the DBBackend and DBDir +// specified in the ctx.Config. +func DefaultDBProvider(ctx *DBContext) (dbm.DB, error) { + return dbm.NewDB(ctx.ID, ctx.Config.DBBackend, ctx.Config.DBDir()), nil +} + +// GenesisDocProvider returns a GenesisDoc. +// It allows the GenesisDoc to be pulled from sources other than the +// filesystem, for instance from a distributed key-value store cluster. +type GenesisDocProvider func() (*types.GenesisDoc, error) + +// DefaultGenesisDocProviderFunc returns a GenesisDocProvider that loads +// the GenesisDoc from the config.GenesisFile() on the filesystem. +func DefaultGenesisDocProviderFunc(config *cfg.Config) GenesisDocProvider { + return func() (*types.GenesisDoc, error) { + return types.GenesisDocFromFile(config.GenesisFile()) + } +} + +// NodeProvider takes a config and a logger and returns a ready to go Node. +type NodeProvider func(*cfg.Config, log.Logger) (*Node, error) + +// DefaultNewNode returns a Tendermint node with default settings for the +// PrivValidator, ClientCreator, GenesisDoc, and DBProvider. +// It implements NodeProvider. +func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { + return NewNode(config, + types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()), + proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), + DefaultGenesisDocProviderFunc(config), + DefaultDBProvider, + logger) +} + +//------------------------------------------------------------------------------ + +// Node is the highest level interface to a full Tendermint node. +// It includes all configuration information and running services. type Node struct { cmn.BaseService // config config *cfg.Config - genesisDoc *types.GenesisDoc // initial validator set - privValidator *types.PrivValidator // local node's validator key + genesisDoc *types.GenesisDoc // initial validator set + privValidator types.PrivValidator // local node's validator key // network privKey crypto.PrivKeyEd25519 // local node's p2p key @@ -58,24 +109,42 @@ type Node struct { txIndexer txindex.TxIndexer } -func NewNodeDefault(config *cfg.Config, logger log.Logger) *Node { - // Get PrivValidator - privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile(), logger) - return NewNode(config, privValidator, - proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) -} +// NewNode returns a new, ready to go, Tendermint Node. +func NewNode(config *cfg.Config, + privValidator types.PrivValidator, + clientCreator proxy.ClientCreator, + genesisDocProvider GenesisDocProvider, + dbProvider DBProvider, + logger log.Logger) (*Node, error) { -func NewNode(config *cfg.Config, privValidator *types.PrivValidator, clientCreator proxy.ClientCreator, logger log.Logger) *Node { // Get BlockStore - blockStoreDB := dbm.NewDB("blockstore", config.DBBackend, config.DBDir()) + blockStoreDB, err := dbProvider(&DBContext{"blockstore", config}) + if err != nil { + return nil, err + } blockStore := bc.NewBlockStore(blockStoreDB) consensusLogger := logger.With("module", "consensus") stateLogger := logger.With("module", "state") // Get State - stateDB := dbm.NewDB("state", config.DBBackend, config.DBDir()) - state := sm.GetState(stateDB, config.GenesisFile()) + stateDB, err := dbProvider(&DBContext{"state", config}) + if err != nil { + return nil, err + } + state := sm.LoadState(stateDB) + if state == nil { + genDoc, err := genesisDocProvider() + if err != nil { + return nil, err + } + state, err = sm.MakeGenesisState(stateDB, genDoc) + if err != nil { + return nil, err + } + state.Save() + } + state.SetLogger(stateLogger) // Create the proxyApp, which manages connections (consensus, mempool, query) @@ -85,7 +154,7 @@ func NewNode(config *cfg.Config, privValidator *types.PrivValidator, clientCreat proxyApp := proxy.NewAppConns(clientCreator, handshaker) proxyApp.SetLogger(logger.With("module", "proxy")) if _, err := proxyApp.Start(); err != nil { - cmn.Exit(cmn.Fmt("Error starting proxy app connections: %v", err)) + return nil, fmt.Errorf("Error starting proxy app connections: %v", err) } // reload the state (it may have been updated by the handshake) @@ -96,7 +165,10 @@ func NewNode(config *cfg.Config, privValidator *types.PrivValidator, clientCreat var txIndexer txindex.TxIndexer switch config.TxIndex { case "kv": - store := dbm.NewDB("tx_index", config.DBBackend, config.DBDir()) + store, err := dbProvider(&DBContext{"tx_index", config}) + if err != nil { + return nil, err + } txIndexer = kv.NewTxIndex(store) default: txIndexer = &null.TxIndex{} @@ -109,9 +181,8 @@ func NewNode(config *cfg.Config, privValidator *types.PrivValidator, clientCreat // Make event switch eventSwitch := types.NewEventSwitch() eventSwitch.SetLogger(logger.With("module", "types")) - _, err := eventSwitch.Start() - if err != nil { - cmn.Exit(cmn.Fmt("Failed to start switch: %v", err)) + if _, err := eventSwitch.Start(); err != nil { + return nil, fmt.Errorf("Failed to start switch: %v", err) } // Decide whether to fast-sync or not @@ -119,13 +190,13 @@ func NewNode(config *cfg.Config, privValidator *types.PrivValidator, clientCreat fastSync := config.FastSync if state.Validators.Size() == 1 { addr, _ := state.Validators.GetByIndex(0) - if bytes.Equal(privValidator.Address, addr) { + if bytes.Equal(privValidator.GetAddress(), addr) { fastSync = false } } // Log whether this node is a validator or an observer - if state.Validators.HasAddress(privValidator.Address) { + if state.Validators.HasAddress(privValidator.GetAddress()) { consensusLogger.Info("This node is a validator") } else { consensusLogger.Info("This node is not a validator") @@ -232,9 +303,10 @@ func NewNode(config *cfg.Config, privValidator *types.PrivValidator, clientCreat txIndexer: txIndexer, } node.BaseService = *cmn.NewBaseService(logger, "Node", node) - return node + return node, nil } +// OnStart starts the Node. It implements cmn.Service. func (n *Node) OnStart() error { // Create & add listener protocol, address := ProtocolAndAddress(n.config.P2P.ListenAddress) @@ -270,6 +342,7 @@ func (n *Node) OnStart() error { return nil } +// OnStop stops the Node. It implements cmn.Service. func (n *Node) OnStop() { n.BaseService.OnStop() @@ -285,6 +358,7 @@ func (n *Node) OnStop() { } } +// RunForever waits for an interupt signal and stops the node. func (n *Node) RunForever() { // Sleep forever and then... cmn.TrapSignal(func() { @@ -292,15 +366,15 @@ func (n *Node) RunForever() { }) } -// Add the event switch to reactors, mempool, etc. +// SetEventSwitch adds the event switch to reactors, mempool, etc. func SetEventSwitch(evsw types.EventSwitch, eventables ...types.Eventable) { for _, e := range eventables { e.SetEventSwitch(evsw) } } -// Add a Listener to accept inbound peer connections. -// Add listeners before starting the Node. +// AddListener adds a listener to accept inbound peer connections. +// It should be called before starting the Node. // The first listener is the primary listener (in NodeInfo) func (n *Node) AddListener(l p2p.Listener) { n.sw.AddListener(l) @@ -314,7 +388,7 @@ func (n *Node) ConfigureRPC() { rpccore.SetConsensusState(n.consensusState) rpccore.SetMempool(n.mempoolReactor.Mempool) rpccore.SetSwitch(n.sw) - rpccore.SetPubKey(n.privValidator.PubKey) + rpccore.SetPubKey(n.privValidator.GetPubKey()) rpccore.SetGenesisDoc(n.genesisDoc) rpccore.SetAddrBook(n.addrBook) rpccore.SetProxyAppQuery(n.proxyApp.Query()) @@ -360,39 +434,48 @@ func (n *Node) startRPC() ([]net.Listener, error) { return listeners, nil } +// Switch returns the Node's Switch. func (n *Node) Switch() *p2p.Switch { return n.sw } +// BlockStore returns the Node's BlockStore. func (n *Node) BlockStore() *bc.BlockStore { return n.blockStore } +// ConsensusState returns the Node's ConsensusState. func (n *Node) ConsensusState() *consensus.ConsensusState { return n.consensusState } +// ConsensusReactor returns the Node's ConsensusReactor. func (n *Node) ConsensusReactor() *consensus.ConsensusReactor { return n.consensusReactor } +// MempoolReactor returns the Node's MempoolReactor. func (n *Node) MempoolReactor() *mempl.MempoolReactor { return n.mempoolReactor } +// EventSwitch returns the Node's EventSwitch. func (n *Node) EventSwitch() types.EventSwitch { return n.evsw } -// XXX: for convenience -func (n *Node) PrivValidator() *types.PrivValidator { +// PrivValidator returns the Node's PrivValidator. +// XXX: for convenience only! +func (n *Node) PrivValidator() types.PrivValidator { return n.privValidator } +// GenesisDoc returns the Node's GenesisDoc. func (n *Node) GenesisDoc() *types.GenesisDoc { return n.genesisDoc } +// ProxyApp returns the Node's AppConns, representing its connections to the ABCI application. func (n *Node) ProxyApp() proxy.AppConns { return n.proxyApp } @@ -442,15 +525,18 @@ func (n *Node) makeNodeInfo() *p2p.NodeInfo { //------------------------------------------------------------------------------ +// NodeInfo returns the Node's Info from the Switch. func (n *Node) NodeInfo() *p2p.NodeInfo { return n.sw.NodeInfo() } +// DialSeeds dials the given seeds on the Switch. func (n *Node) DialSeeds(seeds []string) error { return n.sw.DialSeeds(n.addrBook, seeds) } -// Defaults to tcp +// ProtocolAndAddress returns the transport protocol +// and the ip address from the given string. Defaults to tcp. func ProtocolAndAddress(listenAddr string) (string, string) { protocol, address := "tcp", listenAddr parts := strings.SplitN(address, "://", 2) diff --git a/node/node_test.go b/node/node_test.go index 3c751af6e..641e606c3 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -4,15 +4,19 @@ import ( "testing" "time" - cfg "github.com/tendermint/tendermint/config" + "github.com/stretchr/testify/assert" + "github.com/tendermint/tmlibs/log" + + cfg "github.com/tendermint/tendermint/config" ) func TestNodeStartStop(t *testing.T) { config := cfg.ResetTestRoot("node_node_test") // Create & start node - n := NewNodeDefault(config, log.TestingLogger()) + n, err := DefaultNewNode(config, log.TestingLogger()) + assert.NoError(t, err, "expected no err on DefaultNewNode") n.Start() t.Logf("Started node %v", n.sw.NodeInfo()) diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 14da15c94..55e27f5b8 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -79,8 +79,13 @@ func NewTendermint(app abci.Application) *nm.Node { logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) logger = log.NewFilter(logger, log.AllowError()) privValidatorFile := config.PrivValidatorFile() - privValidator := types.LoadOrGenPrivValidator(privValidatorFile, logger) + privValidator := types.LoadOrGenPrivValidatorFS(privValidatorFile) papp := proxy.NewLocalClientCreator(app) - node := nm.NewNode(config, privValidator, papp, logger) + node, err := nm.NewNode(config, privValidator, papp, + nm.DefaultGenesisDocProviderFunc(config), + nm.DefaultDBProvider, logger) + if err != nil { + panic(err) + } return node } diff --git a/state/execution_test.go b/state/execution_test.go index 3ebc05d37..2987667c5 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -55,13 +55,14 @@ func makeTxs(blockNum int) (txs []types.Tx) { } func state() *State { - return MakeGenesisState(dbm.NewMemDB(), &types.GenesisDoc{ + s, _ := MakeGenesisState(dbm.NewMemDB(), &types.GenesisDoc{ ChainID: chainID, Validators: []types.GenesisValidator{ types.GenesisValidator{privKey.PubKey(), 10000, "test"}, }, AppHash: nil, }) + return s } func makeBlock(num int, state *State) *types.Block { diff --git a/state/state.go b/state/state.go index 2ebbec10e..53ec8cc03 100644 --- a/state/state.go +++ b/state/state.go @@ -68,14 +68,17 @@ type State struct { // GetState loads the most recent state from the database, // or creates a new one from the given genesisFile and persists the result // to the database. -func GetState(stateDB dbm.DB, genesisFile string) *State { +func GetState(stateDB dbm.DB, genesisFile string) (*State, error) { + var err error state := LoadState(stateDB) if state == nil { - state = MakeGenesisStateFromFile(stateDB, genesisFile) + state, err = MakeGenesisStateFromFile(stateDB, genesisFile) + if err != nil { + return nil, err + } state.Save() } - - return state + return state, nil } // LoadState loads the State from the database. @@ -316,25 +319,34 @@ func (vi *ValidatorsInfo) Bytes() []byte { // file. // // Used during replay and in tests. -func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) *State { +func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*State, error) { + genDoc, err := MakeGenesisDocFromFile(genDocFile) + if err != nil { + return nil, err + } + return MakeGenesisState(db, genDoc) +} + +// MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file. +func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) { genDocJSON, err := ioutil.ReadFile(genDocFile) if err != nil { - cmn.Exit(cmn.Fmt("Couldn't read GenesisDoc file: %v", err)) + return nil, fmt.Errorf("Couldn't read GenesisDoc file: %v", err) } genDoc, err := types.GenesisDocFromJSON(genDocJSON) if err != nil { - cmn.Exit(cmn.Fmt("Error reading GenesisDoc: %v", err)) + return nil, fmt.Errorf("Error reading GenesisDoc: %v", err) } - return MakeGenesisState(db, genDoc) + return genDoc, nil } // MakeGenesisState creates state from types.GenesisDoc. // // Used in tests. -func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) *State { +func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) (*State, error) { err := genDoc.ValidateAndComplete() if err != nil { - cmn.Exit(cmn.Fmt("Error in genesis file: %v", err)) + return nil, fmt.Errorf("Error in genesis file: %v", err) } // Make validators slice @@ -363,5 +375,5 @@ func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) *State { AppHash: genDoc.AppHash, TxIndexer: &null.TxIndex{}, // we do not need indexer during replay and in tests LastHeightValidatorsChanged: 1, - } + }, nil } diff --git a/state/state_test.go b/state/state_test.go index 4ff367fa3..2c19a4164 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -23,7 +23,8 @@ import ( func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, *State) { config := cfg.ResetTestRoot("state_") stateDB := dbm.NewDB("state", config.DBBackend, config.DBDir()) - state := GetState(stateDB, config.GenesisFile()) + state, err := GetState(stateDB, config.GenesisFile()) + assert.NoError(t, err, "expected no error on GetState") state.SetLogger(log.TestingLogger()) tearDown := func(t *testing.T) {} diff --git a/types/priv_validator.go b/types/priv_validator.go index 3e84e7f36..8834eb7cc 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -11,8 +11,7 @@ import ( crypto "github.com/tendermint/go-crypto" data "github.com/tendermint/go-wire/data" - . "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tmlibs/log" + cmn "github.com/tendermint/tmlibs/common" ) // TODO: type ? @@ -30,12 +29,26 @@ func voteToStep(vote *Vote) int8 { case VoteTypePrecommit: return stepPrecommit default: - PanicSanity("Unknown vote type") + cmn.PanicSanity("Unknown vote type") return 0 } } -type PrivValidator struct { +// PrivValidator defines the functionality of a local Tendermint validator +// that signs votes, proposals, and heartbeats, and never double signs. +type PrivValidator interface { + GetAddress() data.Bytes // redundant since .PubKey().Address() + GetPubKey() crypto.PubKey + + SignVote(chainID string, vote *Vote) error + SignProposal(chainID string, proposal *Proposal) error + SignHeartbeat(chainID string, heartbeat *Heartbeat) error +} + +// 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 { Address data.Bytes `json:"address"` PubKey crypto.PubKey `json:"pub_key"` LastHeight int `json:"last_height"` @@ -54,121 +67,124 @@ type PrivValidator struct { mtx sync.Mutex } -// This is used to sign votes. +// 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 and SignProposal +// Currently, the only callers are SignVote, SignProposal, and SignHeartbeat. type Signer interface { - PubKey() crypto.PubKey Sign(msg []byte) (crypto.Signature, error) } -// Implements Signer +// DefaultSigner implements Signer. +// It uses a standard, unencrypted crypto.PrivKey. type DefaultSigner struct { - priv crypto.PrivKey + PrivKey crypto.PrivKey `json:"priv_key"` } +// NewDefaultSigner returns an instance of DefaultSigner. func NewDefaultSigner(priv crypto.PrivKey) *DefaultSigner { - return &DefaultSigner{priv: priv} + return &DefaultSigner{ + PrivKey: priv, + } } -// Implements Signer +// Sign implements Signer. It signs the byte slice with a private key. func (ds *DefaultSigner) Sign(msg []byte) (crypto.Signature, error) { - return ds.priv.Sign(msg), nil + return ds.PrivKey.Sign(msg), nil } -// Implements Signer -func (ds *DefaultSigner) PubKey() crypto.PubKey { - return ds.priv.PubKey() +// GetAddress returns the address of the validator. +// Implements PrivValidator. +func (pv *PrivValidatorFS) GetAddress() data.Bytes { + return pv.Address } -func (privVal *PrivValidator) SetSigner(s Signer) { - privVal.Signer = s - privVal.setPubKeyAndAddress() +// GetPubKey returns the public key of the validator. +// Implements PrivValidator. +func (pv *PrivValidatorFS) GetPubKey() crypto.PubKey { + return pv.PubKey } -// Overwrite address and pubkey for convenience -func (privVal *PrivValidator) setPubKeyAndAddress() { - privVal.PubKey = privVal.Signer.PubKey() - privVal.Address = privVal.PubKey.Address() -} - -// Generates a new validator with private key. -func GenPrivValidator() *PrivValidator { +// 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 &PrivValidator{ - Address: pubKey.Address(), - PubKey: pubKey, + return &PrivValidatorFS{ + Address: privKey.PubKey().Address(), + PubKey: privKey.PubKey(), PrivKey: privKey, LastStep: stepNone, - filePath: "", Signer: NewDefaultSigner(privKey), + filePath: filePath, } } -func LoadPrivValidator(filePath string) *PrivValidator { +// LoadPrivValidatorFS loads a PrivValidatorFS from the filePath. +func LoadPrivValidatorFS(filePath string) *PrivValidatorFS { + return LoadPrivValidatorFSWithSigner(filePath, func(privVal PrivValidator) Signer { + return NewDefaultSigner(privVal.(*PrivValidatorFS).PrivKey) + }) +} + +// 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 privVal *PrivValidatorFS + if _, err := os.Stat(filePath); err == nil { + privVal = LoadPrivValidatorFS(filePath) + } else { + privVal = GenPrivValidatorFS(filePath) + privVal.Save() + } + return privVal +} + +// 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(PrivValidator) Signer) *PrivValidatorFS { privValJSONBytes, err := ioutil.ReadFile(filePath) if err != nil { - Exit(err.Error()) + cmn.Exit(err.Error()) } - privVal := PrivValidator{} + privVal := &PrivValidatorFS{} err = json.Unmarshal(privValJSONBytes, &privVal) if err != nil { - Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) + cmn.Exit(cmn.Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) } privVal.filePath = filePath - privVal.Signer = NewDefaultSigner(privVal.PrivKey) - privVal.setPubKeyAndAddress() - return &privVal + privVal.Signer = signerFunc(privVal) + return privVal } -func LoadOrGenPrivValidator(filePath string, logger log.Logger) *PrivValidator { - var privValidator *PrivValidator - if _, err := os.Stat(filePath); err == nil { - privValidator = LoadPrivValidator(filePath) - logger.Info("Loaded PrivValidator", - "file", filePath, "privValidator", privValidator) - } else { - privValidator = GenPrivValidator() - privValidator.SetFile(filePath) - privValidator.Save() - logger.Info("Generated PrivValidator", "file", filePath) - } - return privValidator -} - -func (privVal *PrivValidator) SetFile(filePath string) { - privVal.mtx.Lock() - defer privVal.mtx.Unlock() - privVal.filePath = filePath -} - -func (privVal *PrivValidator) Save() { +// Save persists the PrivValidatorFS to disk. +func (privVal *PrivValidatorFS) Save() { privVal.mtx.Lock() defer privVal.mtx.Unlock() privVal.save() } -func (privVal *PrivValidator) save() { +func (privVal *PrivValidatorFS) save() { if privVal.filePath == "" { - PanicSanity("Cannot save PrivValidator: filePath not set") + cmn.PanicSanity("Cannot save PrivValidator: filePath not set") } jsonBytes, err := json.Marshal(privVal) if err != nil { // `@; BOOM!!! - PanicCrisis(err) + cmn.PanicCrisis(err) } - err = WriteFileAtomic(privVal.filePath, jsonBytes, 0600) + err = cmn.WriteFileAtomic(privVal.filePath, jsonBytes, 0600) if err != nil { // `@; BOOM!!! - PanicCrisis(err) + cmn.PanicCrisis(err) } } +// Reset resets all fields in the PrivValidatorFS. // NOTE: Unsafe! -func (privVal *PrivValidator) Reset() { +func (privVal *PrivValidatorFS) Reset() { privVal.LastHeight = 0 privVal.LastRound = 0 privVal.LastStep = 0 @@ -177,22 +193,22 @@ func (privVal *PrivValidator) Reset() { privVal.Save() } -func (privVal *PrivValidator) GetAddress() []byte { - return privVal.Address -} - -func (privVal *PrivValidator) SignVote(chainID string, vote *Vote) error { +// SignVote signs a canonical representation of the vote, along with the chainID. +// Implements PrivValidator. +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)) if err != nil { - return errors.New(Fmt("Error signing vote: %v", err)) + return errors.New(cmn.Fmt("Error signing vote: %v", err)) } vote.Signature = signature return nil } -func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) error { +// SignProposal signs a canonical representation of the proposal, along with the chainID. +// Implements PrivValidator. +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)) @@ -203,8 +219,11 @@ func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) e return nil } -// check if there's a regression. Else sign and write the hrs+signature to disk -func (privVal *PrivValidator) signBytesHRS(height, round int, step int8, signBytes []byte) (crypto.Signature, error) { +// signBytesHRS signs the given signBytes if the height/round/step (HRS) +// are greater than the latest state. If the HRS are equal, +// it returns the privValidator.LastSignature. +func (privVal *PrivValidatorFS) signBytesHRS(height, round int, step int8, signBytes []byte) (crypto.Signature, error) { + sig := crypto.Signature{} // If height regression, err if privVal.LastHeight > height { @@ -223,7 +242,7 @@ func (privVal *PrivValidator) signBytesHRS(height, round int, step int8, signByt } else if privVal.LastStep == step { if privVal.LastSignBytes != nil { if privVal.LastSignature.Empty() { - PanicSanity("privVal: LastSignature is nil but LastSignBytes is not!") + cmn.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), @@ -253,10 +272,11 @@ func (privVal *PrivValidator) signBytesHRS(height, round int, step int8, signByt privVal.save() return sig, nil - } -func (privVal *PrivValidator) SignHeartbeat(chainID string, heartbeat *Heartbeat) error { +// SignHeartbeat signs a canonical representation of the heartbeat, along with the chainID. +// Implements PrivValidator. +func (privVal *PrivValidatorFS) SignHeartbeat(chainID string, heartbeat *Heartbeat) error { privVal.mtx.Lock() defer privVal.mtx.Unlock() var err error @@ -264,20 +284,21 @@ func (privVal *PrivValidator) SignHeartbeat(chainID string, heartbeat *Heartbeat return err } -func (privVal *PrivValidator) String() string { - return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", privVal.Address, privVal.LastHeight, privVal.LastRound, privVal.LastStep) +// String returns a string representation of the PrivValidatorFS. +func (privVal *PrivValidatorFS) String() string { + return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", privVal.GetAddress(), privVal.LastHeight, privVal.LastRound, privVal.LastStep) } //------------------------------------- -type PrivValidatorsByAddress []*PrivValidator +type PrivValidatorsByAddress []*PrivValidatorFS func (pvs PrivValidatorsByAddress) Len() int { return len(pvs) } func (pvs PrivValidatorsByAddress) Less(i, j int) bool { - return bytes.Compare(pvs[i].Address, pvs[j].Address) == -1 + return bytes.Compare(pvs[i].GetAddress(), pvs[j].GetAddress()) == -1 } func (pvs PrivValidatorsByAddress) Swap(i, j int) { diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index 1eb0b57db..ac91de861 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -4,14 +4,44 @@ import ( "encoding/hex" "encoding/json" "fmt" + "os" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/go-wire/data" + cmn "github.com/tendermint/tmlibs/common" ) -func TestLoadValidator(t *testing.T) { +func TestGenLoadValidator(t *testing.T) { + assert := assert.New(t) + + _, tempFilePath := cmn.Tempfile("priv_validator_") + privVal := GenPrivValidatorFS(tempFilePath) + + height := 100 + privVal.LastHeight = height + privVal.Save() + addr := privVal.GetAddress() + + privVal = LoadPrivValidatorFS(tempFilePath) + assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") + assert.Equal(height, privVal.LastHeight, "expected privval.LastHeight to have been saved") +} + +func TestLoadOrGenValidator(t *testing.T) { + assert := assert.New(t) + + _, tempFilePath := cmn.Tempfile("priv_validator_") + os.Remove(tempFilePath) + privVal := LoadOrGenPrivValidatorFS(tempFilePath) + addr := privVal.GetAddress() + privVal = LoadOrGenPrivValidatorFS(tempFilePath) + assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") +} + +func TestUnmarshalValidator(t *testing.T) { assert, require := assert.New(t), require.New(t) // create some fixed values @@ -34,23 +64,23 @@ func TestLoadValidator(t *testing.T) { "type": "ed25519", "data": "%s" }, - "priv_key": { - "type": "ed25519", - "data": "%s" - }, "last_height": 0, "last_round": 0, "last_step": 0, - "last_signature": null + "last_signature": null, + "priv_key": { + "type": "ed25519", + "data": "%s" + } }`, addrStr, pubStr, privStr) - val := PrivValidator{} + val := PrivValidatorFS{} err = json.Unmarshal([]byte(serialized), &val) require.Nil(err, "%+v", err) // make sure the values match - assert.EqualValues(addrBytes, val.Address) - assert.EqualValues(pubKey, val.PubKey) + assert.EqualValues(addrBytes, val.GetAddress()) + assert.EqualValues(pubKey, val.GetPubKey()) assert.EqualValues(privKey, val.PrivKey) // export it and make sure it is the same @@ -58,3 +88,89 @@ func TestLoadValidator(t *testing.T) { require.Nil(err, "%+v", err) assert.JSONEq(serialized, string(out)) } + +func TestSignVote(t *testing.T) { + assert := assert.New(t) + + _, tempFilePath := cmn.Tempfile("priv_validator_") + privVal := GenPrivValidatorFS(tempFilePath) + + block1 := BlockID{[]byte{1, 2, 3}, PartSetHeader{}} + block2 := BlockID{[]byte{3, 2, 1}, PartSetHeader{}} + height, round := 10, 1 + voteType := VoteTypePrevote + + // sign a vote for first time + vote := newVote(privVal.Address, 0, height, round, voteType, block1) + err := privVal.SignVote("mychainid", vote) + assert.NoError(err, "expected no error signing vote") + + // try to sign the same vote again; should be fine + err = privVal.SignVote("mychainid", vote) + assert.NoError(err, "expected no error on signing same vote") + + // now try some bad votes + cases := []*Vote{ + newVote(privVal.Address, 0, height, round-1, voteType, block1), // round regression + newVote(privVal.Address, 0, height-1, round, voteType, block1), // height regression + newVote(privVal.Address, 0, height-2, round+4, voteType, block1), // height regression and different round + newVote(privVal.Address, 0, height, round, voteType, block2), // different block + } + + for _, c := range cases { + err = privVal.SignVote("mychainid", c) + assert.Error(err, "expected error on signing conflicting vote") + } +} + +func TestSignProposal(t *testing.T) { + assert := assert.New(t) + + _, tempFilePath := cmn.Tempfile("priv_validator_") + privVal := GenPrivValidatorFS(tempFilePath) + + block1 := PartSetHeader{5, []byte{1, 2, 3}} + block2 := PartSetHeader{10, []byte{3, 2, 1}} + height, round := 10, 1 + + // sign a proposal for first time + proposal := newProposal(height, round, block1) + err := privVal.SignProposal("mychainid", proposal) + assert.NoError(err, "expected no error signing proposal") + + // try to sign the same proposal again; should be fine + err = privVal.SignProposal("mychainid", proposal) + assert.NoError(err, "expected no error on signing same proposal") + + // now try some bad Proposals + cases := []*Proposal{ + newProposal(height, round-1, block1), // round regression + newProposal(height-1, round, block1), // height regression + newProposal(height-2, round+4, block1), // height regression and different round + newProposal(height, round, block2), // different block + } + + for _, c := range cases { + err = privVal.SignProposal("mychainid", c) + assert.Error(err, "expected error on signing conflicting proposal") + } +} + +func newVote(addr data.Bytes, idx, height, round int, typ byte, blockID BlockID) *Vote { + return &Vote{ + ValidatorAddress: addr, + ValidatorIndex: idx, + Height: height, + Round: round, + Type: typ, + BlockID: blockID, + } +} + +func newProposal(height, round int, partsHeader PartSetHeader) *Proposal { + return &Proposal{ + Height: height, + Round: round, + BlockPartsHeader: partsHeader, + } +} diff --git a/types/proposal_test.go b/types/proposal_test.go index 6f6189595..d1c991849 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -28,17 +28,17 @@ func BenchmarkProposalWriteSignBytes(b *testing.B) { } func BenchmarkProposalSign(b *testing.B) { - privVal := GenPrivValidator() + privVal := GenPrivValidatorFS("") for i := 0; i < b.N; i++ { - privVal.Sign(SignBytes("test_chain_id", testProposal)) + privVal.Signer.Sign(SignBytes("test_chain_id", testProposal)) } } func BenchmarkProposalVerifySignature(b *testing.B) { signBytes := SignBytes("test_chain_id", testProposal) - privVal := GenPrivValidator() - signature, _ := privVal.Sign(signBytes) - pubKey := privVal.PubKey + privVal := GenPrivValidatorFS("") + signature, _ := privVal.Signer.Sign(signBytes) + pubKey := privVal.GetPubKey() for i := 0; i < b.N; i++ { pubKey.VerifyBytes(SignBytes("test_chain_id", testProposal), signature) diff --git a/types/validator.go b/types/validator.go index 24f8974f3..506828913 100644 --- a/types/validator.go +++ b/types/validator.go @@ -106,14 +106,13 @@ func (vc validatorCodec) Compare(o1 interface{}, o2 interface{}) int { //-------------------------------------------------------------------------------- // For testing... -func RandValidator(randPower bool, minPower int64) (*Validator, *PrivValidator) { - 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()) } - val := NewValidator(privVal.PubKey, votePower) + val := NewValidator(privVal.GetPubKey(), votePower) return val, privVal } diff --git a/types/validator_set.go b/types/validator_set.go index e334ed9f3..1e9e3e314 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -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, []*PrivValidator) { +func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []*PrivValidatorFS) { vals := make([]*Validator, numValidators) - privValidators := make([]*PrivValidator, numValidators) + privValidators := make([]*PrivValidatorFS, numValidators) for i := 0; i < numValidators; i++ { val, privValidator := RandValidator(false, votingPower) vals[i] = val diff --git a/types/vote_set_test.go b/types/vote_set_test.go index ddc055489..80ee6135c 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -11,7 +11,7 @@ import ( ) // NOTE: privValidators are in order -func randVoteSet(height int, round int, type_ byte, numValidators int, votingPower int64) (*VoteSet, *ValidatorSet, []*PrivValidator) { +func randVoteSet(height int, round int, type_ byte, numValidators int, votingPower int64) (*VoteSet, *ValidatorSet, []*PrivValidatorFS) { valSet, privValidators := RandValidatorSet(numValidators, votingPower) return NewVoteSet("test_chain_id", height, round, type_, valSet), valSet, privValidators } @@ -59,9 +59,9 @@ func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote { return vote } -func signAddVote(privVal *PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) { +func signAddVote(privVal *PrivValidatorFS, vote *Vote, voteSet *VoteSet) (bool, error) { var err error - vote.Signature, err = privVal.Sign(SignBytes(voteSet.ChainID(), vote)) + vote.Signature, err = privVal.Signer.Sign(SignBytes(voteSet.ChainID(), vote)) if err != nil { return false, err } @@ -76,7 +76,7 @@ func TestAddVote(t *testing.T) { // t.Logf(">> %v", voteSet) - if voteSet.GetByAddress(val0.Address) != nil { + if voteSet.GetByAddress(val0.GetAddress()) != nil { t.Errorf("Expected GetByAddress(val0.Address) to be nil") } if voteSet.BitArray().GetIndex(0) { @@ -88,7 +88,7 @@ func TestAddVote(t *testing.T) { } vote := &Vote{ - ValidatorAddress: val0.Address, + ValidatorAddress: val0.GetAddress(), ValidatorIndex: 0, // since privValidators are in order Height: height, Round: round, @@ -100,7 +100,7 @@ func TestAddVote(t *testing.T) { t.Error(err) } - if voteSet.GetByAddress(val0.Address) == nil { + if voteSet.GetByAddress(val0.GetAddress()) == nil { t.Errorf("Expected GetByAddress(val0.Address) to be present") } if !voteSet.BitArray().GetIndex(0) { @@ -126,7 +126,7 @@ func Test2_3Majority(t *testing.T) { } // 6 out of 10 voted for nil. for i := 0; i < 6; i++ { - vote := withValidator(voteProto, privValidators[i].Address, i) + vote := withValidator(voteProto, privValidators[i].GetAddress(), i) signAddVote(privValidators[i], vote, voteSet) } blockID, ok := voteSet.TwoThirdsMajority() @@ -136,7 +136,7 @@ func Test2_3Majority(t *testing.T) { // 7th validator voted for some blockhash { - vote := withValidator(voteProto, privValidators[6].Address, 6) + vote := withValidator(voteProto, privValidators[6].GetAddress(), 6) signAddVote(privValidators[6], withBlockHash(vote, RandBytes(32)), voteSet) blockID, ok = voteSet.TwoThirdsMajority() if ok || !blockID.IsZero() { @@ -146,7 +146,7 @@ func Test2_3Majority(t *testing.T) { // 8th validator voted for nil. { - vote := withValidator(voteProto, privValidators[7].Address, 7) + vote := withValidator(voteProto, privValidators[7].GetAddress(), 7) signAddVote(privValidators[7], vote, voteSet) blockID, ok = voteSet.TwoThirdsMajority() if !ok || !blockID.IsZero() { @@ -174,7 +174,7 @@ func Test2_3MajorityRedux(t *testing.T) { // 66 out of 100 voted for nil. for i := 0; i < 66; i++ { - vote := withValidator(voteProto, privValidators[i].Address, i) + vote := withValidator(voteProto, privValidators[i].GetAddress(), i) signAddVote(privValidators[i], vote, voteSet) } blockID, ok := voteSet.TwoThirdsMajority() @@ -184,7 +184,7 @@ func Test2_3MajorityRedux(t *testing.T) { // 67th validator voted for nil { - vote := withValidator(voteProto, privValidators[66].Address, 66) + vote := withValidator(voteProto, privValidators[66].GetAddress(), 66) signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet) blockID, ok = voteSet.TwoThirdsMajority() if ok || !blockID.IsZero() { @@ -194,7 +194,7 @@ func Test2_3MajorityRedux(t *testing.T) { // 68th validator voted for a different BlockParts PartSetHeader { - vote := withValidator(voteProto, privValidators[67].Address, 67) + vote := withValidator(voteProto, privValidators[67].GetAddress(), 67) blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)} signAddVote(privValidators[67], withBlockPartsHeader(vote, blockPartsHeader), voteSet) blockID, ok = voteSet.TwoThirdsMajority() @@ -205,7 +205,7 @@ func Test2_3MajorityRedux(t *testing.T) { // 69th validator voted for different BlockParts Total { - vote := withValidator(voteProto, privValidators[68].Address, 68) + vote := withValidator(voteProto, privValidators[68].GetAddress(), 68) blockPartsHeader := PartSetHeader{blockPartsTotal + 1, blockPartsHeader.Hash} signAddVote(privValidators[68], withBlockPartsHeader(vote, blockPartsHeader), voteSet) blockID, ok = voteSet.TwoThirdsMajority() @@ -216,7 +216,7 @@ func Test2_3MajorityRedux(t *testing.T) { // 70th validator voted for different BlockHash { - vote := withValidator(voteProto, privValidators[69].Address, 69) + vote := withValidator(voteProto, privValidators[69].GetAddress(), 69) signAddVote(privValidators[69], withBlockHash(vote, RandBytes(32)), voteSet) blockID, ok = voteSet.TwoThirdsMajority() if ok || !blockID.IsZero() { @@ -226,7 +226,7 @@ func Test2_3MajorityRedux(t *testing.T) { // 71st validator voted for the right BlockHash & BlockPartsHeader { - vote := withValidator(voteProto, privValidators[70].Address, 70) + vote := withValidator(voteProto, privValidators[70].GetAddress(), 70) signAddVote(privValidators[70], vote, voteSet) blockID, ok = voteSet.TwoThirdsMajority() if !ok || !blockID.Equals(BlockID{blockHash, blockPartsHeader}) { @@ -250,7 +250,7 @@ func TestBadVotes(t *testing.T) { // val0 votes for nil. { - vote := withValidator(voteProto, privValidators[0].Address, 0) + vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) added, err := signAddVote(privValidators[0], vote, voteSet) if !added || err != nil { t.Errorf("Expected VoteSet.Add to succeed") @@ -259,7 +259,7 @@ func TestBadVotes(t *testing.T) { // val0 votes again for some block. { - vote := withValidator(voteProto, privValidators[0].Address, 0) + vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) added, err := signAddVote(privValidators[0], withBlockHash(vote, RandBytes(32)), voteSet) if added || err == nil { t.Errorf("Expected VoteSet.Add to fail, conflicting vote.") @@ -268,7 +268,7 @@ func TestBadVotes(t *testing.T) { // val1 votes on another height { - vote := withValidator(voteProto, privValidators[1].Address, 1) + vote := withValidator(voteProto, privValidators[1].GetAddress(), 1) added, err := signAddVote(privValidators[1], withHeight(vote, height+1), voteSet) if added || err == nil { t.Errorf("Expected VoteSet.Add to fail, wrong height") @@ -277,7 +277,7 @@ func TestBadVotes(t *testing.T) { // val2 votes on another round { - vote := withValidator(voteProto, privValidators[2].Address, 2) + vote := withValidator(voteProto, privValidators[2].GetAddress(), 2) added, err := signAddVote(privValidators[2], withRound(vote, round+1), voteSet) if added || err == nil { t.Errorf("Expected VoteSet.Add to fail, wrong round") @@ -286,7 +286,7 @@ func TestBadVotes(t *testing.T) { // val3 votes of another type. { - vote := withValidator(voteProto, privValidators[3].Address, 3) + vote := withValidator(voteProto, privValidators[3].GetAddress(), 3) added, err := signAddVote(privValidators[3], withType(vote, VoteTypePrecommit), voteSet) if added || err == nil { t.Errorf("Expected VoteSet.Add to fail, wrong type") @@ -311,7 +311,7 @@ func TestConflicts(t *testing.T) { // val0 votes for nil. { - vote := withValidator(voteProto, privValidators[0].Address, 0) + vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) added, err := signAddVote(privValidators[0], vote, voteSet) if !added || err != nil { t.Errorf("Expected VoteSet.Add to succeed") @@ -320,7 +320,7 @@ func TestConflicts(t *testing.T) { // val0 votes again for blockHash1. { - vote := withValidator(voteProto, privValidators[0].Address, 0) + vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet) if added { t.Errorf("Expected VoteSet.Add to fail, conflicting vote.") @@ -335,7 +335,7 @@ func TestConflicts(t *testing.T) { // val0 votes again for blockHash1. { - vote := withValidator(voteProto, privValidators[0].Address, 0) + vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet) if !added { t.Errorf("Expected VoteSet.Add to succeed, called SetPeerMaj23().") @@ -350,7 +350,7 @@ func TestConflicts(t *testing.T) { // val0 votes again for blockHash1. { - vote := withValidator(voteProto, privValidators[0].Address, 0) + vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash2), voteSet) if added { t.Errorf("Expected VoteSet.Add to fail, duplicate SetPeerMaj23() from peerA") @@ -362,7 +362,7 @@ func TestConflicts(t *testing.T) { // val1 votes for blockHash1. { - vote := withValidator(voteProto, privValidators[1].Address, 1) + vote := withValidator(voteProto, privValidators[1].GetAddress(), 1) added, err := signAddVote(privValidators[1], withBlockHash(vote, blockHash1), voteSet) if !added || err != nil { t.Errorf("Expected VoteSet.Add to succeed") @@ -379,7 +379,7 @@ func TestConflicts(t *testing.T) { // val2 votes for blockHash2. { - vote := withValidator(voteProto, privValidators[2].Address, 2) + vote := withValidator(voteProto, privValidators[2].GetAddress(), 2) added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash2), voteSet) if !added || err != nil { t.Errorf("Expected VoteSet.Add to succeed") @@ -399,7 +399,7 @@ func TestConflicts(t *testing.T) { // val2 votes for blockHash1. { - vote := withValidator(voteProto, privValidators[2].Address, 2) + vote := withValidator(voteProto, privValidators[2].GetAddress(), 2) added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash1), voteSet) if !added { t.Errorf("Expected VoteSet.Add to succeed") @@ -439,7 +439,7 @@ func TestMakeCommit(t *testing.T) { // 6 out of 10 voted for some block. for i := 0; i < 6; i++ { - vote := withValidator(voteProto, privValidators[i].Address, i) + vote := withValidator(voteProto, privValidators[i].GetAddress(), i) signAddVote(privValidators[i], vote, voteSet) } @@ -448,7 +448,7 @@ func TestMakeCommit(t *testing.T) { // 7th voted for some other block. { - vote := withValidator(voteProto, privValidators[6].Address, 6) + vote := withValidator(voteProto, privValidators[6].GetAddress(), 6) vote = withBlockHash(vote, RandBytes(32)) vote = withBlockPartsHeader(vote, PartSetHeader{123, RandBytes(32)}) signAddVote(privValidators[6], vote, voteSet) @@ -456,7 +456,7 @@ func TestMakeCommit(t *testing.T) { // The 8th voted like everyone else. { - vote := withValidator(voteProto, privValidators[7].Address, 7) + vote := withValidator(voteProto, privValidators[7].GetAddress(), 7) signAddVote(privValidators[7], vote, voteSet) }