p2p: introduce peerConn to simplify peer creation (#1226)
* expose AuthEnc in the P2P config if AuthEnc is true, dialed peers must have a node ID in the address and it must match the persistent pubkey from the secret handshake. Refs #1157 * fixes after my own review * fix docs * fix build failure ``` p2p/pex/pex_reactor_test.go:288:88: cannot use seed.NodeInfo().NetAddress() (type *p2p.NetAddress) as type string in array or slice literal ``` * p2p: introduce peerConn to simplify peer creation * Introduce `peerConn` containing the known fields of `peer` * `peer` only created in `sw.addPeer` once handshake is complete and NodeInfo is checked * Eliminates some mutable variables and makes the code flow better * Simplifies the `newXxxPeer` funcs * Use ID instead of PubKey where possible. * SetPubKeyFilter -> SetIDFilter * nodeInfo.Validate takes ID * remove peer.PubKey() * persistent node ids * fixes from review * test: use ip_plus_id.sh more * fix invalid memory panic during fast_sync test ``` 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: panic: runtime error: invalid memory address or nil pointer dereference 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: [signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x98dd3e] 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: goroutine 3432 [running]: 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.newOutboundPeerConn(0xc423fd1380, 0xc420933e00, 0x1, 0x1239a60, 0 xc420128c40, 0x2, 0x42caf6, 0xc42001f300, 0xc422831d98, 0xc4227951c0, ...) 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/peer.go:123 +0x31e 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).addOutboundPeerWithConfig(0xc4200ad040, 0xc423fd1380, 0 xc420933e00, 0xc423f48801, 0x28, 0x2) 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:455 +0x12b 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).DialPeerWithAddress(0xc4200ad040, 0xc423fd1380, 0x1, 0x 0, 0x0) 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:371 +0xdc 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).reconnectToPeer(0xc4200ad040, 0x123e000, 0xc42007bb00) 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:290 +0x25f 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: created by github.com/tendermint/tendermint/p2p.(*Switch).StopPeerForError 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:256 +0x1b7 ```
This commit is contained in:
parent
2fd023a239
commit
9293ae76bf
|
@ -25,6 +25,12 @@ BUG FIXES:
|
|||
- Graceful handling/recovery for apps that have non-determinism or fail to halt
|
||||
- Graceful handling/recovery for violations of safety, or liveness
|
||||
|
||||
## 0.16.1 (TBD)
|
||||
|
||||
IMPROVEMENTS:
|
||||
- [config] exposed `auth_enc` flag to enable/disable encryption
|
||||
- [p2p] when `auth_enc` is true, all dialed peers must have a node ID in their address
|
||||
|
||||
## 0.16.0 (February 20th, 2017)
|
||||
|
||||
BREAKING CHANGES:
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
)
|
||||
|
||||
// ShowNodeIDCmd dumps node's ID to the standard output.
|
||||
var ShowNodeIDCmd = &cobra.Command{
|
||||
Use: "show_node_id",
|
||||
Short: "Show this node's ID",
|
||||
RunE: showNodeID,
|
||||
}
|
||||
|
||||
func showNodeID(cmd *cobra.Command, args []string) error {
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(nodeKey.ID())
|
||||
return nil
|
||||
}
|
|
@ -24,6 +24,7 @@ func main() {
|
|||
cmd.ResetPrivValidatorCmd,
|
||||
cmd.ShowValidatorCmd,
|
||||
cmd.TestnetFilesCmd,
|
||||
cmd.ShowNodeIDCmd,
|
||||
cmd.VersionCmd)
|
||||
|
||||
// NOTE:
|
||||
|
|
|
@ -281,6 +281,9 @@ type P2PConfig struct {
|
|||
//
|
||||
// Does not work if the peer-exchange reactor is disabled.
|
||||
SeedMode bool `mapstructure:"seed_mode"`
|
||||
|
||||
// Authenticated encryption
|
||||
AuthEnc bool `mapstructure:"auth_enc"`
|
||||
}
|
||||
|
||||
// DefaultP2PConfig returns a default configuration for the peer-to-peer layer
|
||||
|
@ -296,6 +299,7 @@ func DefaultP2PConfig() *P2PConfig {
|
|||
RecvRate: 512000, // 500 kB/s
|
||||
PexReactor: true,
|
||||
SeedMode: false,
|
||||
AuthEnc: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -159,6 +159,9 @@ pex = {{ .P2P.PexReactor }}
|
|||
# Does not work if the peer-exchange reactor is disabled.
|
||||
seed_mode = {{ .P2P.SeedMode }}
|
||||
|
||||
# Authenticated encryption
|
||||
auth_enc = {{ .P2P.AuthEnc }}
|
||||
|
||||
##### mempool configuration options #####
|
||||
[mempool]
|
||||
|
||||
|
|
|
@ -121,6 +121,9 @@ like the file below, however, double check by inspecting the
|
|||
# Does not work if the peer-exchange reactor is disabled.
|
||||
seed_mode = false
|
||||
|
||||
# Authenticated encryption
|
||||
auth_enc = true
|
||||
|
||||
##### mempool configuration options #####
|
||||
[mempool]
|
||||
|
||||
|
|
|
@ -279,8 +279,8 @@ func NewNode(config *cfg.Config,
|
|||
}
|
||||
return nil
|
||||
})
|
||||
sw.SetPubKeyFilter(func(pubkey crypto.PubKey) error {
|
||||
resQuery, err := proxyApp.Query().QuerySync(abci.RequestQuery{Path: cmn.Fmt("/p2p/filter/pubkey/%X", pubkey.Bytes())})
|
||||
sw.SetIDFilter(func(id p2p.ID) error {
|
||||
resQuery, err := proxyApp.Query().QuerySync(abci.RequestQuery{Path: cmn.Fmt("/p2p/filter/pubkey/%s", id)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -34,15 +34,10 @@ type NodeInfo struct {
|
|||
}
|
||||
|
||||
// Validate checks the self-reported NodeInfo is safe.
|
||||
// It returns an error if the info.PubKey doesn't match the given pubKey,
|
||||
// or if there are too many Channels or any duplicate Channels.
|
||||
// It returns an error if there
|
||||
// are too many Channels or any duplicate Channels.
|
||||
// TODO: constraints for Moniker/Other? Or is that for the UI ?
|
||||
func (info NodeInfo) Validate(pubKey crypto.PubKey) error {
|
||||
if !info.PubKey.Equals(pubKey) {
|
||||
return fmt.Errorf("info.PubKey (%v) doesn't match peer.PubKey (%v)",
|
||||
info.PubKey, pubKey)
|
||||
}
|
||||
|
||||
func (info NodeInfo) Validate() error {
|
||||
if len(info.Channels) > maxNumChannels {
|
||||
return fmt.Errorf("info.Channels is too long (%v). Max is %v", len(info.Channels), maxNumChannels)
|
||||
}
|
||||
|
|
162
p2p/peer.go
162
p2p/peer.go
|
@ -34,23 +34,53 @@ type Peer interface {
|
|||
|
||||
//----------------------------------------------------------
|
||||
|
||||
// peerConn contains the raw connection and its config.
|
||||
type peerConn struct {
|
||||
outbound bool
|
||||
persistent bool
|
||||
config *PeerConfig
|
||||
conn net.Conn // source connection
|
||||
}
|
||||
|
||||
// ID only exists for SecretConnection.
|
||||
// NOTE: Will panic if conn is not *SecretConnection.
|
||||
func (pc peerConn) ID() ID {
|
||||
return PubKeyToID(pc.conn.(*tmconn.SecretConnection).RemotePubKey())
|
||||
}
|
||||
|
||||
// peer implements Peer.
|
||||
//
|
||||
// Before using a peer, you will need to perform a handshake on connection.
|
||||
type peer struct {
|
||||
cmn.BaseService
|
||||
|
||||
outbound bool
|
||||
// raw peerConn and the multiplex connection
|
||||
peerConn
|
||||
mconn *tmconn.MConnection
|
||||
|
||||
conn net.Conn // source connection
|
||||
mconn *tmconn.MConnection // multiplex connection
|
||||
// peer's node info and the channel it knows about
|
||||
// channels = nodeInfo.Channels
|
||||
// cached to avoid copying nodeInfo in hasChannel
|
||||
nodeInfo NodeInfo
|
||||
channels []byte
|
||||
|
||||
persistent bool
|
||||
config *PeerConfig
|
||||
// User data
|
||||
Data *cmn.CMap
|
||||
}
|
||||
|
||||
nodeInfo NodeInfo // peer's node info
|
||||
channels []byte // channels the peer knows about
|
||||
Data *cmn.CMap // User data.
|
||||
func newPeer(pc peerConn, nodeInfo NodeInfo,
|
||||
reactorsByCh map[byte]Reactor, chDescs []*tmconn.ChannelDescriptor,
|
||||
onPeerError func(Peer, interface{})) *peer {
|
||||
|
||||
p := &peer{
|
||||
peerConn: pc,
|
||||
nodeInfo: nodeInfo,
|
||||
channels: nodeInfo.Channels,
|
||||
Data: cmn.NewCMap(),
|
||||
}
|
||||
p.mconn = createMConnection(pc.conn, p, reactorsByCh, chDescs, onPeerError, pc.config.MConfig)
|
||||
p.BaseService = *cmn.NewBaseService(nil, "Peer", p)
|
||||
return p
|
||||
}
|
||||
|
||||
// PeerConfig is a Peer configuration.
|
||||
|
@ -79,36 +109,42 @@ func DefaultPeerConfig() *PeerConfig {
|
|||
}
|
||||
}
|
||||
|
||||
func newOutboundPeer(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*tmconn.ChannelDescriptor,
|
||||
onPeerError func(Peer, interface{}), ourNodePrivKey crypto.PrivKey, config *PeerConfig, persistent bool) (*peer, error) {
|
||||
func newOutboundPeerConn(addr *NetAddress, config *PeerConfig, persistent bool, ourNodePrivKey crypto.PrivKey) (peerConn, error) {
|
||||
var pc peerConn
|
||||
|
||||
conn, err := dial(addr, config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error creating peer")
|
||||
return pc, errors.Wrap(err, "Error creating peer")
|
||||
}
|
||||
|
||||
peer, err := newPeerFromConnAndConfig(conn, true, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config)
|
||||
pc, err = newPeerConn(conn, config, true, persistent, ourNodePrivKey)
|
||||
if err != nil {
|
||||
if err := conn.Close(); err != nil {
|
||||
return nil, err
|
||||
if err2 := conn.Close(); err2 != nil {
|
||||
return pc, errors.Wrap(err, err2.Error())
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
peer.persistent = persistent
|
||||
|
||||
return peer, nil
|
||||
return pc, err
|
||||
}
|
||||
|
||||
func newInboundPeer(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*tmconn.ChannelDescriptor,
|
||||
onPeerError func(Peer, interface{}), ourNodePrivKey crypto.PrivKey, config *PeerConfig) (*peer, error) {
|
||||
// ensure dialed ID matches connection ID
|
||||
if config.AuthEnc && addr.ID != pc.ID() {
|
||||
if err2 := conn.Close(); err2 != nil {
|
||||
return pc, errors.Wrap(err, err2.Error())
|
||||
}
|
||||
return pc, ErrSwitchAuthenticationFailure{addr, pc.ID()}
|
||||
}
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
func newInboundPeerConn(conn net.Conn, config *PeerConfig, ourNodePrivKey crypto.PrivKey) (peerConn, error) {
|
||||
|
||||
// TODO: issue PoW challenge
|
||||
|
||||
return newPeerFromConnAndConfig(conn, false, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config)
|
||||
return newPeerConn(conn, config, false, false, ourNodePrivKey)
|
||||
}
|
||||
|
||||
func newPeerFromConnAndConfig(rawConn net.Conn, outbound bool, reactorsByCh map[byte]Reactor, chDescs []*tmconn.ChannelDescriptor,
|
||||
onPeerError func(Peer, interface{}), ourNodePrivKey crypto.PrivKey, config *PeerConfig) (*peer, error) {
|
||||
func newPeerConn(rawConn net.Conn,
|
||||
config *PeerConfig, outbound, persistent bool,
|
||||
ourNodePrivKey crypto.PrivKey) (pc peerConn, err error) {
|
||||
|
||||
conn := rawConn
|
||||
|
||||
|
@ -118,32 +154,26 @@ func newPeerFromConnAndConfig(rawConn net.Conn, outbound bool, reactorsByCh map[
|
|||
conn = FuzzConnAfterFromConfig(conn, 10*time.Second, config.FuzzConfig)
|
||||
}
|
||||
|
||||
// Encrypt connection
|
||||
if config.AuthEnc {
|
||||
// Set deadline for secret handshake
|
||||
if err := conn.SetDeadline(time.Now().Add(config.HandshakeTimeout * time.Second)); err != nil {
|
||||
return nil, errors.Wrap(err, "Error setting deadline while encrypting connection")
|
||||
return pc, errors.Wrap(err, "Error setting deadline while encrypting connection")
|
||||
}
|
||||
|
||||
var err error
|
||||
// Encrypt connection
|
||||
conn, err = tmconn.MakeSecretConnection(conn, ourNodePrivKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error creating peer")
|
||||
return pc, errors.Wrap(err, "Error creating peer")
|
||||
}
|
||||
}
|
||||
|
||||
// NodeInfo is set after Handshake
|
||||
p := &peer{
|
||||
outbound: outbound,
|
||||
conn: conn,
|
||||
// Only the information we already have
|
||||
return peerConn{
|
||||
config: config,
|
||||
Data: cmn.NewCMap(),
|
||||
}
|
||||
|
||||
p.mconn = createMConnection(conn, p, reactorsByCh, chDescs, onPeerError, config.MConfig)
|
||||
|
||||
p.BaseService = *cmn.NewBaseService(nil, "Peer", p)
|
||||
|
||||
return p, nil
|
||||
outbound: outbound,
|
||||
persistent: persistent,
|
||||
conn: conn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//---------------------------------------------------
|
||||
|
@ -175,17 +205,17 @@ func (p *peer) OnStop() {
|
|||
|
||||
// ID returns the peer's ID - the hex encoded hash of its pubkey.
|
||||
func (p *peer) ID() ID {
|
||||
return PubKeyToID(p.PubKey())
|
||||
return p.nodeInfo.ID()
|
||||
}
|
||||
|
||||
// IsOutbound returns true if the connection is outbound, false otherwise.
|
||||
func (p *peer) IsOutbound() bool {
|
||||
return p.outbound
|
||||
return p.peerConn.outbound
|
||||
}
|
||||
|
||||
// IsPersistent returns true if the peer is persitent, false otherwise.
|
||||
func (p *peer) IsPersistent() bool {
|
||||
return p.persistent
|
||||
return p.peerConn.persistent
|
||||
}
|
||||
|
||||
// NodeInfo returns a copy of the peer's NodeInfo.
|
||||
|
@ -250,68 +280,48 @@ func (p *peer) hasChannel(chID byte) bool {
|
|||
// methods used by the Switch
|
||||
|
||||
// CloseConn should be called by the Switch if the peer was created but never started.
|
||||
func (p *peer) CloseConn() {
|
||||
p.conn.Close() // nolint: errcheck
|
||||
func (pc *peerConn) CloseConn() {
|
||||
pc.conn.Close() // nolint: errcheck
|
||||
}
|
||||
|
||||
// HandshakeTimeout performs the Tendermint P2P handshake between a given node and the peer
|
||||
// by exchanging their NodeInfo. It sets the received nodeInfo on the peer.
|
||||
// NOTE: blocking
|
||||
func (p *peer) HandshakeTimeout(ourNodeInfo NodeInfo, timeout time.Duration) error {
|
||||
func (pc *peerConn) HandshakeTimeout(ourNodeInfo NodeInfo, timeout time.Duration) (peerNodeInfo NodeInfo, err error) {
|
||||
// Set deadline for handshake so we don't block forever on conn.ReadFull
|
||||
if err := p.conn.SetDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return errors.Wrap(err, "Error setting deadline")
|
||||
if err := pc.conn.SetDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return peerNodeInfo, errors.Wrap(err, "Error setting deadline")
|
||||
}
|
||||
|
||||
var peerNodeInfo NodeInfo
|
||||
var err1 error
|
||||
var err2 error
|
||||
cmn.Parallel(
|
||||
func() {
|
||||
var n int
|
||||
wire.WriteBinary(&ourNodeInfo, p.conn, &n, &err1)
|
||||
wire.WriteBinary(&ourNodeInfo, pc.conn, &n, &err1)
|
||||
},
|
||||
func() {
|
||||
var n int
|
||||
wire.ReadBinary(&peerNodeInfo, p.conn, MaxNodeInfoSize(), &n, &err2)
|
||||
p.Logger.Info("Peer handshake", "peerNodeInfo", peerNodeInfo)
|
||||
wire.ReadBinary(&peerNodeInfo, pc.conn, MaxNodeInfoSize(), &n, &err2)
|
||||
})
|
||||
if err1 != nil {
|
||||
return errors.Wrap(err1, "Error during handshake/write")
|
||||
return peerNodeInfo, errors.Wrap(err1, "Error during handshake/write")
|
||||
}
|
||||
if err2 != nil {
|
||||
return errors.Wrap(err2, "Error during handshake/read")
|
||||
return peerNodeInfo, errors.Wrap(err2, "Error during handshake/read")
|
||||
}
|
||||
|
||||
// Remove deadline
|
||||
if err := p.conn.SetDeadline(time.Time{}); err != nil {
|
||||
return errors.Wrap(err, "Error removing deadline")
|
||||
if err := pc.conn.SetDeadline(time.Time{}); err != nil {
|
||||
return peerNodeInfo, errors.Wrap(err, "Error removing deadline")
|
||||
}
|
||||
|
||||
p.setNodeInfo(peerNodeInfo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *peer) setNodeInfo(nodeInfo NodeInfo) {
|
||||
p.nodeInfo = nodeInfo
|
||||
// cache the channels so we dont copy nodeInfo
|
||||
// every time we check hasChannel
|
||||
p.channels = nodeInfo.Channels
|
||||
return peerNodeInfo, nil
|
||||
}
|
||||
|
||||
// Addr returns peer's remote network address.
|
||||
func (p *peer) Addr() net.Addr {
|
||||
return p.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
// PubKey returns peer's public key.
|
||||
func (p *peer) PubKey() crypto.PubKey {
|
||||
if !p.nodeInfo.PubKey.Empty() {
|
||||
return p.nodeInfo.PubKey
|
||||
} else if p.config.AuthEnc {
|
||||
return p.conn.(*tmconn.SecretConnection).RemotePubKey()
|
||||
}
|
||||
panic("Attempt to get peer's PubKey before calling Handshake")
|
||||
return p.peerConn.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
// CanSend returns true if the send queue is not full, false otherwise.
|
||||
|
|
|
@ -13,10 +13,11 @@ import (
|
|||
|
||||
// Returns an empty dummy peer
|
||||
func randPeer() *peer {
|
||||
pubKey := crypto.GenPrivKeyEd25519().Wrap().PubKey()
|
||||
return &peer{
|
||||
nodeInfo: NodeInfo{
|
||||
ListenAddr: cmn.Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256),
|
||||
PubKey: crypto.GenPrivKeyEd25519().Wrap().PubKey(),
|
||||
PubKey: pubKey,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
tmconn "github.com/tendermint/tendermint/p2p/conn"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
const testCh = 0x01
|
||||
|
@ -35,8 +36,8 @@ func TestPeerBasic(t *testing.T) {
|
|||
assert.False(p.IsPersistent())
|
||||
p.persistent = true
|
||||
assert.True(p.IsPersistent())
|
||||
assert.Equal(rp.Addr().String(), p.Addr().String())
|
||||
assert.Equal(rp.PubKey(), p.PubKey())
|
||||
assert.Equal(rp.Addr().DialString(), p.Addr().String())
|
||||
assert.Equal(rp.ID(), p.ID())
|
||||
}
|
||||
|
||||
func TestPeerWithoutAuthEnc(t *testing.T) {
|
||||
|
@ -89,11 +90,11 @@ func createOutboundPeerAndPerformHandshake(addr *NetAddress, config *PeerConfig)
|
|||
}
|
||||
reactorsByCh := map[byte]Reactor{testCh: NewTestReactor(chDescs, true)}
|
||||
pk := crypto.GenPrivKeyEd25519().Wrap()
|
||||
p, err := newOutboundPeer(addr, reactorsByCh, chDescs, func(p Peer, r interface{}) {}, pk, config, false)
|
||||
pc, err := newOutboundPeerConn(addr, config, false, pk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = p.HandshakeTimeout(NodeInfo{
|
||||
nodeInfo, err := pc.HandshakeTimeout(NodeInfo{
|
||||
PubKey: pk.PubKey(),
|
||||
Moniker: "host_peer",
|
||||
Network: "testing",
|
||||
|
@ -103,6 +104,9 @@ func createOutboundPeerAndPerformHandshake(addr *NetAddress, config *PeerConfig)
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := newPeer(pc, nodeInfo, reactorsByCh, chDescs, func(p Peer, r interface{}) {})
|
||||
p.SetLogger(log.TestingLogger().With("peer", addr))
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
@ -117,8 +121,8 @@ func (p *remotePeer) Addr() *NetAddress {
|
|||
return p.addr
|
||||
}
|
||||
|
||||
func (p *remotePeer) PubKey() crypto.PubKey {
|
||||
return p.PrivKey.PubKey()
|
||||
func (p *remotePeer) ID() ID {
|
||||
return PubKeyToID(p.PrivKey.PubKey())
|
||||
}
|
||||
|
||||
func (p *remotePeer) Start() {
|
||||
|
@ -126,7 +130,7 @@ func (p *remotePeer) Start() {
|
|||
if e != nil {
|
||||
golog.Fatalf("net.Listen tcp :0: %+v", e)
|
||||
}
|
||||
p.addr = NewNetAddress("", l.Addr())
|
||||
p.addr = NewNetAddress(PubKeyToID(p.PrivKey.PubKey()), l.Addr())
|
||||
p.quit = make(chan struct{})
|
||||
go p.accept(l)
|
||||
}
|
||||
|
@ -141,11 +145,11 @@ func (p *remotePeer) accept(l net.Listener) {
|
|||
if err != nil {
|
||||
golog.Fatalf("Failed to accept conn: %+v", err)
|
||||
}
|
||||
peer, err := newInboundPeer(conn, make(map[byte]Reactor), make([]*tmconn.ChannelDescriptor, 0), func(p Peer, r interface{}) {}, p.PrivKey, p.Config)
|
||||
pc, err := newInboundPeerConn(conn, p.Config, p.PrivKey)
|
||||
if err != nil {
|
||||
golog.Fatalf("Failed to create a peer: %+v", err)
|
||||
}
|
||||
err = peer.HandshakeTimeout(NodeInfo{
|
||||
_, err = pc.HandshakeTimeout(NodeInfo{
|
||||
PubKey: p.PrivKey.PubKey(),
|
||||
Moniker: "remote_peer",
|
||||
Network: "testing",
|
||||
|
|
|
@ -323,8 +323,9 @@ func (r *PEXReactor) ensurePeers() {
|
|||
// Dial picked addresses
|
||||
for _, item := range toDial {
|
||||
go func(picked *p2p.NetAddress) {
|
||||
_, err := r.Switch.DialPeerWithAddress(picked, false)
|
||||
err := r.Switch.DialPeerWithAddress(picked, false)
|
||||
if err != nil {
|
||||
r.Logger.Error("Dialing failed", "err", err)
|
||||
// TODO: detect more "bad peer" scenarios
|
||||
if _, ok := err.(p2p.ErrSwitchAuthenticationFailure); ok {
|
||||
r.book.MarkBad(picked)
|
||||
|
@ -381,13 +382,11 @@ func (r *PEXReactor) dialSeeds() {
|
|||
for _, i := range perm {
|
||||
// dial a random seed
|
||||
seedAddr := seedAddrs[i]
|
||||
peer, err := r.Switch.DialPeerWithAddress(seedAddr, false)
|
||||
if err != nil {
|
||||
r.Switch.Logger.Error("Error dialing seed", "err", err, "seed", seedAddr)
|
||||
} else {
|
||||
r.Switch.Logger.Info("Connected to seed", "peer", peer)
|
||||
err := r.Switch.DialPeerWithAddress(seedAddr, false)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
r.Switch.Logger.Error("Error dialing seed", "err", err, "seed", seedAddr)
|
||||
}
|
||||
r.Switch.Logger.Error("Couldn't connect to any seeds")
|
||||
}
|
||||
|
@ -470,7 +469,7 @@ func (r *PEXReactor) crawlPeers() {
|
|||
continue
|
||||
}
|
||||
// Otherwise, attempt to connect with the known address
|
||||
_, err := r.Switch.DialPeerWithAddress(pi.Addr, false)
|
||||
err := r.Switch.DialPeerWithAddress(pi.Addr, false)
|
||||
if err != nil {
|
||||
r.book.MarkAttempt(pi.Addr)
|
||||
continue
|
||||
|
|
|
@ -100,7 +100,7 @@ func TestPEXReactorRunning(t *testing.T) {
|
|||
|
||||
// fill the address book and add listeners
|
||||
for _, s := range switches {
|
||||
addr, _ := p2p.NewNetAddressString(s.NodeInfo().ListenAddr)
|
||||
addr := s.NodeInfo().NetAddress()
|
||||
book.AddAddress(addr, addr)
|
||||
s.AddListener(p2p.NewDefaultListener("tcp", s.NodeInfo().ListenAddr, true, log.TestingLogger()))
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) {
|
|||
sw := p2p.MakeSwitch(config, 1, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch {
|
||||
sw.SetLogger(log.TestingLogger())
|
||||
|
||||
r := NewPEXReactor(book, &PEXReactorConfig{Seeds: []string{seed.NodeInfo().ListenAddr}})
|
||||
r := NewPEXReactor(book, &PEXReactorConfig{Seeds: []string{seed.NodeInfo().NetAddress().String()}})
|
||||
r.SetLogger(log.TestingLogger())
|
||||
r.SetEnsurePeersPeriod(250 * time.Millisecond)
|
||||
sw.AddReactor("pex", r)
|
||||
|
|
134
p2p/switch.go
134
p2p/switch.go
|
@ -10,7 +10,6 @@ import (
|
|||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/p2p/conn"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
|
@ -60,7 +59,7 @@ type Switch struct {
|
|||
nodeKey *NodeKey // our node privkey
|
||||
|
||||
filterConnByAddr func(net.Addr) error
|
||||
filterConnByPubKey func(crypto.PubKey) error
|
||||
filterConnByID func(ID) error
|
||||
|
||||
rng *rand.Rand // seed for randomizing dial times and orders
|
||||
}
|
||||
|
@ -85,6 +84,7 @@ func NewSwitch(config *cfg.P2PConfig) *Switch {
|
|||
sw.peerConfig.MConfig.SendRate = config.SendRate
|
||||
sw.peerConfig.MConfig.RecvRate = config.RecvRate
|
||||
sw.peerConfig.MConfig.MaxMsgPacketPayloadSize = config.MaxMsgPacketPayloadSize
|
||||
sw.peerConfig.AuthEnc = config.AuthEnc
|
||||
|
||||
sw.BaseService = *cmn.NewBaseService(nil, "P2P Switch", sw)
|
||||
return sw
|
||||
|
@ -287,14 +287,13 @@ func (sw *Switch) reconnectToPeer(peer Peer) {
|
|||
return
|
||||
}
|
||||
|
||||
peer, err := sw.DialPeerWithAddress(netAddr, true)
|
||||
err := sw.DialPeerWithAddress(netAddr, true)
|
||||
if err != nil {
|
||||
sw.Logger.Info("Error reconnecting to peer. Trying again", "tries", i, "err", err, "peer", peer)
|
||||
// sleep a set amount
|
||||
sw.randomSleep(reconnectInterval)
|
||||
continue
|
||||
} else {
|
||||
sw.Logger.Info("Reconnected to peer", "peer", peer)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -309,14 +308,11 @@ func (sw *Switch) reconnectToPeer(peer Peer) {
|
|||
// sleep an exponentially increasing amount
|
||||
sleepIntervalSeconds := math.Pow(reconnectBackOffBaseSeconds, float64(i))
|
||||
sw.randomSleep(time.Duration(sleepIntervalSeconds) * time.Second)
|
||||
peer, err := sw.DialPeerWithAddress(netAddr, true)
|
||||
if err != nil {
|
||||
sw.Logger.Info("Error reconnecting to peer. Trying again", "tries", i, "err", err, "peer", peer)
|
||||
continue
|
||||
} else {
|
||||
sw.Logger.Info("Reconnected to peer", "peer", peer)
|
||||
return
|
||||
err := sw.DialPeerWithAddress(netAddr, true)
|
||||
if err == nil {
|
||||
return // success
|
||||
}
|
||||
sw.Logger.Info("Error reconnecting to peer. Trying again", "tries", i, "err", err, "peer", peer)
|
||||
}
|
||||
sw.Logger.Error("Failed to reconnect to peer. Giving up", "peer", peer, "elapsed", time.Since(start))
|
||||
}
|
||||
|
@ -358,11 +354,9 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b
|
|||
go func(i int) {
|
||||
sw.randomSleep(0)
|
||||
j := perm[i]
|
||||
peer, err := sw.DialPeerWithAddress(netAddrs[j], persistent)
|
||||
err := sw.DialPeerWithAddress(netAddrs[j], persistent)
|
||||
if err != nil {
|
||||
sw.Logger.Error("Error dialing peer", "err", err)
|
||||
} else {
|
||||
sw.Logger.Info("Connected to peer", "peer", peer)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
@ -371,7 +365,7 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b
|
|||
|
||||
// DialPeerWithAddress dials the given peer and runs sw.addPeer if it connects and authenticates successfully.
|
||||
// If `persistent == true`, the switch will always try to reconnect to this peer if the connection ever fails.
|
||||
func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) (Peer, error) {
|
||||
func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) error {
|
||||
sw.dialing.Set(string(addr.ID), addr)
|
||||
defer sw.dialing.Delete(string(addr.ID))
|
||||
return sw.addOutboundPeerWithConfig(addr, sw.peerConfig, persistent)
|
||||
|
@ -394,10 +388,10 @@ func (sw *Switch) FilterConnByAddr(addr net.Addr) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// FilterConnByPubKey returns an error if connecting to the given public key is forbidden.
|
||||
func (sw *Switch) FilterConnByPubKey(pubkey crypto.PubKey) error {
|
||||
if sw.filterConnByPubKey != nil {
|
||||
return sw.filterConnByPubKey(pubkey)
|
||||
// FilterConnByID returns an error if connecting to the given peer ID is forbidden.
|
||||
func (sw *Switch) FilterConnByID(id ID) error {
|
||||
if sw.filterConnByID != nil {
|
||||
return sw.filterConnByID(id)
|
||||
}
|
||||
return nil
|
||||
|
||||
|
@ -408,9 +402,9 @@ func (sw *Switch) SetAddrFilter(f func(net.Addr) error) {
|
|||
sw.filterConnByAddr = f
|
||||
}
|
||||
|
||||
// SetPubKeyFilter sets the function for filtering connections by public key.
|
||||
func (sw *Switch) SetPubKeyFilter(f func(crypto.PubKey) error) {
|
||||
sw.filterConnByPubKey = f
|
||||
// SetIDFilter sets the function for filtering connections by peer ID.
|
||||
func (sw *Switch) SetIDFilter(f func(ID) error) {
|
||||
sw.filterConnByID = f
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
@ -441,14 +435,13 @@ func (sw *Switch) listenerRoutine(l Listener) {
|
|||
}
|
||||
|
||||
func (sw *Switch) addInboundPeerWithConfig(conn net.Conn, config *PeerConfig) error {
|
||||
peer, err := newInboundPeer(conn, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodeKey.PrivKey, config)
|
||||
peerConn, err := newInboundPeerConn(conn, config, sw.nodeKey.PrivKey)
|
||||
if err != nil {
|
||||
conn.Close() // peer is nil
|
||||
return err
|
||||
}
|
||||
peer.SetLogger(sw.Logger.With("peer", conn.RemoteAddr()))
|
||||
if err = sw.addPeer(peer); err != nil {
|
||||
peer.CloseConn()
|
||||
if err = sw.addPeer(peerConn); err != nil {
|
||||
peerConn.CloseConn()
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -457,31 +450,20 @@ func (sw *Switch) addInboundPeerWithConfig(conn net.Conn, config *PeerConfig) er
|
|||
|
||||
// dial the peer; make secret connection; authenticate against the dialed ID;
|
||||
// add the peer.
|
||||
func (sw *Switch) addOutboundPeerWithConfig(addr *NetAddress, config *PeerConfig, persistent bool) (Peer, error) {
|
||||
func (sw *Switch) addOutboundPeerWithConfig(addr *NetAddress, config *PeerConfig, persistent bool) error {
|
||||
sw.Logger.Info("Dialing peer", "address", addr)
|
||||
peer, err := newOutboundPeer(addr, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodeKey.PrivKey, config, persistent)
|
||||
peerConn, err := newOutboundPeerConn(addr, config, persistent, sw.nodeKey.PrivKey)
|
||||
if err != nil {
|
||||
sw.Logger.Error("Failed to dial peer", "address", addr, "err", err)
|
||||
return nil, err
|
||||
}
|
||||
peer.SetLogger(sw.Logger.With("peer", addr))
|
||||
|
||||
// authenticate peer
|
||||
if addr.ID == "" {
|
||||
peer.Logger.Info("Dialed peer with unknown ID - unable to authenticate", "addr", addr)
|
||||
} else if addr.ID != peer.ID() {
|
||||
peer.CloseConn()
|
||||
return nil, ErrSwitchAuthenticationFailure{addr, peer.ID()}
|
||||
return err
|
||||
}
|
||||
|
||||
err = sw.addPeer(peer)
|
||||
if err != nil {
|
||||
if err := sw.addPeer(peerConn); err != nil {
|
||||
sw.Logger.Error("Failed to add peer", "address", addr, "err", err)
|
||||
peer.CloseConn()
|
||||
return nil, err
|
||||
peerConn.CloseConn()
|
||||
return err
|
||||
}
|
||||
sw.Logger.Info("Dialed and added peer", "address", addr, "peer", peer)
|
||||
return peer, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// addPeer performs the Tendermint P2P handshake with a peer
|
||||
|
@ -489,41 +471,65 @@ func (sw *Switch) addOutboundPeerWithConfig(addr *NetAddress, config *PeerConfig
|
|||
// it starts the peer and adds it to the switch.
|
||||
// NOTE: This performs a blocking handshake before the peer is added.
|
||||
// NOTE: If error is returned, caller is responsible for calling peer.CloseConn()
|
||||
func (sw *Switch) addPeer(peer *peer) error {
|
||||
func (sw *Switch) addPeer(pc peerConn) error {
|
||||
|
||||
addr := pc.conn.RemoteAddr()
|
||||
if err := sw.FilterConnByAddr(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// NOTE: if AuthEnc==false, we don't have a peerID until after the handshake.
|
||||
// If AuthEnc==true then we already know the ID and could do the checks first before the handshake,
|
||||
// but it's simple to just deal with both cases the same after the handshake.
|
||||
|
||||
// Exchange NodeInfo on the conn
|
||||
peerNodeInfo, err := pc.HandshakeTimeout(sw.nodeInfo, time.Duration(sw.peerConfig.HandshakeTimeout*time.Second))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peerID := peerNodeInfo.ID()
|
||||
|
||||
// ensure connection key matches self reported key
|
||||
if pc.config.AuthEnc {
|
||||
connID := pc.ID()
|
||||
|
||||
if peerID != connID {
|
||||
return fmt.Errorf("nodeInfo.ID() (%v) doesn't match conn.ID() (%v)",
|
||||
peerID, connID)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the peers nodeInfo
|
||||
if err := peerNodeInfo.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Avoid self
|
||||
if sw.nodeKey.ID() == peer.ID() {
|
||||
if sw.nodeKey.ID() == peerID {
|
||||
return ErrSwitchConnectToSelf
|
||||
}
|
||||
|
||||
// Avoid duplicate
|
||||
if sw.peers.Has(peer.ID()) {
|
||||
if sw.peers.Has(peerID) {
|
||||
return ErrSwitchDuplicatePeer
|
||||
|
||||
}
|
||||
|
||||
// Filter peer against white list
|
||||
if err := sw.FilterConnByAddr(peer.Addr()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sw.FilterConnByPubKey(peer.PubKey()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Exchange NodeInfo with the peer
|
||||
if err := peer.HandshakeTimeout(sw.nodeInfo, time.Duration(sw.peerConfig.HandshakeTimeout*time.Second)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate the peers nodeInfo against the pubkey
|
||||
if err := peer.NodeInfo().Validate(peer.PubKey()); err != nil {
|
||||
// Filter peer against ID white list
|
||||
if err := sw.FilterConnByID(peerID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check version, chain id
|
||||
if err := sw.nodeInfo.CompatibleWith(peer.NodeInfo()); err != nil {
|
||||
if err := sw.nodeInfo.CompatibleWith(peerNodeInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peer := newPeer(pc, peerNodeInfo, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError)
|
||||
peer.SetLogger(sw.Logger.With("peer", addr))
|
||||
|
||||
peer.Logger.Info("Successful handshake with peer", "peerNodeInfo", peerNodeInfo)
|
||||
|
||||
// All good. Start peer
|
||||
if sw.IsRunning() {
|
||||
sw.startInitPeer(peer)
|
||||
|
|
|
@ -192,7 +192,7 @@ func assertNoPeersAfterTimeout(t *testing.T, sw *Switch, timeout time.Duration)
|
|||
}
|
||||
}
|
||||
|
||||
func TestConnPubKeyFilter(t *testing.T) {
|
||||
func TestConnIDFilter(t *testing.T) {
|
||||
s1 := MakeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
|
||||
s2 := MakeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
|
||||
defer s1.Stop()
|
||||
|
@ -200,15 +200,20 @@ func TestConnPubKeyFilter(t *testing.T) {
|
|||
|
||||
c1, c2 := conn.NetPipe()
|
||||
|
||||
// set pubkey filter
|
||||
s1.SetPubKeyFilter(func(pubkey crypto.PubKey) error {
|
||||
if bytes.Equal(pubkey.Bytes(), s2.nodeInfo.PubKey.Bytes()) {
|
||||
s1.SetIDFilter(func(id ID) error {
|
||||
if id == PubKeyToID(s2.nodeInfo.PubKey) {
|
||||
return fmt.Errorf("Error: pipe is blacklisted")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
s2.SetIDFilter(func(id ID) error {
|
||||
if id == PubKeyToID(s1.nodeInfo.PubKey) {
|
||||
return fmt.Errorf("Error: pipe is blacklisted")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// connect to good peer
|
||||
go func() {
|
||||
err := s1.addPeerWithConnection(c1)
|
||||
assert.NotNil(t, err, "expected error")
|
||||
|
@ -237,13 +242,15 @@ func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) {
|
|||
rp.Start()
|
||||
defer rp.Stop()
|
||||
|
||||
peer, err := newOutboundPeer(rp.Addr(), sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodeKey.PrivKey, DefaultPeerConfig(), false)
|
||||
pc, err := newOutboundPeerConn(rp.Addr(), DefaultPeerConfig(), false, sw.nodeKey.PrivKey)
|
||||
require.Nil(err)
|
||||
err = sw.addPeer(peer)
|
||||
err = sw.addPeer(pc)
|
||||
require.Nil(err)
|
||||
|
||||
peer := sw.Peers().Get(rp.ID())
|
||||
|
||||
// simulate failure by closing connection
|
||||
peer.CloseConn()
|
||||
pc.CloseConn()
|
||||
|
||||
assertNoPeersAfterTimeout(t, sw, 100*time.Millisecond)
|
||||
assert.False(peer.IsRunning())
|
||||
|
@ -264,13 +271,16 @@ func TestSwitchReconnectsToPersistentPeer(t *testing.T) {
|
|||
rp.Start()
|
||||
defer rp.Stop()
|
||||
|
||||
peer, err := newOutboundPeer(rp.Addr(), sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodeKey.PrivKey, DefaultPeerConfig(), true)
|
||||
pc, err := newOutboundPeerConn(rp.Addr(), DefaultPeerConfig(), true, sw.nodeKey.PrivKey)
|
||||
// sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodeKey.PrivKey,
|
||||
require.Nil(err)
|
||||
err = sw.addPeer(peer)
|
||||
err = sw.addPeer(pc)
|
||||
require.Nil(err)
|
||||
|
||||
peer := sw.Peers().Get(rp.ID())
|
||||
|
||||
// simulate failure by closing connection
|
||||
peer.CloseConn()
|
||||
pc.CloseConn()
|
||||
|
||||
// TODO: remove sleep, detect the disconnection, wait for reconnect
|
||||
npeers := sw.Peers().Size()
|
||||
|
|
|
@ -19,11 +19,13 @@ func AddPeerToSwitch(sw *Switch, peer Peer) {
|
|||
func CreateRandomPeer(outbound bool) *peer {
|
||||
addr, netAddr := CreateRoutableAddr()
|
||||
p := &peer{
|
||||
peerConn: peerConn{
|
||||
outbound: outbound,
|
||||
},
|
||||
nodeInfo: NodeInfo{
|
||||
ListenAddr: netAddr.DialString(),
|
||||
PubKey: crypto.GenPrivKeyEd25519().Wrap().PubKey(),
|
||||
},
|
||||
outbound: outbound,
|
||||
mconn: &conn.MConnection{},
|
||||
}
|
||||
p.SetLogger(log.TestingLogger().With("peer", addr))
|
||||
|
@ -98,16 +100,15 @@ func Connect2Switches(switches []*Switch, i, j int) {
|
|||
}
|
||||
|
||||
func (sw *Switch) addPeerWithConnection(conn net.Conn) error {
|
||||
peer, err := newInboundPeer(conn, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodeKey.PrivKey, sw.peerConfig)
|
||||
pc, err := newInboundPeerConn(conn, sw.peerConfig, sw.nodeKey.PrivKey)
|
||||
if err != nil {
|
||||
if err := conn.Close(); err != nil {
|
||||
sw.Logger.Error("Error closing connection", "err", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
peer.SetLogger(sw.Logger.With("peer", conn.RemoteAddr()))
|
||||
if err = sw.addPeer(peer); err != nil {
|
||||
peer.CloseConn()
|
||||
if err = sw.addPeer(pc); err != nil {
|
||||
pc.CloseConn()
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.9.2
|
||||
FROM golang:1.9.4
|
||||
|
||||
# Add testing deps for curl
|
||||
RUN echo 'deb http://httpredir.debian.org/debian testing main non-free contrib' >> /etc/apt/sources.list
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
#! /bin/bash
|
||||
# This is a sample bash script for tendermint core
|
||||
# Edit this script before "mintnet start" to change
|
||||
# the core blockchain engine.
|
||||
|
||||
TMREPO="github.com/tendermint/tendermint"
|
||||
BRANCH="master"
|
||||
|
||||
go get -d $TMREPO/cmd/tendermint
|
||||
### DEPENDENCIES (example)
|
||||
# cd $GOPATH/src/github.com/tendermint/abci
|
||||
# git fetch origin $BRANCH
|
||||
# git checkout $BRANCH
|
||||
### DEPENDENCIES END
|
||||
cd $GOPATH/src/$TMREPO
|
||||
git fetch origin $BRANCH
|
||||
git checkout $BRANCH
|
||||
make install
|
||||
|
||||
tendermint node --p2p.seeds="$TMSEEDS" --moniker="$TMNAME" --proxy_app="$PROXYAPP" --rpc.unsafe
|
|
@ -0,0 +1 @@
|
|||
{"priv_key":{"type":"ed25519","data":"06962D169F314ABB9D05AE5A04B46E48F0FBD8F1830149B47493910CBDCA7796096E5B94CD179F545AE3C281D9BF5C9E0E3D8FF719048B62F7849094CFFA8591"}}
|
|
@ -0,0 +1 @@
|
|||
{"priv_key":{"type":"ed25519","data":"B8CE8B0D5138C10208526ABDADCE91C735FCCC4186E06E0972EC35E64973428A45EBC61F24CE1B91B3D26AFBAB11C2789EF04CBAC28183619C01116B66A9C528"}}
|
|
@ -0,0 +1 @@
|
|||
{"priv_key":{"type":"ed25519","data":"913DE8AC6D18922A53F6B0196EF023B4693FECFBB565E084F0B4941768F3DAE892B35ADD954562FE071C465BC244B2AFAED4A270EC849269341473CE192DE682"}}
|
|
@ -0,0 +1 @@
|
|||
{"priv_key":{"type":"ed25519","data":"408226F3F40411AC22262DD9A33BFE27D6FED42B9F084906B3797118C951CB82F81552170A85C94F0608AE8B59B70A0CA8B604A9057585B28A266140DC615E97"}}
|
|
@ -23,9 +23,9 @@ docker rm -vf local_testnet_$ID
|
|||
set -e
|
||||
|
||||
# restart peer - should have an empty blockchain
|
||||
PERSISTENT_PEERS="$(test/p2p/ip.sh 1):46656"
|
||||
PERSISTENT_PEERS="$(test/p2p/ip_plus_id.sh 1 $DOCKER_IMAGE):46656"
|
||||
for j in `seq 2 $N`; do
|
||||
PERSISTENT_PEERS="$PERSISTENT_PEERS,$(test/p2p/ip.sh $j):46656"
|
||||
PERSISTENT_PEERS="$PERSISTENT_PEERS,$(test/p2p/ip_plus_id.sh $j $DOCKER_IMAGE):46656"
|
||||
done
|
||||
bash test/p2p/peer.sh $DOCKER_IMAGE $NETWORK_NAME $ID $PROXY_APP "--p2p.persistent_peers $PERSISTENT_PEERS --p2p.pex --rpc.unsafe"
|
||||
|
||||
|
|
|
@ -3,5 +3,3 @@ set -eu
|
|||
|
||||
ID=$1
|
||||
echo "172.57.0.$((100+$ID))"
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#! /bin/bash
|
||||
set -eu
|
||||
|
||||
ID=$1
|
||||
DOCKER_IMAGE=$2
|
||||
NODEID="$(docker run --rm -e TMHOME=/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$ID/core $DOCKER_IMAGE tendermint show_node_id)"
|
||||
echo "$NODEID@172.57.0.$((100+$ID))"
|
|
@ -2,11 +2,12 @@
|
|||
set -eu
|
||||
|
||||
N=$1
|
||||
DOCKER_IMAGE=$2
|
||||
|
||||
cd "$GOPATH/src/github.com/tendermint/tendermint"
|
||||
|
||||
persistent_peers="$(test/p2p/ip.sh 1):46656"
|
||||
persistent_peers="$(test/p2p/ip_plus_id.sh 1 $DOCKER_IMAGE):46656"
|
||||
for i in $(seq 2 $N); do
|
||||
persistent_peers="$persistent_peers,$(test/p2p/ip.sh $i):46656"
|
||||
persistent_peers="$persistent_peers,$(test/p2p/ip_plus_id.sh $i $DOCKER_IMAGE):46656"
|
||||
done
|
||||
echo "$persistent_peers"
|
||||
|
|
|
@ -2,31 +2,22 @@
|
|||
set -u
|
||||
|
||||
N=$1
|
||||
PEERS=$2
|
||||
|
||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||
cd "$GOPATH/src/github.com/tendermint/tendermint"
|
||||
|
||||
echo "Waiting for nodes to come online"
|
||||
for i in `seq 1 $N`; do
|
||||
addr=$(test/p2p/ip.sh $i):46657
|
||||
curl -s $addr/status > /dev/null
|
||||
for i in $(seq 1 "$N"); do
|
||||
addr=$(test/p2p/ip.sh "$i"):46657
|
||||
curl -s "$addr/status" > /dev/null
|
||||
ERR=$?
|
||||
while [ "$ERR" != 0 ]; do
|
||||
sleep 1
|
||||
curl -s $addr/status > /dev/null
|
||||
curl -s "$addr/status" > /dev/null
|
||||
ERR=$?
|
||||
done
|
||||
echo "... node $i is up"
|
||||
done
|
||||
|
||||
set -e
|
||||
# peers need quotes
|
||||
peers="\"$(test/p2p/ip.sh 1):46656\""
|
||||
for i in `seq 2 $N`; do
|
||||
peers="$peers,\"$(test/p2p/ip.sh $i):46656\""
|
||||
done
|
||||
echo $peers
|
||||
|
||||
echo $peers
|
||||
IP=$(test/p2p/ip.sh 1)
|
||||
curl "$IP:46657/dial_peers?persistent=true&peers=\[$peers\]"
|
||||
|
||||
curl "$IP:46657/dial_peers?persistent=true&peers=\\[$PEERS\\]"
|
||||
|
|
|
@ -21,14 +21,17 @@ set -e
|
|||
|
||||
# start the testnet on a local network
|
||||
# NOTE we re-use the same network for all tests
|
||||
PERSISTENT_PEERS=""
|
||||
bash test/p2p/local_testnet_start.sh $DOCKER_IMAGE $NETWORK_NAME $N $PROXY_APP $PERSISTENT_PEERS
|
||||
|
||||
bash test/p2p/local_testnet_start.sh $DOCKER_IMAGE $NETWORK_NAME $N $PROXY_APP ""
|
||||
|
||||
PERSISTENT_PEERS="\"$(test/p2p/ip_plus_id.sh 1 $DOCKER_IMAGE):46656\""
|
||||
for i in $(seq 2 $N); do
|
||||
PERSISTENT_PEERS="$PERSISTENT_PEERS,\"$(test/p2p/ip_plus_id.sh $i $DOCKER_IMAGE):46656\""
|
||||
done
|
||||
echo "$PERSISTENT_PEERS"
|
||||
|
||||
# dial peers from one node
|
||||
CLIENT_NAME="dial_peers"
|
||||
bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME $CLIENT_NAME "test/p2p/pex/dial_peers.sh $N"
|
||||
bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME $CLIENT_NAME "test/p2p/pex/dial_peers.sh $N $PERSISTENT_PEERS"
|
||||
|
||||
# test basic connectivity and consensus
|
||||
# start client container and check the num peers and height for all nodes
|
||||
|
|
|
@ -13,7 +13,7 @@ set +e
|
|||
bash test/p2p/local_testnet_stop.sh "$NETWORK_NAME" "$N"
|
||||
set -e
|
||||
|
||||
PERSISTENT_PEERS=$(bash test/p2p/persistent_peers.sh $N)
|
||||
PERSISTENT_PEERS=$(bash test/p2p/persistent_peers.sh $N $DOCKER_IMAGE)
|
||||
|
||||
# start the testnet on a local network
|
||||
# NOTE we re-use the same network for all tests
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -130,7 +129,7 @@ func LoadPrivValidatorFS(filePath string) *PrivValidatorFS {
|
|||
// or else generates a new one and saves it to the filePath.
|
||||
func LoadOrGenPrivValidatorFS(filePath string) *PrivValidatorFS {
|
||||
var privVal *PrivValidatorFS
|
||||
if _, err := os.Stat(filePath); err == nil {
|
||||
if cmn.FileExists(filePath) {
|
||||
privVal = LoadPrivValidatorFS(filePath)
|
||||
} else {
|
||||
privVal = GenPrivValidatorFS(filePath)
|
||||
|
|
Loading…
Reference in New Issue