@ -10,10 +10,11 @@ import (
cmn "github.com/tendermint/tmlibs/common"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/tendermint/config"
tmconn "github.com/tendermint/tendermint/p2p/conn"
tmconn "github.com/tendermint/tendermint/p2p/conn"
)
)
var testIPSuffix uint32 = 0
var testIPSuffix uint32
// Peer is an interface representing a peer connected on a reactor.
// Peer is an interface representing a peer connected on a reactor.
type Peer interface {
type Peer interface {
@ -39,7 +40,7 @@ type Peer interface {
type peerConn struct {
type peerConn struct {
outbound bool
outbound bool
persistent bool
persistent bool
config * Peer Config
config * config . P2 PConfig
conn net . Conn // source connection
conn net . Conn // source connection
ip net . IP
ip net . IP
}
}
@ -99,94 +100,95 @@ type peer struct {
Data * cmn . CMap
Data * cmn . CMap
}
}
func newPeer ( pc peerConn , nodeInfo NodeInfo ,
reactorsByCh map [ byte ] Reactor , chDescs [ ] * tmconn . ChannelDescriptor ,
onPeerError func ( Peer , interface { } ) ) * peer {
func newPeer (
pc peerConn ,
nodeInfo NodeInfo ,
reactorsByCh map [ byte ] Reactor ,
chDescs [ ] * tmconn . ChannelDescriptor ,
onPeerError func ( Peer , interface { } ) ,
) * peer {
p := & peer {
p := & peer {
peerConn : pc ,
peerConn : pc ,
nodeInfo : nodeInfo ,
nodeInfo : nodeInfo ,
channels : nodeInfo . Channels ,
channels : nodeInfo . Channels ,
Data : cmn . NewCMap ( ) ,
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.
type PeerConfig struct {
// times are in seconds
HandshakeTimeout time . Duration ` mapstructure:"handshake_timeout" `
DialTimeout time . Duration ` mapstructure:"dial_timeout" `
MConfig * tmconn . MConnConfig ` mapstructure:"connection" `
DialFail bool ` mapstructure:"dial_fail" ` // for testing
Fuzz bool ` mapstructure:"fuzz" ` // fuzz connection (for testing)
FuzzConfig * FuzzConnConfig ` mapstructure:"fuzz_config" `
}
p . mconn = createMConnection (
pc . conn ,
p ,
reactorsByCh ,
chDescs ,
onPeerError ,
pc . config . MConfig ,
)
p . BaseService = * cmn . NewBaseService ( nil , "Peer" , p )
// DefaultPeerConfig returns the default config.
func DefaultPeerConfig ( ) * PeerConfig {
return & PeerConfig {
HandshakeTimeout : 20 , // * time.Second,
DialTimeout : 3 , // * time.Second,
MConfig : tmconn . DefaultMConnConfig ( ) ,
DialFail : false ,
Fuzz : false ,
FuzzConfig : DefaultFuzzConnConfig ( ) ,
}
return p
}
}
func newOutboundPeerConn ( addr * NetAddress , config * PeerConfig , persistent bool , ourNodePrivKey crypto . PrivKey ) ( peerConn , error ) {
var pc peerConn
func newOutboundPeerConn (
addr * NetAddress ,
config * config . P2PConfig ,
persistent bool ,
ourNodePrivKey crypto . PrivKey ,
) ( peerConn , error ) {
conn , err := dial ( addr , config )
conn , err := dial ( addr , config )
if err != nil {
if err != nil {
return pc , cmn . ErrorWrap ( err , "Error creating peer" )
return peerConn { } , cmn . ErrorWrap ( err , "Error creating peer" )
}
}
pc , err = newPeerConn ( conn , config , true , persistent , ourNodePrivKey )
pc , err : = newPeerConn ( conn , config , true , persistent , ourNodePrivKey )
if err != nil {
if err != nil {
if err2 := conn . Close ( ) ; err2 != nil {
return pc , cmn . ErrorWrap ( err , err2 . Error ( ) )
if c err := conn . Close ( ) ; c err != nil {
return peerConn { } , cmn . ErrorWrap ( err , c err. Error ( ) )
}
}
return pc , err
return peerConn { } , err
}
}
// ensure dialed ID matches connection ID
// ensure dialed ID matches connection ID
if addr . ID != pc . ID ( ) {
if addr . ID != pc . ID ( ) {
if err2 := conn . Close ( ) ; err2 != nil {
return pc , cmn . ErrorWrap ( err , err2 . Error ( ) )
if c err := conn . Close ( ) ; c err != nil {
return peerConn { } , cmn . ErrorWrap ( err , c err. Error ( ) )
}
}
return pc , ErrSwitchAuthenticationFailure { addr , pc . ID ( ) }
return peerConn { } , ErrSwitchAuthenticationFailure { addr , pc . ID ( ) }
}
}
return pc , nil
return pc , nil
}
}
func newInboundPeerConn ( conn net . Conn , config * PeerConfig , ourNodePrivKey crypto . PrivKey ) ( peerConn , error ) {
func newInboundPeerConn (
conn net . Conn ,
config * config . P2PConfig ,
ourNodePrivKey crypto . PrivKey ,
) ( peerConn , error ) {
// TODO: issue PoW challenge
// TODO: issue PoW challenge
return newPeerConn ( conn , config , false , false , ourNodePrivKey )
return newPeerConn ( conn , config , false , false , ourNodePrivKey )
}
}
func newPeerConn ( rawConn net . Conn ,
config * PeerConfig , outbound , persistent bool ,
ourNodePrivKey crypto . PrivKey ) ( pc peerConn , err error ) {
func newPeerConn (
rawConn net . Conn ,
cfg * config . P2PConfig ,
outbound , persistent bool ,
ourNodePrivKey crypto . PrivKey ,
) ( pc peerConn , err error ) {
conn := rawConn
conn := rawConn
// Fuzz connection
// Fuzz connection
if con fi g . Fuzz {
if cfg . Test Fuzz {
// so we have time to do peer handshakes and get set up
// so we have time to do peer handshakes and get set up
conn = FuzzConnAfterFromConfig ( conn , 10 * time . Second , con fi g . FuzzConfig )
conn = FuzzConnAfterFromConfig ( conn , 10 * time . Second , cfg . Test FuzzConfig)
}
}
// Set deadline for secret handshake
// Set deadline for secret handshake
if err := conn . SetDeadline ( time . Now ( ) . Add ( config . HandshakeTimeout * time . Second ) ) ; err != nil {
return pc , cmn . ErrorWrap ( err , "Error setting deadline while encrypting connection" )
dl := time . Now ( ) . Add ( cfg . HandshakeTimeout )
if err := conn . SetDeadline ( dl ) ; err != nil {
return pc , cmn . ErrorWrap (
err ,
"Error setting deadline while encrypting connection" ,
)
}
}
// Encrypt connection
// Encrypt connection
@ -197,7 +199,7 @@ func newPeerConn(rawConn net.Conn,
// Only the information we already have
// Only the information we already have
return peerConn {
return peerConn {
config : con fi g ,
config : cfg ,
outbound : outbound ,
outbound : outbound ,
persistent : persistent ,
persistent : persistent ,
conn : conn ,
conn : conn ,
@ -300,22 +302,33 @@ func (p *peer) hasChannel(chID byte) bool {
}
}
// NOTE: probably will want to remove this
// NOTE: probably will want to remove this
// but could be helpful while the feature is new
// but could be helpful while the feature is new
p . Logger . Debug ( "Unknown channel for peer" , "channel" , chID , "channels" , p . channels )
p . Logger . Debug (
"Unknown channel for peer" ,
"channel" ,
chID ,
"channels" ,
p . channels ,
)
return false
return false
}
}
//---------------------------------------------------
//---------------------------------------------------
// methods used by the Switch
// methods used by the Switch
// CloseConn should be called by the Switch if the peer was created but never started.
// CloseConn should be called by the Switch if the peer was created but never
// started.
func ( pc * peerConn ) CloseConn ( ) {
func ( pc * peerConn ) CloseConn ( ) {
pc . conn . Close ( ) // nolint: errcheck
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.
// 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
// NOTE: blocking
func ( pc * peerConn ) HandshakeTimeout ( ourNodeInfo NodeInfo , timeout time . Duration ) ( peerNodeInfo NodeInfo , err 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
// Set deadline for handshake so we don't block forever on conn.ReadFull
if err := pc . conn . SetDeadline ( time . Now ( ) . Add ( timeout ) ) ; err != nil {
if err := pc . conn . SetDeadline ( time . Now ( ) . Add ( timeout ) ) ; err != nil {
return peerNodeInfo , cmn . ErrorWrap ( err , "Error setting deadline" )
return peerNodeInfo , cmn . ErrorWrap ( err , "Error setting deadline" )
@ -327,7 +340,11 @@ func (pc *peerConn) HandshakeTimeout(ourNodeInfo NodeInfo, timeout time.Duration
return
return
} ,
} ,
func ( _ int ) ( val interface { } , err error , abort bool ) {
func ( _ int ) ( val interface { } , err error , abort bool ) {
_ , err = cdc . UnmarshalBinaryReader ( pc . conn , & peerNodeInfo , int64 ( MaxNodeInfoSize ( ) ) )
_ , err = cdc . UnmarshalBinaryReader (
pc . conn ,
& peerNodeInfo ,
int64 ( MaxNodeInfoSize ( ) ) ,
)
return
return
} ,
} ,
)
)
@ -368,20 +385,26 @@ func (p *peer) String() string {
//------------------------------------------------------------------
//------------------------------------------------------------------
// helper funcs
// helper funcs
func dial ( addr * NetAddress , con fi g * Peer Config ) ( net . Conn , error ) {
if con fi g . DialFail {
func dial ( addr * NetAddress , cfg * config . P2 PConfig) ( net . Conn , error ) {
if cfg . Test DialFail {
return nil , fmt . Errorf ( "dial err (peerConfig.DialFail == true)" )
return nil , fmt . Errorf ( "dial err (peerConfig.DialFail == true)" )
}
}
conn , err := addr . DialTimeout ( con fi g . DialTimeout * time . Second )
conn , err := addr . DialTimeout ( cfg . DialTimeout )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
return conn , nil
return conn , nil
}
}
func createMConnection ( conn net . Conn , p * peer , reactorsByCh map [ byte ] Reactor , chDescs [ ] * tmconn . ChannelDescriptor ,
onPeerError func ( Peer , interface { } ) , config * tmconn . MConnConfig ) * tmconn . MConnection {
func createMConnection (
conn net . Conn ,
p * peer ,
reactorsByCh map [ byte ] Reactor ,
chDescs [ ] * tmconn . ChannelDescriptor ,
onPeerError func ( Peer , interface { } ) ,
config tmconn . MConnConfig ,
) * tmconn . MConnection {
onReceive := func ( chID byte , msgBytes [ ] byte ) {
onReceive := func ( chID byte , msgBytes [ ] byte ) {
reactor := reactorsByCh [ chID ]
reactor := reactorsByCh [ chID ]
@ -397,5 +420,11 @@ func createMConnection(conn net.Conn, p *peer, reactorsByCh map[byte]Reactor, ch
onPeerError ( p , r )
onPeerError ( p , r )
}
}
return tmconn . NewMConnectionWithConfig ( conn , chDescs , onReceive , onError , config )
return tmconn . NewMConnectionWithConfig (
conn ,
chDescs ,
onReceive ,
onError ,
config ,
)
}
}