From 2c129447fdadd42d397611b68941b5073f985094 Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Wed, 30 Aug 2017 22:21:32 +0200 Subject: [PATCH 01/22] Example that showcases how to build your own tendermint node This example shows how a user of the tendermint library can build their own node and supply it with its own commands. It includes two todos in order to make it easier for library users to use tendermint. --- Makefile | 4 + cmd/hsm/commands/run_node.go | 107 ++++++++++++++++++ cmd/hsm/main.go | 35 ++++++ cmd/tendermint/commands/gen_validator.go | 6 +- cmd/tendermint/commands/init.go | 6 +- cmd/tendermint/commands/probe_upnp.go | 6 +- cmd/tendermint/commands/replay.go | 9 +- .../commands/reset_priv_validator.go | 9 +- cmd/tendermint/commands/run_node.go | 5 +- cmd/tendermint/commands/show_validator.go | 6 +- cmd/tendermint/commands/testnet.go | 8 +- cmd/tendermint/commands/version.go | 8 +- 12 files changed, 161 insertions(+), 48 deletions(-) create mode 100644 cmd/hsm/commands/run_node.go create mode 100644 cmd/hsm/main.go diff --git a/Makefile b/Makefile index 8c9c5214d..328e65f8a 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,10 @@ build: go build \ --ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse HEAD`" -o build/tendermint ./cmd/tendermint/ +build_hsm: + go build \ + --ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse HEAD`" -o build/tendermint ./cmd/hsm/ + build_race: go build -race -o build/tendermint ./cmd/tendermint diff --git a/cmd/hsm/commands/run_node.go b/cmd/hsm/commands/run_node.go new file mode 100644 index 000000000..94a1c548e --- /dev/null +++ b/cmd/hsm/commands/run_node.go @@ -0,0 +1,107 @@ +package commands + +import ( + "fmt" + "os" + "time" + + "github.com/spf13/cobra" + + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/node" + "github.com/tendermint/tendermint/proxy" + "github.com/tendermint/tendermint/types" + + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tmlibs/log" +) + +var ( + config = cfg.DefaultConfig() + logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") +) + +var RunNodeCmd = &cobra.Command{ + Use: "node", + Short: "Run the tendermint node", + RunE: runNode, +} + +func init() { + AddNodeFlags(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) { + // bind flags + cmd.Flags().String("moniker", config.Moniker, "Node Name") + + // node flags + cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing") + + // abci flags + cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'dummy' for local testing.") + cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)") + + // rpc flags + cmd.Flags().String("rpc.laddr", config.RPC.ListenAddress, "RPC listen address. Port required") + cmd.Flags().String("rpc.grpc_laddr", config.RPC.GRPCListenAddress, "GRPC listen address (BroadcastTx only). Port required") + cmd.Flags().Bool("rpc.unsafe", config.RPC.Unsafe, "Enabled unsafe rpc methods") + + // p2p flags + cmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)") + cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma delimited host:port seed nodes") + cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration") + cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable Peer-Exchange (dev feature)") + + // consensus flags + 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 { + + // Wait until the genesis doc becomes available + // This is for Mintnet compatibility. + // TODO: If Mintnet gets deprecated or genesis_file is + // always available, remove. + genDocFile := config.GenesisFile() + for !cmn.FileExists(genDocFile) { + logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile)) + time.Sleep(time.Second) + } + + genDoc, err := types.GenesisDocFromFile(genDocFile) + if err != nil { + return err + } + config.ChainID = genDoc.ChainID + + // Create & start node + // n := node.NewNodeDefault(config, logger.With("module", "node")) + + // TODO: Make types.PrivValidator an interface so that it can be provided + // by a hardware wallet or any other wallet provider. + + // The next two lines show how a private validator is setup. + privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile(), logger) + privValidator.SetSigner(types.NewDefaultSigner(privValidator.PrivKey)) + + n := node.NewNode(config, privValidator, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) + 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 +} diff --git a/cmd/hsm/main.go b/cmd/hsm/main.go new file mode 100644 index 000000000..743a8fcd9 --- /dev/null +++ b/cmd/hsm/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "os" + + tc "github.com/tendermint/tendermint/cmd/tendermint/commands" + "github.com/tendermint/tmlibs/cli" + + "github.com/tendermint/tendermint/cmd/hsm/commands" +) + +func main() { + // TODO: Make it easier to build a tendermint instance from scratch. + // All commands should be exported and it should be easy to override + // certain aspects of a single command. + // Probably every command should have a constructor that allows a user + // to vary the configuration. This is at least true for run_node.go + + rootCmd := tc.RootCmd + rootCmd.AddCommand(tc.GenValidatorCmd) + rootCmd.AddCommand(tc.InitFilesCmd) + rootCmd.AddCommand(tc.ProbeUpnpCmd) + rootCmd.AddCommand(tc.ReplayCmd) + rootCmd.AddCommand(tc.ReplayConsoleCmd) + rootCmd.AddCommand(tc.ResetAllCmd) + rootCmd.AddCommand(tc.ResetPrivValidatorCmd) + rootCmd.AddCommand(tc.ShowValidatorCmd) + rootCmd.AddCommand(tc.TestnetFilesCmd) + rootCmd.AddCommand(tc.VersionCmd) + + rootCmd.AddCommand(commands.RunNodeCmd) + + cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) + cmd.Execute() +} diff --git a/cmd/tendermint/commands/gen_validator.go b/cmd/tendermint/commands/gen_validator.go index 97c583c22..df82554d8 100644 --- a/cmd/tendermint/commands/gen_validator.go +++ b/cmd/tendermint/commands/gen_validator.go @@ -9,16 +9,12 @@ import ( "github.com/tendermint/tendermint/types" ) -var genValidatorCmd = &cobra.Command{ +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() privValidatorJSONBytes, _ := json.MarshalIndent(privValidator, "", "\t") diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index ce900defa..b844386c5 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -9,16 +9,12 @@ import ( cmn "github.com/tendermint/tmlibs/common" ) -var initFilesCmd = &cobra.Command{ +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) { diff --git a/cmd/tendermint/commands/probe_upnp.go b/cmd/tendermint/commands/probe_upnp.go index e23e48973..a4c71d59e 100644 --- a/cmd/tendermint/commands/probe_upnp.go +++ b/cmd/tendermint/commands/probe_upnp.go @@ -9,16 +9,12 @@ import ( "github.com/tendermint/tendermint/p2p/upnp" ) -var probeUpnpCmd = &cobra.Command{ +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..422d44159 100644 --- a/cmd/tendermint/commands/replay.go +++ b/cmd/tendermint/commands/replay.go @@ -6,7 +6,7 @@ import ( "github.com/tendermint/tendermint/consensus" ) -var replayCmd = &cobra.Command{ +var ReplayCmd = &cobra.Command{ Use: "replay", Short: "Replay messages from WAL", Run: func(cmd *cobra.Command, args []string) { @@ -14,15 +14,10 @@ var replayCmd = &cobra.Command{ }, } -var replayConsoleCmd = &cobra.Command{ +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..389e5b79b 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -9,23 +9,18 @@ import ( "github.com/tendermint/tmlibs/log" ) -var resetAllCmd = &cobra.Command{ +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{ +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) -} - // XXX: this is totally unsafe. // it's only suitable for testnets. func resetAll(cmd *cobra.Command, args []string) { diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 6740df289..413e2cc7a 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -9,15 +9,14 @@ import ( "github.com/tendermint/tendermint/types" ) -var runNodeCmd = &cobra.Command{ +var RunNodeCmd = &cobra.Command{ Use: "node", Short: "Run the tendermint node", RunE: runNode, } func init() { - AddNodeFlags(runNodeCmd) - RootCmd.AddCommand(runNodeCmd) + AddNodeFlags(RunNodeCmd) } // AddNodeFlags exposes some common configuration options on the command-line diff --git a/cmd/tendermint/commands/show_validator.go b/cmd/tendermint/commands/show_validator.go index 53a687c6d..21cc90270 100644 --- a/cmd/tendermint/commands/show_validator.go +++ b/cmd/tendermint/commands/show_validator.go @@ -9,16 +9,12 @@ import ( "github.com/tendermint/tendermint/types" ) -var showValidatorCmd = &cobra.Command{ +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) pubKeyJSONBytes, _ := data.ToJSON(privValidator.PubKey) diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index 315f3b498..75e016c9d 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -11,7 +11,7 @@ import ( cmn "github.com/tendermint/tmlibs/common" ) -var testnetFilesCmd = &cobra.Command{ +var TestnetFilesCmd = &cobra.Command{ Use: "testnet", Short: "Initialize files for a Tendermint testnet", Run: testnetFiles, @@ -24,12 +24,10 @@ 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) } func testnetFiles(cmd *cobra.Command, args []string) { diff --git a/cmd/tendermint/commands/version.go b/cmd/tendermint/commands/version.go index 5c92160ea..692cba4ad 100644 --- a/cmd/tendermint/commands/version.go +++ b/cmd/tendermint/commands/version.go @@ -8,14 +8,10 @@ import ( "github.com/tendermint/tendermint/version" ) -var versionCmd = &cobra.Command{ +var VersionCmd = &cobra.Command{ Use: "version", - Short: "Show version info", + Short: "Show version infoooooooo", Run: func(cmd *cobra.Command, args []string) { fmt.Println(version.Version) }, } - -func init() { - RootCmd.AddCommand(versionCmd) -} From 83f7d5c95ad2fca459ae605265eba37869eae6e9 Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Thu, 31 Aug 2017 10:34:45 +0200 Subject: [PATCH 02/22] Setup custom tendermint node By exporting all of the commands, we allow users to setup their own tendermint node cli. This enables users to provide a different pivValidator without the need to fork tendermint. --- cmd/hsm/main.go | 15 ++++++++-- cmd/tendermint/commands/run_node.go | 43 +++++++++++++++++++++++++++++ types/priv_validator.go | 10 +++++-- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/cmd/hsm/main.go b/cmd/hsm/main.go index 743a8fcd9..657c946a1 100644 --- a/cmd/hsm/main.go +++ b/cmd/hsm/main.go @@ -3,10 +3,17 @@ package main import ( "os" - tc "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tmlibs/cli" + "github.com/tendermint/tmlibs/log" - "github.com/tendermint/tendermint/cmd/hsm/commands" + tc "github.com/tendermint/tendermint/cmd/tendermint/commands" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/types" +) + +var ( + config = cfg.DefaultConfig() + logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") ) func main() { @@ -28,7 +35,9 @@ func main() { rootCmd.AddCommand(tc.TestnetFilesCmd) rootCmd.AddCommand(tc.VersionCmd) - rootCmd.AddCommand(commands.RunNodeCmd) + privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile(), logger) + privValidator.SetSigner(types.NewDefaultSigner(privValidator.PrivKey)) + rootCmd.AddCommand(tc.NewRunNodeCmd(privValidator)) cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) cmd.Execute() diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 413e2cc7a..51b370300 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -5,16 +5,59 @@ import ( "github.com/spf13/cobra" + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tendermint/node" + "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" ) +// RunNodeCmd creates and starts a tendermint node. var RunNodeCmd = &cobra.Command{ Use: "node", Short: "Run the tendermint node", RunE: runNode, } +// NewRunNodeCmd creates and starts a tendermint node. It allows the user to +// use a custom PrivValidator. +func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { + return &cobra.Command{ + Use: "node", + Short: "Run the tendermint node", + RunE: func(cmd *cobra.Command, args []string) error { + // Wait until the genesis doc becomes available + // This is for Mintnet compatibility. + // TODO: If Mintnet gets deprecated or genesis_file is + // always available, remove. + genDocFile := config.GenesisFile() + for !cmn.FileExists(genDocFile) { + logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile)) + time.Sleep(time.Second) + } + + genDoc, err := types.GenesisDocFromFile(genDocFile) + if err != nil { + return err + } + config.ChainID = genDoc.ChainID + + // Create & start node + n := node.NewNode(config, privVal, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) + 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 + }, + } +} + func init() { AddNodeFlags(RunNodeCmd) } diff --git a/types/priv_validator.go b/types/priv_validator.go index 3e84e7f36..072b394ca 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -35,6 +35,7 @@ func voteToStep(vote *Vote) int8 { } } +// PrivValidator implements the functionality for signing blocks. type PrivValidator struct { Address data.Bytes `json:"address"` PubKey crypto.PubKey `json:"pub_key"` @@ -58,26 +59,29 @@ type PrivValidator struct { // 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 +// Signer is an interface that describes how to sign votes. type Signer interface { PubKey() crypto.PubKey Sign(msg []byte) (crypto.Signature, error) } -// Implements Signer +// DefaultSigner implements Signer. type DefaultSigner struct { priv crypto.PrivKey } +// NewDefaultSigner returns an instance of DefaultSigner. func NewDefaultSigner(priv crypto.PrivKey) *DefaultSigner { return &DefaultSigner{priv: 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 } -// Implements Signer +// 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.priv.PubKey() } From 2ccc3326ec14bce6df4e5448f97936e215a80738 Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Thu, 31 Aug 2017 10:42:10 +0200 Subject: [PATCH 03/22] Remove redundant file --- cmd/hsm/commands/run_node.go | 107 ----------------------------------- 1 file changed, 107 deletions(-) delete mode 100644 cmd/hsm/commands/run_node.go diff --git a/cmd/hsm/commands/run_node.go b/cmd/hsm/commands/run_node.go deleted file mode 100644 index 94a1c548e..000000000 --- a/cmd/hsm/commands/run_node.go +++ /dev/null @@ -1,107 +0,0 @@ -package commands - -import ( - "fmt" - "os" - "time" - - "github.com/spf13/cobra" - - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/node" - "github.com/tendermint/tendermint/proxy" - "github.com/tendermint/tendermint/types" - - cmn "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tmlibs/log" -) - -var ( - config = cfg.DefaultConfig() - logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") -) - -var RunNodeCmd = &cobra.Command{ - Use: "node", - Short: "Run the tendermint node", - RunE: runNode, -} - -func init() { - AddNodeFlags(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) { - // bind flags - cmd.Flags().String("moniker", config.Moniker, "Node Name") - - // node flags - cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing") - - // abci flags - cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'dummy' for local testing.") - cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)") - - // rpc flags - cmd.Flags().String("rpc.laddr", config.RPC.ListenAddress, "RPC listen address. Port required") - cmd.Flags().String("rpc.grpc_laddr", config.RPC.GRPCListenAddress, "GRPC listen address (BroadcastTx only). Port required") - cmd.Flags().Bool("rpc.unsafe", config.RPC.Unsafe, "Enabled unsafe rpc methods") - - // p2p flags - cmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)") - cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma delimited host:port seed nodes") - cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration") - cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable Peer-Exchange (dev feature)") - - // consensus flags - 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 { - - // Wait until the genesis doc becomes available - // This is for Mintnet compatibility. - // TODO: If Mintnet gets deprecated or genesis_file is - // always available, remove. - genDocFile := config.GenesisFile() - for !cmn.FileExists(genDocFile) { - logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile)) - time.Sleep(time.Second) - } - - genDoc, err := types.GenesisDocFromFile(genDocFile) - if err != nil { - return err - } - config.ChainID = genDoc.ChainID - - // Create & start node - // n := node.NewNodeDefault(config, logger.With("module", "node")) - - // TODO: Make types.PrivValidator an interface so that it can be provided - // by a hardware wallet or any other wallet provider. - - // The next two lines show how a private validator is setup. - privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile(), logger) - privValidator.SetSigner(types.NewDefaultSigner(privValidator.PrivKey)) - - n := node.NewNode(config, privValidator, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) - 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 -} From bf5e95608756bc59f65442e4d9a654dd586f7598 Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Thu, 31 Aug 2017 10:43:11 +0200 Subject: [PATCH 04/22] Change capitalisation --- cmd/tendermint/commands/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/tendermint/commands/root.go b/cmd/tendermint/commands/root.go index 6cef5daa9..4488eae61 100644 --- a/cmd/tendermint/commands/root.go +++ b/cmd/tendermint/commands/root.go @@ -38,7 +38,7 @@ 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() From 7e4a704bd1f986c5b12b795a8948164a030ae5c5 Mon Sep 17 00:00:00 2001 From: Duncan Jones Date: Fri, 1 Sep 2017 10:45:39 +0100 Subject: [PATCH 05/22] Remove reliance on default Signer This change allows the default privValidator to use a custom Signer implementation with no reliance on the default Signer implementation. --- cmd/hsm/main.go | 8 ++++++-- types/priv_validator.go | 10 +++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/cmd/hsm/main.go b/cmd/hsm/main.go index 657c946a1..d2144745c 100644 --- a/cmd/hsm/main.go +++ b/cmd/hsm/main.go @@ -35,8 +35,12 @@ func main() { rootCmd.AddCommand(tc.TestnetFilesCmd) rootCmd.AddCommand(tc.VersionCmd) - privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile(), logger) - privValidator.SetSigner(types.NewDefaultSigner(privValidator.PrivKey)) + // Override with HSM implementation, otherwise nil will trigger default + // software signer: + var signer types.Signer = nil + + privValidator := types.LoadPrivValidatorWithSigner(config.PrivValidatorFile(), + signer) rootCmd.AddCommand(tc.NewRunNodeCmd(privValidator)) cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) diff --git a/types/priv_validator.go b/types/priv_validator.go index 072b394ca..fe2f2e730 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -112,6 +112,10 @@ func GenPrivValidator() *PrivValidator { } func LoadPrivValidator(filePath string) *PrivValidator { + return LoadPrivValidatorWithSigner(filePath, nil) +} + +func LoadPrivValidatorWithSigner(filePath string, signer Signer) *PrivValidator { privValJSONBytes, err := ioutil.ReadFile(filePath) if err != nil { Exit(err.Error()) @@ -123,7 +127,11 @@ func LoadPrivValidator(filePath string) *PrivValidator { } privVal.filePath = filePath - privVal.Signer = NewDefaultSigner(privVal.PrivKey) + if signer == nil { + privVal.Signer = NewDefaultSigner(privVal.PrivKey) + } else { + privVal.Signer = signer + } privVal.setPubKeyAndAddress() return &privVal } From 0d392a04425f62f7ed82fa7061807fcf320f3b28 Mon Sep 17 00:00:00 2001 From: Duncan Jones Date: Mon, 11 Sep 2017 18:56:41 +0100 Subject: [PATCH 06/22] Allow Signer to be generated with priv key Prior to this change, a custom Signer would have no knowledge of the private key stored in the configuration file. This changes introduces a generator function, which creates a Signer based on the private key. This provides an opportunity for customer Signers to adjust behaviour based on the key contents. (E.g. imagine key contents are a key label, rather than the key itself). --- cmd/hsm/main.go | 19 +++++++++---------- types/priv_validator.go | 17 ++++++++++------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/cmd/hsm/main.go b/cmd/hsm/main.go index d2144745c..7c12f03c8 100644 --- a/cmd/hsm/main.go +++ b/cmd/hsm/main.go @@ -1,14 +1,13 @@ package main import ( - "os" - - "github.com/tendermint/tmlibs/cli" - "github.com/tendermint/tmlibs/log" - + tcrypto "github.com/tendermint/go-crypto" tc "github.com/tendermint/tendermint/cmd/tendermint/commands" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/types" + "github.com/tendermint/tmlibs/cli" + "github.com/tendermint/tmlibs/log" + "os" ) var ( @@ -35,12 +34,12 @@ func main() { rootCmd.AddCommand(tc.TestnetFilesCmd) rootCmd.AddCommand(tc.VersionCmd) - // Override with HSM implementation, otherwise nil will trigger default - // software signer: - var signer types.Signer = nil + signerGenerator := func(pk tcrypto.PrivKey) types.Signer { + // Return your own signer implementation here + return types.NewDefaultSigner(pk) + } - privValidator := types.LoadPrivValidatorWithSigner(config.PrivValidatorFile(), - signer) + privValidator := types.LoadPrivValidatorWithSigner(config.PrivValidatorFile(), signerGenerator) rootCmd.AddCommand(tc.NewRunNodeCmd(privValidator)) cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) diff --git a/types/priv_validator.go b/types/priv_validator.go index fe2f2e730..9dfd95b5f 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -55,6 +55,10 @@ type PrivValidator struct { mtx sync.Mutex } + +type SignerGenerator func(pk crypto.PrivKey) (Signer) + + // This is used to sign votes. // It is the caller's duty to verify the msg before calling Sign, // eg. to avoid double signing. @@ -112,10 +116,12 @@ func GenPrivValidator() *PrivValidator { } func LoadPrivValidator(filePath string) *PrivValidator { - return LoadPrivValidatorWithSigner(filePath, nil) + return LoadPrivValidatorWithSigner(filePath, func(pk crypto.PrivKey) Signer { + return NewDefaultSigner(pk) + }) } -func LoadPrivValidatorWithSigner(filePath string, signer Signer) *PrivValidator { +func LoadPrivValidatorWithSigner(filePath string, generator SignerGenerator) *PrivValidator { privValJSONBytes, err := ioutil.ReadFile(filePath) if err != nil { Exit(err.Error()) @@ -127,11 +133,8 @@ func LoadPrivValidatorWithSigner(filePath string, signer Signer) *PrivValidator } privVal.filePath = filePath - if signer == nil { - privVal.Signer = NewDefaultSigner(privVal.PrivKey) - } else { - privVal.Signer = signer - } + privVal.Signer = generator(privVal.PrivKey) + privVal.setPubKeyAndAddress() return &privVal } From 7dd3c007c7c965987b7e5a635ff5345400b5dad5 Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Wed, 13 Sep 2017 00:18:07 +0200 Subject: [PATCH 07/22] Refactor priv_validator Users can now just pass an object that implements the Signer interface. --- cmd/hsm/main.go | 47 ------- cmd/tendermint/commands/gen_validator.go | 2 + cmd/tendermint/commands/init.go | 1 + cmd/tendermint/commands/probe_upnp.go | 1 + cmd/tendermint/commands/replay.go | 3 + .../commands/reset_priv_validator.go | 18 ++- cmd/tendermint/commands/root.go | 1 + cmd/tendermint/commands/run_node.go | 97 +++++++------- cmd/tendermint/commands/show_validator.go | 3 +- cmd/tendermint/commands/testnet.go | 14 +- cmd/tendermint/commands/version.go | 1 + cmd/tendermint/main.go | 22 +++- consensus/common_test.go | 2 +- node/node.go | 2 +- rpc/test/helpers.go | 2 +- types/priv_validator.go | 123 +++++++++--------- 16 files changed, 163 insertions(+), 176 deletions(-) delete mode 100644 cmd/hsm/main.go diff --git a/cmd/hsm/main.go b/cmd/hsm/main.go deleted file mode 100644 index 7c12f03c8..000000000 --- a/cmd/hsm/main.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - tcrypto "github.com/tendermint/go-crypto" - tc "github.com/tendermint/tendermint/cmd/tendermint/commands" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/types" - "github.com/tendermint/tmlibs/cli" - "github.com/tendermint/tmlibs/log" - "os" -) - -var ( - config = cfg.DefaultConfig() - logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") -) - -func main() { - // TODO: Make it easier to build a tendermint instance from scratch. - // All commands should be exported and it should be easy to override - // certain aspects of a single command. - // Probably every command should have a constructor that allows a user - // to vary the configuration. This is at least true for run_node.go - - rootCmd := tc.RootCmd - rootCmd.AddCommand(tc.GenValidatorCmd) - rootCmd.AddCommand(tc.InitFilesCmd) - rootCmd.AddCommand(tc.ProbeUpnpCmd) - rootCmd.AddCommand(tc.ReplayCmd) - rootCmd.AddCommand(tc.ReplayConsoleCmd) - rootCmd.AddCommand(tc.ResetAllCmd) - rootCmd.AddCommand(tc.ResetPrivValidatorCmd) - rootCmd.AddCommand(tc.ShowValidatorCmd) - rootCmd.AddCommand(tc.TestnetFilesCmd) - rootCmd.AddCommand(tc.VersionCmd) - - signerGenerator := func(pk tcrypto.PrivKey) types.Signer { - // Return your own signer implementation here - return types.NewDefaultSigner(pk) - } - - privValidator := types.LoadPrivValidatorWithSigner(config.PrivValidatorFile(), signerGenerator) - rootCmd.AddCommand(tc.NewRunNodeCmd(privValidator)) - - cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) - cmd.Execute() -} diff --git a/cmd/tendermint/commands/gen_validator.go b/cmd/tendermint/commands/gen_validator.go index df82554d8..e4dfb0bd1 100644 --- a/cmd/tendermint/commands/gen_validator.go +++ b/cmd/tendermint/commands/gen_validator.go @@ -9,6 +9,8 @@ import ( "github.com/tendermint/tendermint/types" ) +// GenValidatorCmd allows the generation of a keypair for a +// validator. var GenValidatorCmd = &cobra.Command{ Use: "gen_validator", Short: "Generate new validator keypair", diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index b844386c5..5e973335d 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -9,6 +9,7 @@ import ( cmn "github.com/tendermint/tmlibs/common" ) +// InitFilesCmd initialises a fresh Tendermint Core instance. var InitFilesCmd = &cobra.Command{ Use: "init", Short: "Initialize Tendermint", diff --git a/cmd/tendermint/commands/probe_upnp.go b/cmd/tendermint/commands/probe_upnp.go index a4c71d59e..643b7713f 100644 --- a/cmd/tendermint/commands/probe_upnp.go +++ b/cmd/tendermint/commands/probe_upnp.go @@ -9,6 +9,7 @@ import ( "github.com/tendermint/tendermint/p2p/upnp" ) +// ProbeUpnpCmd adds capabilities to test the UPnP functionality. var ProbeUpnpCmd = &cobra.Command{ Use: "probe_upnp", Short: "Test UPnP functionality", diff --git a/cmd/tendermint/commands/replay.go b/cmd/tendermint/commands/replay.go index 422d44159..303ccba6b 100644 --- a/cmd/tendermint/commands/replay.go +++ b/cmd/tendermint/commands/replay.go @@ -6,6 +6,7 @@ import ( "github.com/tendermint/tendermint/consensus" ) +// ReplayCmd allows replaying of messages from the WAL. var ReplayCmd = &cobra.Command{ Use: "replay", Short: "Replay messages from WAL", @@ -14,6 +15,8 @@ var ReplayCmd = &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", diff --git a/cmd/tendermint/commands/reset_priv_validator.go b/cmd/tendermint/commands/reset_priv_validator.go index 389e5b79b..7f01c2313 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -9,18 +9,29 @@ import ( "github.com/tendermint/tmlibs/log" ) +// 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, } +// ResetPrivValidatorCmd resets the private validator files. var ResetPrivValidatorCmd = &cobra.Command{ Use: "unsafe_reset_priv_validator", Short: "(unsafe) Reset this node's validator", Run: resetPrivValidator, } +// ResetAll removes the privValidator files. +// 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) +} + // XXX: this is totally unsafe. // it's only suitable for testnets. func resetAll(cmd *cobra.Command, args []string) { @@ -33,13 +44,6 @@ func resetPrivValidator(cmd *cobra.Command, args []string) { resetPrivValidatorLocal(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) { // Get PrivValidator var privValidator *types.PrivValidator diff --git a/cmd/tendermint/commands/root.go b/cmd/tendermint/commands/root.go index 4488eae61..a54b50069 100644 --- a/cmd/tendermint/commands/root.go +++ b/cmd/tendermint/commands/root.go @@ -34,6 +34,7 @@ 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", diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 51b370300..fd33faefe 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -12,52 +12,6 @@ import ( "github.com/tendermint/tendermint/types" ) -// RunNodeCmd creates and starts a tendermint node. -var RunNodeCmd = &cobra.Command{ - Use: "node", - Short: "Run the tendermint node", - RunE: runNode, -} - -// NewRunNodeCmd creates and starts a tendermint node. It allows the user to -// use a custom PrivValidator. -func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { - return &cobra.Command{ - Use: "node", - Short: "Run the tendermint node", - RunE: func(cmd *cobra.Command, args []string) error { - // Wait until the genesis doc becomes available - // This is for Mintnet compatibility. - // TODO: If Mintnet gets deprecated or genesis_file is - // always available, remove. - genDocFile := config.GenesisFile() - for !cmn.FileExists(genDocFile) { - logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile)) - time.Sleep(time.Second) - } - - genDoc, err := types.GenesisDocFromFile(genDocFile) - if err != nil { - return err - } - config.ChainID = genDoc.ChainID - - // Create & start node - n := node.NewNode(config, privVal, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) - 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 - }, - } -} - func init() { AddNodeFlags(RunNodeCmd) } @@ -90,6 +44,57 @@ 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") } +// RunNodeCmd creates and starts a tendermint node. +var RunNodeCmd = &cobra.Command{ + Use: "node", + Short: "Run the tendermint node", + RunE: runNode, +} + +// NewRunNodeCmd returns the command that allows the CLI to start a +// node. It can be used with a custom PrivValidator. +func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { + return &cobra.Command{ + Use: "node", + Short: "Run the tendermint node", + RunE: func(cmd *cobra.Command, args []string) error { + // Wait until the genesis doc becomes available + // This is for Mintnet compatibility. + // TODO: If Mintnet gets deprecated or genesis_file is + // always available, remove. + genDocFile := config.GenesisFile() + for !cmn.FileExists(genDocFile) { + logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile)) + time.Sleep(time.Second) + } + + genDoc, err := types.GenesisDocFromFile(genDocFile) + if err != nil { + return err + } + config.ChainID = genDoc.ChainID + + // Create & start node + var n *node.Node + if privVal == nil { + n = node.NewNodeDefault(config, logger.With("module", "node")) + } + n = node.NewNode(config, privVal, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) + + 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 + }, + } +} + // Users wishing to: // * Use an external signer for their validators // * Supply an in-proc abci app diff --git a/cmd/tendermint/commands/show_validator.go b/cmd/tendermint/commands/show_validator.go index 21cc90270..86a665f0c 100644 --- a/cmd/tendermint/commands/show_validator.go +++ b/cmd/tendermint/commands/show_validator.go @@ -9,6 +9,7 @@ import ( "github.com/tendermint/tendermint/types" ) +// ShowValidatorCmd adds capabilities for showing the validator info. var ShowValidatorCmd = &cobra.Command{ Use: "show_validator", Short: "Show this node's validator info", @@ -16,7 +17,7 @@ var ShowValidatorCmd = &cobra.Command{ } func showValidator(cmd *cobra.Command, args []string) { - privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile(), logger) + privValidator := types.LoadOrGenPrivValidator(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 75e016c9d..617b66597 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 @@ -30,6 +24,14 @@ func init() { "Directory to store initialization data for the testnet") } +// 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) { genVals := make([]types.GenesisValidator, nValidators) diff --git a/cmd/tendermint/commands/version.go b/cmd/tendermint/commands/version.go index 692cba4ad..b212adc70 100644 --- a/cmd/tendermint/commands/version.go +++ b/cmd/tendermint/commands/version.go @@ -8,6 +8,7 @@ import ( "github.com/tendermint/tendermint/version" ) +// VersionCmd ... var VersionCmd = &cobra.Command{ Use: "version", Short: "Show version infoooooooo", diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index 5493e4f2f..d70df634d 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -3,11 +3,29 @@ package main import ( "os" - "github.com/tendermint/tendermint/cmd/tendermint/commands" + // crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/tmlibs/cli" + + . "github.com/tendermint/tendermint/cmd/tendermint/commands" + // "github.com/tendermint/tendermint/types" ) func main() { - cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) + rootCmd := RootCmd + rootCmd.AddCommand(GenValidatorCmd, InitFilesCmd, ProbeUpnpCmd, + ReplayCmd, ReplayConsoleCmd, ResetAllCmd, ResetPrivValidatorCmd, + ShowValidatorCmd, TestnetFilesCmd, VersionCmd) + + // NOTE: Implement your own type that implements the Signer interface + // and then instantiate it here. + // signer := types.NewDefaultSigner(pk) + // privValidator := types.LoadPrivValidatorWithSigner(signer) + // rootCmd.AddCommand(NewRunNodeCmd(privValidator)) + + // Create & start node + rootCmd.AddCommand(RunNodeCmd) + + cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) cmd.Execute() } diff --git a/consensus/common_test.go b/consensus/common_test.go index 6a05b74e3..cde56a3db 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -261,7 +261,7 @@ func newConsensusStateWithConfig(thisConfig *cfg.Config, state *sm.State, pv *ty func loadPrivValidator(config *cfg.Config) *types.PrivValidator { privValidatorFile := config.PrivValidatorFile() ensureDir(path.Dir(privValidatorFile), 0700) - privValidator := types.LoadOrGenPrivValidator(privValidatorFile, log.TestingLogger()) + privValidator := types.LoadOrGenPrivValidator(privValidatorFile) privValidator.Reset() return privValidator } diff --git a/node/node.go b/node/node.go index e0ddeb5d4..e831ba1d0 100644 --- a/node/node.go +++ b/node/node.go @@ -60,7 +60,7 @@ type Node struct { func NewNodeDefault(config *cfg.Config, logger log.Logger) *Node { // Get PrivValidator - privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile(), logger) + privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile()) return NewNode(config, privValidator, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) } diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 14da15c94..132c4d4d9 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -79,7 +79,7 @@ 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.LoadOrGenPrivValidator(privValidatorFile) papp := proxy.NewLocalClientCreator(app) node := nm.NewNode(config, privValidator, papp, logger) return node diff --git a/types/priv_validator.go b/types/priv_validator.go index 9dfd95b5f..ed0cde070 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -12,7 +12,6 @@ import ( crypto "github.com/tendermint/go-crypto" data "github.com/tendermint/go-wire/data" . "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tmlibs/log" ) // TODO: type ? @@ -35,30 +34,6 @@ func voteToStep(vote *Vote) int8 { } } -// PrivValidator implements the functionality for signing blocks. -type PrivValidator 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 - - // PrivKey should be empty if a Signer other than the default is being used. - PrivKey crypto.PrivKey `json:"priv_key"` - Signer `json:"-"` - - // For persistence. - // Overloaded for testing. - filePath string - mtx sync.Mutex -} - - -type SignerGenerator func(pk crypto.PrivKey) (Signer) - - // This is used to sign votes. // It is the caller's duty to verify the msg before calling Sign, // eg. to avoid double signing. @@ -90,15 +65,53 @@ func (ds *DefaultSigner) PubKey() crypto.PubKey { return ds.priv.PubKey() } -func (privVal *PrivValidator) SetSigner(s Signer) { - privVal.Signer = s - privVal.setPubKeyAndAddress() +// PrivValidator implements the functionality for signing blocks. +type PrivValidator 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 + + // PrivKey should be empty if a Signer other than the default is being used. + PrivKey crypto.PrivKey `json:"priv_key"` + Signer `json:"-"` + + // For persistence. + // Overloaded for testing. + filePath string + mtx sync.Mutex } -// Overwrite address and pubkey for convenience -func (privVal *PrivValidator) setPubKeyAndAddress() { - privVal.PubKey = privVal.Signer.PubKey() - privVal.Address = privVal.PubKey.Address() +func LoadOrGenPrivValidator(filePath string) *PrivValidator { + var privValidator *PrivValidator + if _, err := os.Stat(filePath); err == nil { + privValidator = LoadPrivValidator(filePath) + } else { + privValidator = GenPrivValidator() + privValidator.SetFile(filePath) + privValidator.Save() + } + return privValidator +} + +func LoadPrivValidator(filePath string) *PrivValidator { + privValJSONBytes, err := ioutil.ReadFile(filePath) + if err != nil { + Exit(err.Error()) + } + privVal := PrivValidator{} + err = json.Unmarshal(privValJSONBytes, &privVal) + if err != nil { + Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) + } + + privVal.filePath = filePath + privVal.Signer = NewDefaultSigner(privVal.PrivKey) + privVal.setPubKeyAndAddress() + return &privVal } // Generates a new validator with private key. @@ -115,43 +128,25 @@ func GenPrivValidator() *PrivValidator { } } -func LoadPrivValidator(filePath string) *PrivValidator { - return LoadPrivValidatorWithSigner(filePath, func(pk crypto.PrivKey) Signer { - return NewDefaultSigner(pk) - }) +func LoadPrivValidatorWithSigner(signer Signer) *PrivValidator { + return &PrivValidator{ + Address: signer.PubKey().Address(), + PubKey: signer.PubKey(), + LastStep: stepNone, + filePath: "", + Signer: signer, + } } -func LoadPrivValidatorWithSigner(filePath string, generator SignerGenerator) *PrivValidator { - privValJSONBytes, err := ioutil.ReadFile(filePath) - if err != nil { - Exit(err.Error()) - } - privVal := PrivValidator{} - err = json.Unmarshal(privValJSONBytes, &privVal) - if err != nil { - Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) - } - - privVal.filePath = filePath - privVal.Signer = generator(privVal.PrivKey) - +func (privVal *PrivValidator) SetSigner(s Signer) { + privVal.Signer = s privVal.setPubKeyAndAddress() - 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 +// Overwrite address and pubkey for convenience +func (privVal *PrivValidator) setPubKeyAndAddress() { + privVal.PubKey = privVal.Signer.PubKey() + privVal.Address = privVal.PubKey.Address() } func (privVal *PrivValidator) SetFile(filePath string) { From 4e13a19339a033c405435b4180447d9f4cbbd147 Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Wed, 13 Sep 2017 08:32:53 +0200 Subject: [PATCH 08/22] Add ability to construct new instance of Tendermint core from scratch --- cmd/tendermint/commands/run_node.go | 46 +++++------------------------ cmd/tendermint/main.go | 10 ++++--- types/priv_validator.go | 8 +++-- 3 files changed, 19 insertions(+), 45 deletions(-) diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index fd33faefe..d52f2977c 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -2,6 +2,7 @@ package commands import ( "fmt" + "time" "github.com/spf13/cobra" @@ -12,10 +13,6 @@ import ( "github.com/tendermint/tendermint/types" ) -func init() { - AddNodeFlags(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) { @@ -44,24 +41,13 @@ 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") } -// RunNodeCmd creates and starts a tendermint node. -var RunNodeCmd = &cobra.Command{ - Use: "node", - Short: "Run the tendermint node", - RunE: runNode, -} - // NewRunNodeCmd returns the command that allows the CLI to start a // node. It can be used with a custom PrivValidator. func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "node", Short: "Run the tendermint node", RunE: func(cmd *cobra.Command, args []string) error { - // Wait until the genesis doc becomes available - // This is for Mintnet compatibility. - // TODO: If Mintnet gets deprecated or genesis_file is - // always available, remove. genDocFile := config.GenesisFile() for !cmn.FileExists(genDocFile) { logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile)) @@ -79,7 +65,8 @@ func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { if privVal == nil { n = node.NewNodeDefault(config, logger.With("module", "node")) } - n = node.NewNode(config, privVal, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) + n = node.NewNode(config, privVal, proxy.DefaultClientCreator(config.ProxyApp, + config.ABCI, config.DBDir()), logger.With("module", "node")) if _, err := n.Start(); err != nil { return fmt.Errorf("Failed to start node: %v", err) @@ -93,6 +80,9 @@ func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { return nil }, } + + AddNodeFlags(cmd) + return cmd } // Users wishing to: @@ -101,25 +91,3 @@ func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { // 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 { - - genDocFile := config.GenesisFile() - genDoc, err := types.GenesisDocFromFile(genDocFile) - if err != nil { - return err - } - config.ChainID = genDoc.ChainID - - // 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 -} diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index d70df634d..e231f6c67 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -19,12 +19,14 @@ func main() { // NOTE: Implement your own type that implements the Signer interface // and then instantiate it here. - // signer := types.NewDefaultSigner(pk) - // privValidator := types.LoadPrivValidatorWithSigner(signer) - // rootCmd.AddCommand(NewRunNodeCmd(privValidator)) + /* + signer := types.NewDefaultSigner(pk) + privValidator := types.LoadPrivValidatorWithSigner(signer) + rootCmd.AddCommand(NewRunNodeCmd(privValidator)) + */ // Create & start node - rootCmd.AddCommand(RunNodeCmd) + rootCmd.AddCommand(NewRunNodeCmd(nil)) cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) cmd.Execute() diff --git a/types/priv_validator.go b/types/priv_validator.go index ed0cde070..3944a9cdf 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -128,12 +128,16 @@ func GenPrivValidator() *PrivValidator { } } -func LoadPrivValidatorWithSigner(signer Signer) *PrivValidator { +// 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 Signer, filePath string) *PrivValidator { return &PrivValidator{ Address: signer.PubKey().Address(), PubKey: signer.PubKey(), LastStep: stepNone, - filePath: "", + filePath: filePath, Signer: signer, } } From 66fcdf7c7a2f4cb8e0bb123412a218191d61d618 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 18 Sep 2017 17:11:35 -0400 Subject: [PATCH 09/22] minor fixes --- Makefile | 4 ---- cmd/tendermint/commands/run_node.go | 8 -------- cmd/tendermint/commands/version.go | 2 +- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 328e65f8a..8c9c5214d 100644 --- a/Makefile +++ b/Makefile @@ -17,10 +17,6 @@ build: go build \ --ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse HEAD`" -o build/tendermint ./cmd/tendermint/ -build_hsm: - go build \ - --ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse HEAD`" -o build/tendermint ./cmd/hsm/ - build_race: go build -race -o build/tendermint ./cmd/tendermint diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index d52f2977c..3eac5d981 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -2,12 +2,9 @@ package commands import ( "fmt" - "time" "github.com/spf13/cobra" - cmn "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" @@ -49,11 +46,6 @@ func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { Short: "Run the tendermint node", RunE: func(cmd *cobra.Command, args []string) error { genDocFile := config.GenesisFile() - for !cmn.FileExists(genDocFile) { - logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile)) - time.Sleep(time.Second) - } - genDoc, err := types.GenesisDocFromFile(genDocFile) if err != nil { return err diff --git a/cmd/tendermint/commands/version.go b/cmd/tendermint/commands/version.go index b212adc70..f9f545e59 100644 --- a/cmd/tendermint/commands/version.go +++ b/cmd/tendermint/commands/version.go @@ -11,7 +11,7 @@ import ( // VersionCmd ... var VersionCmd = &cobra.Command{ Use: "version", - Short: "Show version infoooooooo", + Short: "Show version info", Run: func(cmd *cobra.Command, args []string) { fmt.Println(version.Version) }, From abe912c61095615a92f4c14056815734609cf718 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 18 Sep 2017 17:40:24 -0400 Subject: [PATCH 10/22] FuncSignerAndApp allows custom signer and abci app --- cmd/tendermint/commands/run_node.go | 30 +++++++++++------------ cmd/tendermint/main.go | 37 ++++++++++++++++------------- types/priv_validator.go | 6 ++--- 3 files changed, 39 insertions(+), 34 deletions(-) diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 3eac5d981..b9d23a27a 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/cobra" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" @@ -38,9 +39,19 @@ 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") } +// FuncSignerAndApp takes a config and returns a PrivValidator and ClientCreator. +// It allows other projects to make Tendermint binaries with custom signers and applications. +type FuncSignerAndApp func(*cfg.Config) (*types.PrivValidator, proxy.ClientCreator) + +func DefaultSignerAndApp(config *cfg.Config) (*types.PrivValidator, proxy.ClientCreator) { + privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile()) + clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) + return privValidator, clientCreator +} + // NewRunNodeCmd returns the command that allows the CLI to start a -// node. It can be used with a custom PrivValidator. -func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { +// node. It can be used with a custom PrivValidator and in-process ABCI application. +func NewRunNodeCmd(signerAndApp FuncSignerAndApp) *cobra.Command { cmd := &cobra.Command{ Use: "node", Short: "Run the tendermint node", @@ -53,12 +64,8 @@ func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { config.ChainID = genDoc.ChainID // Create & start node - var n *node.Node - if privVal == nil { - n = node.NewNodeDefault(config, logger.With("module", "node")) - } - n = node.NewNode(config, privVal, proxy.DefaultClientCreator(config.ProxyApp, - config.ABCI, config.DBDir()), logger.With("module", "node")) + privVal, clientCreator := signerAndApp(config) + n := node.NewNode(config, privVal, clientCreator, logger.With("module", "node")) if _, err := n.Start(); err != nil { return fmt.Errorf("Failed to start node: %v", err) @@ -76,10 +83,3 @@ func NewRunNodeCmd(privVal *types.PrivValidator) *cobra.Command { AddNodeFlags(cmd) return cmd } - -// 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 diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index e231f6c67..7cc1b2183 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -3,30 +3,35 @@ package main import ( "os" - // crypto "github.com/tendermint/go-crypto" - "github.com/tendermint/tmlibs/cli" - . "github.com/tendermint/tendermint/cmd/tendermint/commands" - // "github.com/tendermint/tendermint/types" + cmd "github.com/tendermint/tendermint/cmd/tendermint/commands" ) func main() { - rootCmd := RootCmd - rootCmd.AddCommand(GenValidatorCmd, InitFilesCmd, ProbeUpnpCmd, - ReplayCmd, ReplayConsoleCmd, ResetAllCmd, ResetPrivValidatorCmd, - ShowValidatorCmd, TestnetFilesCmd, VersionCmd) + 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: Implement your own type that implements the Signer interface - // and then instantiate it here. - /* - signer := types.NewDefaultSigner(pk) - privValidator := types.LoadPrivValidatorWithSigner(signer) - rootCmd.AddCommand(NewRunNodeCmd(privValidator)) - */ + // NOTE: + // Users wishing to: + // * Use an external signer for their validators + // * Supply an in-proc abci app + // can copy this file and use something other than the + // default SignerAndApp function + signerAndApp := cmd.DefaultSignerAndApp // Create & start node - rootCmd.AddCommand(NewRunNodeCmd(nil)) + rootCmd.AddCommand(cmd.NewRunNodeCmd(signerAndApp)) cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) cmd.Execute() diff --git a/types/priv_validator.go b/types/priv_validator.go index 3944a9cdf..72d05c49e 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -34,17 +34,17 @@ func voteToStep(vote *Vote) int8 { } } -// This is used to sign votes. +// 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 -// Signer is an interface that describes how to sign votes. +// 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 { priv crypto.PrivKey } From fd1b0b997a38e764345d8abc2b3c748c6bf9c688 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 18 Sep 2017 18:12:31 -0400 Subject: [PATCH 11/22] PrivValidator interface --- cmd/tendermint/commands/init.go | 2 +- .../commands/reset_priv_validator.go | 2 +- cmd/tendermint/commands/run_node.go | 4 +- cmd/tendermint/commands/testnet.go | 2 +- consensus/state.go | 24 +-- node/node.go | 14 +- types/priv_validator.go | 179 ++++++++++-------- types/priv_validator_test.go | 37 ++-- types/proposal_test.go | 6 +- types/validator.go | 4 +- types/validator_set.go | 4 +- types/vote_set_test.go | 60 +++--- 12 files changed, 178 insertions(+), 160 deletions(-) diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index 5e973335d..259645044 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -30,7 +30,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.PubKey(), Power: 10, }} diff --git a/cmd/tendermint/commands/reset_priv_validator.go b/cmd/tendermint/commands/reset_priv_validator.go index 7f01c2313..bedd38926 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -46,7 +46,7 @@ func resetPrivValidator(cmd *cobra.Command, args []string) { func resetPrivValidatorLocal(privValFile string, logger log.Logger) { // Get PrivValidator - var privValidator *types.PrivValidator + var privValidator types.PrivValidator if _, err := os.Stat(privValFile); err == nil { privValidator = types.LoadPrivValidator(privValFile) privValidator.Reset() diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index b9d23a27a..883ec6ec4 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -41,9 +41,9 @@ func AddNodeFlags(cmd *cobra.Command) { // FuncSignerAndApp takes a config and returns a PrivValidator and ClientCreator. // It allows other projects to make Tendermint binaries with custom signers and applications. -type FuncSignerAndApp func(*cfg.Config) (*types.PrivValidator, proxy.ClientCreator) +type FuncSignerAndApp func(*cfg.Config) (types.PrivValidator, proxy.ClientCreator) -func DefaultSignerAndApp(config *cfg.Config) (*types.PrivValidator, proxy.ClientCreator) { +func DefaultSignerAndApp(config *cfg.Config) (types.PrivValidator, proxy.ClientCreator) { privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile()) clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) return privValidator, clientCreator diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index 617b66597..9412379a5 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -47,7 +47,7 @@ func testnetFiles(cmd *cobra.Command, args []string) { privValFile := path.Join(dataDir, mach, "priv_validator.json") privVal := types.LoadPrivValidator(privValFile) genVals[i] = types.GenesisValidator{ - PubKey: privVal.PubKey, + PubKey: privVal.PubKey(), Power: 1, Name: mach, } diff --git a/consensus/state.go b/consensus/state.go index ac1656209..f141cad58 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 @@ -825,7 +817,7 @@ func (cs *ConsensusState) needProofBlock(height int) bool { func (cs *ConsensusState) proposalHeartbeat(height, round int) { counter := 0 - addr := cs.privValidator.GetAddress() + addr := cs.privValidator.Address() valIndex, v := cs.Validators.GetByAddress(addr) if v == nil { // not a validator @@ -886,7 +878,7 @@ func (cs *ConsensusState) enterPropose(height int, round int) { if !cs.isProposer() { cs.Logger.Info("enterPropose: Not our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator) - if cs.Validators.HasAddress(cs.privValidator.GetAddress()) { + if cs.Validators.HasAddress(cs.privValidator.Address()) { cs.Logger.Debug("This node is a validator") } else { cs.Logger.Debug("This node is not a validator") @@ -899,7 +891,7 @@ func (cs *ConsensusState) enterPropose(height int, round int) { } func (cs *ConsensusState) isProposer() bool { - return bytes.Equal(cs.Validators.GetProposer().Address, cs.privValidator.GetAddress()) + return bytes.Equal(cs.Validators.GetProposer().Address, cs.privValidator.Address()) } func (cs *ConsensusState) defaultDecideProposal(height, round int) { @@ -1444,7 +1436,7 @@ func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerKey string) error { if err == ErrVoteHeightMismatch { return err } else if _, ok := err.(*types.ErrVoteConflictingVotes); ok { - if bytes.Equal(vote.ValidatorAddress, cs.privValidator.GetAddress()) { + if bytes.Equal(vote.ValidatorAddress, cs.privValidator.Address()) { cs.Logger.Error("Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", vote.Height, "round", vote.Round, "type", vote.Type) return err } @@ -1573,7 +1565,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerKey string) (added bool, } func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) { - addr := cs.privValidator.GetAddress() + addr := cs.privValidator.Address() valIndex, _ := cs.Validators.GetByAddress(addr) vote := &types.Vote{ ValidatorAddress: addr, @@ -1590,7 +1582,7 @@ func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSet // sign the vote and publish on internalMsgQueue func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.PartSetHeader) *types.Vote { // if we don't have a key or we're not in the validator set, do nothing - if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetAddress()) { + if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.Address()) { return nil } vote, err := cs.signVote(type_, hash, header) diff --git a/node/node.go b/node/node.go index e831ba1d0..a0a8c529a 100644 --- a/node/node.go +++ b/node/node.go @@ -38,8 +38,8 @@ type Node struct { // 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 @@ -65,7 +65,7 @@ func NewNodeDefault(config *cfg.Config, logger log.Logger) *Node { proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) } -func NewNode(config *cfg.Config, privValidator *types.PrivValidator, clientCreator proxy.ClientCreator, logger log.Logger) *Node { +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()) blockStore := bc.NewBlockStore(blockStoreDB) @@ -119,13 +119,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.Address(), addr) { fastSync = false } } // Log whether this node is a validator or an observer - if state.Validators.HasAddress(privValidator.Address) { + if state.Validators.HasAddress(privValidator.PubKey().Address()) { consensusLogger.Info("This node is a validator") } else { consensusLogger.Info("This node is not a validator") @@ -314,7 +314,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.PubKey()) rpccore.SetGenesisDoc(n.genesisDoc) rpccore.SetAddrBook(n.addrBook) rpccore.SetProxyAppQuery(n.proxyApp.Query()) @@ -385,7 +385,7 @@ func (n *Node) EventSwitch() types.EventSwitch { } // XXX: for convenience -func (n *Node) PrivValidator() *types.PrivValidator { +func (n *Node) PrivValidator() types.PrivValidator { return n.privValidator } diff --git a/types/priv_validator.go b/types/priv_validator.go index 72d05c49e..e8fb3e240 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -46,38 +46,43 @@ type Signer interface { // DefaultSigner implements Signer. // It uses a standard 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} } // 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 } // 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.priv.PubKey() + return ds.PrivKey.PubKey() } -// PrivValidator implements the functionality for signing blocks. -type PrivValidator 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 +type PrivValidator interface { + Address() data.Bytes // redundant since .PubKey().Address() + PubKey() crypto.PubKey - // PrivKey should be empty if a Signer other than the default is being used. - PrivKey crypto.PrivKey `json:"priv_key"` - Signer `json:"-"` + SignVote(chainID string, vote *Vote) error + SignProposal(chainID string, proposal *Proposal) error + SignHeartbeat(chainID string, heartbeat *Heartbeat) error + + Reset() + + SetFile(file string) + Save() +} + +// DefaultPrivValidator implements the functionality for signing blocks. +type DefaultPrivValidator struct { + Info PrivValidatorInfo `json:"info"` + Signer *DefaultSigner `json:"signer"` // For persistence. // Overloaded for testing. @@ -85,8 +90,26 @@ type PrivValidator struct { mtx sync.Mutex } -func LoadOrGenPrivValidator(filePath string) *PrivValidator { - var privValidator *PrivValidator +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 if _, err := os.Stat(filePath); err == nil { privValidator = LoadPrivValidator(filePath) } else { @@ -97,34 +120,33 @@ func LoadOrGenPrivValidator(filePath string) *PrivValidator { return privValidator } -func LoadPrivValidator(filePath string) *PrivValidator { +func LoadPrivValidator(filePath string) *DefaultPrivValidator { privValJSONBytes, err := ioutil.ReadFile(filePath) if err != nil { Exit(err.Error()) } - privVal := PrivValidator{} + privVal := DefaultPrivValidator{} err = json.Unmarshal(privValJSONBytes, &privVal) if err != nil { Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) } privVal.filePath = filePath - privVal.Signer = NewDefaultSigner(privVal.PrivKey) - privVal.setPubKeyAndAddress() return &privVal } // Generates a new validator with private key. -func GenPrivValidator() *PrivValidator { +func GenPrivValidator() *DefaultPrivValidator { privKey := crypto.GenPrivKeyEd25519().Wrap() pubKey := privKey.PubKey() - return &PrivValidator{ - Address: pubKey.Address(), - PubKey: pubKey, - PrivKey: privKey, - LastStep: stepNone, - filePath: "", + return &DefaultPrivValidator{ + Info: PrivValidatorInfo{ + Address: pubKey.Address(), + PubKey: pubKey, + LastStep: stepNone, + }, Signer: NewDefaultSigner(privKey), + filePath: "", } } @@ -132,40 +154,37 @@ func GenPrivValidator() *PrivValidator { // 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 Signer, filePath string) *PrivValidator { - return &PrivValidator{ - Address: signer.PubKey().Address(), - PubKey: signer.PubKey(), - LastStep: stepNone, - filePath: filePath, +func LoadPrivValidatorWithSigner(signer *DefaultSigner, filePath string) *DefaultPrivValidator { + return &DefaultPrivValidator{ + Info: PrivValidatorInfo{ + Address: signer.PubKey().Address(), + PubKey: signer.PubKey(), + LastStep: stepNone, + }, Signer: signer, + filePath: filePath, } } -func (privVal *PrivValidator) SetSigner(s Signer) { - privVal.Signer = s - privVal.setPubKeyAndAddress() -} - // Overwrite address and pubkey for convenience -func (privVal *PrivValidator) setPubKeyAndAddress() { +/*func (privVal *DefaultPrivValidator) setPubKeyAndAddress() { privVal.PubKey = privVal.Signer.PubKey() privVal.Address = privVal.PubKey.Address() -} +}*/ -func (privVal *PrivValidator) SetFile(filePath string) { +func (privVal *DefaultPrivValidator) SetFile(filePath string) { privVal.mtx.Lock() defer privVal.mtx.Unlock() privVal.filePath = filePath } -func (privVal *PrivValidator) Save() { +func (privVal *DefaultPrivValidator) Save() { privVal.mtx.Lock() defer privVal.mtx.Unlock() privVal.save() } -func (privVal *PrivValidator) save() { +func (privVal *DefaultPrivValidator) save() { if privVal.filePath == "" { PanicSanity("Cannot save PrivValidator: filePath not set") } @@ -182,20 +201,20 @@ func (privVal *PrivValidator) save() { } // NOTE: Unsafe! -func (privVal *PrivValidator) Reset() { - privVal.LastHeight = 0 - privVal.LastRound = 0 - privVal.LastStep = 0 - privVal.LastSignature = crypto.Signature{} - privVal.LastSignBytes = nil +func (privVal *DefaultPrivValidator) Reset() { + privVal.Info.LastHeight = 0 + privVal.Info.LastRound = 0 + privVal.Info.LastStep = 0 + privVal.Info.LastSignature = crypto.Signature{} + privVal.Info.LastSignBytes = nil privVal.Save() } -func (privVal *PrivValidator) GetAddress() []byte { - return privVal.Address +func (privVal *DefaultPrivValidator) GetAddress() []byte { + return privVal.Address() } -func (privVal *PrivValidator) SignVote(chainID string, vote *Vote) error { +func (privVal *DefaultPrivValidator) SignVote(chainID string, vote *Vote) error { privVal.mtx.Lock() defer privVal.mtx.Unlock() signature, err := privVal.signBytesHRS(vote.Height, vote.Round, voteToStep(vote), SignBytes(chainID, vote)) @@ -206,7 +225,7 @@ func (privVal *PrivValidator) SignVote(chainID string, vote *Vote) error { return nil } -func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) error { +func (privVal *DefaultPrivValidator) SignProposal(chainID string, proposal *Proposal) error { privVal.mtx.Lock() defer privVal.mtx.Unlock() signature, err := privVal.signBytesHRS(proposal.Height, proposal.Round, stepPropose, SignBytes(chainID, proposal)) @@ -218,33 +237,34 @@ func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) e } // 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) { +func (privVal *DefaultPrivValidator) signBytesHRS(height, round int, step int8, signBytes []byte) (crypto.Signature, error) { sig := crypto.Signature{} + info := privVal.Info // If height regression, err - if privVal.LastHeight > height { + if info.LastHeight > height { return sig, errors.New("Height regression") } // More cases for when the height matches - if privVal.LastHeight == height { + if info.LastHeight == height { // If round regression, err - if privVal.LastRound > round { + if info.LastRound > round { return sig, errors.New("Round regression") } // If step regression, err - if privVal.LastRound == round { - if privVal.LastStep > step { + if info.LastRound == round { + if info.LastStep > step { return sig, errors.New("Step regression") - } else if privVal.LastStep == step { - if privVal.LastSignBytes != nil { - if privVal.LastSignature.Empty() { + } else if info.LastStep == step { + if info.LastSignBytes != nil { + if info.LastSignature.Empty() { PanicSanity("privVal: LastSignature is nil but LastSignBytes is not!") } // so we dont sign a conflicting vote or proposal // NOTE: proposals are non-deterministic (include time), // so we can actually lose them, but will still never sign conflicting ones - if bytes.Equal(privVal.LastSignBytes, signBytes) { - // log.Notice("Using privVal.LastSignature", "sig", privVal.LastSignature) - return privVal.LastSignature, nil + if bytes.Equal(info.LastSignBytes, signBytes) { + // log.Notice("Using info.LastSignature", "sig", info.LastSignature) + return info.LastSignature, nil } } return sig, errors.New("Step regression") @@ -253,45 +273,46 @@ func (privVal *PrivValidator) signBytesHRS(height, round int, step int8, signByt } // Sign - sig, err := privVal.Sign(signBytes) + sig, err := privVal.Signer.Sign(signBytes) if err != nil { return sig, err } // Persist height/round/step - privVal.LastHeight = height - privVal.LastRound = round - privVal.LastStep = step - privVal.LastSignature = sig - privVal.LastSignBytes = signBytes + privVal.Info.LastHeight = height + privVal.Info.LastRound = round + privVal.Info.LastStep = step + privVal.Info.LastSignature = sig + privVal.Info.LastSignBytes = signBytes privVal.save() return sig, nil } -func (privVal *PrivValidator) SignHeartbeat(chainID string, heartbeat *Heartbeat) error { +func (privVal *DefaultPrivValidator) SignHeartbeat(chainID string, heartbeat *Heartbeat) error { privVal.mtx.Lock() defer privVal.mtx.Unlock() var err error - heartbeat.Signature, err = privVal.Sign(SignBytes(chainID, heartbeat)) + heartbeat.Signature, err = privVal.Signer.Sign(SignBytes(chainID, 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) +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) } //------------------------------------- -type PrivValidatorsByAddress []*PrivValidator +type PrivValidatorsByAddress []*DefaultPrivValidator 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].Info.Address, pvs[j].Info.Address) == -1 } func (pvs PrivValidatorsByAddress) Swap(i, j int) { diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index 1eb0b57db..d0fcfd185 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -29,29 +29,34 @@ func TestLoadValidator(t *testing.T) { require.Nil(err, "%+v", err) serialized := fmt.Sprintf(`{ - "address": "%s", - "pub_key": { - "type": "ed25519", - "data": "%s" + "info": { + "address": "%s", + "pub_key": { + "type": "ed25519", + "data": "%s" + }, + "last_height": 0, + "last_round": 0, + "last_step": 0, + "last_signature": null }, - "priv_key": { - "type": "ed25519", - "data": "%s" - }, - "last_height": 0, - "last_round": 0, - "last_step": 0, - "last_signature": null + "signer": { + "priv_key": { + "type": "ed25519", + "data": "%s" + } + } }`, addrStr, pubStr, privStr) - val := PrivValidator{} + val := DefaultPrivValidator{} 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(privKey, val.PrivKey) + assert.EqualValues(addrBytes, val.Address()) + assert.EqualValues(pubKey, val.PubKey()) + valPrivKey := val.Signer.PrivKey + assert.EqualValues(privKey, valPrivKey) // export it and make sure it is the same out, err := json.Marshal(val) diff --git a/types/proposal_test.go b/types/proposal_test.go index 6f6189595..ce1bbc8d5 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -30,15 +30,15 @@ func BenchmarkProposalWriteSignBytes(b *testing.B) { func BenchmarkProposalSign(b *testing.B) { privVal := GenPrivValidator() 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 + signature, _ := privVal.Signer.Sign(signBytes) + pubKey := privVal.PubKey() 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..29cbfea27 100644 --- a/types/validator.go +++ b/types/validator.go @@ -106,7 +106,7 @@ func (vc validatorCodec) Compare(o1 interface{}, o2 interface{}) int { //-------------------------------------------------------------------------------- // For testing... -func RandValidator(randPower bool, minPower int64) (*Validator, *PrivValidator) { +func RandValidator(randPower bool, minPower int64) (*Validator, *DefaultPrivValidator) { privVal := GenPrivValidator() _, tempFilePath := cmn.Tempfile("priv_validator_") privVal.SetFile(tempFilePath) @@ -114,6 +114,6 @@ func RandValidator(randPower bool, minPower int64) (*Validator, *PrivValidator) if randPower { votePower += int64(cmn.RandUint32()) } - val := NewValidator(privVal.PubKey, votePower) + val := NewValidator(privVal.PubKey(), votePower) return val, privVal } diff --git a/types/validator_set.go b/types/validator_set.go index e334ed9f3..5531f4af3 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, []*DefaultPrivValidator) { vals := make([]*Validator, numValidators) - privValidators := make([]*PrivValidator, numValidators) + privValidators := make([]*DefaultPrivValidator, 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..716becf52 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, []*DefaultPrivValidator) { 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 *DefaultPrivValidator, 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.Address()) != 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.Address(), 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.Address()) == 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 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].Address(), 7) signAddVote(privValidators[7], vote, voteSet) } From 944ebccfe9063550923276f2b3fd0c9d3a8bd953 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 18 Sep 2017 22:05:33 -0400 Subject: [PATCH 12/22] more PrivValidator interface --- cmd/tendermint/commands/gen_validator.go | 2 +- cmd/tendermint/commands/init.go | 3 +- .../commands/reset_priv_validator.go | 6 +- cmd/tendermint/commands/run_node.go | 2 +- cmd/tendermint/commands/show_validator.go | 2 +- cmd/tendermint/commands/testnet.go | 5 +- consensus/common_test.go | 3 +- node/node.go | 4 +- types/priv_validator.go | 245 +++++++++--------- types/validator.go | 5 +- types/validator_set.go | 4 +- 11 files changed, 138 insertions(+), 143 deletions(-) diff --git a/cmd/tendermint/commands/gen_validator.go b/cmd/tendermint/commands/gen_validator.go index e4dfb0bd1..984176d2a 100644 --- a/cmd/tendermint/commands/gen_validator.go +++ b/cmd/tendermint/commands/gen_validator.go @@ -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)) diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index 259645044..d8e8bde40 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -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() diff --git a/cmd/tendermint/commands/reset_priv_validator.go b/cmd/tendermint/commands/reset_priv_validator.go index bedd38926..6255f9613 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -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) } diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 883ec6ec4..d4acd98df 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -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 } diff --git a/cmd/tendermint/commands/show_validator.go b/cmd/tendermint/commands/show_validator.go index 86a665f0c..458f11c2d 100644 --- a/cmd/tendermint/commands/show_validator.go +++ b/cmd/tendermint/commands/show_validator.go @@ -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)) } diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index 9412379a5..7fc4f94fe 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -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() } diff --git a/consensus/common_test.go b/consensus/common_test.go index cde56a3db..09b113553 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -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()) diff --git a/node/node.go b/node/node.go index a0a8c529a..4f22ee923 100644 --- a/node/node.go +++ b/node/node.go @@ -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") diff --git a/types/priv_validator.go b/types/priv_validator.go index e8fb3e240..a90f620b6 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -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: "", - } -} - -// 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, } } -// Overwrite address and pubkey for convenience -/*func (privVal *DefaultPrivValidator) setPubKeyAndAddress() { - privVal.PubKey = privVal.Signer.PubKey() - privVal.Address = privVal.PubKey.Address() -}*/ +// 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)) + } -func (privVal *DefaultPrivValidator) SetFile(filePath string) { - privVal.mtx.Lock() - defer privVal.mtx.Unlock() privVal.filePath = filePath + privVal.Signer = signerFunc(privVal.ID) + return &privVal } -func (privVal *DefaultPrivValidator) Save() { +// Address returns the address of the validator. +func (pv *PrivValidatorFS) Address() data.Bytes { + return pv.ID.Address +} + +// PubKey returns the public key of the validator. +func (pv *PrivValidatorFS) PubKey() crypto.PubKey { + return pv.ID.PubKey +} + +// Save persists the PrivValidatorFS to disk. +func (privVal *PrivValidatorFS) Save() { privVal.mtx.Lock() 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 - } -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 -} - -func (privVal *DefaultPrivValidator) String() string { +// 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}", info.Address, info.LastHeight, info.LastRound, info.LastStep) + return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", privVal.Address(), info.LastHeight, info.LastRound, info.LastStep) } //------------------------------------- -type PrivValidatorsByAddress []*DefaultPrivValidator +// ValidatorID contains the identity of the validator. +type ValidatorID struct { + Address data.Bytes `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` +} + +// LastSignedInfo contains information about the latest +// data signed by a validator to help prevent double signing. +type LastSignedInfo struct { + LastHeight int `json:"last_height"` + LastRound int `json:"last_round"` + LastStep int8 `json:"last_step"` + LastSignature crypto.Signature `json:"last_signature,omitempty"` // so we dont lose signatures + LastSignBytes data.Bytes `json:"last_signbytes,omitempty"` // so we dont lose signatures +} + +// Signer is an interface that defines how to sign messages. +// It is the caller's duty to verify the msg before calling Sign, +// eg. to avoid double signing. +// Currently, the only callers are SignVote, SignProposal, and SignHeartbeat. +type Signer interface { + Sign(msg []byte) (crypto.Signature, error) +} + +// DefaultSigner implements Signer. +// It uses a standard, unencrypted crypto.PrivKey. +type DefaultSigner struct { + PrivKey crypto.PrivKey `json:"priv_key"` +} + +// NewDefaultSigner returns an instance of DefaultSigner. +func NewDefaultSigner(priv crypto.PrivKey) *DefaultSigner { + return &DefaultSigner{ + PrivKey: priv, + } +} + +// Sign implements Signer. It signs the byte slice with a private key. +func (ds *DefaultSigner) Sign(msg []byte) (crypto.Signature, error) { + return ds.PrivKey.Sign(msg), nil +} + +//------------------------------------- + +type PrivValidatorsByAddress []*PrivValidatorFS func (pvs PrivValidatorsByAddress) Len() int { 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) { diff --git a/types/validator.go b/types/validator.go index 29cbfea27..e52831e27 100644 --- a/types/validator.go +++ b/types/validator.go @@ -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()) diff --git a/types/validator_set.go b/types/validator_set.go index 5531f4af3..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, []*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 From 4382c8d28be23f14ff5a7f33b5978a2e1eabab35 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 18 Sep 2017 23:16:14 -0400 Subject: [PATCH 13/22] fix tests --- config/toml.go | 26 ++++++++++++++--------- consensus/byzantine_test.go | 35 +++++++++++++------------------ consensus/common_test.go | 30 +++++++++++++------------- consensus/height_vote_set_test.go | 4 ++-- consensus/reactor_test.go | 14 ++++++------- consensus/replay_test.go | 24 ++++++++++----------- consensus/state_test.go | 20 ++++++++---------- rpc/test/helpers.go | 2 +- types/priv_validator.go | 24 +++++++++++++++++++++ types/priv_validator_test.go | 10 +++++---- types/proposal_test.go | 4 ++-- types/vote_set_test.go | 4 ++-- 12 files changed, 110 insertions(+), 87 deletions(-) diff --git a/config/toml.go b/config/toml.go index 5dcbe5332..02ae3fc61 100644 --- a/config/toml.go +++ b/config/toml.go @@ -127,16 +127,22 @@ var testGenesis = `{ }` var testPrivValidator = `{ - "address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456", - "pub_key": { - "type": "ed25519", - "data": "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" + "id": { + "address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456", + "pub_key": { + "type": "ed25519", + "data": "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" + } }, - "priv_key": { - "type": "ed25519", - "data": "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" + "signer": { + "priv_key": { + "type": "ed25519", + "data": "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" + } }, - "last_height": 0, - "last_round": 0, - "last_step": 0 + "info": { + "last_height": 0, + "last_round": 0, + "last_step": 0 + } }` diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 81a4ab42c..d8b6b0495 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,47 +259,38 @@ 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) Address() data.Bytes { + return privVal.pv.Address() +} + +func (privVal *ByzantinePrivValidator) PubKey() crypto.PubKey { + return privVal.pv.PubKey() } 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 } diff --git a/consensus/common_test.go b/consensus/common_test.go index 09b113553..bf56b8bff 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.Address(), 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.Address()); 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.Address()); 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.Address()); 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,10 +258,10 @@ 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) + privValidator := types.LoadOrGenPrivValidatorFS(privValidatorFile) privValidator.Reset() return privValidator } @@ -364,12 +364,12 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF 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 { _, tempFilePath := Tempfile("priv_validator_") - privVal = types.GenPrivValidator(tempFilePath) + privVal = types.GenPrivValidatorFS(tempFilePath) } css[i] = newConsensusStateWithConfig(thisConfig, state, privVal, appFunc()) @@ -392,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{ @@ -411,7 +411,7 @@ 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) diff --git a/consensus/height_vote_set_test.go b/consensus/height_vote_set_test.go index 29751b40a..3d22a1dab 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.Address(), ValidatorIndex: valIndex, Height: height, Round: round, diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index b1f9a0a5b..752c759f1 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -119,7 +119,7 @@ func TestVotingPowerChange(t *testing.T) { // map of active validators activeVals := make(map[string]struct{}) for i := 0; i < nVals; i++ { - activeVals[string(css[i].privValidator.GetAddress())] = struct{}{} + activeVals[string(css[i].privValidator.Address())] = struct{}{} } // wait till everyone makes block 1 @@ -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.PubKey() updateValidatorTx := dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 25) previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower() @@ -180,7 +180,7 @@ func TestValidatorSetChanges(t *testing.T) { // map of active validators activeVals := make(map[string]struct{}) for i := 0; i < nVals; i++ { - activeVals[string(css[i].privValidator.GetAddress())] = struct{}{} + activeVals[string(css[i].privValidator.Address())] = struct{}{} } // wait till everyone makes block 1 @@ -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.PubKey() 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.PubKey() 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.PubKey() newValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), uint64(testMinPower)) - newValidatorPubKey3 := css[nVals+2].privValidator.(*types.PrivValidator).PubKey + newValidatorPubKey3 := css[nVals+2].privValidator.PubKey() newValidatorTx3 := dummy.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), uint64(testMinPower)) waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3) diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 03ca6c8d7..d2d848bc5 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) { @@ -184,10 +184,10 @@ func setupReplayTest(t *testing.T, thisCase *testCase, nLines int, crashAfter bo cs := fixedConsensusStateDummy() // set the last step according to when we crashed vs the wal - toPV(cs.privValidator).LastHeight = 1 // first block - toPV(cs.privValidator).LastStep = thisCase.stepMap[lineStep] + toPV(cs.privValidator).Info.LastHeight = 1 // first block + toPV(cs.privValidator).Info.LastStep = thisCase.stepMap[lineStep] - t.Logf("[WARN] setupReplayTest LastStep=%v", toPV(cs.privValidator).LastStep) + t.Logf("[WARN] setupReplayTest LastStep=%v", toPV(cs.privValidator).Info.LastStep) newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1) @@ -230,8 +230,8 @@ func TestWALCrashBeforeWritePropose(t *testing.T) { msg := readTimedWALMessage(t, proposalMsg) proposal := msg.Msg.(msgInfo).Msg.(*ProposalMessage) // Set LastSig - toPV(cs.privValidator).LastSignBytes = types.SignBytes(cs.state.ChainID, proposal.Proposal) - toPV(cs.privValidator).LastSignature = proposal.Proposal.Signature + toPV(cs.privValidator).Info.LastSignBytes = types.SignBytes(cs.state.ChainID, proposal.Proposal) + toPV(cs.privValidator).Info.LastSignature = proposal.Proposal.Signature runReplayTest(t, cs, walFile, newBlockCh, thisCase, lineNum) } } @@ -255,8 +255,8 @@ func testReplayCrashBeforeWriteVote(t *testing.T, thisCase *testCase, lineNum in msg := readTimedWALMessage(t, voteMsg) vote := msg.Msg.(msgInfo).Msg.(*VoteMessage) // Set LastSig - toPV(cs.privValidator).LastSignBytes = types.SignBytes(cs.state.ChainID, vote.Vote) - toPV(cs.privValidator).LastSignature = vote.Vote.Signature + toPV(cs.privValidator).Info.LastSignBytes = types.SignBytes(cs.state.ChainID, vote.Vote) + toPV(cs.privValidator).Info.LastSignature = vote.Vote.Signature }) runReplayTest(t, cs, walFile, newBlockCh, thisCase, lineNum) } @@ -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.PubKey()) 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.PubKey()) buildAppStateFromChain(proxyApp, state, chain, nBlocks, mode) } diff --git a/consensus/state_test.go b/consensus/state_test.go index 9ae052033..977d6445c 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -65,7 +65,7 @@ func TestProposerSelection0(t *testing.T) { // lets commit a block and ensure proposer for the next height is correct prop := cs1.GetRoundState().Validators.GetProposer() - if !bytes.Equal(prop.Address, cs1.privValidator.GetAddress()) { + if !bytes.Equal(prop.Address, cs1.privValidator.Address()) { t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address) } @@ -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].Address()) { 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)].Address()) { 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 /* @@ -615,7 +613,7 @@ func TestLockPOLUnlock(t *testing.T) { timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1) unlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringUnlock(), 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) // everything done from perspective of cs1 @@ -709,7 +707,7 @@ func TestLockPOLSafety1(t *testing.T) { timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, 0) @@ -831,7 +829,7 @@ func TestLockPOLSafety2(t *testing.T) { timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1) unlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringUnlock(), 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) // the block for R0: gets polkad but we miss it // (even though we signed it, shhh) @@ -923,7 +921,7 @@ func TestSlashingPrevotes(t *testing.T) { proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1) timeoutWaitCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringTimeoutWait() , 1) newRoundCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringNewRound() , 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, 0) @@ -958,7 +956,7 @@ func TestSlashingPrecommits(t *testing.T) { proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1) timeoutWaitCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringTimeoutWait() , 1) newRoundCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringNewRound() , 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, 0) @@ -1005,7 +1003,7 @@ func TestHalt1(t *testing.T) { timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1) newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlock(), 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, 0) diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 132c4d4d9..82b14ce34 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -79,7 +79,7 @@ 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) + privValidator := types.LoadOrGenPrivValidatorFS(privValidatorFile) papp := proxy.NewLocalClientCreator(app) node := nm.NewNode(config, privValidator, papp, logger) return node diff --git a/types/priv_validator.go b/types/priv_validator.go index a90f620b6..e5b537789 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -157,6 +157,30 @@ func (privVal *PrivValidatorFS) save() { } } +// UnmarshalJSON unmarshals the given jsonString +// into a PrivValidatorFS using a DefaultSigner. +func (pv *PrivValidatorFS) UnmarshalJSON(jsonString []byte) error { + idAndInfo := &struct { + ID ValidatorID `json:"id"` + Info LastSignedInfo `json:"info"` + }{} + if err := json.Unmarshal(jsonString, idAndInfo); err != nil { + return err + } + + signer := &struct { + Signer *DefaultSigner `json:"signer"` + }{} + if err := json.Unmarshal(jsonString, signer); err != nil { + return err + } + + pv.ID = idAndInfo.ID + pv.Info = idAndInfo.Info + pv.Signer = signer.Signer + return nil +} + // Reset resets all fields in the PrivValidatorFS.Info. // NOTE: Unsafe! func (privVal *PrivValidatorFS) Reset() { diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index d0fcfd185..58a43d489 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -29,12 +29,14 @@ func TestLoadValidator(t *testing.T) { require.Nil(err, "%+v", err) serialized := fmt.Sprintf(`{ - "info": { + "id": { "address": "%s", "pub_key": { "type": "ed25519", "data": "%s" - }, + } + }, + "info": { "last_height": 0, "last_round": 0, "last_step": 0, @@ -48,14 +50,14 @@ func TestLoadValidator(t *testing.T) { } }`, addrStr, pubStr, privStr) - val := DefaultPrivValidator{} + 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()) - valPrivKey := val.Signer.PrivKey + valPrivKey := val.Signer.(*DefaultSigner).PrivKey assert.EqualValues(privKey, valPrivKey) // export it and make sure it is the same diff --git a/types/proposal_test.go b/types/proposal_test.go index ce1bbc8d5..2e4e7e198 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -28,7 +28,7 @@ func BenchmarkProposalWriteSignBytes(b *testing.B) { } func BenchmarkProposalSign(b *testing.B) { - privVal := GenPrivValidator() + privVal := GenPrivValidatorFS("") for i := 0; i < b.N; i++ { privVal.Signer.Sign(SignBytes("test_chain_id", testProposal)) } @@ -36,7 +36,7 @@ func BenchmarkProposalSign(b *testing.B) { func BenchmarkProposalVerifySignature(b *testing.B) { signBytes := SignBytes("test_chain_id", testProposal) - privVal := GenPrivValidator() + privVal := GenPrivValidatorFS("") signature, _ := privVal.Signer.Sign(signBytes) pubKey := privVal.PubKey() diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 716becf52..ab1456ed9 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, []*DefaultPrivValidator) { +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,7 +59,7 @@ func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote { return vote } -func signAddVote(privVal *DefaultPrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) { +func signAddVote(privVal *PrivValidatorFS, vote *Vote, voteSet *VoteSet) (bool, error) { var err error vote.Signature, err = privVal.Signer.Sign(SignBytes(voteSet.ChainID(), vote)) if err != nil { From 147a18b34a7e5d5871e777b8393c5615aac63237 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 18 Sep 2017 23:20:36 -0400 Subject: [PATCH 14/22] fix some comments --- cmd/tendermint/commands/run_node.go | 2 ++ cmd/tendermint/main.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index d4acd98df..3befae279 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -43,6 +43,8 @@ func AddNodeFlags(cmd *cobra.Command) { // It allows other projects to make Tendermint binaries with custom signers and applications. type FuncSignerAndApp func(*cfg.Config) (types.PrivValidator, proxy.ClientCreator) +// DefaultSignerAndApp is a default FuncSignerAndApp that returns a PrivValidatorFS +// and a DefaultClientCreator using the relevant fields from the config. func DefaultSignerAndApp(config *cfg.Config) (types.PrivValidator, proxy.ClientCreator) { privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()) clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index 7cc1b2183..1e2c00051 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -27,7 +27,7 @@ func main() { // * Use an external signer for their validators // * Supply an in-proc abci app // can copy this file and use something other than the - // default SignerAndApp function + // DefaultSignerAndApp function signerAndApp := cmd.DefaultSignerAndApp // Create & start node From 779c2a22d0bddb4e2c7949dbc42128f0cecf217a Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 20 Sep 2017 18:29:36 -0400 Subject: [PATCH 15/22] node: NewNode takes DBProvider and GenDocProvider --- cmd/tendermint/commands/run_node.go | 10 ++- consensus/common_test.go | 8 +- consensus/replay_file.go | 7 +- consensus/replay_test.go | 2 +- node/node.go | 128 +++++++++++++++++++++++----- node/node_test.go | 8 +- rpc/test/helpers.go | 7 +- state/execution_test.go | 3 +- state/state.go | 34 +++++--- state/state_test.go | 3 +- 10 files changed, 164 insertions(+), 46 deletions(-) diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 3befae279..6a793706f 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -67,7 +67,15 @@ func NewRunNodeCmd(signerAndApp FuncSignerAndApp) *cobra.Command { // Create & start node privVal, clientCreator := signerAndApp(config) - n := node.NewNode(config, privVal, clientCreator, logger.With("module", "node")) + n, err := node.NewNode(config, + privVal, + clientCreator, + node.DefaultGenesisDocProviderFunc(config), + node.DefaultDBProvider, + logger.With("module", "node")) + if err != nil { + return fmt.Errorf("Failed to create node: %v", err) + } if _, err := n.Start(); err != nil { return fmt.Errorf("Failed to start node: %v", err) diff --git a/consensus/common_test.go b/consensus/common_test.go index bf56b8bff..a746ec59c 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -268,7 +268,7 @@ func loadPrivValidator(config *cfg.Config) *types.PrivValidatorFS { 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,7 +359,7 @@ 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)) @@ -414,7 +414,7 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G 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/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 d2d848bc5..c6504380d 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -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/node/node.go b/node/node.go index 4f22ee923..761f4d5e4 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,13 +31,44 @@ 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()) + } +} + +//------------------------------------------------------------------------------ + +// Node is the highest level interface to a full Tendermint node. +// It includes all configuration information and running services. type Node struct { cmn.BaseService @@ -58,24 +94,55 @@ type Node struct { txIndexer txindex.TxIndexer } -func NewNodeDefault(config *cfg.Config, logger log.Logger) *Node { +// NewNodeDefault returns a Tendermint node with default settings for the +// PrivValidator, ClientCreator, GenesisDoc, and DBProvider, +func NewNodeDefault(config *cfg.Config, logger log.Logger) (*Node, error) { // Get PrivValidator privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()) - return NewNode(config, privValidator, - proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), logger) + return NewNode(config, + privValidator, + proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), + DefaultGenesisDocProviderFunc(config), + DefaultDBProvider, + logger) } -func NewNode(config *cfg.Config, privValidator types.PrivValidator, clientCreator proxy.ClientCreator, logger log.Logger) *Node { +// NewNode returns a new, ready to go, Tendermint Node. +func NewNode(config *cfg.Config, + privValidator types.PrivValidator, + clientCreator proxy.ClientCreator, + genDocProvider GenesisDocProvider, + dbProvider DBProvider, + logger log.Logger) (*Node, error) { + // 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 := genDocProvider() + 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 +152,7 @@ func NewNode(config *cfg.Config, privValidator types.PrivValidator, clientCreato 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 +163,10 @@ func NewNode(config *cfg.Config, privValidator types.PrivValidator, clientCreato 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 +179,8 @@ func NewNode(config *cfg.Config, privValidator types.PrivValidator, clientCreato // 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 @@ -232,9 +301,10 @@ func NewNode(config *cfg.Config, privValidator types.PrivValidator, clientCreato 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 +340,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 +356,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 +364,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) @@ -360,39 +432,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 +// 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 +523,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..1b6d27f47 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 := NewNodeDefault(config, log.TestingLogger()) + assert.NoError(t, err, "expected no err on NewNodeDefault") n.Start() t.Logf("Started node %v", n.sw.NodeInfo()) diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 82b14ce34..55e27f5b8 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -81,6 +81,11 @@ func NewTendermint(app abci.Application) *nm.Node { privValidatorFile := config.PrivValidatorFile() 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) {} From 3ca7b10ad4c3e1a40f507489aa857c46acf1f115 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 20 Sep 2017 18:40:41 -0400 Subject: [PATCH 16/22] types: more . -> cmn --- types/priv_validator.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/types/priv_validator.go b/types/priv_validator.go index e5b537789..7fe6e1530 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -11,7 +11,7 @@ import ( crypto "github.com/tendermint/go-crypto" data "github.com/tendermint/go-wire/data" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) // TODO: type ? @@ -29,7 +29,7 @@ func voteToStep(vote *Vote) int8 { case VoteTypePrecommit: return stepPrecommit default: - PanicSanity("Unknown vote type") + cmn.PanicSanity("Unknown vote type") return 0 } } @@ -78,12 +78,12 @@ func LoadOrGenPrivValidatorFS(filePath string) *PrivValidatorFS { func LoadPrivValidatorFS(filePath string) *PrivValidatorFS { privValJSONBytes, err := ioutil.ReadFile(filePath) if err != nil { - Exit(err.Error()) + cmn.Exit(err.Error()) } 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 @@ -111,12 +111,12 @@ func GenPrivValidatorFS(filePath string) *PrivValidatorFS { func LoadPrivValidatorFSWithSigner(filePath string, signerFunc func(ValidatorID) Signer) *PrivValidatorFS { privValJSONBytes, err := ioutil.ReadFile(filePath) if err != nil { - Exit(err.Error()) + cmn.Exit(err.Error()) } 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 @@ -143,17 +143,17 @@ func (privVal *PrivValidatorFS) 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) } } @@ -198,7 +198,7 @@ func (privVal *PrivValidatorFS) SignVote(chainID string, vote *Vote) error { 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 @@ -246,7 +246,7 @@ func (privVal *PrivValidatorFS) signBytesHRS(height, round int, step int8, signB } else if info.LastStep == step { if info.LastSignBytes != nil { if info.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), From 7b99039c34dd513d2f1feca49ad726d46f309123 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 21 Sep 2017 12:19:21 -0400 Subject: [PATCH 17/22] make signBytesHRS a method on LastSignedInfo --- types/priv_validator.go | 274 +++++++++++++++++++++------------------- 1 file changed, 145 insertions(+), 129 deletions(-) diff --git a/types/priv_validator.go b/types/priv_validator.go index 7fe6e1530..892a4f06a 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -34,7 +34,8 @@ func voteToStep(vote *Vote) int8 { } } -// PrivValidator defines the functionality of a local Tendermint validator. +// PrivValidator defines the functionality of a local Tendermint validator +// that signs votes, proposals, and heartbeats, and never double signs. type PrivValidator interface { Address() data.Bytes // redundant since .PubKey().Address() PubKey() crypto.PubKey @@ -61,6 +62,122 @@ type PrivValidatorFS struct { filePath string } +// Address returns the address of the validator. +// Implements PrivValidator. +func (pv *PrivValidatorFS) Address() data.Bytes { + return pv.ID.Address +} + +// PubKey returns the public key of the validator. +// Implements PrivValidator. +func (pv *PrivValidatorFS) PubKey() crypto.PubKey { + return pv.ID.PubKey +} + +// 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.Info.SignBytesHRS(privVal.Signer, + vote.Height, vote.Round, voteToStep(vote), SignBytes(chainID, vote)) + if err != nil { + return errors.New(cmn.Fmt("Error signing vote: %v", err)) + } + privVal.save() + vote.Signature = signature + return nil +} + +// 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.Info.SignBytesHRS(privVal.Signer, + proposal.Height, proposal.Round, stepPropose, SignBytes(chainID, proposal)) + if err != nil { + return fmt.Errorf("Error signing proposal: %v", err) + } + privVal.save() + proposal.Signature = signature + return nil +} + +// 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 + heartbeat.Signature, err = privVal.Signer.Sign(SignBytes(chainID, heartbeat)) + return err +} + +// Save persists the PrivValidatorFS to disk. +func (privVal *PrivValidatorFS) Save() { + privVal.mtx.Lock() + defer privVal.mtx.Unlock() + privVal.save() +} + +func (privVal *PrivValidatorFS) save() { + if privVal.filePath == "" { + cmn.PanicSanity("Cannot save PrivValidator: filePath not set") + } + jsonBytes, err := json.Marshal(privVal) + if err != nil { + // `@; BOOM!!! + cmn.PanicCrisis(err) + } + err = cmn.WriteFileAtomic(privVal.filePath, jsonBytes, 0600) + if err != nil { + // `@; BOOM!!! + cmn.PanicCrisis(err) + } +} + +// UnmarshalJSON unmarshals the given jsonString +// into a PrivValidatorFS using a DefaultSigner. +func (pv *PrivValidatorFS) UnmarshalJSON(jsonString []byte) error { + idAndInfo := &struct { + ID ValidatorID `json:"id"` + Info LastSignedInfo `json:"info"` + }{} + if err := json.Unmarshal(jsonString, idAndInfo); err != nil { + return err + } + + signer := &struct { + Signer *DefaultSigner `json:"signer"` + }{} + if err := json.Unmarshal(jsonString, signer); err != nil { + return err + } + + pv.ID = idAndInfo.ID + pv.Info = idAndInfo.Info + pv.Signer = signer.Signer + return nil +} + +// Reset resets all fields in the PrivValidatorFS.Info. +// NOTE: Unsafe! +func (privVal *PrivValidatorFS) Reset() { + privVal.Info.LastHeight = 0 + privVal.Info.LastRound = 0 + privVal.Info.LastStep = 0 + privVal.Info.LastSignature = crypto.Signature{} + privVal.Info.LastSignBytes = nil + privVal.Save() +} + +// 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) +} + // 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 { @@ -124,111 +241,33 @@ func LoadPrivValidatorFSWithSigner(filePath string, signerFunc func(ValidatorID) return &privVal } -// Address returns the address of the validator. -func (pv *PrivValidatorFS) Address() data.Bytes { - return pv.ID.Address +//------------------------------------- + +// ValidatorID contains the identity of the validator. +type ValidatorID struct { + Address data.Bytes `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` } -// PubKey returns the public key of the validator. -func (pv *PrivValidatorFS) PubKey() crypto.PubKey { - return pv.ID.PubKey +//------------------------------------- + +// 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 } -// Save persists the PrivValidatorFS to disk. -func (privVal *PrivValidatorFS) Save() { - privVal.mtx.Lock() - defer privVal.mtx.Unlock() - privVal.save() -} +// SignBytesHRS signs the given signBytes with the signer if the height/round/step (HRS) +// are greater than the latest state of the LastSignedInfo. If the HRS are equal, +// it returns the LastSignedInfo.LastSignature. +func (info *LastSignedInfo) SignBytesHRS(signer Signer, + height, round int, step int8, signBytes []byte) (crypto.Signature, error) { -func (privVal *PrivValidatorFS) save() { - if privVal.filePath == "" { - cmn.PanicSanity("Cannot save PrivValidator: filePath not set") - } - jsonBytes, err := json.Marshal(privVal) - if err != nil { - // `@; BOOM!!! - cmn.PanicCrisis(err) - } - err = cmn.WriteFileAtomic(privVal.filePath, jsonBytes, 0600) - if err != nil { - // `@; BOOM!!! - cmn.PanicCrisis(err) - } -} - -// UnmarshalJSON unmarshals the given jsonString -// into a PrivValidatorFS using a DefaultSigner. -func (pv *PrivValidatorFS) UnmarshalJSON(jsonString []byte) error { - idAndInfo := &struct { - ID ValidatorID `json:"id"` - Info LastSignedInfo `json:"info"` - }{} - if err := json.Unmarshal(jsonString, idAndInfo); err != nil { - return err - } - - signer := &struct { - Signer *DefaultSigner `json:"signer"` - }{} - if err := json.Unmarshal(jsonString, signer); err != nil { - return err - } - - pv.ID = idAndInfo.ID - pv.Info = idAndInfo.Info - pv.Signer = signer.Signer - return nil -} - -// Reset resets all fields in the PrivValidatorFS.Info. -// NOTE: Unsafe! -func (privVal *PrivValidatorFS) Reset() { - privVal.Info.LastHeight = 0 - privVal.Info.LastRound = 0 - privVal.Info.LastStep = 0 - privVal.Info.LastSignature = crypto.Signature{} - privVal.Info.LastSignBytes = nil - privVal.Save() -} - -// 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)) - if err != nil { - return errors.New(cmn.Fmt("Error signing vote: %v", err)) - } - vote.Signature = signature - return nil -} - -// 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)) - if err != nil { - return fmt.Errorf("Error signing proposal: %v", err) - } - proposal.Signature = signature - 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 *PrivValidatorFS) signBytesHRS(height, round int, step int8, signBytes []byte) (crypto.Signature, error) { sig := crypto.Signature{} - info := privVal.Info // If height regression, err if info.LastHeight > height { return sig, errors.New("Height regression") @@ -262,46 +301,23 @@ func (privVal *PrivValidatorFS) signBytesHRS(height, round int, step int8, signB } // Sign - sig, err := privVal.Signer.Sign(signBytes) + sig, err := signer.Sign(signBytes) if err != nil { return sig, err } // Persist height/round/step - privVal.Info.LastHeight = height - privVal.Info.LastRound = round - privVal.Info.LastStep = step - privVal.Info.LastSignature = sig - privVal.Info.LastSignBytes = signBytes - privVal.save() + info.LastHeight = height + info.LastRound = round + info.LastStep = step + info.LastSignature = sig + info.LastSignBytes = signBytes 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) -} - //------------------------------------- -// ValidatorID contains the identity of the validator. -type ValidatorID struct { - Address data.Bytes `json:"address"` - PubKey crypto.PubKey `json:"pub_key"` -} - -// LastSignedInfo contains information about the latest -// data signed by a validator to help prevent double signing. -type LastSignedInfo struct { - LastHeight int `json:"last_height"` - LastRound int `json:"last_round"` - LastStep int8 `json:"last_step"` - LastSignature crypto.Signature `json:"last_signature,omitempty"` // so we dont lose signatures - LastSignBytes data.Bytes `json:"last_signbytes,omitempty"` // so we dont lose signatures -} - // Signer is an interface that defines how to sign messages. // It is the caller's duty to verify the msg before calling Sign, // eg. to avoid double signing. From 75b97a5a658ab96bd2279ee0abdcb644962daf5a Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 21 Sep 2017 16:32:02 -0400 Subject: [PATCH 18/22] PrivValidatorFS is like old PrivValidator, for now --- cmd/tendermint/commands/init.go | 2 +- cmd/tendermint/commands/testnet.go | 2 +- config/toml.go | 10 +- consensus/byzantine_test.go | 10 +- consensus/common_test.go | 8 +- consensus/height_vote_set_test.go | 2 +- consensus/reactor_test.go | 14 +- consensus/replay_test.go | 18 +-- consensus/state.go | 12 +- consensus/state_test.go | 18 +-- node/node.go | 6 +- types/priv_validator.go | 211 ++++++++++++----------------- types/priv_validator_test.go | 17 +-- types/proposal_test.go | 2 +- types/validator.go | 2 +- types/vote_set_test.go | 54 ++++---- 16 files changed, 167 insertions(+), 221 deletions(-) diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index d8e8bde40..cbafac3ef 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -29,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/testnet.go b/cmd/tendermint/commands/testnet.go index 7fc4f94fe..ac6f337a9 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -47,7 +47,7 @@ func testnetFiles(cmd *cobra.Command, args []string) { privValFile := path.Join(dataDir, mach, "priv_validator.json") privVal := types.LoadPrivValidatorFS(privValFile) genVals[i] = types.GenesisValidator{ - PubKey: privVal.PubKey(), + PubKey: privVal.GetPubKey(), Power: 1, Name: mach, } diff --git a/config/toml.go b/config/toml.go index 02ae3fc61..4366695db 100644 --- a/config/toml.go +++ b/config/toml.go @@ -127,22 +127,16 @@ var testGenesis = `{ }` var testPrivValidator = `{ - "id": { "address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456", "pub_key": { "type": "ed25519", "data": "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" - } - }, - "signer": { + }, "priv_key": { "type": "ed25519", "data": "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" - } - }, - "info": { + }, "last_height": 0, "last_round": 0, "last_step": 0 - } }` diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index d8b6b0495..0f2d7b040 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -272,12 +272,12 @@ func NewByzantinePrivValidator(pv types.PrivValidator) *ByzantinePrivValidator { } } -func (privVal *ByzantinePrivValidator) Address() data.Bytes { - return privVal.pv.Address() +func (privVal *ByzantinePrivValidator) GetAddress() data.Bytes { + return privVal.pv.GetAddress() } -func (privVal *ByzantinePrivValidator) PubKey() crypto.PubKey { - return privVal.pv.PubKey() +func (privVal *ByzantinePrivValidator) GetPubKey() crypto.PubKey { + return privVal.pv.GetPubKey() } func (privVal *ByzantinePrivValidator) SignVote(chainID string, vote *types.Vote) (err error) { @@ -296,5 +296,5 @@ func (privVal *ByzantinePrivValidator) SignHeartbeat(chainID string, heartbeat * } 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 a746ec59c..b16afc3d0 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -65,7 +65,7 @@ func NewValidatorStub(privValidator types.PrivValidator, valIndex int) *validato 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") } diff --git a/consensus/height_vote_set_test.go b/consensus/height_vote_set_test.go index 3d22a1dab..7e03e40f5 100644 --- a/consensus/height_vote_set_test.go +++ b/consensus/height_vote_set_test.go @@ -47,7 +47,7 @@ func TestPeerCatchupRounds(t *testing.T) { 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 752c759f1..623a65413 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -119,7 +119,7 @@ func TestVotingPowerChange(t *testing.T) { // map of active validators activeVals := make(map[string]struct{}) for i := 0; i < nVals; i++ { - activeVals[string(css[i].privValidator.Address())] = struct{}{} + activeVals[string(css[i].privValidator.GetAddress())] = struct{}{} } // wait till everyone makes block 1 @@ -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.PubKey() + val1PubKey := css[0].privValidator.GetPubKey() updateValidatorTx := dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 25) previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower() @@ -180,7 +180,7 @@ func TestValidatorSetChanges(t *testing.T) { // map of active validators activeVals := make(map[string]struct{}) for i := 0; i < nVals; i++ { - activeVals[string(css[i].privValidator.Address())] = struct{}{} + activeVals[string(css[i].privValidator.GetAddress())] = struct{}{} } // wait till everyone makes block 1 @@ -193,7 +193,7 @@ func TestValidatorSetChanges(t *testing.T) { //--------------------------------------------------------------------------- t.Log("---------------------------- Testing adding one validator") - newValidatorPubKey1 := css[nVals].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.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.PubKey() + newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey() newValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), uint64(testMinPower)) - newValidatorPubKey3 := css[nVals+2].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_test.go b/consensus/replay_test.go index c6504380d..765434677 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -184,10 +184,10 @@ func setupReplayTest(t *testing.T, thisCase *testCase, nLines int, crashAfter bo cs := fixedConsensusStateDummy() // set the last step according to when we crashed vs the wal - toPV(cs.privValidator).Info.LastHeight = 1 // first block - toPV(cs.privValidator).Info.LastStep = thisCase.stepMap[lineStep] + toPV(cs.privValidator).LastHeight = 1 // first block + toPV(cs.privValidator).LastStep = thisCase.stepMap[lineStep] - t.Logf("[WARN] setupReplayTest LastStep=%v", toPV(cs.privValidator).Info.LastStep) + t.Logf("[WARN] setupReplayTest LastStep=%v", toPV(cs.privValidator).LastStep) newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1) @@ -230,8 +230,8 @@ func TestWALCrashBeforeWritePropose(t *testing.T) { msg := readTimedWALMessage(t, proposalMsg) proposal := msg.Msg.(msgInfo).Msg.(*ProposalMessage) // Set LastSig - toPV(cs.privValidator).Info.LastSignBytes = types.SignBytes(cs.state.ChainID, proposal.Proposal) - toPV(cs.privValidator).Info.LastSignature = proposal.Proposal.Signature + toPV(cs.privValidator).LastSignBytes = types.SignBytes(cs.state.ChainID, proposal.Proposal) + toPV(cs.privValidator).LastSignature = proposal.Proposal.Signature runReplayTest(t, cs, walFile, newBlockCh, thisCase, lineNum) } } @@ -255,8 +255,8 @@ func testReplayCrashBeforeWriteVote(t *testing.T, thisCase *testCase, lineNum in msg := readTimedWALMessage(t, voteMsg) vote := msg.Msg.(msgInfo).Msg.(*VoteMessage) // Set LastSig - toPV(cs.privValidator).Info.LastSignBytes = types.SignBytes(cs.state.ChainID, vote.Vote) - toPV(cs.privValidator).Info.LastSignature = vote.Vote.Signature + toPV(cs.privValidator).LastSignBytes = types.SignBytes(cs.state.ChainID, vote.Vote) + toPV(cs.privValidator).LastSignature = vote.Vote.Signature }) runReplayTest(t, cs, walFile, newBlockCh, thisCase, lineNum) } @@ -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) } diff --git a/consensus/state.go b/consensus/state.go index f141cad58..648fc0559 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -817,7 +817,7 @@ func (cs *ConsensusState) needProofBlock(height int) bool { func (cs *ConsensusState) proposalHeartbeat(height, round int) { counter := 0 - addr := cs.privValidator.Address() + addr := cs.privValidator.GetAddress() valIndex, v := cs.Validators.GetByAddress(addr) if v == nil { // not a validator @@ -878,7 +878,7 @@ func (cs *ConsensusState) enterPropose(height int, round int) { if !cs.isProposer() { cs.Logger.Info("enterPropose: Not our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator) - if cs.Validators.HasAddress(cs.privValidator.Address()) { + if cs.Validators.HasAddress(cs.privValidator.GetAddress()) { cs.Logger.Debug("This node is a validator") } else { cs.Logger.Debug("This node is not a validator") @@ -891,7 +891,7 @@ func (cs *ConsensusState) enterPropose(height int, round int) { } func (cs *ConsensusState) isProposer() bool { - return bytes.Equal(cs.Validators.GetProposer().Address, cs.privValidator.Address()) + return bytes.Equal(cs.Validators.GetProposer().Address, cs.privValidator.GetAddress()) } func (cs *ConsensusState) defaultDecideProposal(height, round int) { @@ -1436,7 +1436,7 @@ func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerKey string) error { if err == ErrVoteHeightMismatch { return err } else if _, ok := err.(*types.ErrVoteConflictingVotes); ok { - if bytes.Equal(vote.ValidatorAddress, cs.privValidator.Address()) { + if bytes.Equal(vote.ValidatorAddress, cs.privValidator.GetAddress()) { cs.Logger.Error("Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", vote.Height, "round", vote.Round, "type", vote.Type) return err } @@ -1565,7 +1565,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerKey string) (added bool, } func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) { - addr := cs.privValidator.Address() + addr := cs.privValidator.GetAddress() valIndex, _ := cs.Validators.GetByAddress(addr) vote := &types.Vote{ ValidatorAddress: addr, @@ -1582,7 +1582,7 @@ func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSet // sign the vote and publish on internalMsgQueue func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.PartSetHeader) *types.Vote { // if we don't have a key or we're not in the validator set, do nothing - if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.Address()) { + if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetAddress()) { return nil } vote, err := cs.signVote(type_, hash, header) diff --git a/consensus/state_test.go b/consensus/state_test.go index 977d6445c..c4a6769e9 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -65,7 +65,7 @@ func TestProposerSelection0(t *testing.T) { // lets commit a block and ensure proposer for the next height is correct prop := cs1.GetRoundState().Validators.GetProposer() - if !bytes.Equal(prop.Address, cs1.privValidator.Address()) { + if !bytes.Equal(prop.Address, cs1.privValidator.GetAddress()) { t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address) } @@ -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)) } @@ -613,7 +613,7 @@ func TestLockPOLUnlock(t *testing.T) { timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1) unlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringUnlock(), 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) // everything done from perspective of cs1 @@ -707,7 +707,7 @@ func TestLockPOLSafety1(t *testing.T) { timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1) timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, 0) @@ -829,7 +829,7 @@ func TestLockPOLSafety2(t *testing.T) { timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1) unlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringUnlock(), 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) // the block for R0: gets polkad but we miss it // (even though we signed it, shhh) @@ -921,7 +921,7 @@ func TestSlashingPrevotes(t *testing.T) { proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1) timeoutWaitCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringTimeoutWait() , 1) newRoundCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringNewRound() , 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, 0) @@ -956,7 +956,7 @@ func TestSlashingPrecommits(t *testing.T) { proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1) timeoutWaitCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringTimeoutWait() , 1) newRoundCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringNewRound() , 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, 0) @@ -1003,7 +1003,7 @@ func TestHalt1(t *testing.T) { timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1) newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1) newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlock(), 1) - voteCh := subscribeToVoter(cs1, cs1.privValidator.Address()) + voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, 0) diff --git a/node/node.go b/node/node.go index 761f4d5e4..0fb064514 100644 --- a/node/node.go +++ b/node/node.go @@ -188,13 +188,13 @@ func NewNode(config *cfg.Config, 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") @@ -386,7 +386,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()) diff --git a/types/priv_validator.go b/types/priv_validator.go index 892a4f06a..6db240966 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -37,8 +37,8 @@ func voteToStep(vote *Vote) int8 { // PrivValidator defines the functionality of a local Tendermint validator // that signs votes, proposals, and heartbeats, and never double signs. type PrivValidator interface { - Address() data.Bytes // redundant since .PubKey().Address() - PubKey() crypto.PubKey + GetAddress() data.Bytes // redundant since .PubKey().Address() + GetPubKey() crypto.PubKey SignVote(chainID string, vote *Vote) error SignProposal(chainID string, proposal *Proposal) error @@ -49,29 +49,34 @@ type PrivValidator interface { // 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"` + 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 - // mutable state to be persisted to disk - // after each signature to prevent double signing - mtx sync.Mutex - Info LastSignedInfo `json:"info"` + // PrivKey should be empty if a Signer other than the default is being used. + PrivKey crypto.PrivKey `json:"priv_key"` + Signer `json:"-"` // For persistence. // Overloaded for testing. filePath string + mtx sync.Mutex } -// Address returns the address of the validator. +// GetAddress returns the address of the validator. // Implements PrivValidator. -func (pv *PrivValidatorFS) Address() data.Bytes { - return pv.ID.Address +func (pv *PrivValidatorFS) GetAddress() data.Bytes { + return pv.Address } -// PubKey returns the public key of the validator. +// GetPubKey returns the public key of the validator. // Implements PrivValidator. -func (pv *PrivValidatorFS) PubKey() crypto.PubKey { - return pv.ID.PubKey +func (pv *PrivValidatorFS) GetPubKey() crypto.PubKey { + return pv.PubKey } // SignVote signs a canonical representation of the vote, along with the chainID. @@ -79,12 +84,10 @@ func (pv *PrivValidatorFS) PubKey() crypto.PubKey { func (privVal *PrivValidatorFS) SignVote(chainID string, vote *Vote) error { privVal.mtx.Lock() defer privVal.mtx.Unlock() - signature, err := privVal.Info.SignBytesHRS(privVal.Signer, - vote.Height, vote.Round, voteToStep(vote), SignBytes(chainID, vote)) + signature, err := privVal.signBytesHRS(vote.Height, vote.Round, voteToStep(vote), SignBytes(chainID, vote)) if err != nil { return errors.New(cmn.Fmt("Error signing vote: %v", err)) } - privVal.save() vote.Signature = signature return nil } @@ -94,12 +97,10 @@ func (privVal *PrivValidatorFS) SignVote(chainID string, vote *Vote) error { func (privVal *PrivValidatorFS) SignProposal(chainID string, proposal *Proposal) error { privVal.mtx.Lock() defer privVal.mtx.Unlock() - signature, err := privVal.Info.SignBytesHRS(privVal.Signer, - proposal.Height, proposal.Round, stepPropose, SignBytes(chainID, proposal)) + signature, err := privVal.signBytesHRS(proposal.Height, proposal.Round, stepPropose, SignBytes(chainID, proposal)) if err != nil { return fmt.Errorf("Error signing proposal: %v", err) } - privVal.save() proposal.Signature = signature return nil } @@ -137,45 +138,75 @@ func (privVal *PrivValidatorFS) save() { } } -// UnmarshalJSON unmarshals the given jsonString -// into a PrivValidatorFS using a DefaultSigner. -func (pv *PrivValidatorFS) UnmarshalJSON(jsonString []byte) error { - idAndInfo := &struct { - ID ValidatorID `json:"id"` - Info LastSignedInfo `json:"info"` - }{} - if err := json.Unmarshal(jsonString, idAndInfo); err != nil { - return err +// 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 { + return sig, errors.New("Height regression") + } + // More cases for when the height matches + if privVal.LastHeight == height { + // If round regression, err + if privVal.LastRound > round { + return sig, errors.New("Round regression") + } + // If step regression, err + if privVal.LastRound == round { + if privVal.LastStep > step { + return sig, errors.New("Step regression") + } else if privVal.LastStep == step { + if privVal.LastSignBytes != nil { + if privVal.LastSignature.Empty() { + 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), + // so we can actually lose them, but will still never sign conflicting ones + if bytes.Equal(privVal.LastSignBytes, signBytes) { + // log.Notice("Using privVal.LastSignature", "sig", privVal.LastSignature) + return privVal.LastSignature, nil + } + } + return sig, errors.New("Step regression") + } + } } - signer := &struct { - Signer *DefaultSigner `json:"signer"` - }{} - if err := json.Unmarshal(jsonString, signer); err != nil { - return err + // Sign + sig, err := privVal.Signer.Sign(signBytes) + if err != nil { + return sig, err } - pv.ID = idAndInfo.ID - pv.Info = idAndInfo.Info - pv.Signer = signer.Signer - return nil + // Persist height/round/step + privVal.LastHeight = height + privVal.LastRound = round + privVal.LastStep = step + privVal.LastSignature = sig + privVal.LastSignBytes = signBytes + privVal.save() + + return sig, nil } -// Reset resets all fields in the PrivValidatorFS.Info. +// Reset resets all fields in the PrivValidatorFS. // NOTE: Unsafe! func (privVal *PrivValidatorFS) Reset() { - privVal.Info.LastHeight = 0 - privVal.Info.LastRound = 0 - privVal.Info.LastStep = 0 - privVal.Info.LastSignature = crypto.Signature{} - privVal.Info.LastSignBytes = nil + privVal.LastHeight = 0 + privVal.LastRound = 0 + privVal.LastStep = 0 + privVal.LastSignature = crypto.Signature{} + privVal.LastSignBytes = nil privVal.Save() } // 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) + return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", privVal.GetAddress(), privVal.LastHeight, privVal.LastRound, privVal.LastStep) } // LoadOrGenPrivValidatorFS loads a PrivValidatorFS from the given filePath @@ -204,6 +235,7 @@ func LoadPrivValidatorFS(filePath string) *PrivValidatorFS { } privVal.filePath = filePath + privVal.Signer = NewDefaultSigner(privVal.PrivKey) return &privVal } @@ -212,10 +244,10 @@ func LoadPrivValidatorFS(filePath string) *PrivValidatorFS { func GenPrivValidatorFS(filePath string) *PrivValidatorFS { privKey := crypto.GenPrivKeyEd25519().Wrap() return &PrivValidatorFS{ - ID: ValidatorID{privKey.PubKey().Address(), privKey.PubKey()}, - Info: LastSignedInfo{ - LastStep: stepNone, - }, + Address: privKey.PubKey().Address(), + PubKey: privKey.PubKey(), + PrivKey: privKey, + LastStep: stepNone, Signer: NewDefaultSigner(privKey), filePath: filePath, } @@ -225,7 +257,7 @@ func GenPrivValidatorFS(filePath string) *PrivValidatorFS { // 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 { +func LoadPrivValidatorFSWithSigner(filePath string, signerFunc func(crypto.PubKey) Signer) *PrivValidatorFS { privValJSONBytes, err := ioutil.ReadFile(filePath) if err != nil { cmn.Exit(err.Error()) @@ -237,85 +269,12 @@ func LoadPrivValidatorFSWithSigner(filePath string, signerFunc func(ValidatorID) } privVal.filePath = filePath - privVal.Signer = signerFunc(privVal.ID) + privVal.Signer = signerFunc(privVal.PubKey) return &privVal } //------------------------------------- -// ValidatorID contains the identity of the validator. -type ValidatorID struct { - Address data.Bytes `json:"address"` - PubKey crypto.PubKey `json:"pub_key"` -} - -//------------------------------------- - -// LastSignedInfo contains information about the latest -// data signed by a validator to help prevent double signing. -type LastSignedInfo struct { - LastHeight int `json:"last_height"` - LastRound int `json:"last_round"` - LastStep int8 `json:"last_step"` - LastSignature crypto.Signature `json:"last_signature,omitempty"` // so we dont lose signatures - LastSignBytes data.Bytes `json:"last_signbytes,omitempty"` // so we dont lose signatures -} - -// SignBytesHRS signs the given signBytes with the signer if the height/round/step (HRS) -// are greater than the latest state of the LastSignedInfo. If the HRS are equal, -// it returns the LastSignedInfo.LastSignature. -func (info *LastSignedInfo) SignBytesHRS(signer Signer, - height, round int, step int8, signBytes []byte) (crypto.Signature, error) { - - sig := crypto.Signature{} - // If height regression, err - if info.LastHeight > height { - return sig, errors.New("Height regression") - } - // More cases for when the height matches - if info.LastHeight == height { - // If round regression, err - if info.LastRound > round { - return sig, errors.New("Round regression") - } - // If step regression, err - if info.LastRound == round { - if info.LastStep > step { - return sig, errors.New("Step regression") - } else if info.LastStep == step { - if info.LastSignBytes != nil { - if info.LastSignature.Empty() { - 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), - // so we can actually lose them, but will still never sign conflicting ones - if bytes.Equal(info.LastSignBytes, signBytes) { - // log.Notice("Using info.LastSignature", "sig", info.LastSignature) - return info.LastSignature, nil - } - } - return sig, errors.New("Step regression") - } - } - } - - // Sign - sig, err := signer.Sign(signBytes) - if err != nil { - return sig, err - } - - // Persist height/round/step - info.LastHeight = height - info.LastRound = round - info.LastStep = step - info.LastSignature = sig - info.LastSignBytes = signBytes - - return sig, nil -} - //------------------------------------- // Signer is an interface that defines how to sign messages. @@ -353,7 +312,7 @@ func (pvs PrivValidatorsByAddress) Len() int { } 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 58a43d489..d56cb9ec9 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -29,25 +29,19 @@ func TestLoadValidator(t *testing.T) { require.Nil(err, "%+v", err) serialized := fmt.Sprintf(`{ - "id": { "address": "%s", "pub_key": { "type": "ed25519", "data": "%s" - } - }, - "info": { + }, "last_height": 0, "last_round": 0, "last_step": 0, - "last_signature": null - }, - "signer": { + "last_signature": null, "priv_key": { "type": "ed25519", "data": "%s" } - } }`, addrStr, pubStr, privStr) val := PrivValidatorFS{} @@ -55,10 +49,9 @@ func TestLoadValidator(t *testing.T) { require.Nil(err, "%+v", err) // make sure the values match - assert.EqualValues(addrBytes, val.Address()) - assert.EqualValues(pubKey, val.PubKey()) - valPrivKey := val.Signer.(*DefaultSigner).PrivKey - assert.EqualValues(privKey, valPrivKey) + assert.EqualValues(addrBytes, val.GetAddress()) + assert.EqualValues(pubKey, val.GetPubKey()) + assert.EqualValues(privKey, val.PrivKey) // export it and make sure it is the same out, err := json.Marshal(val) diff --git a/types/proposal_test.go b/types/proposal_test.go index 2e4e7e198..d1c991849 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -38,7 +38,7 @@ func BenchmarkProposalVerifySignature(b *testing.B) { signBytes := SignBytes("test_chain_id", testProposal) privVal := GenPrivValidatorFS("") signature, _ := privVal.Signer.Sign(signBytes) - pubKey := privVal.PubKey() + 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 e52831e27..506828913 100644 --- a/types/validator.go +++ b/types/validator.go @@ -113,6 +113,6 @@ func RandValidator(randPower bool, minPower int64) (*Validator, *PrivValidatorFS if randPower { votePower += int64(cmn.RandUint32()) } - val := NewValidator(privVal.PubKey(), votePower) + val := NewValidator(privVal.GetPubKey(), votePower) return val, privVal } diff --git a/types/vote_set_test.go b/types/vote_set_test.go index ab1456ed9..80ee6135c 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -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) } From 8ae2ffda8984e7b8d693521bfa7de907e141b7a6 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 21 Sep 2017 16:58:18 -0400 Subject: [PATCH 19/22] put funcs back in order to simplify review --- config/toml.go | 24 ++-- types/priv_validator.go | 268 ++++++++++++++++++++-------------------- 2 files changed, 144 insertions(+), 148 deletions(-) diff --git a/config/toml.go b/config/toml.go index 4366695db..5dcbe5332 100644 --- a/config/toml.go +++ b/config/toml.go @@ -127,16 +127,16 @@ var testGenesis = `{ }` var testPrivValidator = `{ - "address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456", - "pub_key": { - "type": "ed25519", - "data": "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" - }, - "priv_key": { - "type": "ed25519", - "data": "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" - }, - "last_height": 0, - "last_round": 0, - "last_step": 0 + "address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456", + "pub_key": { + "type": "ed25519", + "data": "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" + }, + "priv_key": { + "type": "ed25519", + "data": "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" + }, + "last_height": 0, + "last_round": 0, + "last_step": 0 }` diff --git a/types/priv_validator.go b/types/priv_validator.go index 6db240966..4357c6f3b 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -67,6 +67,32 @@ type PrivValidatorFS struct { mtx sync.Mutex } +// 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 +} + // GetAddress returns the address of the validator. // Implements PrivValidator. func (pv *PrivValidatorFS) GetAddress() data.Bytes { @@ -79,6 +105,104 @@ func (pv *PrivValidatorFS) GetPubKey() crypto.PubKey { return pv.PubKey } +// 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() + return &PrivValidatorFS{ + Address: privKey.PubKey().Address(), + PubKey: privKey.PubKey(), + PrivKey: privKey, + LastStep: stepNone, + Signer: NewDefaultSigner(privKey), + filePath: filePath, + } +} + +// LoadPrivValidatorFS loads a PrivValidatorFS from the filePath. +func LoadPrivValidatorFS(filePath string) *PrivValidatorFS { + privValJSONBytes, err := ioutil.ReadFile(filePath) + if err != nil { + cmn.Exit(err.Error()) + } + privVal := PrivValidatorFS{} + err = json.Unmarshal(privValJSONBytes, &privVal) + if err != nil { + cmn.Exit(cmn.Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) + } + + privVal.filePath = filePath + privVal.Signer = NewDefaultSigner(privVal.PrivKey) + return &privVal +} + +// 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 { + PrivValidatorFS = LoadPrivValidatorFS(filePath) + } else { + PrivValidatorFS = GenPrivValidatorFS(filePath) + PrivValidatorFS.Save() + } + return PrivValidatorFS +} + +// 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(crypto.PubKey) Signer) *PrivValidatorFS { + privValJSONBytes, err := ioutil.ReadFile(filePath) + if err != nil { + cmn.Exit(err.Error()) + } + privVal := PrivValidatorFS{} + err = json.Unmarshal(privValJSONBytes, &privVal) + if err != nil { + cmn.Exit(cmn.Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) + } + + privVal.filePath = filePath + privVal.Signer = signerFunc(privVal.PubKey) + return &privVal +} + +// Save persists the PrivValidatorFS to disk. +func (privVal *PrivValidatorFS) Save() { + privVal.mtx.Lock() + defer privVal.mtx.Unlock() + privVal.save() +} + +func (privVal *PrivValidatorFS) save() { + if privVal.filePath == "" { + cmn.PanicSanity("Cannot save PrivValidator: filePath not set") + } + jsonBytes, err := json.Marshal(privVal) + if err != nil { + // `@; BOOM!!! + cmn.PanicCrisis(err) + } + err = cmn.WriteFileAtomic(privVal.filePath, jsonBytes, 0600) + if err != nil { + // `@; BOOM!!! + cmn.PanicCrisis(err) + } +} + +// Reset resets all fields in the PrivValidatorFS. +// NOTE: Unsafe! +func (privVal *PrivValidatorFS) Reset() { + privVal.LastHeight = 0 + privVal.LastRound = 0 + privVal.LastStep = 0 + privVal.LastSignature = crypto.Signature{} + privVal.LastSignBytes = nil + privVal.Save() +} + // SignVote signs a canonical representation of the vote, along with the chainID. // Implements PrivValidator. func (privVal *PrivValidatorFS) SignVote(chainID string, vote *Vote) error { @@ -105,39 +229,6 @@ func (privVal *PrivValidatorFS) SignProposal(chainID string, proposal *Proposal) return nil } -// 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 - heartbeat.Signature, err = privVal.Signer.Sign(SignBytes(chainID, heartbeat)) - return err -} - -// Save persists the PrivValidatorFS to disk. -func (privVal *PrivValidatorFS) Save() { - privVal.mtx.Lock() - defer privVal.mtx.Unlock() - privVal.save() -} - -func (privVal *PrivValidatorFS) save() { - if privVal.filePath == "" { - cmn.PanicSanity("Cannot save PrivValidator: filePath not set") - } - jsonBytes, err := json.Marshal(privVal) - if err != nil { - // `@; BOOM!!! - cmn.PanicCrisis(err) - } - err = cmn.WriteFileAtomic(privVal.filePath, jsonBytes, 0600) - if err != nil { - // `@; BOOM!!! - cmn.PanicCrisis(err) - } -} - // 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. @@ -193,15 +284,14 @@ func (privVal *PrivValidatorFS) signBytesHRS(height, round int, step int8, signB return sig, nil } -// Reset resets all fields in the PrivValidatorFS. -// NOTE: Unsafe! -func (privVal *PrivValidatorFS) Reset() { - privVal.LastHeight = 0 - privVal.LastRound = 0 - privVal.LastStep = 0 - privVal.LastSignature = crypto.Signature{} - privVal.LastSignBytes = nil - privVal.Save() +// 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 + heartbeat.Signature, err = privVal.Signer.Sign(SignBytes(chainID, heartbeat)) + return err } // String returns a string representation of the PrivValidatorFS. @@ -209,100 +299,6 @@ func (privVal *PrivValidatorFS) String() string { return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", privVal.GetAddress(), privVal.LastHeight, privVal.LastRound, privVal.LastStep) } -// 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 { - PrivValidatorFS = LoadPrivValidatorFS(filePath) - } else { - PrivValidatorFS = GenPrivValidatorFS(filePath) - PrivValidatorFS.Save() - } - return PrivValidatorFS -} - -// LoadPrivValidatorFS loads a PrivValidatorFS from the filePath. -func LoadPrivValidatorFS(filePath string) *PrivValidatorFS { - privValJSONBytes, err := ioutil.ReadFile(filePath) - if err != nil { - cmn.Exit(err.Error()) - } - privVal := PrivValidatorFS{} - err = json.Unmarshal(privValJSONBytes, &privVal) - if err != nil { - cmn.Exit(cmn.Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) - } - - privVal.filePath = filePath - privVal.Signer = NewDefaultSigner(privVal.PrivKey) - return &privVal -} - -// 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() - return &PrivValidatorFS{ - Address: privKey.PubKey().Address(), - PubKey: privKey.PubKey(), - PrivKey: privKey, - LastStep: stepNone, - Signer: NewDefaultSigner(privKey), - 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(crypto.PubKey) Signer) *PrivValidatorFS { - privValJSONBytes, err := ioutil.ReadFile(filePath) - if err != nil { - cmn.Exit(err.Error()) - } - privVal := PrivValidatorFS{} - err = json.Unmarshal(privValJSONBytes, &privVal) - if err != nil { - cmn.Exit(cmn.Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) - } - - privVal.filePath = filePath - privVal.Signer = signerFunc(privVal.PubKey) - return &privVal -} - -//------------------------------------- - -//------------------------------------- - -// Signer is an interface that defines how to sign messages. -// It is the caller's duty to verify the msg before calling Sign, -// eg. to avoid double signing. -// Currently, the only callers are SignVote, SignProposal, and SignHeartbeat. -type Signer interface { - Sign(msg []byte) (crypto.Signature, error) -} - -// DefaultSigner implements Signer. -// It uses a standard, unencrypted crypto.PrivKey. -type DefaultSigner struct { - PrivKey crypto.PrivKey `json:"priv_key"` -} - -// NewDefaultSigner returns an instance of DefaultSigner. -func NewDefaultSigner(priv crypto.PrivKey) *DefaultSigner { - return &DefaultSigner{ - PrivKey: priv, - } -} - -// Sign implements Signer. It signs the byte slice with a private key. -func (ds *DefaultSigner) Sign(msg []byte) (crypto.Signature, error) { - return ds.PrivKey.Sign(msg), nil -} - //------------------------------------- type PrivValidatorsByAddress []*PrivValidatorFS From 2131f8d3309124922416305a99d5b1ebef02589f Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 21 Sep 2017 17:08:17 -0400 Subject: [PATCH 20/22] some fixes from review --- cmd/tendermint/commands/run_node.go | 19 +++---------------- cmd/tendermint/main.go | 9 ++++++--- node/node.go | 28 +++++++++++++++------------- node/node_test.go | 4 ++-- types/priv_validator.go | 24 +++++++----------------- types/priv_validator_test.go | 26 +++++++++++++------------- 6 files changed, 46 insertions(+), 64 deletions(-) diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 6a793706f..b3ef821aa 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -6,7 +6,7 @@ import ( "github.com/spf13/cobra" cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/node" + nm "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" ) @@ -53,26 +53,13 @@ func DefaultSignerAndApp(config *cfg.Config) (types.PrivValidator, proxy.ClientC // 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(signerAndApp FuncSignerAndApp) *cobra.Command { +func NewRunNodeCmd(nodeFunc nm.NodeProvider) *cobra.Command { cmd := &cobra.Command{ Use: "node", Short: "Run the tendermint node", RunE: func(cmd *cobra.Command, args []string) error { - genDocFile := config.GenesisFile() - genDoc, err := types.GenesisDocFromFile(genDocFile) - if err != nil { - return err - } - config.ChainID = genDoc.ChainID - // Create & start node - privVal, clientCreator := signerAndApp(config) - n, err := node.NewNode(config, - privVal, - clientCreator, - node.DefaultGenesisDocProviderFunc(config), - node.DefaultDBProvider, - logger.With("module", "node")) + n, err := nodeFunc(config, logger) if err != nil { return fmt.Errorf("Failed to create node: %v", err) } diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index 1e2c00051..d1aba6f26 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -6,6 +6,7 @@ import ( "github.com/tendermint/tmlibs/cli" cmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + nm "github.com/tendermint/tendermint/node" ) func main() { @@ -26,12 +27,14 @@ func main() { // 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 - // DefaultSignerAndApp function - signerAndApp := cmd.DefaultSignerAndApp + // DefaultNewNode function + nodeFunc := nm.DefaultNewNode // Create & start node - rootCmd.AddCommand(cmd.NewRunNodeCmd(signerAndApp)) + rootCmd.AddCommand(cmd.NewRunNodeCmd(nodeFunc)) cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv("$HOME/.tendermint")) cmd.Execute() diff --git a/node/node.go b/node/node.go index 0fb064514..834cb4c35 100644 --- a/node/node.go +++ b/node/node.go @@ -65,6 +65,21 @@ func DefaultGenesisDocProviderFunc(config *cfg.Config) GenesisDocProvider { } } +// 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. @@ -94,19 +109,6 @@ type Node struct { txIndexer txindex.TxIndexer } -// NewNodeDefault returns a Tendermint node with default settings for the -// PrivValidator, ClientCreator, GenesisDoc, and DBProvider, -func NewNodeDefault(config *cfg.Config, logger log.Logger) (*Node, error) { - // Get PrivValidator - privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()) - return NewNode(config, - privValidator, - proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), - DefaultGenesisDocProviderFunc(config), - DefaultDBProvider, - logger) -} - // NewNode returns a new, ready to go, Tendermint Node. func NewNode(config *cfg.Config, privValidator types.PrivValidator, diff --git a/node/node_test.go b/node/node_test.go index 1b6d27f47..641e606c3 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -15,8 +15,8 @@ func TestNodeStartStop(t *testing.T) { config := cfg.ResetTestRoot("node_node_test") // Create & start node - n, err := NewNodeDefault(config, log.TestingLogger()) - assert.NoError(t, err, "expected no err on NewNodeDefault") + 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/types/priv_validator.go b/types/priv_validator.go index 4357c6f3b..47276f288 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -121,19 +121,9 @@ func GenPrivValidatorFS(filePath string) *PrivValidatorFS { // LoadPrivValidatorFS loads a PrivValidatorFS from the filePath. func LoadPrivValidatorFS(filePath string) *PrivValidatorFS { - privValJSONBytes, err := ioutil.ReadFile(filePath) - if err != nil { - cmn.Exit(err.Error()) - } - privVal := PrivValidatorFS{} - err = json.Unmarshal(privValJSONBytes, &privVal) - if err != nil { - cmn.Exit(cmn.Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) - } - - privVal.filePath = filePath - privVal.Signer = NewDefaultSigner(privVal.PrivKey) - return &privVal + return LoadPrivValidatorFSWithSigner(filePath, func(privVal PrivValidator) Signer { + return NewDefaultSigner(privVal.(*PrivValidatorFS).PrivKey) + }) } // LoadOrGenPrivValidatorFS loads a PrivValidatorFS from the given filePath @@ -153,20 +143,20 @@ func LoadOrGenPrivValidatorFS(filePath string) *PrivValidatorFS { // 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(crypto.PubKey) Signer) *PrivValidatorFS { +func LoadPrivValidatorFSWithSigner(filePath string, signerFunc func(PrivValidator) Signer) *PrivValidatorFS { privValJSONBytes, err := ioutil.ReadFile(filePath) if err != nil { cmn.Exit(err.Error()) } - privVal := PrivValidatorFS{} + privVal := &PrivValidatorFS{} err = json.Unmarshal(privValJSONBytes, &privVal) if err != nil { cmn.Exit(cmn.Fmt("Error reading PrivValidator from %v: %v\n", filePath, err)) } privVal.filePath = filePath - privVal.Signer = signerFunc(privVal.PubKey) - return &privVal + privVal.Signer = signerFunc(privVal) + return privVal } // Save persists the PrivValidatorFS to disk. diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index d56cb9ec9..5b6026740 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -29,19 +29,19 @@ func TestLoadValidator(t *testing.T) { require.Nil(err, "%+v", err) serialized := fmt.Sprintf(`{ - "address": "%s", - "pub_key": { - "type": "ed25519", - "data": "%s" - }, - "last_height": 0, - "last_round": 0, - "last_step": 0, - "last_signature": null, - "priv_key": { - "type": "ed25519", - "data": "%s" - } + "address": "%s", + "pub_key": { + "type": "ed25519", + "data": "%s" + }, + "last_height": 0, + "last_round": 0, + "last_step": 0, + "last_signature": null, + "priv_key": { + "type": "ed25519", + "data": "%s" + } }`, addrStr, pubStr, privStr) val := PrivValidatorFS{} From 756818f940ea68b8f64044c49683b7b038421c83 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 21 Sep 2017 21:44:36 -0400 Subject: [PATCH 21/22] fixes from review --- .../commands/reset_priv_validator.go | 6 +++--- cmd/tendermint/commands/run_node.go | 19 ++----------------- cmd/tendermint/main.go | 2 +- node/node.go | 4 ++-- types/priv_validator.go | 4 ++-- 5 files changed, 10 insertions(+), 25 deletions(-) diff --git a/cmd/tendermint/commands/reset_priv_validator.go b/cmd/tendermint/commands/reset_priv_validator.go index 6255f9613..b9c08715f 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -27,7 +27,7 @@ var ResetPrivValidatorCmd = &cobra.Command{ // ResetAll removes the privValidator files. // Exported so other CLI tools can use it func ResetAll(dbDir, privValFile string, logger log.Logger) { - resetPrivValidatorLocal(privValFile, logger) + resetPrivValidatorFS(privValFile, logger) os.RemoveAll(dbDir) logger.Info("Removed all data", "dir", dbDir) } @@ -41,10 +41,10 @@ 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) } -func resetPrivValidatorLocal(privValFile string, logger log.Logger) { +func resetPrivValidatorFS(privValFile string, logger log.Logger) { // Get PrivValidator if _, err := os.Stat(privValFile); err == nil { privValidator := types.LoadPrivValidatorFS(privValFile) diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index b3ef821aa..f0a1eede2 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -5,10 +5,7 @@ import ( "github.com/spf13/cobra" - cfg "github.com/tendermint/tendermint/config" nm "github.com/tendermint/tendermint/node" - "github.com/tendermint/tendermint/proxy" - "github.com/tendermint/tendermint/types" ) // AddNodeFlags exposes some common configuration options on the command-line @@ -39,27 +36,15 @@ 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") } -// FuncSignerAndApp takes a config and returns a PrivValidator and ClientCreator. -// It allows other projects to make Tendermint binaries with custom signers and applications. -type FuncSignerAndApp func(*cfg.Config) (types.PrivValidator, proxy.ClientCreator) - -// DefaultSignerAndApp is a default FuncSignerAndApp that returns a PrivValidatorFS -// and a DefaultClientCreator using the relevant fields from the config. -func DefaultSignerAndApp(config *cfg.Config) (types.PrivValidator, proxy.ClientCreator) { - privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()) - clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) - return privValidator, clientCreator -} - // 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(nodeFunc nm.NodeProvider) *cobra.Command { +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 := nodeFunc(config, logger) + n, err := nodeProvider(config, logger) if err != nil { return fmt.Errorf("Failed to create node: %v", err) } diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index d1aba6f26..86ca1531d 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -28,7 +28,7 @@ func main() { // * 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 + // * Provide their own DB implementation // can copy this file and use something other than the // DefaultNewNode function nodeFunc := nm.DefaultNewNode diff --git a/node/node.go b/node/node.go index 834cb4c35..4286fece2 100644 --- a/node/node.go +++ b/node/node.go @@ -113,7 +113,7 @@ type Node struct { func NewNode(config *cfg.Config, privValidator types.PrivValidator, clientCreator proxy.ClientCreator, - genDocProvider GenesisDocProvider, + genesisDocProvider GenesisDocProvider, dbProvider DBProvider, logger log.Logger) (*Node, error) { @@ -134,7 +134,7 @@ func NewNode(config *cfg.Config, } state := sm.LoadState(stateDB) if state == nil { - genDoc, err := genDocProvider() + genDoc, err := genesisDocProvider() if err != nil { return nil, err } diff --git a/types/priv_validator.go b/types/priv_validator.go index 47276f288..66f02a6f6 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -258,7 +258,7 @@ func (privVal *PrivValidatorFS) signBytesHRS(height, round int, step int8, signB } // Sign - sig, err := privVal.Signer.Sign(signBytes) + sig, err := privVal.Sign(signBytes) if err != nil { return sig, err } @@ -280,7 +280,7 @@ func (privVal *PrivValidatorFS) SignHeartbeat(chainID string, heartbeat *Heartbe privVal.mtx.Lock() defer privVal.mtx.Unlock() var err error - heartbeat.Signature, err = privVal.Signer.Sign(SignBytes(chainID, heartbeat)) + heartbeat.Signature, err = privVal.Sign(SignBytes(chainID, heartbeat)) return err } From 24f7b9387ace8b80832b5daa901bc77a40acccd5 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 22 Sep 2017 00:05:39 -0400 Subject: [PATCH 22/22] more tests --- types/priv_validator.go | 10 +-- types/priv_validator_test.go | 118 ++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 6 deletions(-) diff --git a/types/priv_validator.go b/types/priv_validator.go index 66f02a6f6..8834eb7cc 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -129,14 +129,14 @@ func LoadPrivValidatorFS(filePath string) *PrivValidatorFS { // 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 + var privVal *PrivValidatorFS if _, err := os.Stat(filePath); err == nil { - PrivValidatorFS = LoadPrivValidatorFS(filePath) + privVal = LoadPrivValidatorFS(filePath) } else { - PrivValidatorFS = GenPrivValidatorFS(filePath) - PrivValidatorFS.Save() + privVal = GenPrivValidatorFS(filePath) + privVal.Save() } - return PrivValidatorFS + return privVal } // LoadPrivValidatorWithSigner loads a PrivValidatorFS with a custom diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index 5b6026740..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 @@ -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, + } +}