OSDN Git Service

docs(release note): update bytom version 1.1.0 release note
[bytom/bytom.git] / p2p / peer.go
index 89e0a28..5c0a07b 100644 (file)
@@ -2,262 +2,219 @@ package p2p
 
 import (
        "fmt"
-       "io"
        "net"
+       "reflect"
+       "strconv"
        "time"
 
+       "github.com/btcsuite/go-socks/socks"
        "github.com/pkg/errors"
        log "github.com/sirupsen/logrus"
-       crypto "github.com/tendermint/go-crypto"
-       wire "github.com/tendermint/go-wire"
+       "github.com/tendermint/go-crypto"
+       "github.com/tendermint/go-wire"
        cmn "github.com/tendermint/tmlibs/common"
-)
+       "github.com/tendermint/tmlibs/flowrate"
 
-// 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
+       cfg "github.com/bytom/bytom/config"
+       "github.com/bytom/bytom/consensus"
+       "github.com/bytom/bytom/p2p/connection"
+)
 
+// peerConn contains the raw connection and its config.
+type peerConn struct {
        outbound bool
-
-       conn  net.Conn     // source connection
-       mconn *MConnection // multiplex connection
-
-       persistent bool
-       config     *PeerConfig
-
-       *NodeInfo
-       Key  string
-       Data *cmn.CMap // User data.
+       config   *PeerConfig
+       conn     net.Conn // source connection
 }
 
 // PeerConfig is a Peer configuration.
 type PeerConfig struct {
-       AuthEnc bool `mapstructure:"auth_enc"` // authenticated encryption
-
-       // times are in seconds
-       HandshakeTimeout time.Duration `mapstructure:"handshake_timeout"`
-       DialTimeout      time.Duration `mapstructure:"dial_timeout"`
-
-       MConfig *MConnConfig `mapstructure:"connection"`
-
-       Fuzz       bool            `mapstructure:"fuzz"` // fuzz connection (for testing)
-       FuzzConfig *FuzzConnConfig `mapstructure:"fuzz_config"`
+       HandshakeTimeout time.Duration           `mapstructure:"handshake_timeout"` // times are in seconds
+       DialTimeout      time.Duration           `mapstructure:"dial_timeout"`
+       ProxyAddress     string                  `mapstructure:"proxy_address"`
+       ProxyUsername    string                  `mapstructure:"proxy_username"`
+       ProxyPassword    string                  `mapstructure:"proxy_password"`
+       MConfig          *connection.MConnConfig `mapstructure:"connection"`
 }
 
 // DefaultPeerConfig returns the default config.
