import (
"fmt"
- "io"
"net"
"time"
cmn "github.com/tendermint/tmlibs/common"
cfg "github.com/bytom/config"
+ "github.com/bytom/p2p/connection"
)
-// Peer could be marked as persistent, in which case you can use
-// Redial function to reconnect. Note that inbound peers can't be
-// made persistent. They should be made persistent on the other end.
-//
-// Before using a peer, you will need to perform a handshake on connection.
-type Peer struct {
- cmn.BaseService
-
+// peerConn contains the raw connection and its config.
+type peerConn struct {
outbound bool
+ config *PeerConfig
+ conn net.Conn // source connection
+}
- conn net.Conn // source connection
- mconn *MConnection // multiplex connection
+// Peer represent a bytom network node
+type Peer struct {
+ cmn.BaseService
- persistent bool
- config *PeerConfig
+ // raw peerConn and the multiplex connection
+ *peerConn
+ mconn *connection.MConnection // multiplex connection
*NodeInfo
Key string
HandshakeTimeout time.Duration `mapstructure:"handshake_timeout"`
DialTimeout time.Duration `mapstructure:"dial_timeout"`
- MConfig *MConnConfig `mapstructure:"connection"`
+ MConfig *connection.MConnConfig `mapstructure:"connection"`
Fuzz bool `mapstructure:"fuzz"` // fuzz connection (for testing)
FuzzConfig *FuzzConnConfig `mapstructure:"fuzz_config"`
return &PeerConfig{
AuthEnc: true,
HandshakeTimeout: time.Duration(config.HandshakeTimeout), // * time.Second,
- DialTimeout: time.Duration(config.DialTimeout), // * time.Second,
- MConfig: DefaultMConnConfig(),
+ DialTimeout: time.Duration(config.DialTimeout), // * time.Second,
+ MConfig: connection.DefaultMConnConfig(),
Fuzz: false,
FuzzConfig: DefaultFuzzConnConfig(),
}
}
-func newOutboundPeer(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *cfg.P2PConfig) (*Peer, error) {
- return newOutboundPeerWithConfig(addr, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig(config))
+func newPeer(pc *peerConn, nodeInfo *NodeInfo, reactorsByCh map[byte]Reactor, chDescs []*connection.ChannelDescriptor, onPeerError func(*Peer, interface{})) *Peer {
+ // Key and NodeInfo are set after Handshake
+ p := &Peer{
+ peerConn: pc,
+ NodeInfo: nodeInfo,
+
+ Data: cmn.NewCMap(),
+ }
+ p.Key = nodeInfo.PubKey.KeyString()
+ p.mconn = createMConnection(pc.conn, p, reactorsByCh, chDescs, onPeerError, pc.config.MConfig)
+
+ p.BaseService = *cmn.NewBaseService(nil, "Peer", p)
+ return p
+}
+
+func newOutboundPeer(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*connection.ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *cfg.P2PConfig) (*peerConn, error) {
+ return newOutboundPeerConn(addr, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig(config))
}
-func newOutboundPeerWithConfig(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) {
+func newOutboundPeerConn(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*connection.ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*peerConn, error) {
conn, err := dial(addr, config)
if err != nil {
- return nil, errors.Wrap(err, "Error creating peer")
+ return nil, errors.Wrap(err, "Error dial peer")
}
- peer, err := newPeerFromConnAndConfig(conn, true, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config)
+ pc, err := newPeerConn(conn, true, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config)
if err != nil {
conn.Close()
return nil, err
}
- return peer, nil
-}
-func newInboundPeer(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *cfg.P2PConfig) (*Peer, error) {
- return newInboundPeerWithConfig(conn, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig(config))
+ return pc, nil
}
-func newInboundPeerWithConfig(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) {
- return newPeerFromConnAndConfig(conn, false, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config)
+func newInboundPeerConn(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*connection.ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *cfg.P2PConfig) (*peerConn, error) {
+ return newPeerConn(conn, false, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig(config))
}
-func newPeerFromConnAndConfig(rawConn net.Conn, outbound bool, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) {
+func newPeerConn(rawConn net.Conn, outbound bool, reactorsByCh map[byte]Reactor, chDescs []*connection.ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*peerConn, error) {
conn := rawConn
// Fuzz connection
conn.SetDeadline(time.Now().Add(config.HandshakeTimeout * time.Second))
var err error
- conn, err = MakeSecretConnection(conn, ourNodePrivKey)
+ conn, err = connection.MakeSecretConnection(conn, ourNodePrivKey)
if err != nil {
return nil, errors.Wrap(err, "Error creating peer")
}
}
- // Key and NodeInfo are set after Handshake
- p := &Peer{
+ // Only the information we already have
+ return &peerConn{
+ config: config,
outbound: outbound,
conn: conn,
- 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
+ }, nil
}
// CloseConn should be used when the peer was created, but never started.
-func (p *Peer) CloseConn() {
- p.conn.Close()
-}
-
-// makePersistent marks the peer as persistent.
-func (p *Peer) makePersistent() {
- if !p.outbound {
- panic("inbound peers can't be made persistent")
- }
-
- p.persistent = true
-}
-
-// IsPersistent returns true if the peer is persitent, false otherwise.
-func (p *Peer) IsPersistent() bool {
- return p.persistent
+func (pc *peerConn) CloseConn() {
+ pc.conn.Close()
}
// HandshakeTimeout performs a handshake between a given node and the peer.
// NOTE: blocking
-func (p *Peer) HandshakeTimeout(ourNodeInfo *NodeInfo, timeout time.Duration) error {
+func (pc *peerConn) HandshakeTimeout(ourNodeInfo *NodeInfo, timeout time.Duration) (*NodeInfo, error) {
// Set deadline for handshake so we don't block forever on conn.ReadFull
- p.conn.SetDeadline(time.Now().Add(timeout))
+ pc.conn.SetDeadline(time.Now().Add(timeout))
var peerNodeInfo = new(NodeInfo)
var err1 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)
+ wire.ReadBinary(peerNodeInfo, pc.conn, maxNodeInfoSize, &n, &err2)
log.WithField("peerNodeInfo", peerNodeInfo).Info("Peer handshake")
})
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")
- }
-
- if p.config.AuthEnc {
- // Check that the professed PubKey matches the sconn's.
- if !peerNodeInfo.PubKey.Equals(p.PubKey().Wrap()) {
- return fmt.Errorf("Ignoring connection with unmatching pubkey: %v vs %v",
- peerNodeInfo.PubKey, p.PubKey())
- }
+ return peerNodeInfo, errors.Wrap(err2, "Error during handshake/read")
}
// Remove deadline
- p.conn.SetDeadline(time.Time{})
-
- peerNodeInfo.RemoteAddr = p.Addr().String()
-
- p.NodeInfo = peerNodeInfo
- p.Key = peerNodeInfo.PubKey.KeyString()
-
- return nil
+ pc.conn.SetDeadline(time.Time{})
+ peerNodeInfo.RemoteAddr = pc.conn.RemoteAddr().String()
+ return peerNodeInfo, nil
}
// Addr returns peer's remote network address.
// PubKey returns peer's public key.
func (p *Peer) PubKey() crypto.PubKeyEd25519 {
if p.config.AuthEnc {
- return p.conn.(*SecretConnection).RemotePubKey()
+ return p.conn.(*connection.SecretConnection).RemotePubKey()
}
if p.NodeInfo == nil {
panic("Attempt to get peer's PubKey before calling Handshake")
}
// Connection returns underlying MConnection.
-func (p *Peer) Connection() *MConnection {
+func (p *Peer) Connection() *connection.MConnection {
return p.mconn
}
return p.mconn.CanSend(chID)
}
-// WriteTo writes the peer's public key to w.
-func (p *Peer) WriteTo(w io.Writer) (n int64, err error) {
- var n_ int
- wire.WriteString(p.Key, w, &n_, &err)
- n += int64(n_)
- return
-}
-
// String representation.
func (p *Peer) String() string {
if p.outbound {
return conn, nil
}
-func createMConnection(conn net.Conn, p *Peer, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), config *MConnConfig) *MConnection {
+func createMConnection(conn net.Conn, p *Peer, reactorsByCh map[byte]Reactor, chDescs []*connection.ChannelDescriptor, onPeerError func(*Peer, interface{}), config *connection.MConnConfig) *connection.MConnection {
onReceive := func(chID byte, msgBytes []byte) {
reactor := reactorsByCh[chID]
if reactor == nil {
onPeerError(p, r)
}
- return NewMConnectionWithConfig(conn, chDescs, onReceive, onError, config)
+ return connection.NewMConnectionWithConfig(conn, chDescs, onReceive, onError, config)
}