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.
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 {
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
}