-func DefaultPeerConfig() *PeerConfig {
+func DefaultPeerConfig(config *cfg.P2PConfig) *PeerConfig {
        return &PeerConfig{
-               AuthEnc:          true,
-               HandshakeTimeout: 20, // * time.Second,
-               DialTimeout:      3,  // * time.Second,
-               MConfig:          DefaultMConnConfig(),
-               Fuzz:             false,
-               FuzzConfig:       DefaultFuzzConnConfig(),
+               HandshakeTimeout: time.Duration(config.HandshakeTimeout) * time.Second, // * time.Second,
+               DialTimeout:      time.Duration(config.DialTimeout) * time.Second,      // * time.Second,
+               ProxyAddress:     config.ProxyAddress,
+               ProxyUsername:    config.ProxyUsername,
+               ProxyPassword:    config.ProxyPassword,
+               MConfig:          connection.DefaultMConnConfig(),
        }
 }
 
-func newOutboundPeer(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519) (*Peer, error) {
-       return newOutboundPeerWithConfig(addr, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig())
+// Peer represent a bytom network node
+type Peer struct {
+       cmn.BaseService
+       *NodeInfo
+       *peerConn
+       mconn *connection.MConnection // multiplex connection
+       Key   string
+       isLAN bool
+}
+
+// OnStart implements BaseService.
+func (p *Peer) OnStart() error {
+       p.BaseService.OnStart()
+       _, err := p.mconn.Start()
+       return err
+}
+
+// OnStop implements BaseService.
+func (p *Peer) OnStop() {
+       p.BaseService.OnStop()
+       p.mconn.Stop()
+}
+
+func newPeer(pc *peerConn, nodeInfo *NodeInfo, reactorsByCh map[byte]Reactor, chDescs []*connection.ChannelDescriptor, onPeerError func(*Peer, interface{}), isLAN bool) *Peer {
+       // Key and NodeInfo are set after Handshake
+       p := &Peer{
+               peerConn: pc,
+               NodeInfo: nodeInfo,
+               Key:      nodeInfo.PubKey.KeyString(),
+               isLAN:    isLAN,
+       }
+       p.mconn = createMConnection(pc.conn, p, reactorsByCh, chDescs, onPeerError, pc.config.MConfig)
+       p.BaseService = *cmn.NewBaseService(nil, "Peer", p)
+       return p
 }
 
-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, 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, 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) (*Peer, error) {
-       return newInboundPeerWithConfig(conn, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig())
+       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, ourNodePrivKey crypto.PrivKeyEd25519, config *cfg.P2PConfig) (*peerConn, error) {
+       return newPeerConn(conn, false, 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) {
-       conn := rawConn
-
-       // Fuzz connection
-       if config.Fuzz {
-               // so we have time to do peer handshakes and get set up
-               conn = FuzzConnAfterFromConfig(conn, 10*time.Second, config.FuzzConfig)
-       }
-
-       // Encrypt connection
-       if config.AuthEnc {
-               conn.SetDeadline(time.Now().Add(config.HandshakeTimeout * time.Second))
-
-               var err error
-               conn, err = MakeSecretConnection(conn, ourNodePrivKey)
-               if err != nil {
-                       return nil, errors.Wrap(err, "Error creating peer")
-               }
+func newPeerConn(rawConn net.Conn, outbound bool, ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*peerConn, error) {
+       rawConn.SetDeadline(time.Now().Add(config.HandshakeTimeout))
+       conn, err := connection.MakeSecretConnection(rawConn, ourNodePrivKey)
+       if err != nil {
+               return nil, errors.Wrap(err, "Error creating peer")
        }
 
-       // Key and NodeInfo are set after Handshake
-       p := &Peer{
+       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()
+// Addr returns peer's remote network address.
+func (p *Peer) Addr() net.Addr {
+       return p.conn.RemoteAddr()
 }
 
-// makePersistent marks the peer as persistent.
-func (p *Peer) makePersistent() {
-       if !p.outbound {
-               panic("inbound peers can't be made persistent")
+// CanSend returns true if the send queue is not full, false otherwise.
+func (p *Peer) CanSend(chID byte) bool {
+       if !p.IsRunning() {
+               return false
        }
+       return p.mconn.CanSend(chID)
+}
 
-       p.persistent = true
+// CloseConn should be used when the peer was created, but never started.
+func (pc *peerConn) CloseConn() {
+       pc.conn.Close()
 }
 
-// IsPersistent returns true if the peer is persitent, false otherwise.
-func (p *Peer) IsPersistent() bool {
-       return p.persistent
+// Equals reports whenever 2 peers are actually represent the same node.
+func (p *Peer) Equals(other *Peer) bool {
+       return p.Key == other.Key
 }
 
 // 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))
+       if err := pc.conn.SetDeadline(time.Now().Add(timeout)); err != nil {
+               return nil, err
+       }
 
        var peerNodeInfo = new(NodeInfo)
-       var err1 error
-       var err2 error
+       var err1, 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)
-                       log.WithField("peerNodeInfo", peerNodeInfo).Info("Peer handshake")
+                       wire.ReadBinary(peerNodeInfo, pc.conn, maxNodeInfoSize, &n, &err2)
+                       log.WithFields(log.Fields{"module": logModule, "address": pc.conn.RemoteAddr().String()}).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
-}
-
-// 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.PubKeyEd25519 {
-       if p.config.AuthEnc {
-               return p.conn.(*SecretConnection).RemotePubKey()
-       }
-       if p.NodeInfo == nil {
-               panic("Attempt to get peer's PubKey before calling Handshake")
+       if err := pc.conn.SetDeadline(time.Time{}); err != nil {
+               return nil, err
        }
-       return p.PubKey()
+       peerNodeInfo.RemoteAddr = pc.conn.RemoteAddr().String()
+       return peerNodeInfo, nil
 }
 
-// OnStart implements BaseService.
-func (p *Peer) OnStart() error {
-       p.BaseService.OnStart()
-       _, err := p.mconn.Start()
-       return err
+// ID return the uuid of the peer
+func (p *Peer) ID() string {
+       return p.Key
 }
 
-// OnStop implements BaseService.
-func (p *Peer) OnStop() {
-       p.BaseService.OnStop()
-       p.mconn.Stop()
+// IsOutbound returns true if the connection is outbound, false otherwise.
+func (p *Peer) IsOutbound() bool {
+       return p.outbound
 }
 
-// Connection returns underlying MConnection.
-func (p *Peer) Connection() *MConnection {
-       return p.mconn
+// IsLAN returns true if peer is LAN peer, false otherwise.
+func (p *Peer) IsLAN() bool {
+       return p.isLAN
 }
 
-// IsOutbound returns true if the connection is outbound, false otherwise.
-func (p *Peer) IsOutbound() bool {
-       return p.outbound
+// PubKey returns peer's public key.
+func (p *Peer) PubKey() crypto.PubKeyEd25519 {
+       return p.conn.(*connection.SecretConnection).RemotePubKey()
 }
 
 // Send msg to the channel identified by chID byte. Returns false if the send
 // queue is full after timeout, specified by MConnection.
 func (p *Peer) Send(chID byte, msg interface{}) bool {
        if !p.IsRunning() {
-               // see Switch#Broadcast, where we fetch the list of peers and loop over
-               // them - while we're looping, one peer may be removed and stopped.
                return false
        }
        return p.mconn.Send(chID, msg)
 }
 
-// TrySend msg to the channel identified by chID byte. Immediately returns
-// false if the send queue is full.
-func (p *Peer) TrySend(chID byte, msg interface{}) bool {
-       if !p.IsRunning() {
-               return false
+// ServiceFlag return the ServiceFlag of this peer
+func (p *Peer) ServiceFlag() consensus.ServiceFlag {
+       services := consensus.SFFullNode
+       if len(p.Other) == 0 {
+               return services
        }
-       return p.mconn.TrySend(chID, msg)
-}
 
-// CanSend returns true if the send queue is not full, false otherwise.
-func (p *Peer) CanSend(chID byte) bool {
-       if !p.IsRunning() {
-               return false
+       if serviceFlag, err := strconv.ParseUint(p.Other[0], 10, 64); err == nil {
+               services = consensus.ServiceFlag(serviceFlag)
        }
-       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
+       return services
 }
 
 // String representation.
@@ -265,29 +222,31 @@ func (p *Peer) String() string {
        if p.outbound {
                return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.Key[:12])
        }
-
        return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.Key[:12])
 }
 
-// Equals reports whenever 2 peers are actually represent the same node.
-func (p *Peer) Equals(other *Peer) bool {
-       return p.Key == other.Key
+// TrafficStatus return the in and out traffic status
+func (p *Peer) TrafficStatus() (*flowrate.Status, *flowrate.Status) {
+       return p.mconn.TrafficStatus()
 }
 
-// Get the data for a given key.
-func (p *Peer) Get(key string) interface{} {
-       return p.Data.Get(key)
-}
-
-func dial(addr *NetAddress, config *PeerConfig) (net.Conn, error) {
-       conn, err := addr.DialTimeout(config.DialTimeout * time.Second)
-       if err != nil {
-               return nil, err
+// TrySend msg to the channel identified by chID byte. Immediately returns
+// false if the send queue is full.
+func (p *Peer) TrySend(chID byte, msg interface{}) bool {
+       if !p.IsRunning() {
+               return false
        }
-       return conn, nil
+
+       log.WithFields(log.Fields{
+               "module": logModule,
+               "peer":   p.Addr(),
+               "msg":    msg,
+               "type":   reflect.TypeOf(msg),
+       }).Info("send message to peer")
+       return p.mconn.TrySend(chID, msg)
 }
 
-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 {
@@ -299,6 +258,25 @@ func createMConnection(conn net.Conn, p *Peer, reactorsByCh map[byte]Reactor, ch
        onError := func(r interface{}) {
                onPeerError(p, r)
        }
+       return connection.NewMConnectionWithConfig(conn, chDescs, onReceive, onError, config)
+}
 
-       return NewMConnectionWithConfig(conn, chDescs, onReceive, onError, config)
+func dial(addr *NetAddress, config *PeerConfig) (net.Conn, error) {
+       var conn net.Conn
+       var err error
+       if config.ProxyAddress == "" {
+               conn, err = addr.DialTimeout(config.DialTimeout)
+       } else {
+               proxy := &socks.Proxy{
+                       Addr:         config.ProxyAddress,
+                       Username:     config.ProxyUsername,
+                       Password:     config.ProxyPassword,
+                       TorIsolation: false,
+               }
+               conn, err = addr.DialTimeoutWithProxy(proxy, config.DialTimeout)
+       }
+       if err != nil {
+               return nil, err
+       }
+       return conn, nil
 }