From 23eb84db3568a084a9e0938ab7b582af1ca5720e Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 4 Jan 2018 19:51:27 -0500 Subject: [PATCH 01/18] wip: priv val via sockets --- cmd/priv_val_server/main.go | 32 +++++++++++++++++++++++++++++ cmd/tendermint/commands/run_node.go | 3 +++ config/config.go | 3 +++ node/node.go | 14 ++++++++++++- 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 cmd/priv_val_server/main.go diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go new file mode 100644 index 000000000..651707e83 --- /dev/null +++ b/cmd/priv_val_server/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "flag" + "fmt" + "os" + + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tmlibs/log" + + priv_val "github.com/tendermint/tendermint/types/priv_validator" +) + +var chainID = flag.String("chain-id", "mychain", "chain id") +var privValPath = flag.String("priv", "", "priv val file path") + +func main() { + flag.Parse() + + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "priv_val") + socketAddr := "localhost:46659" + fmt.Println(*chainID) + fmt.Println(*privValPath) + privVal := priv_val.LoadPrivValidatorJSON(*privValPath) + + pvss := priv_val.NewPrivValidatorSocketServer(logger, socketAddr, *chainID, privVal) + pvss.Start() + + cmn.TrapSignal(func() { + pvss.Stop() + }) +} diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 76d61671d..bebef5edc 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -14,6 +14,9 @@ func AddNodeFlags(cmd *cobra.Command) { // bind flags cmd.Flags().String("moniker", config.Moniker, "Node Name") + // priv val flags + cmd.Flags().String("priv_validator_addr", config.PrivValidatorAddr, "Socket address for PrivValidator") + // node flags cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing") diff --git a/config/config.go b/config/config.go index 621847bd0..6a882185f 100644 --- a/config/config.go +++ b/config/config.go @@ -103,6 +103,9 @@ type BaseConfig struct { // A custom human readable name for this node Moniker string `mapstructure:"moniker"` + // TCP or UNIX socket address of the PrivValidator server + PrivValidatorAddr string `mapstructure:"priv_validator_addr"` + // TCP or UNIX socket address of the ABCI application, // or the name of an ABCI application compiled in with the Tendermint binary ProxyApp string `mapstructure:"proxy_app"` diff --git a/node/node.go b/node/node.go index cd2b4bcb5..859cc1df1 100644 --- a/node/node.go +++ b/node/node.go @@ -77,8 +77,20 @@ type NodeProvider func(*cfg.Config, log.Logger) (*Node, error) // PrivValidator, ClientCreator, GenesisDoc, and DBProvider. // It implements NodeProvider. func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { + var privVal types.PrivValidator + privVal = types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()) + /* + if config.PrivValidatorAddr != "" { + pvsc := priv_val.NewPrivValidatorSocketClient(logger.With("module", "priv_val"), + config.PrivValidatorAddr) + pvsc.Start() + privVal = pvsc + } + */ + fmt.Println("PRIV", config.PrivValidatorAddr) + return NewNode(config, - types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()), + privVal, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), DefaultGenesisDocProviderFunc(config), DefaultDBProvider, From c27fda09dd011785d84b050ce42e10b0245e271b Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Wed, 17 Jan 2018 17:12:04 +0100 Subject: [PATCH 02/18] wip: Comment types * add comments to all public types * fix comments to adhere to comment standards --- types/priv_validator/socket.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index 9fdc8c205..489076651 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -34,7 +34,8 @@ const ( dialRetryIntervalSeconds = 1 ) -// NewPrivValidatorSocket returns an instance of PrivValidatorSocket. +// NewPrivValidatorSocketClient returns an instance of +// PrivValidatorSocketClient. func NewPrivValidatorSocketClient(logger log.Logger, socketAddr string) *PrivValidatorSocketClient { pvsc := &PrivValidatorSocketClient{ SocketAddress: socketAddr, @@ -43,6 +44,7 @@ func NewPrivValidatorSocketClient(logger log.Logger, socketAddr string) *PrivVal return pvsc } +// OnStart implements cmn.Service. func (pvsc *PrivValidatorSocketClient) OnStart() error { if err := pvsc.BaseService.OnStart(); err != nil { return err @@ -63,6 +65,7 @@ RETRY_LOOP: } } +// OnStop implements cmn.Service. func (pvsc *PrivValidatorSocketClient) OnStop() { pvsc.BaseService.OnStop() @@ -71,11 +74,13 @@ func (pvsc *PrivValidatorSocketClient) OnStop() { } } +// Address is an alias for PubKey().Address(). func (pvsc *PrivValidatorSocketClient) Address() data.Bytes { pubKey := pvsc.PubKey() return pubKey.Address() } +// PubKey implements PrivValidator. func (pvsc *PrivValidatorSocketClient) PubKey() crypto.PubKey { res, err := readWrite(pvsc.conn, PubKeyMsg{}) if err != nil { @@ -84,6 +89,7 @@ func (pvsc *PrivValidatorSocketClient) PubKey() crypto.PubKey { return res.(PubKeyMsg).PubKey } +// SignVote implements PrivValidator. func (pvsc *PrivValidatorSocketClient) SignVote(chainID string, vote *types.Vote) error { res, err := readWrite(pvsc.conn, SignVoteMsg{Vote: vote}) if err != nil { @@ -93,6 +99,7 @@ func (pvsc *PrivValidatorSocketClient) SignVote(chainID string, vote *types.Vote return nil } +// SignProposal implements PrivValidator. func (pvsc *PrivValidatorSocketClient) SignProposal(chainID string, proposal *types.Proposal) error { res, err := readWrite(pvsc.conn, SignProposalMsg{Proposal: proposal}) if err != nil { @@ -102,6 +109,7 @@ func (pvsc *PrivValidatorSocketClient) SignProposal(chainID string, proposal *ty return nil } +// SignHeartbeat implements PrivValidator. func (pvsc *PrivValidatorSocketClient) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) error { res, err := readWrite(pvsc.conn, SignHeartbeatMsg{Heartbeat: heartbeat}) if err != nil { @@ -126,6 +134,8 @@ type PrivValidatorSocketServer struct { chainID string } +// NewPrivValidatorSocketServer returns an instance of +// PrivValidatorSocketServer. func NewPrivValidatorSocketServer(logger log.Logger, socketAddr, chainID string, privVal PrivValidator) *PrivValidatorSocketServer { proto, addr := cmn.ProtocolAndAddress(socketAddr) pvss := &PrivValidatorSocketServer{ @@ -138,6 +148,7 @@ func NewPrivValidatorSocketServer(logger log.Logger, socketAddr, chainID string, return pvss } +// OnStart implements cmn.Service. func (pvss *PrivValidatorSocketServer) OnStart() error { if err := pvss.BaseService.OnStart(); err != nil { return err @@ -151,6 +162,7 @@ func (pvss *PrivValidatorSocketServer) OnStart() error { return nil } +// OnStop implements cmn.Service. func (pvss *PrivValidatorSocketServer) OnStop() { pvss.BaseService.OnStop() if err := pvss.listener.Close(); err != nil { @@ -226,6 +238,8 @@ const ( msgTypeSignHeartbeat = byte(0x12) ) +// PrivValidatorSocketMsg is a message sent between PrivValidatorSocket client +// and server. type PrivValidatorSocketMsg interface{} var _ = wire.RegisterInterface( @@ -256,18 +270,22 @@ func decodeMsg(bz []byte) (msg PrivValidatorSocketMsg, err error) { return msg, err } +// PubKeyMsg is a PrivValidatorSocket message containing the public key. type PubKeyMsg struct { PubKey crypto.PubKey } +// SignVoteMsg is a PrivValidatorSocket message containing a vote. type SignVoteMsg struct { Vote *types.Vote } +// SignProposalMsg is a PrivValidatorSocket message containing a Proposal. type SignProposalMsg struct { Proposal *types.Proposal } +// SignHeartbeatMsg is a PrivValidatorSocket message containing a Heartbeat. type SignHeartbeatMsg struct { Heartbeat *types.Heartbeat } From d3218396691aae31f83713f10e54816090561b1e Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Wed, 17 Jan 2018 17:17:23 +0100 Subject: [PATCH 03/18] wip: fix code block in ADR --- docs/architecture/adr-008-priv-validator.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/architecture/adr-008-priv-validator.md b/docs/architecture/adr-008-priv-validator.md index dfee112c6..94d164785 100644 --- a/docs/architecture/adr-008-priv-validator.md +++ b/docs/architecture/adr-008-priv-validator.md @@ -8,7 +8,7 @@ For instance, see https://github.com/tendermint/tendermint/issues/673 The goal is to have a clean PrivValidator interface like: -`` +``` type PrivValidator interface { Address() data.Bytes PubKey() crypto.PubKey @@ -60,6 +60,7 @@ type ValidatorID struct { Address data.Bytes `json:"address"` PubKey crypto.PubKey `json:"pub_key"` } +``` ### LastSignedInfo From 4b997c29ee98f2d91299f5ef30aad87d55802112 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Thu, 18 Jan 2018 19:23:44 +0100 Subject: [PATCH 04/18] wip: fix nil pointer deference --- types/priv_validator/socket.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index 489076651..0e868791e 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -126,7 +126,6 @@ func (pvsc *PrivValidatorSocketClient) SignHeartbeat(chainID string, heartbeat * type PrivValidatorSocketServer struct { cmn.BaseService - conn net.Conn proto, addr string listener net.Listener @@ -165,12 +164,13 @@ func (pvss *PrivValidatorSocketServer) OnStart() error { // OnStop implements cmn.Service. func (pvss *PrivValidatorSocketServer) OnStop() { pvss.BaseService.OnStop() - if err := pvss.listener.Close(); err != nil { - pvss.Logger.Error("Error closing listener", "err", err) + + if pvss.listener == nil { + return } - if err := pvss.conn.Close(); err != nil { - pvss.Logger.Error("Error closing connection", "conn", pvss.conn, "err", err) + if err := pvss.listener.Close(); err != nil { + pvss.Logger.Error("Error closing listener", "err", err) } } @@ -178,8 +178,8 @@ func (pvss *PrivValidatorSocketServer) acceptConnectionsRoutine() { for { // Accept a connection pvss.Logger.Info("Waiting for new connection...") - var err error - pvss.conn, err = pvss.listener.Accept() + + conn, err := pvss.listener.Accept() if err != nil { if !pvss.IsRunning() { return // Ignore error from listener closing. @@ -198,7 +198,7 @@ func (pvss *PrivValidatorSocketServer) acceptConnectionsRoutine() { var n int var err error - b := wire.ReadByteSlice(pvss.conn, 0, &n, &err) //XXX: no max + b := wire.ReadByteSlice(conn, 0, &n, &err) //XXX: no max req_, err := decodeMsg(b) if err != nil { panic(err) @@ -221,7 +221,7 @@ func (pvss *PrivValidatorSocketServer) acceptConnectionsRoutine() { } b = wire.BinaryBytes(res) - _, err = pvss.conn.Write(b) + _, err = conn.Write(b) if err != nil { panic(err) } From a49357b19e58d866591c6e9b5f1441914e085668 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Thu, 18 Jan 2018 19:28:29 +0100 Subject: [PATCH 05/18] wip: Avoid underscore in var name --- types/priv_validator/socket.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index 0e868791e..f55b6c7f4 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -199,25 +199,25 @@ func (pvss *PrivValidatorSocketServer) acceptConnectionsRoutine() { var n int var err error b := wire.ReadByteSlice(conn, 0, &n, &err) //XXX: no max - req_, err := decodeMsg(b) + req, err := decodeMsg(b) if err != nil { panic(err) } var res PrivValidatorSocketMsg - switch req := req_.(type) { + switch r := req.(type) { case PubKeyMsg: res = PubKeyMsg{pvss.privVal.PubKey()} case SignVoteMsg: - pvss.privVal.SignVote(pvss.chainID, req.Vote) - res = SignVoteMsg{req.Vote} + pvss.privVal.SignVote(pvss.chainID, r.Vote) + res = SignVoteMsg{r.Vote} case SignProposalMsg: - pvss.privVal.SignProposal(pvss.chainID, req.Proposal) - res = SignProposalMsg{req.Proposal} + pvss.privVal.SignProposal(pvss.chainID, r.Proposal) + res = SignProposalMsg{r.Proposal} case SignHeartbeatMsg: - pvss.privVal.SignHeartbeat(pvss.chainID, req.Heartbeat) - res = SignHeartbeatMsg{req.Heartbeat} + pvss.privVal.SignHeartbeat(pvss.chainID, r.Heartbeat) + res = SignHeartbeatMsg{r.Heartbeat} default: - panic(fmt.Sprintf("unknown msg: %v", req_)) + panic(fmt.Sprintf("unknown msg: %v", r)) } b = wire.BinaryBytes(res) From ff600e9aa0ebb472101b5ba453c8b389066bcf03 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Thu, 18 Jan 2018 19:40:34 +0100 Subject: [PATCH 06/18] wip: check error of wire read --- types/priv_validator/socket.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index f55b6c7f4..2ed4c46f2 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -199,6 +199,10 @@ func (pvss *PrivValidatorSocketServer) acceptConnectionsRoutine() { var n int var err error b := wire.ReadByteSlice(conn, 0, &n, &err) //XXX: no max + if err != nil { + panic(err) + } + req, err := decodeMsg(b) if err != nil { panic(err) From fec541373d95dbb4684693110fce1f737a4a88ee Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Thu, 25 Jan 2018 16:25:42 +0100 Subject: [PATCH 07/18] Correct server protocol --- cmd/priv_val_server/main.go | 26 +++-- types/priv_validator/socket.go | 158 +++++++++++++++++----------- types/priv_validator/socket_test.go | 44 ++++++++ 3 files changed, 154 insertions(+), 74 deletions(-) create mode 100644 types/priv_validator/socket_test.go diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index 651707e83..b494a442c 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -2,7 +2,6 @@ package main import ( "flag" - "fmt" "os" cmn "github.com/tendermint/tmlibs/common" @@ -11,20 +10,27 @@ import ( priv_val "github.com/tendermint/tendermint/types/priv_validator" ) -var chainID = flag.String("chain-id", "mychain", "chain id") -var privValPath = flag.String("priv", "", "priv val file path") - func main() { + var ( + chainID = flag.String("chain-id", "mychain", "chain id") + privValPath = flag.String("priv", "", "priv val file path") + socketAddr = flag.String("socket.addr", ":46659", "socket bind addr") + + logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "priv_val") + ) flag.Parse() - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "priv_val") - socketAddr := "localhost:46659" - fmt.Println(*chainID) - fmt.Println(*privValPath) + logger.Info("args", "chainID", *chainID, "privPath", *privValPath) + privVal := priv_val.LoadPrivValidatorJSON(*privValPath) - pvss := priv_val.NewPrivValidatorSocketServer(logger, socketAddr, *chainID, privVal) - pvss.Start() + pvss := priv_val.NewPrivValidatorSocketServer( + logger, + *socketAddr, + *chainID, + privVal, + ) + // pvss.Start() cmn.TrapSignal(func() { pvss.Stop() diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index 2ed4c46f2..c22e2698d 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -3,6 +3,7 @@ package types import ( "bytes" "fmt" + "io" "net" "time" @@ -11,6 +12,7 @@ import ( "github.com/tendermint/go-wire/data" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" + "golang.org/x/net/netutil" "github.com/tendermint/tendermint/types" ) @@ -82,16 +84,16 @@ func (pvsc *PrivValidatorSocketClient) Address() data.Bytes { // PubKey implements PrivValidator. func (pvsc *PrivValidatorSocketClient) PubKey() crypto.PubKey { - res, err := readWrite(pvsc.conn, PubKeyMsg{}) + res, err := readWrite(pvsc.conn, &PubKeyMsg{}) if err != nil { panic(err) } - return res.(PubKeyMsg).PubKey + return res.(*PubKeyMsg).PubKey } // SignVote implements PrivValidator. func (pvsc *PrivValidatorSocketClient) SignVote(chainID string, vote *types.Vote) error { - res, err := readWrite(pvsc.conn, SignVoteMsg{Vote: vote}) + res, err := readWrite(pvsc.conn, &SignVoteMsg{Vote: vote}) if err != nil { return err } @@ -101,7 +103,7 @@ func (pvsc *PrivValidatorSocketClient) SignVote(chainID string, vote *types.Vote // SignProposal implements PrivValidator. func (pvsc *PrivValidatorSocketClient) SignProposal(chainID string, proposal *types.Proposal) error { - res, err := readWrite(pvsc.conn, SignProposalMsg{Proposal: proposal}) + res, err := readWrite(pvsc.conn, &SignProposalMsg{Proposal: proposal}) if err != nil { return err } @@ -111,7 +113,7 @@ func (pvsc *PrivValidatorSocketClient) SignProposal(chainID string, proposal *ty // SignHeartbeat implements PrivValidator. func (pvsc *PrivValidatorSocketClient) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) error { - res, err := readWrite(pvsc.conn, SignHeartbeatMsg{Heartbeat: heartbeat}) + res, err := readWrite(pvsc.conn, &SignHeartbeatMsg{Heartbeat: heartbeat}) if err != nil { return err } @@ -126,8 +128,10 @@ func (pvsc *PrivValidatorSocketClient) SignHeartbeat(chainID string, heartbeat * type PrivValidatorSocketServer struct { cmn.BaseService - proto, addr string - listener net.Listener + proto, addr string + listener net.Listener + maxConnections int + privKey crypto.PrivKeyEd25519 privVal PrivValidator chainID string @@ -135,13 +139,21 @@ type PrivValidatorSocketServer struct { // NewPrivValidatorSocketServer returns an instance of // PrivValidatorSocketServer. -func NewPrivValidatorSocketServer(logger log.Logger, socketAddr, chainID string, privVal PrivValidator) *PrivValidatorSocketServer { +func NewPrivValidatorSocketServer( + logger log.Logger, + socketAddr, chainID string, + privVal PrivValidator, + privKey crypto.PrivKeyEd25519, + maxConnections int, +) *PrivValidatorSocketServer { proto, addr := cmn.ProtocolAndAddress(socketAddr) pvss := &PrivValidatorSocketServer{ - proto: proto, - addr: addr, - privVal: privVal, - chainID: chainID, + proto: proto, + addr: addr, + maxConnections: maxConnections, + privKey: privKey, + privVal: privVal, + chainID: chainID, } pvss.BaseService = *cmn.NewBaseService(logger, "privValidatorSocketServer", pvss) return pvss @@ -149,22 +161,20 @@ func NewPrivValidatorSocketServer(logger log.Logger, socketAddr, chainID string, // OnStart implements cmn.Service. func (pvss *PrivValidatorSocketServer) OnStart() error { - if err := pvss.BaseService.OnStart(); err != nil { - return err - } ln, err := net.Listen(pvss.proto, pvss.addr) if err != nil { return err } - pvss.listener = ln + + pvss.listener = netutil.LimitListener(ln, pvss.maxConnections) + go pvss.acceptConnectionsRoutine() + return nil } // OnStop implements cmn.Service. func (pvss *PrivValidatorSocketServer) OnStop() { - pvss.BaseService.OnStop() - if pvss.listener == nil { return } @@ -176,9 +186,6 @@ func (pvss *PrivValidatorSocketServer) OnStop() { func (pvss *PrivValidatorSocketServer) acceptConnectionsRoutine() { for { - // Accept a connection - pvss.Logger.Info("Waiting for new connection...") - conn, err := pvss.listener.Accept() if err != nil { if !pvss.IsRunning() { @@ -188,47 +195,46 @@ func (pvss *PrivValidatorSocketServer) acceptConnectionsRoutine() { continue } - pvss.Logger.Info("Accepted a new connection") + go pvss.handleConnection(conn) + } +} - // read/write - for { - if !pvss.IsRunning() { - return // Ignore error from listener closing. - } +func (pvss *PrivValidatorSocketServer) handleConnection(conn net.Conn) { + defer conn.Close() - var n int - var err error - b := wire.ReadByteSlice(conn, 0, &n, &err) //XXX: no max - if err != nil { - panic(err) - } + for { + if !pvss.IsRunning() { + return // Ignore error from listener closing. + } - req, err := decodeMsg(b) - if err != nil { - panic(err) - } - var res PrivValidatorSocketMsg - switch r := req.(type) { - case PubKeyMsg: - res = PubKeyMsg{pvss.privVal.PubKey()} - case SignVoteMsg: - pvss.privVal.SignVote(pvss.chainID, r.Vote) - res = SignVoteMsg{r.Vote} - case SignProposalMsg: - pvss.privVal.SignProposal(pvss.chainID, r.Proposal) - res = SignProposalMsg{r.Proposal} - case SignHeartbeatMsg: - pvss.privVal.SignHeartbeat(pvss.chainID, r.Heartbeat) - res = SignHeartbeatMsg{r.Heartbeat} - default: - panic(fmt.Sprintf("unknown msg: %v", r)) - } + req, err := readMsg(conn) + if err != nil { + pvss.Logger.Error("readMsg", "err", err) + return + } - b = wire.BinaryBytes(res) - _, err = conn.Write(b) - if err != nil { - panic(err) - } + var res PrivValidatorSocketMsg + + switch r := req.(type) { + case *PubKeyMsg: + res = &PubKeyMsg{pvss.privVal.PubKey()} + case *SignVoteMsg: + pvss.privVal.SignVote(pvss.chainID, r.Vote) + res = &SignVoteMsg{r.Vote} + case *SignProposalMsg: + pvss.privVal.SignProposal(pvss.chainID, r.Proposal) + res = &SignProposalMsg{r.Proposal} + case *SignHeartbeatMsg: + pvss.privVal.SignHeartbeat(pvss.chainID, r.Heartbeat) + res = &SignHeartbeatMsg{r.Heartbeat} + default: + panic(fmt.Sprintf("unknown msg: %v", r)) + } + + b := wire.BinaryBytes(res) + _, err = conn.Write(b) + if err != nil { + panic(err) } } } @@ -254,23 +260,47 @@ var _ = wire.RegisterInterface( wire.ConcreteType{&SignHeartbeatMsg{}, msgTypeSignHeartbeat}, ) +func readMsg(r io.Reader) (PrivValidatorSocketMsg, error) { + var ( + n int + err error + ) + + read := wire.ReadBinary(struct{ PrivValidatorSocketMsg }{}, r, 0, &n, &err) + if err != nil { + return nil, err + } + + w, ok := read.(struct{ PrivValidatorSocketMsg }) + if !ok { + return nil, fmt.Errorf("unknown type") + } + + return w.PrivValidatorSocketMsg, nil +} + func readWrite(conn net.Conn, req PrivValidatorSocketMsg) (res PrivValidatorSocketMsg, err error) { b := wire.BinaryBytes(req) + _, err = conn.Write(b) if err != nil { return nil, err } - var n int - b = wire.ReadByteSlice(conn, 0, &n, &err) //XXX: no max - return decodeMsg(b) + return readMsg(conn) } func decodeMsg(bz []byte) (msg PrivValidatorSocketMsg, err error) { - n := new(int) - r := bytes.NewReader(bz) - msgI := wire.ReadBinary(struct{ PrivValidatorSocketMsg }{}, r, 0, n, &err) + var ( + r = bytes.NewReader(bz) + + n int + ) + + msgI := wire.ReadBinary(struct{ PrivValidatorSocketMsg }{}, r, 0, &n, &err) + msg = msgI.(struct{ PrivValidatorSocketMsg }).PrivValidatorSocketMsg + return msg, err } diff --git a/types/priv_validator/socket_test.go b/types/priv_validator/socket_test.go new file mode 100644 index 000000000..971e5308b --- /dev/null +++ b/types/priv_validator/socket_test.go @@ -0,0 +1,44 @@ +package types + +import ( + "reflect" + "testing" + + crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/tendermint/types" + "github.com/tendermint/tmlibs/log" +) + +func TestPrivValidatorSocketServer(t *testing.T) { + var ( + chainID = "test-chain" + logger = log.TestingLogger() + signer = types.GenSigner() + privKey = crypto.GenPrivKeyEd25519() + privVal = NewTestPrivValidator(signer) + pvss = NewPrivValidatorSocketServer( + logger, + "127.0.0.1:0", + chainID, + privVal, + privKey, + 1, + ) + ) + + err := pvss.Start() + if err != nil { + t.Fatal(err) + } + + c := NewPrivValidatorSocketClient(logger, pvss.listener.Addr().String()) + + err = c.Start() + if err != nil { + t.Fatal(err) + } + + if have, want := c.PubKey(), pvss.privVal.PubKey(); !reflect.DeepEqual(have, want) { + t.Errorf("have %v, want %v", have, want) + } +} From 18f7e52562aa0b9fe5bc3fed4151d5475c9fa8e8 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Mon, 29 Jan 2018 15:39:26 +0100 Subject: [PATCH 08/18] Use secret connection --- types/priv_validator/socket.go | 127 ++++++++++++++++++++++------ types/priv_validator/socket_test.go | 43 ++++++---- 2 files changed, 129 insertions(+), 41 deletions(-) diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index c22e2698d..35cfe4efd 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -12,8 +12,8 @@ import ( "github.com/tendermint/go-wire/data" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" - "golang.org/x/net/netutil" + "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" ) @@ -26,7 +26,8 @@ var _ types.PrivValidator = (*PrivValidatorSocketClient)(nil) type PrivValidatorSocketClient struct { cmn.BaseService - conn net.Conn + conn net.Conn + privKey *crypto.PrivKeyEd25519 ID types.ValidatorID SocketAddress string @@ -38,11 +39,18 @@ const ( // NewPrivValidatorSocketClient returns an instance of // PrivValidatorSocketClient. -func NewPrivValidatorSocketClient(logger log.Logger, socketAddr string) *PrivValidatorSocketClient { +func NewPrivValidatorSocketClient( + logger log.Logger, + socketAddr string, + privKey *crypto.PrivKeyEd25519, +) *PrivValidatorSocketClient { pvsc := &PrivValidatorSocketClient{ SocketAddress: socketAddr, + privKey: privKey, } + pvsc.BaseService = *cmn.NewBaseService(logger, "privValidatorSocketClient", pvsc) + return pvsc } @@ -54,6 +62,7 @@ func (pvsc *PrivValidatorSocketClient) OnStart() error { var err error var conn net.Conn + RETRY_LOOP: for { conn, err = cmn.Connect(pvsc.SocketAddress) @@ -62,6 +71,15 @@ RETRY_LOOP: time.Sleep(time.Second * dialRetryIntervalSeconds) continue RETRY_LOOP } + + if pvsc.privKey != nil { + conn, err = p2p.MakeSecretConnection(conn, *pvsc.privKey) + if err != nil { + pvsc.Logger.Error("failed to encrypt connection: " + err.Error()) + continue RETRY_LOOP + } + } + pvsc.conn = conn return nil } @@ -84,40 +102,67 @@ func (pvsc *PrivValidatorSocketClient) Address() data.Bytes { // PubKey implements PrivValidator. func (pvsc *PrivValidatorSocketClient) PubKey() crypto.PubKey { - res, err := readWrite(pvsc.conn, &PubKeyMsg{}) + err := writeMsg(pvsc.conn, &PubKeyMsg{}) if err != nil { panic(err) } + + res, err := readMsg(pvsc.conn) + if err != nil { + panic(err) + } + return res.(*PubKeyMsg).PubKey } // SignVote implements PrivValidator. func (pvsc *PrivValidatorSocketClient) SignVote(chainID string, vote *types.Vote) error { - res, err := readWrite(pvsc.conn, &SignVoteMsg{Vote: vote}) + err := writeMsg(pvsc.conn, &SignVoteMsg{Vote: vote}) if err != nil { return err } + + res, err := readMsg(pvsc.conn) + if err != nil { + return err + } + *vote = *res.(SignVoteMsg).Vote + return nil } // SignProposal implements PrivValidator. func (pvsc *PrivValidatorSocketClient) SignProposal(chainID string, proposal *types.Proposal) error { - res, err := readWrite(pvsc.conn, &SignProposalMsg{Proposal: proposal}) + err := writeMsg(pvsc.conn, &SignProposalMsg{Proposal: proposal}) if err != nil { return err } + + res, err := readMsg(pvsc.conn) + if err != nil { + return err + } + *proposal = *res.(SignProposalMsg).Proposal + return nil } // SignHeartbeat implements PrivValidator. func (pvsc *PrivValidatorSocketClient) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) error { - res, err := readWrite(pvsc.conn, &SignHeartbeatMsg{Heartbeat: heartbeat}) + err := writeMsg(pvsc.conn, &SignHeartbeatMsg{Heartbeat: heartbeat}) if err != nil { return err } + + res, err := readMsg(pvsc.conn) + if err != nil { + return err + } + *heartbeat = *res.(SignHeartbeatMsg).Heartbeat + return nil } @@ -131,7 +176,7 @@ type PrivValidatorSocketServer struct { proto, addr string listener net.Listener maxConnections int - privKey crypto.PrivKeyEd25519 + privKey *crypto.PrivKeyEd25519 privVal PrivValidator chainID string @@ -143,7 +188,7 @@ func NewPrivValidatorSocketServer( logger log.Logger, socketAddr, chainID string, privVal PrivValidator, - privKey crypto.PrivKeyEd25519, + privKey *crypto.PrivKeyEd25519, maxConnections int, ) *PrivValidatorSocketServer { proto, addr := cmn.ProtocolAndAddress(socketAddr) @@ -166,7 +211,8 @@ func (pvss *PrivValidatorSocketServer) OnStart() error { return err } - pvss.listener = netutil.LimitListener(ln, pvss.maxConnections) + // pvss.listener = netutil.LimitListener(ln, pvss.maxConnections) + pvss.listener = ln go pvss.acceptConnectionsRoutine() @@ -195,6 +241,19 @@ func (pvss *PrivValidatorSocketServer) acceptConnectionsRoutine() { continue } + if err := conn.SetDeadline(time.Now().Add(time.Second)); err != nil { + pvss.Logger.Error("failed to set timeout for ocnnection: " + err.Error()) + continue + } + + if pvss.privKey != nil { + conn, err = p2p.MakeSecretConnection(conn, *pvss.privKey) + if err != nil { + pvss.Logger.Error("Failed to make secret connection: " + err.Error()) + continue + } + } + go pvss.handleConnection(conn) } } @@ -209,7 +268,9 @@ func (pvss *PrivValidatorSocketServer) handleConnection(conn net.Conn) { req, err := readMsg(conn) if err != nil { - pvss.Logger.Error("readMsg", "err", err) + if err != io.EOF { + pvss.Logger.Error("readMsg", "err", err) + } return } @@ -219,22 +280,38 @@ func (pvss *PrivValidatorSocketServer) handleConnection(conn net.Conn) { case *PubKeyMsg: res = &PubKeyMsg{pvss.privVal.PubKey()} case *SignVoteMsg: - pvss.privVal.SignVote(pvss.chainID, r.Vote) + err := pvss.privVal.SignVote(pvss.chainID, r.Vote) + if err != nil { + pvss.Logger.Error("handleConnection", "err", err) + return + } + res = &SignVoteMsg{r.Vote} case *SignProposalMsg: - pvss.privVal.SignProposal(pvss.chainID, r.Proposal) + err := pvss.privVal.SignProposal(pvss.chainID, r.Proposal) + if err != nil { + pvss.Logger.Error("handleConnection", "err", err) + return + } + res = &SignProposalMsg{r.Proposal} case *SignHeartbeatMsg: - pvss.privVal.SignHeartbeat(pvss.chainID, r.Heartbeat) + err := pvss.privVal.SignHeartbeat(pvss.chainID, r.Heartbeat) + if err != nil { + pvss.Logger.Error("handleConnection", "err", err) + return + } + res = &SignHeartbeatMsg{r.Heartbeat} default: - panic(fmt.Sprintf("unknown msg: %v", r)) + pvss.Logger.Error("handleConnection", "err", fmt.Sprintf("unknown msg: %v", r)) + return } - b := wire.BinaryBytes(res) - _, err = conn.Write(b) + err = writeMsg(conn, res) if err != nil { - panic(err) + pvss.Logger.Error("handleConnection", "err", err) + return } } } @@ -279,15 +356,15 @@ func readMsg(r io.Reader) (PrivValidatorSocketMsg, error) { return w.PrivValidatorSocketMsg, nil } -func readWrite(conn net.Conn, req PrivValidatorSocketMsg) (res PrivValidatorSocketMsg, err error) { - b := wire.BinaryBytes(req) +func writeMsg(w io.Writer, msg interface{}) error { + var ( + err error + n int + ) - _, err = conn.Write(b) - if err != nil { - return nil, err - } + wire.WriteBinary(struct{ PrivValidatorSocketMsg }{msg}, w, &n, &err) - return readMsg(conn) + return err } func decodeMsg(bz []byte) (msg PrivValidatorSocketMsg, err error) { diff --git a/types/priv_validator/socket_test.go b/types/priv_validator/socket_test.go index 971e5308b..16cc59c5f 100644 --- a/types/priv_validator/socket_test.go +++ b/types/priv_validator/socket_test.go @@ -4,6 +4,9 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + crypto "github.com/tendermint/go-crypto" "github.com/tendermint/tendermint/types" "github.com/tendermint/tmlibs/log" @@ -11,34 +14,42 @@ import ( func TestPrivValidatorSocketServer(t *testing.T) { var ( - chainID = "test-chain" - logger = log.TestingLogger() - signer = types.GenSigner() - privKey = crypto.GenPrivKeyEd25519() - privVal = NewTestPrivValidator(signer) - pvss = NewPrivValidatorSocketServer( + assert, require = assert.New(t), require.New(t) + chainID = "test-chain" + logger = log.TestingLogger() + signer = types.GenSigner() + clientPrivKey = crypto.GenPrivKeyEd25519() + serverPrivKey = crypto.GenPrivKeyEd25519() + privVal = NewTestPrivValidator(signer) + pvss = NewPrivValidatorSocketServer( logger, "127.0.0.1:0", chainID, privVal, - privKey, + &serverPrivKey, 1, ) ) err := pvss.Start() - if err != nil { - t.Fatal(err) - } + require.Nil(err) + defer pvss.Stop() - c := NewPrivValidatorSocketClient(logger, pvss.listener.Addr().String()) + assert.True(pvss.IsRunning()) - err = c.Start() - if err != nil { - t.Fatal(err) - } + pvsc := NewPrivValidatorSocketClient( + logger, + pvss.listener.Addr().String(), + &clientPrivKey, + ) + + err = pvsc.Start() + require.Nil(err) + defer pvsc.Stop() + + assert.True(pvsc.IsRunning()) - if have, want := c.PubKey(), pvss.privVal.PubKey(); !reflect.DeepEqual(have, want) { + if have, want := pvsc.PubKey(), pvss.privVal.PubKey(); !reflect.DeepEqual(have, want) { t.Errorf("have %v, want %v", have, want) } } From 32d9563a15376fccb208a1b5d37fdc4a973dcd2e Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Wed, 31 Jan 2018 14:48:56 +0100 Subject: [PATCH 09/18] Format and consolidate --- types/priv_validator/socket.go | 138 +++++++++++++--------------- types/priv_validator/socket_test.go | 4 +- 2 files changed, 68 insertions(+), 74 deletions(-) diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index 35cfe4efd..260303465 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -1,17 +1,18 @@ package types import ( - "bytes" "fmt" "io" "net" "time" + "github.com/pkg/errors" crypto "github.com/tendermint/go-crypto" wire "github.com/tendermint/go-wire" "github.com/tendermint/go-wire/data" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" + "golang.org/x/net/netutil" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" @@ -60,14 +61,21 @@ func (pvsc *PrivValidatorSocketClient) OnStart() error { return err } - var err error - var conn net.Conn + var ( + err error + conn net.Conn + ) RETRY_LOOP: for { conn, err = cmn.Connect(pvsc.SocketAddress) if err != nil { - pvsc.Logger.Error(fmt.Sprintf("PrivValidatorSocket failed to connect to %v. Retrying...", pvsc.SocketAddress)) + pvsc.Logger.Error( + "OnStart", + "addr", pvsc.SocketAddress, + "err", errors.Wrap(err, "connection failed"), + ) + time.Sleep(time.Second * dialRetryIntervalSeconds) continue RETRY_LOOP } @@ -75,7 +83,10 @@ RETRY_LOOP: if pvsc.privKey != nil { conn, err = p2p.MakeSecretConnection(conn, *pvsc.privKey) if err != nil { - pvsc.Logger.Error("failed to encrypt connection: " + err.Error()) + pvsc.Logger.Error( + "OnStart", + "err", errors.Wrap(err, "encrypting connection failed"), + ) continue RETRY_LOOP } } @@ -96,8 +107,7 @@ func (pvsc *PrivValidatorSocketClient) OnStop() { // Address is an alias for PubKey().Address(). func (pvsc *PrivValidatorSocketClient) Address() data.Bytes { - pubKey := pvsc.PubKey() - return pubKey.Address() + return pvsc.PubKey().Address() } // PubKey implements PrivValidator. @@ -186,10 +196,10 @@ type PrivValidatorSocketServer struct { // PrivValidatorSocketServer. func NewPrivValidatorSocketServer( logger log.Logger, - socketAddr, chainID string, + chainID, socketAddr string, + maxConnections int, privVal PrivValidator, privKey *crypto.PrivKeyEd25519, - maxConnections int, ) *PrivValidatorSocketServer { proto, addr := cmn.ProtocolAndAddress(socketAddr) pvss := &PrivValidatorSocketServer{ @@ -211,10 +221,9 @@ func (pvss *PrivValidatorSocketServer) OnStart() error { return err } - // pvss.listener = netutil.LimitListener(ln, pvss.maxConnections) - pvss.listener = ln + pvss.listener = netutil.LimitListener(ln, pvss.maxConnections) - go pvss.acceptConnectionsRoutine() + go pvss.acceptConnections() return nil } @@ -226,30 +235,39 @@ func (pvss *PrivValidatorSocketServer) OnStop() { } if err := pvss.listener.Close(); err != nil { - pvss.Logger.Error("Error closing listener", "err", err) + pvss.Logger.Error("OnStop", "err", errors.Wrap(err, "closing listener failed")) } } -func (pvss *PrivValidatorSocketServer) acceptConnectionsRoutine() { +func (pvss *PrivValidatorSocketServer) acceptConnections() { for { conn, err := pvss.listener.Accept() if err != nil { if !pvss.IsRunning() { return // Ignore error from listener closing. } - pvss.Logger.Error("Failed to accept connection: " + err.Error()) + pvss.Logger.Error( + "accpetConnections", + "err", errors.Wrap(err, "failed to accept connection"), + ) continue } if err := conn.SetDeadline(time.Now().Add(time.Second)); err != nil { - pvss.Logger.Error("failed to set timeout for ocnnection: " + err.Error()) + pvss.Logger.Error( + "acceptConnetions", + "err", errors.Wrap(err, "setting connection timeout failed"), + ) continue } if pvss.privKey != nil { conn, err = p2p.MakeSecretConnection(conn, *pvss.privKey) if err != nil { - pvss.Logger.Error("Failed to make secret connection: " + err.Error()) + pvss.Logger.Error( + "acceptConnections", + "err", errors.Wrap(err, "secret connection failed"), + ) continue } } @@ -269,7 +287,7 @@ func (pvss *PrivValidatorSocketServer) handleConnection(conn net.Conn) { req, err := readMsg(conn) if err != nil { if err != io.EOF { - pvss.Logger.Error("readMsg", "err", err) + pvss.Logger.Error("handleConnection", "err", err) } return } @@ -280,31 +298,20 @@ func (pvss *PrivValidatorSocketServer) handleConnection(conn net.Conn) { case *PubKeyMsg: res = &PubKeyMsg{pvss.privVal.PubKey()} case *SignVoteMsg: - err := pvss.privVal.SignVote(pvss.chainID, r.Vote) - if err != nil { - pvss.Logger.Error("handleConnection", "err", err) - return - } - + err = pvss.privVal.SignVote(pvss.chainID, r.Vote) res = &SignVoteMsg{r.Vote} case *SignProposalMsg: - err := pvss.privVal.SignProposal(pvss.chainID, r.Proposal) - if err != nil { - pvss.Logger.Error("handleConnection", "err", err) - return - } - + err = pvss.privVal.SignProposal(pvss.chainID, r.Proposal) res = &SignProposalMsg{r.Proposal} case *SignHeartbeatMsg: - err := pvss.privVal.SignHeartbeat(pvss.chainID, r.Heartbeat) - if err != nil { - pvss.Logger.Error("handleConnection", "err", err) - return - } - + err = pvss.privVal.SignHeartbeat(pvss.chainID, r.Heartbeat) res = &SignHeartbeatMsg{r.Heartbeat} default: - pvss.Logger.Error("handleConnection", "err", fmt.Sprintf("unknown msg: %v", r)) + err = fmt.Errorf("unknown msg: %v", r) + } + + if err != nil { + pvss.Logger.Error("handleConnection", "err", err) return } @@ -337,6 +344,26 @@ var _ = wire.RegisterInterface( wire.ConcreteType{&SignHeartbeatMsg{}, msgTypeSignHeartbeat}, ) +// PubKeyMsg is a PrivValidatorSocket message containing the public key. +type PubKeyMsg struct { + PubKey crypto.PubKey +} + +// SignVoteMsg is a PrivValidatorSocket message containing a vote. +type SignVoteMsg struct { + Vote *types.Vote +} + +// SignProposalMsg is a PrivValidatorSocket message containing a Proposal. +type SignProposalMsg struct { + Proposal *types.Proposal +} + +// SignHeartbeatMsg is a PrivValidatorSocket message containing a Heartbeat. +type SignHeartbeatMsg struct { + Heartbeat *types.Heartbeat +} + func readMsg(r io.Reader) (PrivValidatorSocketMsg, error) { var ( n int @@ -350,7 +377,7 @@ func readMsg(r io.Reader) (PrivValidatorSocketMsg, error) { w, ok := read.(struct{ PrivValidatorSocketMsg }) if !ok { - return nil, fmt.Errorf("unknown type") + return nil, errors.New("unknwon type") } return w.PrivValidatorSocketMsg, nil @@ -362,41 +389,8 @@ func writeMsg(w io.Writer, msg interface{}) error { n int ) + // TODO(xla): This extra wrap should be gone with the sdk-2 update. wire.WriteBinary(struct{ PrivValidatorSocketMsg }{msg}, w, &n, &err) return err } - -func decodeMsg(bz []byte) (msg PrivValidatorSocketMsg, err error) { - var ( - r = bytes.NewReader(bz) - - n int - ) - - msgI := wire.ReadBinary(struct{ PrivValidatorSocketMsg }{}, r, 0, &n, &err) - - msg = msgI.(struct{ PrivValidatorSocketMsg }).PrivValidatorSocketMsg - - return msg, err -} - -// PubKeyMsg is a PrivValidatorSocket message containing the public key. -type PubKeyMsg struct { - PubKey crypto.PubKey -} - -// SignVoteMsg is a PrivValidatorSocket message containing a vote. -type SignVoteMsg struct { - Vote *types.Vote -} - -// SignProposalMsg is a PrivValidatorSocket message containing a Proposal. -type SignProposalMsg struct { - Proposal *types.Proposal -} - -// SignHeartbeatMsg is a PrivValidatorSocket message containing a Heartbeat. -type SignHeartbeatMsg struct { - Heartbeat *types.Heartbeat -} diff --git a/types/priv_validator/socket_test.go b/types/priv_validator/socket_test.go index 16cc59c5f..88e190094 100644 --- a/types/priv_validator/socket_test.go +++ b/types/priv_validator/socket_test.go @@ -23,11 +23,11 @@ func TestPrivValidatorSocketServer(t *testing.T) { privVal = NewTestPrivValidator(signer) pvss = NewPrivValidatorSocketServer( logger, - "127.0.0.1:0", chainID, + "127.0.0.1:0", + 1, privVal, &serverPrivKey, - 1, ) ) From 38d18ca11a5647889d1ad8ca1b3d53ed9627f6e1 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Tue, 6 Feb 2018 17:39:10 +0100 Subject: [PATCH 10/18] Harden tests --- types/priv_validator/socket.go | 6 +-- types/priv_validator/socket_test.go | 77 +++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index 260303465..1fd45ed65 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -137,7 +137,7 @@ func (pvsc *PrivValidatorSocketClient) SignVote(chainID string, vote *types.Vote return err } - *vote = *res.(SignVoteMsg).Vote + *vote = *res.(*SignVoteMsg).Vote return nil } @@ -154,7 +154,7 @@ func (pvsc *PrivValidatorSocketClient) SignProposal(chainID string, proposal *ty return err } - *proposal = *res.(SignProposalMsg).Proposal + *proposal = *res.(*SignProposalMsg).Proposal return nil } @@ -171,7 +171,7 @@ func (pvsc *PrivValidatorSocketClient) SignHeartbeat(chainID string, heartbeat * return err } - *heartbeat = *res.(SignHeartbeatMsg).Heartbeat + *heartbeat = *res.(*SignHeartbeatMsg).Heartbeat return nil } diff --git a/types/priv_validator/socket_test.go b/types/priv_validator/socket_test.go index 88e190094..725d5af65 100644 --- a/types/priv_validator/socket_test.go +++ b/types/priv_validator/socket_test.go @@ -1,13 +1,14 @@ package types import ( - "reflect" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" + data "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/types" "github.com/tendermint/tmlibs/log" ) @@ -15,7 +16,7 @@ import ( func TestPrivValidatorSocketServer(t *testing.T) { var ( assert, require = assert.New(t), require.New(t) - chainID = "test-chain" + chainID = "test-chain-secret" logger = log.TestingLogger() signer = types.GenSigner() clientPrivKey = crypto.GenPrivKeyEd25519() @@ -49,7 +50,73 @@ func TestPrivValidatorSocketServer(t *testing.T) { assert.True(pvsc.IsRunning()) - if have, want := pvsc.PubKey(), pvss.privVal.PubKey(); !reflect.DeepEqual(have, want) { - t.Errorf("have %v, want %v", have, want) - } + assert.Equal(pvsc.Address(), data.Bytes(pvss.privVal.PubKey().Address())) + assert.Equal(pvsc.PubKey(), pvss.privVal.PubKey()) + + err = pvsc.SignProposal(chainID, &types.Proposal{ + Timestamp: time.Now(), + }) + require.Nil(err) + + err = pvsc.SignVote(chainID, &types.Vote{ + Timestamp: time.Now(), + Type: types.VoteTypePrecommit, + }) + require.Nil(err) + + err = pvsc.SignHeartbeat(chainID, &types.Heartbeat{}) + require.Nil(err) +} + +func TestPrivValidatorSocketServerWithoutSecret(t *testing.T) { + var ( + assert, require = assert.New(t), require.New(t) + chainID = "test-chain-secret" + logger = log.TestingLogger() + signer = types.GenSigner() + privVal = NewTestPrivValidator(signer) + pvss = NewPrivValidatorSocketServer( + logger, + chainID, + "127.0.0.1:0", + 1, + privVal, + nil, + ) + ) + + err := pvss.Start() + require.Nil(err) + defer pvss.Stop() + + assert.True(pvss.IsRunning()) + + pvsc := NewPrivValidatorSocketClient( + logger, + pvss.listener.Addr().String(), + nil, + ) + + err = pvsc.Start() + require.Nil(err) + defer pvsc.Stop() + + assert.True(pvsc.IsRunning()) + + assert.Equal(pvsc.Address(), data.Bytes(pvss.privVal.PubKey().Address())) + assert.Equal(pvsc.PubKey(), pvss.privVal.PubKey()) + + err = pvsc.SignProposal(chainID, &types.Proposal{ + Timestamp: time.Now(), + }) + require.Nil(err) + + err = pvsc.SignVote(chainID, &types.Vote{ + Timestamp: time.Now(), + Type: types.VoteTypePrecommit, + }) + require.Nil(err) + + err = pvsc.SignHeartbeat(chainID, &types.Heartbeat{}) + require.Nil(err) } From 7d71e702d8e3f6cb78c432b8b95ecdc31d0a5f79 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Wed, 7 Feb 2018 18:35:01 +0100 Subject: [PATCH 11/18] Integrate privVal client with node secret --- cmd/priv_val_server/main.go | 9 ++++++--- node/node.go | 32 ++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index b494a442c..904447c09 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -13,6 +13,7 @@ import ( func main() { var ( chainID = flag.String("chain-id", "mychain", "chain id") + numClients = flag.Int("clients", 1, "number of concurrently connected clients") privValPath = flag.String("priv", "", "priv val file path") socketAddr = flag.String("socket.addr", ":46659", "socket bind addr") @@ -20,17 +21,19 @@ func main() { ) flag.Parse() - logger.Info("args", "chainID", *chainID, "privPath", *privValPath) + logger.Info("Reading args privValidatorSocketServer", "chainID", *chainID, "privPath", *privValPath) privVal := priv_val.LoadPrivValidatorJSON(*privValPath) pvss := priv_val.NewPrivValidatorSocketServer( logger, - *socketAddr, *chainID, + *socketAddr, + *numClients, privVal, + nil, ) - // pvss.Start() + pvss.Start() cmn.TrapSignal(func() { pvss.Stop() diff --git a/node/node.go b/node/node.go index 859cc1df1..64e8c02c1 100644 --- a/node/node.go +++ b/node/node.go @@ -79,13 +79,13 @@ type NodeProvider func(*cfg.Config, log.Logger) (*Node, error) func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { var privVal types.PrivValidator privVal = types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()) - /* - if config.PrivValidatorAddr != "" { - pvsc := priv_val.NewPrivValidatorSocketClient(logger.With("module", "priv_val"), - config.PrivValidatorAddr) - pvsc.Start() - privVal = pvsc - } + /* TODO + if config.PrivValidatorAddr != "" { + pvsc := priv_val.NewPrivValidatorSocketClient(logger.With("module", "priv_val"), + config.PrivValidatorAddr) + pvsc.Start() + privVal = pvsc + } */ fmt.Println("PRIV", config.PrivValidatorAddr) @@ -94,7 +94,8 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), DefaultGenesisDocProviderFunc(config), DefaultDBProvider, - logger) + logger, + ) } //------------------------------------------------------------------------------ @@ -183,6 +184,21 @@ func NewNode(config *cfg.Config, // reload the state (it may have been updated by the handshake) state = sm.LoadState(stateDB) + /* TODO + // Generate node PrivKey + privKey := crypto.GenPrivKeyEd25519() + + if config.PrivValidatorAddr != "" { + pvsc := priv_val.NewPrivValidatorSocketClient( + logger.With("module", "priv_val"), + config.PrivValidatorAddr, + &privKey, + ) + pvsc.Start() + privValidator = pvsc + } + */ + // Decide whether to fast-sync or not // We don't fast-sync when the only validator is us. fastSync := config.FastSync From 8da2a6a14780e8e0d489a3e0d49ecc5f09905a4f Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 9 Feb 2018 17:24:30 -0500 Subject: [PATCH 12/18] types/priv_validator: fixes for latest p2p and cmn --- types/canonical_json.go | 6 +-- types/priv_validator.go | 54 ++++++++++++++++++++- types/priv_validator/json.go | 4 +- types/priv_validator/priv_validator_test.go | 6 +-- types/priv_validator/sign_info.go | 4 +- types/priv_validator/socket.go | 11 ++--- types/priv_validator/socket_test.go | 9 ++-- types/priv_validator/unencrypted.go | 6 +-- types/priv_validator/upgrade.go | 6 +-- types/proposal_test.go | 2 +- types/vote_test.go | 2 +- 11 files changed, 80 insertions(+), 30 deletions(-) diff --git a/types/canonical_json.go b/types/canonical_json.go index 45d12b45f..879bb5c52 100644 --- a/types/canonical_json.go +++ b/types/canonical_json.go @@ -9,8 +9,8 @@ import ( // canonical json is go-wire's json for structs with fields in alphabetical order -// timeFormat is used for generating the sigs -const timeFormat = wire.RFC3339Millis +// TimeFormat is used for generating the sigs +const TimeFormat = wire.RFC3339Millis type CanonicalJSONBlockID struct { Hash cmn.HexBytes `json:"hash,omitempty"` @@ -117,5 +117,5 @@ func CanonicalTime(t time.Time) string { // note that sending time over go-wire resets it to // local time, we need to force UTC here, so the // signatures match - return t.UTC().Format(timeFormat) + return t.UTC().Format(TimeFormat) } diff --git a/types/priv_validator.go b/types/priv_validator.go index 062fe09d2..4038b1adf 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -34,6 +34,56 @@ func voteToStep(vote *Vote) int8 { } } +//-------------------------------------------------------------- +// PrivValidator is being upgraded! See types/priv_validator + +// ValidatorID contains the identity of the validator. +type ValidatorID struct { + Address cmn.HexBytes `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` +} + +// PrivValidator defines the functionality of a local Tendermint validator +// that signs votes, proposals, and heartbeats, and never double signs. +type PrivValidator2 interface { + Address() Address // redundant since .PubKey().Address() + PubKey() crypto.PubKey + + SignVote(chainID string, vote *Vote) error + SignProposal(chainID string, proposal *Proposal) error + SignHeartbeat(chainID string, heartbeat *Heartbeat) error +} + +type TestSigner interface { + Address() cmn.HexBytes + PubKey() crypto.PubKey + Sign([]byte) (crypto.Signature, error) +} + +func GenSigner() TestSigner { + return &DefaultTestSigner{ + crypto.GenPrivKeyEd25519().Wrap(), + } +} + +type DefaultTestSigner struct { + crypto.PrivKey +} + +func (ds *DefaultTestSigner) Address() cmn.HexBytes { + return ds.PubKey().Address() +} + +func (ds *DefaultTestSigner) PubKey() crypto.PubKey { + return ds.PrivKey.PubKey() +} + +func (ds *DefaultTestSigner) Sign(msg []byte) (crypto.Signature, error) { + return ds.PrivKey.Sign(msg), nil +} + +//-------------------------------------------------------------- + // PrivValidator defines the functionality of a local Tendermint validator // that signs votes, proposals, and heartbeats, and never double signs. type PrivValidator interface { @@ -379,7 +429,7 @@ func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.T panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err)) } - lastTime, err := time.Parse(timeFormat, lastVote.Vote.Timestamp) + lastTime, err := time.Parse(TimeFormat, lastVote.Vote.Timestamp) if err != nil { panic(err) } @@ -405,7 +455,7 @@ func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (ti panic(fmt.Sprintf("signBytes cannot be unmarshalled into proposal: %v", err)) } - lastTime, err := time.Parse(timeFormat, lastProposal.Proposal.Timestamp) + lastTime, err := time.Parse(TimeFormat, lastProposal.Proposal.Timestamp) if err != nil { panic(err) } diff --git a/types/priv_validator/json.go b/types/priv_validator/json.go index 446587765..8fe7a3749 100644 --- a/types/priv_validator/json.go +++ b/types/priv_validator/json.go @@ -13,7 +13,7 @@ import ( ) // PrivValidator aliases types.PrivValidator -type PrivValidator = types.PrivValidator +type PrivValidator = types.PrivValidator2 //----------------------------------------------------- @@ -42,7 +42,7 @@ func (pk *PrivKey) UnmarshalJSON(b []byte) error { //----------------------------------------------------- -var _ types.PrivValidator = (*PrivValidatorJSON)(nil) +var _ types.PrivValidator2 = (*PrivValidatorJSON)(nil) // PrivValidatorJSON wraps PrivValidatorUnencrypted // and persists it to disk after every SignVote and SignProposal. diff --git a/types/priv_validator/priv_validator_test.go b/types/priv_validator/priv_validator_test.go index 33f8338d4..6f68256da 100644 --- a/types/priv_validator/priv_validator_test.go +++ b/types/priv_validator/priv_validator_test.go @@ -11,9 +11,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-wire/data" - "github.com/tendermint/tendermint/types" cmn "github.com/tendermint/tmlibs/common" + + "github.com/tendermint/tendermint/types" ) func TestGenLoadValidator(t *testing.T) { @@ -230,7 +230,7 @@ func TestDifferByTimestamp(t *testing.T) { } } -func newVote(addr data.Bytes, idx int, height int64, round int, typ byte, blockID types.BlockID) *types.Vote { +func newVote(addr cmn.HexBytes, idx int, height int64, round int, typ byte, blockID types.BlockID) *types.Vote { return &types.Vote{ ValidatorAddress: addr, ValidatorIndex: idx, diff --git a/types/priv_validator/sign_info.go b/types/priv_validator/sign_info.go index 1a3dae442..60113c570 100644 --- a/types/priv_validator/sign_info.go +++ b/types/priv_validator/sign_info.go @@ -8,8 +8,8 @@ import ( "time" crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/types" + cmn "github.com/tendermint/tmlibs/common" ) // TODO: type ? @@ -40,7 +40,7 @@ type LastSignedInfo struct { Round int `json:"round"` Step int8 `json:"step"` Signature crypto.Signature `json:"signature,omitempty"` // so we dont lose signatures - SignBytes data.Bytes `json:"signbytes,omitempty"` // so we dont lose signatures + SignBytes cmn.HexBytes `json:"signbytes,omitempty"` // so we dont lose signatures } func NewLastSignedInfo() *LastSignedInfo { diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index 1fd45ed65..4093ce9ca 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -9,18 +9,17 @@ import ( "github.com/pkg/errors" crypto "github.com/tendermint/go-crypto" wire "github.com/tendermint/go-wire" - "github.com/tendermint/go-wire/data" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" "golang.org/x/net/netutil" - "github.com/tendermint/tendermint/p2p" + p2pconn "github.com/tendermint/tendermint/p2p/conn" "github.com/tendermint/tendermint/types" ) //----------------------------------------------------------------- -var _ types.PrivValidator = (*PrivValidatorSocketClient)(nil) +var _ types.PrivValidator2 = (*PrivValidatorSocketClient)(nil) // PrivValidatorSocketClient implements PrivValidator. // It uses a socket to request signatures. @@ -81,7 +80,7 @@ RETRY_LOOP: } if pvsc.privKey != nil { - conn, err = p2p.MakeSecretConnection(conn, *pvsc.privKey) + conn, err = p2pconn.MakeSecretConnection(conn, pvsc.privKey.Wrap()) if err != nil { pvsc.Logger.Error( "OnStart", @@ -106,7 +105,7 @@ func (pvsc *PrivValidatorSocketClient) OnStop() { } // Address is an alias for PubKey().Address(). -func (pvsc *PrivValidatorSocketClient) Address() data.Bytes { +func (pvsc *PrivValidatorSocketClient) Address() cmn.HexBytes { return pvsc.PubKey().Address() } @@ -262,7 +261,7 @@ func (pvss *PrivValidatorSocketServer) acceptConnections() { } if pvss.privKey != nil { - conn, err = p2p.MakeSecretConnection(conn, *pvss.privKey) + conn, err = p2pconn.MakeSecretConnection(conn, pvss.privKey.Wrap()) if err != nil { pvss.Logger.Error( "acceptConnections", diff --git a/types/priv_validator/socket_test.go b/types/priv_validator/socket_test.go index 725d5af65..627d4fb18 100644 --- a/types/priv_validator/socket_test.go +++ b/types/priv_validator/socket_test.go @@ -8,9 +8,10 @@ import ( "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-wire/data" - "github.com/tendermint/tendermint/types" + cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" + + "github.com/tendermint/tendermint/types" ) func TestPrivValidatorSocketServer(t *testing.T) { @@ -50,7 +51,7 @@ func TestPrivValidatorSocketServer(t *testing.T) { assert.True(pvsc.IsRunning()) - assert.Equal(pvsc.Address(), data.Bytes(pvss.privVal.PubKey().Address())) + assert.Equal(pvsc.Address(), cmn.HexBytes(pvss.privVal.PubKey().Address())) assert.Equal(pvsc.PubKey(), pvss.privVal.PubKey()) err = pvsc.SignProposal(chainID, &types.Proposal{ @@ -103,7 +104,7 @@ func TestPrivValidatorSocketServerWithoutSecret(t *testing.T) { assert.True(pvsc.IsRunning()) - assert.Equal(pvsc.Address(), data.Bytes(pvss.privVal.PubKey().Address())) + assert.Equal(pvsc.Address(), cmn.HexBytes(pvss.privVal.PubKey().Address())) assert.Equal(pvsc.PubKey(), pvss.privVal.PubKey()) err = pvsc.SignProposal(chainID, &types.Proposal{ diff --git a/types/priv_validator/unencrypted.go b/types/priv_validator/unencrypted.go index e9b0bb880..483f57c11 100644 --- a/types/priv_validator/unencrypted.go +++ b/types/priv_validator/unencrypted.go @@ -4,13 +4,13 @@ import ( "fmt" crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/types" + cmn "github.com/tendermint/tmlibs/common" ) //----------------------------------------------------------------- -var _ types.PrivValidator = (*PrivValidatorUnencrypted)(nil) +var _ types.PrivValidator2 = (*PrivValidatorUnencrypted)(nil) // PrivValidatorUnencrypted implements PrivValidator. // It uses an in-memory crypto.PrivKey that is @@ -38,7 +38,7 @@ func (upv *PrivValidatorUnencrypted) String() string { return fmt.Sprintf("PrivValidator{%v %v}", upv.Address(), upv.LastSignedInfo.String()) } -func (upv *PrivValidatorUnencrypted) Address() data.Bytes { +func (upv *PrivValidatorUnencrypted) Address() cmn.HexBytes { return upv.PrivKey.PubKey().Address() } diff --git a/types/priv_validator/upgrade.go b/types/priv_validator/upgrade.go index 6d5c472ab..063655421 100644 --- a/types/priv_validator/upgrade.go +++ b/types/priv_validator/upgrade.go @@ -5,18 +5,18 @@ import ( "io/ioutil" crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/types" + cmn "github.com/tendermint/tmlibs/common" ) type PrivValidatorV1 struct { - Address data.Bytes `json:"address"` + Address cmn.HexBytes `json:"address"` PubKey crypto.PubKey `json:"pub_key"` LastHeight int64 `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 + LastSignBytes cmn.HexBytes `json:"last_signbytes,omitempty"` // so we dont lose signatures PrivKey crypto.PrivKey `json:"priv_key"` } diff --git a/types/proposal_test.go b/types/proposal_test.go index 0d2af71e2..6fbfbba05 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -12,7 +12,7 @@ import ( var testProposal *Proposal func init() { - var stamp, err = time.Parse(timeFormat, "2018-02-11T07:09:22.765Z") + var stamp, err = time.Parse(TimeFormat, "2018-02-11T07:09:22.765Z") if err != nil { panic(err) } diff --git a/types/vote_test.go b/types/vote_test.go index 51eca12de..5e2d5c0df 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -18,7 +18,7 @@ func examplePrecommit() *Vote { } func exampleVote(t byte) *Vote { - var stamp, err = time.Parse(timeFormat, "2017-12-25T03:00:01.234Z") + var stamp, err = time.Parse(TimeFormat, "2017-12-25T03:00:01.234Z") if err != nil { panic(err) } From 82b1a34a360d41f46553e8c0d353c428711bc445 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Tue, 13 Feb 2018 19:08:21 +0100 Subject: [PATCH 13/18] Separate connect logic * break out connect functionality out of OnStart * introduce max retries --- types/priv_validator/socket.go | 91 +++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index 4093ce9ca..1ca3b7d1b 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -17,6 +17,16 @@ import ( "github.com/tendermint/tendermint/types" ) +const ( + dialRetryIntervalSeconds = 1 + dialRetryMax = 10 +) + +// Socket errors. +var ( + ErrDialRetryMax = errors.New("Error max client retries") +) + //----------------------------------------------------------------- var _ types.PrivValidator2 = (*PrivValidatorSocketClient)(nil) @@ -33,10 +43,6 @@ type PrivValidatorSocketClient struct { SocketAddress string } -const ( - dialRetryIntervalSeconds = 1 -) - // NewPrivValidatorSocketClient returns an instance of // PrivValidatorSocketClient. func NewPrivValidatorSocketClient( @@ -60,39 +66,14 @@ func (pvsc *PrivValidatorSocketClient) OnStart() error { return err } - var ( - err error - conn net.Conn - ) - -RETRY_LOOP: - for { - conn, err = cmn.Connect(pvsc.SocketAddress) - if err != nil { - pvsc.Logger.Error( - "OnStart", - "addr", pvsc.SocketAddress, - "err", errors.Wrap(err, "connection failed"), - ) - - time.Sleep(time.Second * dialRetryIntervalSeconds) - continue RETRY_LOOP - } + conn, err := pvsc.connect() + if err != nil { + return err + } - if pvsc.privKey != nil { - conn, err = p2pconn.MakeSecretConnection(conn, pvsc.privKey.Wrap()) - if err != nil { - pvsc.Logger.Error( - "OnStart", - "err", errors.Wrap(err, "encrypting connection failed"), - ) - continue RETRY_LOOP - } - } + pvsc.conn = conn - pvsc.conn = conn - return nil - } + return nil } // OnStop implements cmn.Service. @@ -175,6 +156,46 @@ func (pvsc *PrivValidatorSocketClient) SignHeartbeat(chainID string, heartbeat * return nil } +func (pvsc *PrivValidatorSocketClient) connect() (net.Conn, error) { + retries := dialRetryMax + +RETRY_LOOP: + for retries > 0 { + if retries != dialRetryMax { + time.Sleep(time.Second * dialRetryIntervalSeconds) + } + + retries-- + + conn, err := cmn.Connect(pvsc.SocketAddress) + if err != nil { + pvsc.Logger.Error( + "OnStart", + "addr", pvsc.SocketAddress, + "err", errors.Wrap(err, "connection failed"), + ) + + continue RETRY_LOOP + } + + if pvsc.privKey != nil { + conn, err = p2pconn.MakeSecretConnection(conn, pvsc.privKey.Wrap()) + if err != nil { + pvsc.Logger.Error( + "OnStart", + "err", errors.Wrap(err, "encrypting connection failed"), + ) + + continue RETRY_LOOP + } + } + + return conn, nil + } + + return nil, ErrDialRetryMax +} + //--------------------------------------------------------- // PrivValidatorSocketServer implements PrivValidator. From 2a292efb56e252a00e67b55c1e7faf574cdeeea1 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Tue, 13 Feb 2018 19:34:50 +0100 Subject: [PATCH 14/18] Return error for all PrivValidator methoods As calls to the private validator can involve side-effects like network communication it is desirable for all methods returning an error to not break the control flow of the caller. * adjust PrivValidator interface --- types/priv_validator.go | 4 +- types/priv_validator/json.go | 19 ++++++- types/priv_validator/priv_validator_test.go | 57 +++++++++++++++------ types/priv_validator/socket.go | 22 +++++--- types/priv_validator/socket_test.go | 35 +++++++++++-- types/priv_validator/unencrypted.go | 15 ++++-- 6 files changed, 114 insertions(+), 38 deletions(-) diff --git a/types/priv_validator.go b/types/priv_validator.go index 4038b1adf..237e3f796 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -46,8 +46,8 @@ type ValidatorID struct { // PrivValidator defines the functionality of a local Tendermint validator // that signs votes, proposals, and heartbeats, and never double signs. type PrivValidator2 interface { - Address() Address // redundant since .PubKey().Address() - PubKey() crypto.PubKey + Address() (Address, error) // redundant since .PubKey().Address() + PubKey() (crypto.PubKey, error) SignVote(chainID string, vote *Vote) error SignProposal(chainID string, proposal *Proposal) error diff --git a/types/priv_validator/json.go b/types/priv_validator/json.go index 8fe7a3749..6e5261386 100644 --- a/types/priv_validator/json.go +++ b/types/priv_validator/json.go @@ -76,7 +76,12 @@ func (pvj *PrivValidatorJSON) SignProposal(chainID string, proposal *types.Propo // String returns a string representation of the PrivValidatorJSON. func (pvj *PrivValidatorJSON) String() string { - return fmt.Sprintf("PrivValidator{%v %v}", pvj.Address(), pvj.PrivValidatorUnencrypted.String()) + addr, err := pvj.Address() + if err != nil { + panic(err) + } + + return fmt.Sprintf("PrivValidator{%v %v}", addr, pvj.PrivValidatorUnencrypted.String()) } func (pvj *PrivValidatorJSON) Save() { @@ -170,7 +175,17 @@ func (pvs PrivValidatorsByAddress) Len() int { } func (pvs PrivValidatorsByAddress) Less(i, j int) bool { - return bytes.Compare(pvs[i].Address(), pvs[j].Address()) == -1 + iaddr, err := pvs[j].Address() + if err != nil { + panic(err) + } + + jaddr, err := pvs[i].Address() + if err != nil { + panic(err) + } + + return bytes.Compare(iaddr, jaddr) == -1 } func (pvs PrivValidatorsByAddress) Swap(i, j int) { diff --git a/types/priv_validator/priv_validator_test.go b/types/priv_validator/priv_validator_test.go index 6f68256da..664d59cf2 100644 --- a/types/priv_validator/priv_validator_test.go +++ b/types/priv_validator/priv_validator_test.go @@ -17,7 +17,7 @@ import ( ) func TestGenLoadValidator(t *testing.T) { - assert := assert.New(t) + assert, require := assert.New(t), require.New(t) _, tempFilePath := cmn.Tempfile("priv_validator_") privVal := GenPrivValidatorJSON(tempFilePath) @@ -25,24 +25,33 @@ func TestGenLoadValidator(t *testing.T) { height := int64(100) privVal.LastSignedInfo.Height = height privVal.Save() - addr := privVal.Address() + addr, err := privVal.Address() + require.Nil(err) privVal = LoadPrivValidatorJSON(tempFilePath) - assert.Equal(addr, privVal.Address(), "expected privval addr to be the same") + pAddr, err := privVal.Address() + require.Nil(err) + + assert.Equal(addr, pAddr, "expected privval addr to be the same") assert.Equal(height, privVal.LastSignedInfo.Height, "expected privval.LastHeight to have been saved") } func TestLoadOrGenValidator(t *testing.T) { - assert := assert.New(t) + assert, require := assert.New(t), require.New(t) _, tempFilePath := cmn.Tempfile("priv_validator_") if err := os.Remove(tempFilePath); err != nil { t.Error(err) } privVal := LoadOrGenPrivValidatorJSON(tempFilePath) - addr := privVal.Address() + addr, err := privVal.Address() + require.Nil(err) + privVal = LoadOrGenPrivValidatorJSON(tempFilePath) - assert.Equal(addr, privVal.Address(), "expected privval addr to be the same") + pAddr, err := privVal.Address() + require.Nil(err) + + assert.Equal(addr, pAddr, "expected privval addr to be the same") } func TestUnmarshalValidator(t *testing.T) { @@ -87,8 +96,14 @@ func TestUnmarshalValidator(t *testing.T) { require.Nil(err, "%+v", err) // make sure the values match - assert.EqualValues(addrBytes, val.Address()) - assert.EqualValues(pubKey, val.PubKey()) + vAddr, err := val.Address() + require.Nil(err) + + pKey, err := val.PubKey() + require.Nil(err) + + assert.EqualValues(addrBytes, vAddr) + assert.EqualValues(pubKey, pKey) assert.EqualValues(privKey, val.PrivKey) // export it and make sure it is the same @@ -98,7 +113,7 @@ func TestUnmarshalValidator(t *testing.T) { } func TestSignVote(t *testing.T) { - assert := assert.New(t) + assert, require := assert.New(t), require.New(t) _, tempFilePath := cmn.Tempfile("priv_validator_") privVal := GenPrivValidatorJSON(tempFilePath) @@ -109,8 +124,11 @@ func TestSignVote(t *testing.T) { voteType := types.VoteTypePrevote // sign a vote for first time - vote := newVote(privVal.Address(), 0, height, round, voteType, block1) - err := privVal.SignVote("mychainid", vote) + addr, err := privVal.Address() + require.Nil(err) + + vote := newVote(addr, 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 @@ -119,10 +137,10 @@ func TestSignVote(t *testing.T) { // now try some bad votes cases := []*types.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 + newVote(addr, 0, height, round-1, voteType, block1), // round regression + newVote(addr, 0, height-1, round, voteType, block1), // height regression + newVote(addr, 0, height-2, round+4, voteType, block1), // height regression and different round + newVote(addr, 0, height, round, voteType, block2), // different block } for _, c := range cases { @@ -179,6 +197,8 @@ func TestSignProposal(t *testing.T) { } func TestDifferByTimestamp(t *testing.T) { + require := require.New(t) + _, tempFilePath := cmn.Tempfile("priv_validator_") privVal := GenPrivValidatorJSON(tempFilePath) @@ -208,10 +228,13 @@ func TestDifferByTimestamp(t *testing.T) { // test vote { + addr, err := privVal.Address() + require.Nil(err) + voteType := types.VoteTypePrevote blockID := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}} - vote := newVote(privVal.Address(), 0, height, round, voteType, blockID) - err := privVal.SignVote("mychainid", vote) + vote := newVote(addr, 0, height, round, voteType, blockID) + err = privVal.SignVote("mychainid", vote) assert.NoError(t, err, "expected no error signing vote") signBytes := types.SignBytes(chainID, vote) diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index 1ca3b7d1b..ae94371a9 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -86,23 +86,28 @@ func (pvsc *PrivValidatorSocketClient) OnStop() { } // Address is an alias for PubKey().Address(). -func (pvsc *PrivValidatorSocketClient) Address() cmn.HexBytes { - return pvsc.PubKey().Address() +func (pvsc *PrivValidatorSocketClient) Address() (cmn.HexBytes, error) { + p, err := pvsc.PubKey() + if err != nil { + return nil, err + } + + return p.Address(), nil } // PubKey implements PrivValidator. -func (pvsc *PrivValidatorSocketClient) PubKey() crypto.PubKey { +func (pvsc *PrivValidatorSocketClient) PubKey() (crypto.PubKey, error) { err := writeMsg(pvsc.conn, &PubKeyMsg{}) if err != nil { - panic(err) + return crypto.PubKey{}, err } res, err := readMsg(pvsc.conn) if err != nil { - panic(err) + return crypto.PubKey{}, err } - return res.(*PubKeyMsg).PubKey + return res.(*PubKeyMsg).PubKey, nil } // SignVote implements PrivValidator. @@ -316,7 +321,10 @@ func (pvss *PrivValidatorSocketServer) handleConnection(conn net.Conn) { switch r := req.(type) { case *PubKeyMsg: - res = &PubKeyMsg{pvss.privVal.PubKey()} + var p crypto.PubKey + + p, err = pvss.privVal.PubKey() + res = &PubKeyMsg{p} case *SignVoteMsg: err = pvss.privVal.SignVote(pvss.chainID, r.Vote) res = &SignVoteMsg{r.Vote} diff --git a/types/priv_validator/socket_test.go b/types/priv_validator/socket_test.go index 627d4fb18..9a290abb4 100644 --- a/types/priv_validator/socket_test.go +++ b/types/priv_validator/socket_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" - cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" "github.com/tendermint/tendermint/types" @@ -51,8 +50,21 @@ func TestPrivValidatorSocketServer(t *testing.T) { assert.True(pvsc.IsRunning()) - assert.Equal(pvsc.Address(), cmn.HexBytes(pvss.privVal.PubKey().Address())) - assert.Equal(pvsc.PubKey(), pvss.privVal.PubKey()) + cAddr, err := pvsc.Address() + require.Nil(err) + + sAddr, err := pvss.privVal.Address() + require.Nil(err) + + assert.Equal(cAddr, sAddr) + + cKey, err := pvsc.PubKey() + require.Nil(err) + + sKey, err := pvss.privVal.PubKey() + require.Nil(err) + + assert.Equal(cKey, sKey) err = pvsc.SignProposal(chainID, &types.Proposal{ Timestamp: time.Now(), @@ -104,8 +116,21 @@ func TestPrivValidatorSocketServerWithoutSecret(t *testing.T) { assert.True(pvsc.IsRunning()) - assert.Equal(pvsc.Address(), cmn.HexBytes(pvss.privVal.PubKey().Address())) - assert.Equal(pvsc.PubKey(), pvss.privVal.PubKey()) + cAddr, err := pvsc.Address() + require.Nil(err) + + sAddr, err := pvss.privVal.Address() + require.Nil(err) + + assert.Equal(cAddr, sAddr) + + cKey, err := pvsc.PubKey() + require.Nil(err) + + sKey, err := pvss.privVal.PubKey() + require.Nil(err) + + assert.Equal(cKey, sKey) err = pvsc.SignProposal(chainID, &types.Proposal{ Timestamp: time.Now(), diff --git a/types/priv_validator/unencrypted.go b/types/priv_validator/unencrypted.go index 483f57c11..3ef38eeca 100644 --- a/types/priv_validator/unencrypted.go +++ b/types/priv_validator/unencrypted.go @@ -35,15 +35,20 @@ func NewPrivValidatorUnencrypted(priv crypto.PrivKey) *PrivValidatorUnencrypted // String returns a string representation of the PrivValidatorUnencrypted func (upv *PrivValidatorUnencrypted) String() string { - return fmt.Sprintf("PrivValidator{%v %v}", upv.Address(), upv.LastSignedInfo.String()) + addr, err := upv.Address() + if err != nil { + panic(err) + } + + return fmt.Sprintf("PrivValidator{%v %v}", addr, upv.LastSignedInfo.String()) } -func (upv *PrivValidatorUnencrypted) Address() cmn.HexBytes { - return upv.PrivKey.PubKey().Address() +func (upv *PrivValidatorUnencrypted) Address() (cmn.HexBytes, error) { + return upv.PrivKey.PubKey().Address(), nil } -func (upv *PrivValidatorUnencrypted) PubKey() crypto.PubKey { - return upv.PrivKey.PubKey() +func (upv *PrivValidatorUnencrypted) PubKey() (crypto.PubKey, error) { + return upv.PrivKey.PubKey(), nil } func (upv *PrivValidatorUnencrypted) SignVote(chainID string, vote *types.Vote) error { From 6c70b4ce056088614789fd60b5a33ee6be137d5e Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Wed, 14 Feb 2018 02:25:17 +0100 Subject: [PATCH 15/18] Apply connection deadline consistently --- types/priv_validator/socket.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/types/priv_validator/socket.go b/types/priv_validator/socket.go index ae94371a9..a6b4b87bf 100644 --- a/types/priv_validator/socket.go +++ b/types/priv_validator/socket.go @@ -18,6 +18,7 @@ import ( ) const ( + connDeadlineSeconds = 3 dialRetryIntervalSeconds = 1 dialRetryMax = 10 ) @@ -27,6 +28,10 @@ var ( ErrDialRetryMax = errors.New("Error max client retries") ) +var ( + connDeadline = time.Second * connDeadlineSeconds +) + //----------------------------------------------------------------- var _ types.PrivValidator2 = (*PrivValidatorSocketClient)(nil) @@ -175,7 +180,7 @@ RETRY_LOOP: conn, err := cmn.Connect(pvsc.SocketAddress) if err != nil { pvsc.Logger.Error( - "OnStart", + "pvsc connect", "addr", pvsc.SocketAddress, "err", errors.Wrap(err, "connection failed"), ) @@ -183,11 +188,19 @@ RETRY_LOOP: continue RETRY_LOOP } + if err := conn.SetDeadline(time.Now().Add(connDeadline)); err != nil { + pvsc.Logger.Error( + "pvsc connect", + "err", errors.Wrap(err, "setting connection timeout failed"), + ) + continue + } + if pvsc.privKey != nil { conn, err = p2pconn.MakeSecretConnection(conn, pvsc.privKey.Wrap()) if err != nil { pvsc.Logger.Error( - "OnStart", + "pvsc connect", "err", errors.Wrap(err, "encrypting connection failed"), ) @@ -278,7 +291,7 @@ func (pvss *PrivValidatorSocketServer) acceptConnections() { continue } - if err := conn.SetDeadline(time.Now().Add(time.Second)); err != nil { + if err := conn.SetDeadline(time.Now().Add(connDeadline)); err != nil { pvss.Logger.Error( "acceptConnetions", "err", errors.Wrap(err, "setting connection timeout failed"), From a1020307a021b26448018bfe3e632a4ca6dc02dc Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Wed, 14 Feb 2018 02:41:16 +0100 Subject: [PATCH 16/18] Clean up flags --- cmd/priv_val_server/main.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index 904447c09..25a8bd4c3 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -13,23 +13,29 @@ import ( func main() { var ( chainID = flag.String("chain-id", "mychain", "chain id") - numClients = flag.Int("clients", 1, "number of concurrently connected clients") + listenAddr = flag.String("laddr", ":46659", "Validator listen address (0.0.0.0:0 means any interface, any port") + maxClients = flag.Int("clients", 3, "number of concurrently connected clients") privValPath = flag.String("priv", "", "priv val file path") - socketAddr = flag.String("socket.addr", ":46659", "socket bind addr") logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "priv_val") ) flag.Parse() - logger.Info("Reading args privValidatorSocketServer", "chainID", *chainID, "privPath", *privValPath) + logger.Info( + "Starting private validator", + "chainID", *chainID, + "listenAddr", *listenAddr, + "maxClients", *maxClients, + "privPath", *privValPath, + ) privVal := priv_val.LoadPrivValidatorJSON(*privValPath) pvss := priv_val.NewPrivValidatorSocketServer( logger, *chainID, - *socketAddr, - *numClients, + *listenAddr, + *maxClients, privVal, nil, ) From 106d804357e0c4435e948e8c0104d7d58202570a Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Wed, 14 Feb 2018 02:51:05 +0100 Subject: [PATCH 17/18] Correct config description --- cmd/tendermint/commands/run_node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index bebef5edc..619a94b1d 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -15,7 +15,7 @@ func AddNodeFlags(cmd *cobra.Command) { cmd.Flags().String("moniker", config.Moniker, "Node Name") // priv val flags - cmd.Flags().String("priv_validator_addr", config.PrivValidatorAddr, "Socket address for PrivValidator") + cmd.Flags().String("priv_validator_addr", config.PrivValidatorAddr, "Socket address for private validator") // node flags cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing") From a14aab67dec3a8f3e81bfc75bbc019bb06b2ff73 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Mon, 19 Feb 2018 19:20:01 +0100 Subject: [PATCH 18/18] Integrate PrivValidator socket server --- cmd/priv_val_server/main.go | 6 ++--- cmd/tendermint/commands/run_node.go | 1 + config/config.go | 36 +++++++++++++++------------ node/node.go | 38 +++++++++++------------------ 4 files changed, 39 insertions(+), 42 deletions(-) diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index 25a8bd4c3..57c3355f9 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -14,7 +14,7 @@ func main() { var ( chainID = flag.String("chain-id", "mychain", "chain id") listenAddr = flag.String("laddr", ":46659", "Validator listen address (0.0.0.0:0 means any interface, any port") - maxClients = flag.Int("clients", 3, "number of concurrently connected clients") + maxConn = flag.Int("clients", 3, "maximum of concurrent connections") privValPath = flag.String("priv", "", "priv val file path") logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "priv_val") @@ -25,7 +25,7 @@ func main() { "Starting private validator", "chainID", *chainID, "listenAddr", *listenAddr, - "maxClients", *maxClients, + "maxConn", *maxConn, "privPath", *privValPath, ) @@ -35,7 +35,7 @@ func main() { logger, *chainID, *listenAddr, - *maxClients, + *maxConn, privVal, nil, ) diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 619a94b1d..481aa0729 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -16,6 +16,7 @@ func AddNodeFlags(cmd *cobra.Command) { // priv val flags cmd.Flags().String("priv_validator_addr", config.PrivValidatorAddr, "Socket address for private validator") + cmd.Flags().Int("priv_validator_max_conn", config.PrivValidatorMaxConn, "Limit of concurrent connections to the PrivValidator") // node flags cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing") diff --git a/config/config.go b/config/config.go index 6a882185f..bdafa7a62 100644 --- a/config/config.go +++ b/config/config.go @@ -20,9 +20,11 @@ var ( defaultConfigFileName = "config.toml" defaultGenesisJSONName = "genesis.json" - defaultPrivValName = "priv_validator.json" - defaultNodeKeyName = "node_key.json" - defaultAddrBookName = "addrbook.json" + + defaultPrivValName = "priv_validator.json" + defaultPrivValMaxConn = 3 + defaultNodeKeyName = "node_key.json" + defaultAddrBookName = "addrbook.json" defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName) defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName) @@ -106,6 +108,9 @@ type BaseConfig struct { // TCP or UNIX socket address of the PrivValidator server PrivValidatorAddr string `mapstructure:"priv_validator_addr"` + // Limit of concurrent connections to the PrivValidator. + PrivValidatorMaxConn int `mapstructure:"priv_validator_max_conn"` + // TCP or UNIX socket address of the ABCI application, // or the name of an ABCI application compiled in with the Tendermint binary ProxyApp string `mapstructure:"proxy_app"` @@ -142,18 +147,19 @@ func (c BaseConfig) ChainID() string { // DefaultBaseConfig returns a default base configuration for a Tendermint node func DefaultBaseConfig() BaseConfig { return BaseConfig{ - Genesis: defaultGenesisJSONPath, - PrivValidator: defaultPrivValPath, - NodeKey: defaultNodeKeyPath, - Moniker: defaultMoniker, - ProxyApp: "tcp://127.0.0.1:46658", - ABCI: "socket", - LogLevel: DefaultPackageLogLevels(), - ProfListenAddress: "", - FastSync: true, - FilterPeers: false, - DBBackend: "leveldb", - DBPath: "data", + Genesis: defaultGenesisJSONPath, + PrivValidator: defaultPrivValPath, + PrivValidatorMaxConn: defaultPrivValMaxConn, + NodeKey: defaultNodeKeyPath, + Moniker: defaultMoniker, + ProxyApp: "tcp://127.0.0.1:46658", + ABCI: "socket", + LogLevel: DefaultPackageLogLevels(), + ProfListenAddress: "", + FastSync: true, + FilterPeers: false, + DBBackend: "leveldb", + DBPath: "data", } } diff --git a/node/node.go b/node/node.go index 64e8c02c1..4a0afd852 100644 --- a/node/node.go +++ b/node/node.go @@ -34,6 +34,7 @@ import ( "github.com/tendermint/tendermint/state/txindex/kv" "github.com/tendermint/tendermint/state/txindex/null" "github.com/tendermint/tendermint/types" + priv_val "github.com/tendermint/tendermint/types/priv_validator" "github.com/tendermint/tendermint/version" _ "net/http/pprof" @@ -77,20 +78,8 @@ type NodeProvider func(*cfg.Config, log.Logger) (*Node, error) // PrivValidator, ClientCreator, GenesisDoc, and DBProvider. // It implements NodeProvider. func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { - var privVal types.PrivValidator - privVal = types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()) - /* TODO - if config.PrivValidatorAddr != "" { - pvsc := priv_val.NewPrivValidatorSocketClient(logger.With("module", "priv_val"), - config.PrivValidatorAddr) - pvsc.Start() - privVal = pvsc - } - */ - fmt.Println("PRIV", config.PrivValidatorAddr) - return NewNode(config, - privVal, + types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()), proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), DefaultGenesisDocProviderFunc(config), DefaultDBProvider, @@ -184,20 +173,21 @@ func NewNode(config *cfg.Config, // reload the state (it may have been updated by the handshake) state = sm.LoadState(stateDB) - /* TODO - // Generate node PrivKey - privKey := crypto.GenPrivKeyEd25519() - if config.PrivValidatorAddr != "" { - pvsc := priv_val.NewPrivValidatorSocketClient( - logger.With("module", "priv_val"), - config.PrivValidatorAddr, - &privKey, + var ( + privKey = crypto.GenPrivKeyEd25519() + pvss = priv_val.NewPrivValidatorSocketServer( + logger.With("module", "priv_val"), + config.ChainID(), + config.PrivValidatorAddr, + config.PrivValidatorMaxConn, + priv_val.LoadPrivValidatorJSON(config.PrivValidatorFile()), + &privKey, + ) ) - pvsc.Start() - privValidator = pvsc + + pvss.Start() } - */ // Decide whether to fast-sync or not // We don't fast-sync when the only validator is us.