mirror of
https://github.com/btcsuite/btcd.git
synced 2025-03-10 09:19:28 +01:00
peer+wire: add addrv2 message, protocol negotiation
This commit is contained in:
parent
201c0836ec
commit
cb6f21b598
8 changed files with 714 additions and 81 deletions
231
peer/peer.go
231
peer/peer.go
|
@ -29,7 +29,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// MaxProtocolVersion is the max protocol version the peer supports.
|
// MaxProtocolVersion is the max protocol version the peer supports.
|
||||||
MaxProtocolVersion = wire.FeeFilterVersion
|
MaxProtocolVersion = wire.AddrV2Version
|
||||||
|
|
||||||
// DefaultTrickleInterval is the min time between attempts to send an
|
// DefaultTrickleInterval is the min time between attempts to send an
|
||||||
// inv message to a peer.
|
// inv message to a peer.
|
||||||
|
@ -102,6 +102,9 @@ type MessageListeners struct {
|
||||||
// OnAddr is invoked when a peer receives an addr bitcoin message.
|
// OnAddr is invoked when a peer receives an addr bitcoin message.
|
||||||
OnAddr func(p *Peer, msg *wire.MsgAddr)
|
OnAddr func(p *Peer, msg *wire.MsgAddr)
|
||||||
|
|
||||||
|
// OnAddrV2 is invoked when a peer receives an addrv2 bitcoin message.
|
||||||
|
OnAddrV2 func(p *Peer, msg *wire.MsgAddrV2)
|
||||||
|
|
||||||
// OnPing is invoked when a peer receives a ping bitcoin message.
|
// OnPing is invoked when a peer receives a ping bitcoin message.
|
||||||
OnPing func(p *Peer, msg *wire.MsgPing)
|
OnPing func(p *Peer, msg *wire.MsgPing)
|
||||||
|
|
||||||
|
@ -197,6 +200,9 @@ type MessageListeners struct {
|
||||||
// message.
|
// message.
|
||||||
OnSendHeaders func(p *Peer, msg *wire.MsgSendHeaders)
|
OnSendHeaders func(p *Peer, msg *wire.MsgSendHeaders)
|
||||||
|
|
||||||
|
// OnSendAddrV2 is invoked when a peer receives a sendaddrv2 message.
|
||||||
|
OnSendAddrV2 func(p *Peer, msg *wire.MsgSendAddrV2)
|
||||||
|
|
||||||
// OnRead is invoked when a peer receives a bitcoin message. It
|
// OnRead is invoked when a peer receives a bitcoin message. It
|
||||||
// consists of the number of bytes read, the message, and whether or not
|
// consists of the number of bytes read, the message, and whether or not
|
||||||
// an error in the read occurred. Typically, callers will opt to use
|
// an error in the read occurred. Typically, callers will opt to use
|
||||||
|
@ -399,7 +405,7 @@ type AddrFunc func(remoteAddr *wire.NetAddress) *wire.NetAddress
|
||||||
// HostToNetAddrFunc is a func which takes a host, port, services and returns
|
// HostToNetAddrFunc is a func which takes a host, port, services and returns
|
||||||
// the netaddress.
|
// the netaddress.
|
||||||
type HostToNetAddrFunc func(host string, port uint16,
|
type HostToNetAddrFunc func(host string, port uint16,
|
||||||
services wire.ServiceFlag) (*wire.NetAddress, error)
|
services wire.ServiceFlag) (*wire.NetAddressV2, error)
|
||||||
|
|
||||||
// NOTE: The overall data flow of a peer is split into 3 goroutines. Inbound
|
// NOTE: The overall data flow of a peer is split into 3 goroutines. Inbound
|
||||||
// messages are read via the inHandler goroutine and generally dispatched to
|
// messages are read via the inHandler goroutine and generally dispatched to
|
||||||
|
@ -445,7 +451,7 @@ type Peer struct {
|
||||||
inbound bool
|
inbound bool
|
||||||
|
|
||||||
flagsMtx sync.Mutex // protects the peer flags below
|
flagsMtx sync.Mutex // protects the peer flags below
|
||||||
na *wire.NetAddress
|
na *wire.NetAddressV2
|
||||||
id int32
|
id int32
|
||||||
userAgent string
|
userAgent string
|
||||||
services wire.ServiceFlag
|
services wire.ServiceFlag
|
||||||
|
@ -455,6 +461,7 @@ type Peer struct {
|
||||||
sendHeadersPreferred bool // peer sent a sendheaders message
|
sendHeadersPreferred bool // peer sent a sendheaders message
|
||||||
verAckReceived bool
|
verAckReceived bool
|
||||||
witnessEnabled bool
|
witnessEnabled bool
|
||||||
|
sendAddrV2 bool
|
||||||
|
|
||||||
wireEncoding wire.MessageEncoding
|
wireEncoding wire.MessageEncoding
|
||||||
|
|
||||||
|
@ -585,7 +592,7 @@ func (p *Peer) ID() int32 {
|
||||||
// NA returns the peer network address.
|
// NA returns the peer network address.
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
func (p *Peer) NA() *wire.NetAddress {
|
func (p *Peer) NA() *wire.NetAddressV2 {
|
||||||
p.flagsMtx.Lock()
|
p.flagsMtx.Lock()
|
||||||
na := p.na
|
na := p.na
|
||||||
p.flagsMtx.Unlock()
|
p.flagsMtx.Unlock()
|
||||||
|
@ -820,6 +827,16 @@ func (p *Peer) IsWitnessEnabled() bool {
|
||||||
return witnessEnabled
|
return witnessEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WantsAddrV2 returns if the peer supports addrv2 messages instead of the
|
||||||
|
// legacy addr messages.
|
||||||
|
func (p *Peer) WantsAddrV2() bool {
|
||||||
|
p.flagsMtx.Lock()
|
||||||
|
wantsAddrV2 := p.sendAddrV2
|
||||||
|
p.flagsMtx.Unlock()
|
||||||
|
|
||||||
|
return wantsAddrV2
|
||||||
|
}
|
||||||
|
|
||||||
// PushAddrMsg sends an addr message to the connected peer using the provided
|
// PushAddrMsg sends an addr message to the connected peer using the provided
|
||||||
// addresses. This function is useful over manually sending the message via
|
// addresses. This function is useful over manually sending the message via
|
||||||
// QueueMessage since it automatically limits the addresses to the maximum
|
// QueueMessage since it automatically limits the addresses to the maximum
|
||||||
|
@ -856,6 +873,38 @@ func (p *Peer) PushAddrMsg(addresses []*wire.NetAddress) ([]*wire.NetAddress, er
|
||||||
return msg.AddrList, nil
|
return msg.AddrList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushAddrV2Msg is used to push an addrv2 message to the remote peer.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (p *Peer) PushAddrV2Msg(addrs []*wire.NetAddressV2) (
|
||||||
|
[]*wire.NetAddressV2, error) {
|
||||||
|
|
||||||
|
count := len(addrs)
|
||||||
|
|
||||||
|
// Nothing to send.
|
||||||
|
if count == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m := wire.NewMsgAddrV2()
|
||||||
|
m.AddrList = make([]*wire.NetAddressV2, count)
|
||||||
|
copy(m.AddrList, addrs)
|
||||||
|
|
||||||
|
// Randomize the addresses sent if there are more than the maximum.
|
||||||
|
if count > wire.MaxV2AddrPerMsg {
|
||||||
|
rand.Shuffle(count, func(i, j int) {
|
||||||
|
m.AddrList[i] = m.AddrList[j]
|
||||||
|
m.AddrList[j] = m.AddrList[i]
|
||||||
|
})
|
||||||
|
|
||||||
|
// Truncate it to the maximum size.
|
||||||
|
m.AddrList = m.AddrList[:wire.MaxV2AddrPerMsg]
|
||||||
|
}
|
||||||
|
|
||||||
|
p.QueueMessage(m, nil)
|
||||||
|
return m.AddrList, nil
|
||||||
|
}
|
||||||
|
|
||||||
// PushGetBlocksMsg sends a getblocks message for the provided block locator
|
// PushGetBlocksMsg sends a getblocks message for the provided block locator
|
||||||
// and stop hash. It will ignore back-to-back duplicate requests.
|
// and stop hash. It will ignore back-to-back duplicate requests.
|
||||||
//
|
//
|
||||||
|
@ -1363,6 +1412,19 @@ out:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since the protocol version is 70016 but we don't
|
||||||
|
// implement compact blocks, we have to ignore unknown
|
||||||
|
// messages after the version-verack handshake. This
|
||||||
|
// matches bitcoind's behavior and is necessary since
|
||||||
|
// compact blocks negotiation occurs after the
|
||||||
|
// handshake.
|
||||||
|
if err == wire.ErrUnknownMessage {
|
||||||
|
log.Debugf("Received unknown message from %s:"+
|
||||||
|
" %v", p, err)
|
||||||
|
idleTimer.Reset(idleTimeout)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Only log the error and send reject message if the
|
// Only log the error and send reject message if the
|
||||||
// local peer is not forcibly disconnecting and the
|
// local peer is not forcibly disconnecting and the
|
||||||
// remote peer has not disconnected.
|
// remote peer has not disconnected.
|
||||||
|
@ -1404,6 +1466,11 @@ out:
|
||||||
)
|
)
|
||||||
break out
|
break out
|
||||||
|
|
||||||
|
case *wire.MsgSendAddrV2:
|
||||||
|
// Disconnect if peer sends this after the handshake is
|
||||||
|
// completed.
|
||||||
|
break out
|
||||||
|
|
||||||
case *wire.MsgGetAddr:
|
case *wire.MsgGetAddr:
|
||||||
if p.cfg.Listeners.OnGetAddr != nil {
|
if p.cfg.Listeners.OnGetAddr != nil {
|
||||||
p.cfg.Listeners.OnGetAddr(p, msg)
|
p.cfg.Listeners.OnGetAddr(p, msg)
|
||||||
|
@ -1414,6 +1481,11 @@ out:
|
||||||
p.cfg.Listeners.OnAddr(p, msg)
|
p.cfg.Listeners.OnAddr(p, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case *wire.MsgAddrV2:
|
||||||
|
if p.cfg.Listeners.OnAddrV2 != nil {
|
||||||
|
p.cfg.Listeners.OnAddrV2(p, msg)
|
||||||
|
}
|
||||||
|
|
||||||
case *wire.MsgPing:
|
case *wire.MsgPing:
|
||||||
p.handlePingMsg(msg)
|
p.handlePingMsg(msg)
|
||||||
if p.cfg.Listeners.OnPing != nil {
|
if p.cfg.Listeners.OnPing != nil {
|
||||||
|
@ -1986,29 +2058,8 @@ func (p *Peer) readRemoteVersionMsg() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readRemoteVerAckMsg waits for the next message to arrive from the remote
|
// processRemoteVerAckMsg takes the verack from the remote peer and handles it.
|
||||||
// peer. If this message is not a verack message, then an error is returned.
|
func (p *Peer) processRemoteVerAckMsg(msg *wire.MsgVerAck) {
|
||||||
// This method is to be used as part of the version negotiation upon a new
|
|
||||||
// connection.
|
|
||||||
func (p *Peer) readRemoteVerAckMsg() error {
|
|
||||||
// Read the next message from the wire.
|
|
||||||
remoteMsg, _, err := p.readMessage(wire.LatestEncoding)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// It should be a verack message, otherwise send a reject message to the
|
|
||||||
// peer explaining why.
|
|
||||||
msg, ok := remoteMsg.(*wire.MsgVerAck)
|
|
||||||
if !ok {
|
|
||||||
reason := "a verack message must follow version"
|
|
||||||
rejectMsg := wire.NewMsgReject(
|
|
||||||
msg.Command(), wire.RejectMalformed, reason,
|
|
||||||
)
|
|
||||||
_ = p.writeMessage(rejectMsg, wire.LatestEncoding)
|
|
||||||
return errors.New(reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.flagsMtx.Lock()
|
p.flagsMtx.Lock()
|
||||||
p.verAckReceived = true
|
p.verAckReceived = true
|
||||||
p.flagsMtx.Unlock()
|
p.flagsMtx.Unlock()
|
||||||
|
@ -2016,8 +2067,6 @@ func (p *Peer) readRemoteVerAckMsg() error {
|
||||||
if p.cfg.Listeners.OnVerAck != nil {
|
if p.cfg.Listeners.OnVerAck != nil {
|
||||||
p.cfg.Listeners.OnVerAck(p, msg)
|
p.cfg.Listeners.OnVerAck(p, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// localVersionMsg creates a version message that can be used to send to the
|
// localVersionMsg creates a version message that can be used to send to the
|
||||||
|
@ -2032,7 +2081,15 @@ func (p *Peer) localVersionMsg() (*wire.MsgVersion, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
theirNA := p.na
|
theirNA := p.na.ToLegacy()
|
||||||
|
|
||||||
|
// If p.na is a torv3 hidden service address, we'll need to send over
|
||||||
|
// an empty NetAddress for their address.
|
||||||
|
if p.na.IsTorV3() {
|
||||||
|
theirNA = wire.NewNetAddressIPPort(
|
||||||
|
net.IP([]byte{0, 0, 0, 0}), p.na.Port, p.na.Services,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// If we are behind a proxy and the connection comes from the proxy then
|
// If we are behind a proxy and the connection comes from the proxy then
|
||||||
// we return an unroutable address as their address. This is to prevent
|
// we return an unroutable address as their address. This is to prevent
|
||||||
|
@ -2040,7 +2097,7 @@ func (p *Peer) localVersionMsg() (*wire.MsgVersion, error) {
|
||||||
if p.cfg.Proxy != "" {
|
if p.cfg.Proxy != "" {
|
||||||
proxyaddress, _, err := net.SplitHostPort(p.cfg.Proxy)
|
proxyaddress, _, err := net.SplitHostPort(p.cfg.Proxy)
|
||||||
// invalid proxy means poorly configured, be on the safe side.
|
// invalid proxy means poorly configured, be on the safe side.
|
||||||
if err != nil || p.na.IP.String() == proxyaddress {
|
if err != nil || p.na.Addr.String() == proxyaddress {
|
||||||
theirNA = wire.NewNetAddressIPPort(net.IP([]byte{0, 0, 0, 0}), 0,
|
theirNA = wire.NewNetAddressIPPort(net.IP([]byte{0, 0, 0, 0}), 0,
|
||||||
theirNA.Services)
|
theirNA.Services)
|
||||||
}
|
}
|
||||||
|
@ -2092,14 +2149,71 @@ func (p *Peer) writeLocalVersionMsg() error {
|
||||||
return p.writeMessage(localVerMsg, wire.LatestEncoding)
|
return p.writeMessage(localVerMsg, wire.LatestEncoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeSendAddrV2Msg writes our sendaddrv2 message to the remote peer if the
|
||||||
|
// peer supports protocol version 70016 and above.
|
||||||
|
func (p *Peer) writeSendAddrV2Msg(pver uint32) error {
|
||||||
|
if pver < wire.AddrV2Version {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sendAddrMsg := wire.NewMsgSendAddrV2()
|
||||||
|
return p.writeMessage(sendAddrMsg, wire.LatestEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitToFinishNegotiation waits until desired negotiation messages are
|
||||||
|
// received, recording the remote peer's preference for sendaddrv2 as an
|
||||||
|
// example. The list of negotiated features can be expanded in the future. If a
|
||||||
|
// verack is received, negotiation stops and the connection is live.
|
||||||
|
func (p *Peer) waitToFinishNegotiation(pver uint32) error {
|
||||||
|
// There are several possible messages that can be received here. We
|
||||||
|
// could immediately receive verack and be done with the handshake. We
|
||||||
|
// could receive sendaddrv2 and still have to wait for verack. Or we
|
||||||
|
// can receive unknown messages before and after sendaddrv2 and still
|
||||||
|
// have to wait for verack.
|
||||||
|
for {
|
||||||
|
remoteMsg, _, err := p.readMessage(wire.LatestEncoding)
|
||||||
|
if err == wire.ErrUnknownMessage {
|
||||||
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch m := remoteMsg.(type) {
|
||||||
|
case *wire.MsgSendAddrV2:
|
||||||
|
if pver >= wire.AddrV2Version {
|
||||||
|
p.flagsMtx.Lock()
|
||||||
|
p.sendAddrV2 = true
|
||||||
|
p.flagsMtx.Unlock()
|
||||||
|
|
||||||
|
if p.cfg.Listeners.OnSendAddrV2 != nil {
|
||||||
|
p.cfg.Listeners.OnSendAddrV2(p, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *wire.MsgVerAck:
|
||||||
|
// Receiving a verack means we are done with the
|
||||||
|
// handshake.
|
||||||
|
p.processRemoteVerAckMsg(m)
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
// This is triggered if the peer sends, for example, a
|
||||||
|
// GETDATA message during this negotiation.
|
||||||
|
return wire.ErrInvalidHandshake
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// negotiateInboundProtocol performs the negotiation protocol for an inbound
|
// negotiateInboundProtocol performs the negotiation protocol for an inbound
|
||||||
// peer. The events should occur in the following order, otherwise an error is
|
// peer. The events should occur in the following order, otherwise an error is
|
||||||
// returned:
|
// returned:
|
||||||
//
|
//
|
||||||
// 1. Remote peer sends their version.
|
// 1. Remote peer sends their version.
|
||||||
// 2. We send our version.
|
// 2. We send our version.
|
||||||
// 3. We send our verack.
|
// 3. We send sendaddrv2 if their version is >= 70016.
|
||||||
// 4. Remote peer sends their verack.
|
// 4. We send our verack.
|
||||||
|
// 5. Wait until sendaddrv2 or verack is received. Unknown messages are
|
||||||
|
// skipped as it could be wtxidrelay or a different message in the future
|
||||||
|
// that btcd does not implement but bitcoind does.
|
||||||
|
// 6. If remote peer sent sendaddrv2 above, wait until receipt of verack.
|
||||||
func (p *Peer) negotiateInboundProtocol() error {
|
func (p *Peer) negotiateInboundProtocol() error {
|
||||||
if err := p.readRemoteVersionMsg(); err != nil {
|
if err := p.readRemoteVersionMsg(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2109,12 +2223,22 @@ func (p *Peer) negotiateInboundProtocol() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var protoVersion uint32
|
||||||
|
p.flagsMtx.Lock()
|
||||||
|
protoVersion = p.protocolVersion
|
||||||
|
p.flagsMtx.Unlock()
|
||||||
|
|
||||||
|
if err := p.writeSendAddrV2Msg(protoVersion); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err := p.writeMessage(wire.NewMsgVerAck(), wire.LatestEncoding)
|
err := p.writeMessage(wire.NewMsgVerAck(), wire.LatestEncoding)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.readRemoteVerAckMsg()
|
// Finish the negotiation by waiting for negotiable messages or verack.
|
||||||
|
return p.waitToFinishNegotiation(protoVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// negotiateOutboundProtocol performs the negotiation protocol for an outbound
|
// negotiateOutboundProtocol performs the negotiation protocol for an outbound
|
||||||
|
@ -2123,8 +2247,11 @@ func (p *Peer) negotiateInboundProtocol() error {
|
||||||
//
|
//
|
||||||
// 1. We send our version.
|
// 1. We send our version.
|
||||||
// 2. Remote peer sends their version.
|
// 2. Remote peer sends their version.
|
||||||
// 3. Remote peer sends their verack.
|
// 3. We send sendaddrv2 if their version is >= 70016.
|
||||||
// 4. We send our verack.
|
// 4. We send our verack.
|
||||||
|
// 5. We wait to receive sendaddrv2 or verack, skipping unknown messages as
|
||||||
|
// in the inbound case.
|
||||||
|
// 6. If sendaddrv2 was received, wait for receipt of verack.
|
||||||
func (p *Peer) negotiateOutboundProtocol() error {
|
func (p *Peer) negotiateOutboundProtocol() error {
|
||||||
if err := p.writeLocalVersionMsg(); err != nil {
|
if err := p.writeLocalVersionMsg(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2134,11 +2261,22 @@ func (p *Peer) negotiateOutboundProtocol() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.readRemoteVerAckMsg(); err != nil {
|
var protoVersion uint32
|
||||||
|
p.flagsMtx.Lock()
|
||||||
|
protoVersion = p.protocolVersion
|
||||||
|
p.flagsMtx.Unlock()
|
||||||
|
|
||||||
|
if err := p.writeSendAddrV2Msg(protoVersion); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.writeMessage(wire.NewMsgVerAck(), wire.LatestEncoding)
|
err := p.writeMessage(wire.NewMsgVerAck(), wire.LatestEncoding)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish the negotiation by waiting for negotiable messages or verack.
|
||||||
|
return p.waitToFinishNegotiation(protoVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// start begins processing input and output messages.
|
// start begins processing input and output messages.
|
||||||
|
@ -2201,7 +2339,12 @@ func (p *Peer) AssociateConnection(conn net.Conn) {
|
||||||
p.Disconnect()
|
p.Disconnect()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.na = na
|
|
||||||
|
// Convert the NetAddress created above into NetAddressV2.
|
||||||
|
currentNa := wire.NetAddressV2FromBytes(
|
||||||
|
na.Timestamp, na.Services, na.IP, na.Port,
|
||||||
|
)
|
||||||
|
p.na = currentNa
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -2267,7 +2410,10 @@ func NewInboundPeer(cfg *Config) *Peer {
|
||||||
return newPeerBase(cfg, true)
|
return newPeerBase(cfg, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOutboundPeer returns a new outbound bitcoin peer.
|
// NewOutboundPeer returns a new outbound bitcoin peer. If the Config argument
|
||||||
|
// does not set HostToNetAddress, connecting to anything other than an ipv4 or
|
||||||
|
// ipv6 address will fail and may cause a nil-pointer-dereference. This
|
||||||
|
// includes hostnames and onion services.
|
||||||
func NewOutboundPeer(cfg *Config, addr string) (*Peer, error) {
|
func NewOutboundPeer(cfg *Config, addr string) (*Peer, error) {
|
||||||
p := newPeerBase(cfg, false)
|
p := newPeerBase(cfg, false)
|
||||||
p.addr = addr
|
p.addr = addr
|
||||||
|
@ -2289,7 +2435,12 @@ func NewOutboundPeer(cfg *Config, addr string) (*Peer, error) {
|
||||||
}
|
}
|
||||||
p.na = na
|
p.na = na
|
||||||
} else {
|
} else {
|
||||||
p.na = wire.NewNetAddressIPPort(net.ParseIP(host), uint16(port), 0)
|
// If host is an onion hidden service or a hostname, it is
|
||||||
|
// likely that a nil-pointer-dereference will occur. The caller
|
||||||
|
// should set HostToNetAddress if connecting to these.
|
||||||
|
p.na = wire.NetAddressV2FromBytes(
|
||||||
|
time.Now(), 0, net.ParseIP(host), uint16(port),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return p, nil
|
return p, nil
|
||||||
|
|
|
@ -289,18 +289,16 @@ func TestPeerConnection(t *testing.T) {
|
||||||
{
|
{
|
||||||
"basic handshake",
|
"basic handshake",
|
||||||
func() (*peer.Peer, *peer.Peer, error) {
|
func() (*peer.Peer, *peer.Peer, error) {
|
||||||
inConn, outConn := pipe(
|
|
||||||
&conn{raddr: "10.0.0.1:8333"},
|
|
||||||
&conn{raddr: "10.0.0.2:8333"},
|
|
||||||
)
|
|
||||||
inPeer := peer.NewInboundPeer(peer1Cfg)
|
inPeer := peer.NewInboundPeer(peer1Cfg)
|
||||||
inPeer.AssociateConnection(inConn)
|
|
||||||
|
|
||||||
outPeer, err := peer.NewOutboundPeer(peer2Cfg, "10.0.0.2:8333")
|
outPeer, err := peer.NewOutboundPeer(peer2Cfg, "10.0.0.2:8333")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
outPeer.AssociateConnection(outConn)
|
|
||||||
|
err = setupPeerConnection(inPeer, outPeer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
select {
|
select {
|
||||||
|
@ -315,18 +313,16 @@ func TestPeerConnection(t *testing.T) {
|
||||||
{
|
{
|
||||||
"socks proxy",
|
"socks proxy",
|
||||||
func() (*peer.Peer, *peer.Peer, error) {
|
func() (*peer.Peer, *peer.Peer, error) {
|
||||||
inConn, outConn := pipe(
|
|
||||||
&conn{raddr: "10.0.0.1:8333", proxy: true},
|
|
||||||
&conn{raddr: "10.0.0.2:8333"},
|
|
||||||
)
|
|
||||||
inPeer := peer.NewInboundPeer(peer1Cfg)
|
inPeer := peer.NewInboundPeer(peer1Cfg)
|
||||||
inPeer.AssociateConnection(inConn)
|
|
||||||
|
|
||||||
outPeer, err := peer.NewOutboundPeer(peer2Cfg, "10.0.0.2:8333")
|
outPeer, err := peer.NewOutboundPeer(peer2Cfg, "10.0.0.2:8333")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
outPeer.AssociateConnection(outConn)
|
|
||||||
|
err = setupPeerConnection(inPeer, outPeer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
select {
|
select {
|
||||||
|
@ -359,7 +355,7 @@ func TestPeerConnection(t *testing.T) {
|
||||||
// TestPeerListeners tests that the peer listeners are called as expected.
|
// TestPeerListeners tests that the peer listeners are called as expected.
|
||||||
func TestPeerListeners(t *testing.T) {
|
func TestPeerListeners(t *testing.T) {
|
||||||
verack := make(chan struct{}, 1)
|
verack := make(chan struct{}, 1)
|
||||||
ok := make(chan wire.Message, 20)
|
ok := make(chan wire.Message, 22)
|
||||||
peerCfg := &peer.Config{
|
peerCfg := &peer.Config{
|
||||||
Listeners: peer.MessageListeners{
|
Listeners: peer.MessageListeners{
|
||||||
OnGetAddr: func(p *peer.Peer, msg *wire.MsgGetAddr) {
|
OnGetAddr: func(p *peer.Peer, msg *wire.MsgGetAddr) {
|
||||||
|
@ -447,6 +443,12 @@ func TestPeerListeners(t *testing.T) {
|
||||||
OnSendHeaders: func(p *peer.Peer, msg *wire.MsgSendHeaders) {
|
OnSendHeaders: func(p *peer.Peer, msg *wire.MsgSendHeaders) {
|
||||||
ok <- msg
|
ok <- msg
|
||||||
},
|
},
|
||||||
|
OnSendAddrV2: func(p *peer.Peer, msg *wire.MsgSendAddrV2) {
|
||||||
|
ok <- msg
|
||||||
|
},
|
||||||
|
OnAddrV2: func(p *peer.Peer, msg *wire.MsgAddrV2) {
|
||||||
|
ok <- msg
|
||||||
|
},
|
||||||
},
|
},
|
||||||
UserAgentName: "peer",
|
UserAgentName: "peer",
|
||||||
UserAgentVersion: "1.0",
|
UserAgentVersion: "1.0",
|
||||||
|
@ -456,12 +458,7 @@ func TestPeerListeners(t *testing.T) {
|
||||||
TrickleInterval: time.Second * 10,
|
TrickleInterval: time.Second * 10,
|
||||||
AllowSelfConns: true,
|
AllowSelfConns: true,
|
||||||
}
|
}
|
||||||
inConn, outConn := pipe(
|
|
||||||
&conn{raddr: "10.0.0.1:8333"},
|
|
||||||
&conn{raddr: "10.0.0.2:8333"},
|
|
||||||
)
|
|
||||||
inPeer := peer.NewInboundPeer(peerCfg)
|
inPeer := peer.NewInboundPeer(peerCfg)
|
||||||
inPeer.AssociateConnection(inConn)
|
|
||||||
|
|
||||||
peerCfg.Listeners = peer.MessageListeners{
|
peerCfg.Listeners = peer.MessageListeners{
|
||||||
OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) {
|
OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) {
|
||||||
|
@ -473,7 +470,12 @@ func TestPeerListeners(t *testing.T) {
|
||||||
t.Errorf("NewOutboundPeer: unexpected err %v\n", err)
|
t.Errorf("NewOutboundPeer: unexpected err %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
outPeer.AssociateConnection(outConn)
|
|
||||||
|
err = setupPeerConnection(inPeer, outPeer)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("setupPeerConnection: failed: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
select {
|
select {
|
||||||
|
@ -597,6 +599,14 @@ func TestPeerListeners(t *testing.T) {
|
||||||
"OnSendHeaders",
|
"OnSendHeaders",
|
||||||
wire.NewMsgSendHeaders(),
|
wire.NewMsgSendHeaders(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"OnSendAddrV2",
|
||||||
|
wire.NewMsgSendAddrV2(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"OnAddrV2",
|
||||||
|
wire.NewMsgAddrV2(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
t.Logf("Running %d tests", len(tests))
|
t.Logf("Running %d tests", len(tests))
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -881,17 +891,17 @@ func TestDuplicateVersionMsg(t *testing.T) {
|
||||||
Services: 0,
|
Services: 0,
|
||||||
AllowSelfConns: true,
|
AllowSelfConns: true,
|
||||||
}
|
}
|
||||||
inConn, outConn := pipe(
|
outPeer, err := peer.NewOutboundPeer(peerCfg, "10.0.0.2:8333")
|
||||||
&conn{laddr: "10.0.0.1:9108", raddr: "10.0.0.2:9108"},
|
|
||||||
&conn{laddr: "10.0.0.2:9108", raddr: "10.0.0.1:9108"},
|
|
||||||
)
|
|
||||||
outPeer, err := peer.NewOutboundPeer(peerCfg, inConn.laddr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewOutboundPeer: unexpected err: %v\n", err)
|
t.Fatalf("NewOutboundPeer: unexpected err: %v\n", err)
|
||||||
}
|
}
|
||||||
outPeer.AssociateConnection(outConn)
|
|
||||||
inPeer := peer.NewInboundPeer(peerCfg)
|
inPeer := peer.NewInboundPeer(peerCfg)
|
||||||
inPeer.AssociateConnection(inConn)
|
|
||||||
|
err = setupPeerConnection(inPeer, outPeer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("setupPeerConnection failed to connect: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for the veracks from the initial protocol version negotiation.
|
// Wait for the veracks from the initial protocol version negotiation.
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
select {
|
select {
|
||||||
|
@ -947,17 +957,16 @@ func TestUpdateLastBlockHeight(t *testing.T) {
|
||||||
remotePeerCfg.NewestBlock = func() (*chainhash.Hash, int32, error) {
|
remotePeerCfg.NewestBlock = func() (*chainhash.Hash, int32, error) {
|
||||||
return &chainhash.Hash{}, remotePeerHeight, nil
|
return &chainhash.Hash{}, remotePeerHeight, nil
|
||||||
}
|
}
|
||||||
inConn, outConn := pipe(
|
localPeer, err := peer.NewOutboundPeer(&peerCfg, "10.0.0.2:8333")
|
||||||
&conn{laddr: "10.0.0.1:9108", raddr: "10.0.0.2:9108"},
|
|
||||||
&conn{laddr: "10.0.0.2:9108", raddr: "10.0.0.1:9108"},
|
|
||||||
)
|
|
||||||
localPeer, err := peer.NewOutboundPeer(&peerCfg, inConn.laddr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewOutboundPeer: unexpected err: %v\n", err)
|
t.Fatalf("NewOutboundPeer: unexpected err: %v\n", err)
|
||||||
}
|
}
|
||||||
localPeer.AssociateConnection(outConn)
|
|
||||||
inPeer := peer.NewInboundPeer(&remotePeerCfg)
|
inPeer := peer.NewInboundPeer(&remotePeerCfg)
|
||||||
inPeer.AssociateConnection(inConn)
|
|
||||||
|
err = setupPeerConnection(inPeer, localPeer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("setupPeerConnection failed to connect: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for the veracks from the initial protocol version negotiation.
|
// Wait for the veracks from the initial protocol version negotiation.
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
|
@ -989,3 +998,214 @@ func TestUpdateLastBlockHeight(t *testing.T) {
|
||||||
remotePeerHeight+1)
|
remotePeerHeight+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setupPeerConnection initiates a tcp connection between two peers.
|
||||||
|
func setupPeerConnection(in, out *peer.Peer) error {
|
||||||
|
// listenFunc is a function closure that listens for a tcp connection.
|
||||||
|
// The tcp connection will be the one the inbound peer uses. This will
|
||||||
|
// be run as a goroutine.
|
||||||
|
listenFunc := func(l *net.TCPListener, errChan chan error,
|
||||||
|
listenChan chan struct{}) {
|
||||||
|
|
||||||
|
listenChan <- struct{}{}
|
||||||
|
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
in.AssociateConnection(conn)
|
||||||
|
errChan <- nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialFunc is a function closure that initiates the tcp connection.
|
||||||
|
// The tcp connection will be the one the outbound peer uses.
|
||||||
|
dialFunc := func(addr *net.TCPAddr) error {
|
||||||
|
conn, err := net.Dial("tcp", addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
out.AssociateConnection(conn)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
listenAddr := "localhost:0"
|
||||||
|
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := net.ListenTCP("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
errChan := make(chan error, 1)
|
||||||
|
listenChan := make(chan struct{}, 1)
|
||||||
|
|
||||||
|
go listenFunc(l, errChan, listenChan)
|
||||||
|
<-listenChan
|
||||||
|
|
||||||
|
if err := dialFunc(l.Addr().(*net.TCPAddr)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err = <-errChan:
|
||||||
|
return err
|
||||||
|
case <-time.After(time.Second * 2):
|
||||||
|
return errors.New("failed to create connection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSendAddrV2Handshake tests that the version-verack handshake with the
|
||||||
|
// addition of the sendaddrv2 message works as expected.
|
||||||
|
func TestSendAddrV2Handshake(t *testing.T) {
|
||||||
|
verack := make(chan struct{}, 2)
|
||||||
|
sendaddr := make(chan struct{}, 2)
|
||||||
|
peer1Cfg := &peer.Config{
|
||||||
|
Listeners: peer.MessageListeners{
|
||||||
|
OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) {
|
||||||
|
verack <- struct{}{}
|
||||||
|
},
|
||||||
|
OnSendAddrV2: func(p *peer.Peer,
|
||||||
|
msg *wire.MsgSendAddrV2) {
|
||||||
|
|
||||||
|
sendaddr <- struct{}{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AllowSelfConns: true,
|
||||||
|
ChainParams: &chaincfg.MainNetParams,
|
||||||
|
}
|
||||||
|
|
||||||
|
peer2Cfg := &peer.Config{
|
||||||
|
Listeners: peer1Cfg.Listeners,
|
||||||
|
AllowSelfConns: true,
|
||||||
|
ChainParams: &chaincfg.MainNetParams,
|
||||||
|
}
|
||||||
|
|
||||||
|
verackErr := errors.New("verack timeout")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expectsV2 bool
|
||||||
|
setup func() (*peer.Peer, *peer.Peer, error)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"successful sendaddrv2 handshake",
|
||||||
|
true,
|
||||||
|
func() (*peer.Peer, *peer.Peer, error) {
|
||||||
|
inPeer := peer.NewInboundPeer(peer1Cfg)
|
||||||
|
outPeer, err := peer.NewOutboundPeer(
|
||||||
|
peer2Cfg, "10.0.0.2:8333",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setupPeerConnection(inPeer, outPeer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
select {
|
||||||
|
case <-sendaddr:
|
||||||
|
case <-verack:
|
||||||
|
case <-time.After(time.Second * 2):
|
||||||
|
return nil, nil, verackErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inPeer, outPeer, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handshake with legacy inbound peer",
|
||||||
|
false,
|
||||||
|
func() (*peer.Peer, *peer.Peer, error) {
|
||||||
|
legacyVersion := wire.AddrV2Version - 1
|
||||||
|
peer1Cfg.ProtocolVersion = legacyVersion
|
||||||
|
inPeer := peer.NewInboundPeer(peer1Cfg)
|
||||||
|
outPeer, err := peer.NewOutboundPeer(
|
||||||
|
peer2Cfg, "10.0.0.2:8333",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setupPeerConnection(inPeer, outPeer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
select {
|
||||||
|
case <-verack:
|
||||||
|
case <-time.After(time.Second * 2):
|
||||||
|
return nil, nil, verackErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inPeer, outPeer, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handshake with legacy outbound peer",
|
||||||
|
false,
|
||||||
|
func() (*peer.Peer, *peer.Peer, error) {
|
||||||
|
inPeer := peer.NewInboundPeer(peer1Cfg)
|
||||||
|
legacyVersion := wire.AddrV2Version - 1
|
||||||
|
peer2Cfg.ProtocolVersion = legacyVersion
|
||||||
|
outPeer, err := peer.NewOutboundPeer(
|
||||||
|
peer2Cfg, "10.0.0.2:8333",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setupPeerConnection(inPeer, outPeer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
select {
|
||||||
|
case <-verack:
|
||||||
|
case <-time.After(time.Second * 2):
|
||||||
|
return nil, nil, verackErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inPeer, outPeer, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
inPeer, outPeer, err := test.setup()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("TestSendAddrV2Handshake setup #%d: "+
|
||||||
|
"unexpected err: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if inPeer.WantsAddrV2() != test.expectsV2 {
|
||||||
|
t.Fatalf("TestSendAddrV2Handshake #%d expected "+
|
||||||
|
"wantsAddrV2 to be %v instead was %v", i,
|
||||||
|
test.expectsV2, inPeer.WantsAddrV2())
|
||||||
|
} else if outPeer.WantsAddrV2() != test.expectsV2 {
|
||||||
|
t.Fatalf("TestSendAddrV2Handshake #%d expected "+
|
||||||
|
"wantsAddrV2 to be %v instead was %v", i,
|
||||||
|
test.expectsV2, outPeer.WantsAddrV2())
|
||||||
|
}
|
||||||
|
|
||||||
|
inPeer.Disconnect()
|
||||||
|
outPeer.Disconnect()
|
||||||
|
inPeer.WaitForDisconnect()
|
||||||
|
outPeer.WaitForDisconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
68
server.go
68
server.go
|
@ -344,6 +344,34 @@ func (sp *serverPeer) relayTxDisabled() bool {
|
||||||
// pushAddrMsg sends a legacy addr message to the connected peer using the
|
// pushAddrMsg sends a legacy addr message to the connected peer using the
|
||||||
// provided addresses.
|
// provided addresses.
|
||||||
func (sp *serverPeer) pushAddrMsg(addresses []*wire.NetAddressV2) {
|
func (sp *serverPeer) pushAddrMsg(addresses []*wire.NetAddressV2) {
|
||||||
|
if sp.WantsAddrV2() {
|
||||||
|
// If the peer supports addrv2, we'll be pushing an addrv2
|
||||||
|
// message instead. The logic is otherwise identical to the
|
||||||
|
// addr case below.
|
||||||
|
addrs := make([]*wire.NetAddressV2, 0, len(addresses))
|
||||||
|
for _, addr := range addresses {
|
||||||
|
// Filter addresses already known to the peer.
|
||||||
|
if sp.addressKnown(addr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs = append(addrs, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
known, err := sp.PushAddrV2Msg(addrs)
|
||||||
|
if err != nil {
|
||||||
|
peerLog.Errorf("Can't push addrv2 message to %s: %v",
|
||||||
|
sp.Peer, err)
|
||||||
|
sp.Disconnect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the final set of addresses sent to the set the peer
|
||||||
|
// knows of.
|
||||||
|
sp.addKnownAddresses(known)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
addrs := make([]*wire.NetAddress, 0, len(addresses))
|
addrs := make([]*wire.NetAddress, 0, len(addresses))
|
||||||
for _, addr := range addresses {
|
for _, addr := range addresses {
|
||||||
// Filter addresses already known to the peer.
|
// Filter addresses already known to the peer.
|
||||||
|
@ -1328,6 +1356,45 @@ func (sp *serverPeer) OnAddr(_ *peer.Peer, msg *wire.MsgAddr) {
|
||||||
sp.server.addrManager.AddAddresses(addrs, sp.NA())
|
sp.server.addrManager.AddAddresses(addrs, sp.NA())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnAddrV2 is invoked when a peer receives an addrv2 bitcoin message and is
|
||||||
|
// used to notify the server about advertised addresses.
|
||||||
|
func (sp *serverPeer) OnAddrV2(_ *peer.Peer, msg *wire.MsgAddrV2) {
|
||||||
|
// Ignore if simnet for the same reasons as the regular addr message.
|
||||||
|
if cfg.SimNet {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// An empty AddrV2 message is invalid.
|
||||||
|
if len(msg.AddrList) == 0 {
|
||||||
|
peerLog.Errorf("Command [%s] from %s does not contain any "+
|
||||||
|
"addresses", msg.Command(), sp.Peer)
|
||||||
|
sp.Disconnect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, na := range msg.AddrList {
|
||||||
|
// Don't add more to the set of known addresses if we're
|
||||||
|
// disconnecting.
|
||||||
|
if !sp.Connected() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the timestamp to 5 days ago if the timestamp received is
|
||||||
|
// more than 10 minutes in the future so this address is one of
|
||||||
|
// the first to be removed.
|
||||||
|
now := time.Now()
|
||||||
|
if na.Timestamp.After(now.Add(time.Minute * 10)) {
|
||||||
|
na.Timestamp = now.Add(-1 * time.Hour * 24 * 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the set of known addresses.
|
||||||
|
sp.addKnownAddresses([]*wire.NetAddressV2{na})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the addresses to the addrmanager.
|
||||||
|
sp.server.addrManager.AddAddresses(msg.AddrList, sp.NA())
|
||||||
|
}
|
||||||
|
|
||||||
// OnRead is invoked when a peer receives a message and it is used to update
|
// OnRead is invoked when a peer receives a message and it is used to update
|
||||||
// the bytes received by the server.
|
// the bytes received by the server.
|
||||||
func (sp *serverPeer) OnRead(_ *peer.Peer, bytesRead int, msg wire.Message, err error) {
|
func (sp *serverPeer) OnRead(_ *peer.Peer, bytesRead int, msg wire.Message, err error) {
|
||||||
|
@ -2074,6 +2141,7 @@ func newPeerConfig(sp *serverPeer) *peer.Config {
|
||||||
OnFilterLoad: sp.OnFilterLoad,
|
OnFilterLoad: sp.OnFilterLoad,
|
||||||
OnGetAddr: sp.OnGetAddr,
|
OnGetAddr: sp.OnGetAddr,
|
||||||
OnAddr: sp.OnAddr,
|
OnAddr: sp.OnAddr,
|
||||||
|
OnAddrV2: sp.OnAddrV2,
|
||||||
OnRead: sp.OnRead,
|
OnRead: sp.OnRead,
|
||||||
OnWrite: sp.OnWrite,
|
OnWrite: sp.OnWrite,
|
||||||
OnNotFound: sp.OnNotFound,
|
OnNotFound: sp.OnNotFound,
|
||||||
|
|
|
@ -32,6 +32,7 @@ const (
|
||||||
CmdVerAck = "verack"
|
CmdVerAck = "verack"
|
||||||
CmdGetAddr = "getaddr"
|
CmdGetAddr = "getaddr"
|
||||||
CmdAddr = "addr"
|
CmdAddr = "addr"
|
||||||
|
CmdAddrV2 = "addrv2"
|
||||||
CmdGetBlocks = "getblocks"
|
CmdGetBlocks = "getblocks"
|
||||||
CmdInv = "inv"
|
CmdInv = "inv"
|
||||||
CmdGetData = "getdata"
|
CmdGetData = "getdata"
|
||||||
|
@ -78,6 +79,13 @@ const (
|
||||||
// protocol.
|
// protocol.
|
||||||
var LatestEncoding = WitnessEncoding
|
var LatestEncoding = WitnessEncoding
|
||||||
|
|
||||||
|
// ErrUnknownMessage is the error returned when decoding an unknown message.
|
||||||
|
var ErrUnknownMessage = fmt.Errorf("received unknown message")
|
||||||
|
|
||||||
|
// ErrInvalidHandshake is the error returned when a peer sends us a known
|
||||||
|
// message that does not belong in the version-verack handshake.
|
||||||
|
var ErrInvalidHandshake = fmt.Errorf("invalid message during handshake")
|
||||||
|
|
||||||
// Message is an interface that describes a bitcoin message. A type that
|
// Message is an interface that describes a bitcoin message. A type that
|
||||||
// implements Message has complete control over the representation of its data
|
// implements Message has complete control over the representation of its data
|
||||||
// and may therefore contain additional or fewer fields than those which
|
// and may therefore contain additional or fewer fields than those which
|
||||||
|
@ -109,6 +117,9 @@ func makeEmptyMessage(command string) (Message, error) {
|
||||||
case CmdAddr:
|
case CmdAddr:
|
||||||
msg = &MsgAddr{}
|
msg = &MsgAddr{}
|
||||||
|
|
||||||
|
case CmdAddrV2:
|
||||||
|
msg = &MsgAddrV2{}
|
||||||
|
|
||||||
case CmdGetBlocks:
|
case CmdGetBlocks:
|
||||||
msg = &MsgGetBlocks{}
|
msg = &MsgGetBlocks{}
|
||||||
|
|
||||||
|
@ -185,7 +196,7 @@ func makeEmptyMessage(command string) (Message, error) {
|
||||||
msg = &MsgCFCheckpt{}
|
msg = &MsgCFCheckpt{}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unhandled command [%s]", command)
|
return nil, ErrUnknownMessage
|
||||||
}
|
}
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
@ -378,9 +389,10 @@ func ReadMessageWithEncodingN(r io.Reader, pver uint32, btcnet BitcoinNet,
|
||||||
// Create struct of appropriate message type based on the command.
|
// Create struct of appropriate message type based on the command.
|
||||||
msg, err := makeEmptyMessage(command)
|
msg, err := makeEmptyMessage(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// makeEmptyMessage can only return ErrUnknownMessage and it is
|
||||||
|
// important that we bubble it up to the caller.
|
||||||
discardInput(r, hdr.length)
|
discardInput(r, hdr.length)
|
||||||
return totalBytes, nil, nil, messageError("ReadMessage",
|
return totalBytes, nil, nil, err
|
||||||
err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for maximum length based on the message type as a malicious client
|
// Check for maximum length based on the message type as a malicious client
|
||||||
|
|
|
@ -295,7 +295,7 @@ func TestReadMessageWireErrors(t *testing.T) {
|
||||||
pver,
|
pver,
|
||||||
btcnet,
|
btcnet,
|
||||||
len(unsupportedCommandBytes),
|
len(unsupportedCommandBytes),
|
||||||
&MessageError{},
|
ErrUnknownMessage,
|
||||||
24,
|
24,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ func TestReadMessageWireErrors(t *testing.T) {
|
||||||
pver,
|
pver,
|
||||||
btcnet,
|
btcnet,
|
||||||
len(discardBytes),
|
len(discardBytes),
|
||||||
&MessageError{},
|
ErrUnknownMessage,
|
||||||
24,
|
24,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
102
wire/msgaddrv2.go
Normal file
102
wire/msgaddrv2.go
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxV2AddrPerMsg is the maximum number of version 2 addresses that will exist
|
||||||
|
// in a single addrv2 message (MsgAddrV2).
|
||||||
|
const MaxV2AddrPerMsg = 1000
|
||||||
|
|
||||||
|
// MsgAddrV2 implements the Message interface and represents a bitcoin addrv2
|
||||||
|
// message that can support longer-length addresses like torv3, cjdns, and i2p.
|
||||||
|
// It is used to gossip addresses on the network. Each message is limited to
|
||||||
|
// MaxV2AddrPerMsg addresses. This is the same limit as MsgAddr.
|
||||||
|
type MsgAddrV2 struct {
|
||||||
|
AddrList []*NetAddressV2
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol into a MsgAddrV2.
|
||||||
|
func (m *MsgAddrV2) BtcDecode(r io.Reader, pver uint32,
|
||||||
|
enc MessageEncoding) error {
|
||||||
|
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit to max addresses per message.
|
||||||
|
if count > MaxV2AddrPerMsg {
|
||||||
|
str := fmt.Sprintf("too many addresses for message [count %v,"+
|
||||||
|
" max %v]", count, MaxV2AddrPerMsg)
|
||||||
|
return messageError("MsgAddrV2.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrList := make([]NetAddressV2, count)
|
||||||
|
m.AddrList = make([]*NetAddressV2, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
na := &addrList[i]
|
||||||
|
err := readNetAddressV2(r, pver, na)
|
||||||
|
switch err {
|
||||||
|
case ErrSkippedNetworkID:
|
||||||
|
// This may be a network ID we don't know of, but is
|
||||||
|
// still valid. We can safely skip those.
|
||||||
|
continue
|
||||||
|
case ErrInvalidAddressSize:
|
||||||
|
// The encoding used by the peer does not follow
|
||||||
|
// BIP-155 and we should stop processing this message.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.AddrList = append(m.AddrList, na)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the MsgAddrV2 into a writer w.
|
||||||
|
func (m *MsgAddrV2) BtcEncode(w io.Writer, pver uint32,
|
||||||
|
enc MessageEncoding) error {
|
||||||
|
|
||||||
|
count := len(m.AddrList)
|
||||||
|
if count > MaxV2AddrPerMsg {
|
||||||
|
str := fmt.Sprintf("too many addresses for message [count %v,"+
|
||||||
|
" max %v]", count, MaxV2AddrPerMsg)
|
||||||
|
return messageError("MsgAddrV2.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, na := range m.AddrList {
|
||||||
|
err = writeNetAddressV2(w, pver, na)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for MsgAddrV2.
|
||||||
|
func (m *MsgAddrV2) Command() string {
|
||||||
|
return CmdAddrV2
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length payload possible for MsgAddrV2.
|
||||||
|
func (m *MsgAddrV2) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// The varint that can store the maximum number of addresses is 3 bytes
|
||||||
|
// long. The maximum payload is then 3 + 1000 * maxNetAddressV2Payload.
|
||||||
|
return 3 + (MaxV2AddrPerMsg * maxNetAddressV2Payload())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgAddrV2 returns a new bitcoin addrv2 message that conforms to the
|
||||||
|
// Message interface.
|
||||||
|
func NewMsgAddrV2() *MsgAddrV2 {
|
||||||
|
return &MsgAddrV2{
|
||||||
|
AddrList: make([]*NetAddressV2, 0, MaxV2AddrPerMsg),
|
||||||
|
}
|
||||||
|
}
|
73
wire/msgaddrv2_test.go
Normal file
73
wire/msgaddrv2_test.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestAddrV2Decode checks that decoding an addrv2 message off the wire behaves
|
||||||
|
// as expected. This means ignoring certain addresses, and failing in certain
|
||||||
|
// failure scenarios.
|
||||||
|
func TestAddrV2Decode(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
buf []byte
|
||||||
|
expectedError bool
|
||||||
|
expectedAddrs int
|
||||||
|
}{
|
||||||
|
// Exceeding max addresses.
|
||||||
|
{
|
||||||
|
[]byte{0xfd, 0xff, 0xff},
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Invalid address size.
|
||||||
|
{
|
||||||
|
[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05},
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
|
||||||
|
// One valid address and one skipped address
|
||||||
|
{
|
||||||
|
[]byte{
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04,
|
||||||
|
0x7f, 0x00, 0x00, 0x01, 0x22, 0x22, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x02, 0x10, 0xfd, 0x87, 0xd8,
|
||||||
|
0x7e, 0xeb, 0x43, 0xff, 0xfe, 0xcc, 0x39, 0xa8,
|
||||||
|
0x73, 0x69, 0x15, 0xff, 0xff, 0x22, 0x22,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
r := bytes.NewReader(test.buf)
|
||||||
|
m := &MsgAddrV2{}
|
||||||
|
|
||||||
|
err := m.BtcDecode(r, 0, LatestEncoding)
|
||||||
|
if test.expectedError {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Test #%d expected error", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
t.Errorf("Test #%d unexpected error %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trying to read more should give EOF.
|
||||||
|
var b [1]byte
|
||||||
|
if _, err := r.Read(b[:]); err != io.EOF {
|
||||||
|
t.Errorf("Test #%d did not cleanly finish reading", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(m.AddrList) != test.expectedAddrs {
|
||||||
|
t.Errorf("Test #%d expected %d addrs, instead of %d",
|
||||||
|
i, test.expectedAddrs, len(m.AddrList))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ import (
|
||||||
// XXX pedro: we will probably need to bump this.
|
// XXX pedro: we will probably need to bump this.
|
||||||
const (
|
const (
|
||||||
// ProtocolVersion is the latest protocol version this package supports.
|
// ProtocolVersion is the latest protocol version this package supports.
|
||||||
ProtocolVersion uint32 = 70013
|
ProtocolVersion uint32 = 70016
|
||||||
|
|
||||||
// MultipleAddressVersion is the protocol version which added multiple
|
// MultipleAddressVersion is the protocol version which added multiple
|
||||||
// addresses per message (pver >= MultipleAddressVersion).
|
// addresses per message (pver >= MultipleAddressVersion).
|
||||||
|
@ -51,6 +51,13 @@ const (
|
||||||
// FeeFilterVersion is the protocol version which added a new
|
// FeeFilterVersion is the protocol version which added a new
|
||||||
// feefilter message.
|
// feefilter message.
|
||||||
FeeFilterVersion uint32 = 70013
|
FeeFilterVersion uint32 = 70013
|
||||||
|
|
||||||
|
// AddrV2Version is the protocol version which added two new messages.
|
||||||
|
// sendaddrv2 is sent during the version-verack handshake and signals
|
||||||
|
// support for sending and receiving the addrv2 message. In the future,
|
||||||
|
// new messages that occur during the version-verack handshake will not
|
||||||
|
// come with a protocol version bump.
|
||||||
|
AddrV2Version uint32 = 70016
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceFlag identifies services supported by a bitcoin peer.
|
// ServiceFlag identifies services supported by a bitcoin peer.
|
||||||
|
|
Loading…
Add table
Reference in a new issue