mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-19 01:40:07 +01:00
Merge pull request #1812 from Crypt-iQ/btcd_addrv2
multi: implement BIP-155 addrv2 support
This commit is contained in:
commit
bf64c8bdbb
@ -70,7 +70,7 @@ type serializedAddrManager struct {
|
||||
}
|
||||
|
||||
type localAddress struct {
|
||||
na *wire.NetAddress
|
||||
na *wire.NetAddressV2
|
||||
score AddressPriority
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ const (
|
||||
|
||||
// updateAddress is a helper function to either update an address already known
|
||||
// to the address manager, or to add the address if not already known.
|
||||
func (a *AddrManager) updateAddress(netAddr, srcAddr *wire.NetAddress) {
|
||||
func (a *AddrManager) updateAddress(netAddr, srcAddr *wire.NetAddressV2) {
|
||||
// Filter out non-routable addresses. Note that non-routable
|
||||
// also includes invalid and local addresses.
|
||||
if !IsRoutable(netAddr) {
|
||||
@ -296,7 +296,7 @@ func (a *AddrManager) pickTried(bucket int) *list.Element {
|
||||
return oldestElem
|
||||
}
|
||||
|
||||
func (a *AddrManager) getNewBucket(netAddr, srcAddr *wire.NetAddress) int {
|
||||
func (a *AddrManager) getNewBucket(netAddr, srcAddr *wire.NetAddressV2) int {
|
||||
// bitcoind:
|
||||
// doublesha256(key + sourcegroup + int64(doublesha256(key + group + sourcegroup))%bucket_per_source_group) % num_new_buckets
|
||||
|
||||
@ -318,7 +318,7 @@ func (a *AddrManager) getNewBucket(netAddr, srcAddr *wire.NetAddress) int {
|
||||
return int(binary.LittleEndian.Uint64(hash2) % newBucketCount)
|
||||
}
|
||||
|
||||
func (a *AddrManager) getTriedBucket(netAddr *wire.NetAddress) int {
|
||||
func (a *AddrManager) getTriedBucket(netAddr *wire.NetAddressV2) int {
|
||||
// bitcoind hashes this as:
|
||||
// doublesha256(key + group + truncate_to_64bits(doublesha256(key)) % buckets_per_group) % num_buckets
|
||||
data1 := []byte{}
|
||||
@ -550,7 +550,7 @@ func (a *AddrManager) deserializePeers(filePath string) error {
|
||||
|
||||
// DeserializeNetAddress converts a given address string to a *wire.NetAddress.
|
||||
func (a *AddrManager) DeserializeNetAddress(addr string,
|
||||
services wire.ServiceFlag) (*wire.NetAddress, error) {
|
||||
services wire.ServiceFlag) (*wire.NetAddressV2, error) {
|
||||
|
||||
host, portStr, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
@ -599,7 +599,7 @@ func (a *AddrManager) Stop() error {
|
||||
// AddAddresses adds new addresses to the address manager. It enforces a max
|
||||
// number of addresses and silently ignores duplicate addresses. It is
|
||||
// safe for concurrent access.
|
||||
func (a *AddrManager) AddAddresses(addrs []*wire.NetAddress, srcAddr *wire.NetAddress) {
|
||||
func (a *AddrManager) AddAddresses(addrs []*wire.NetAddressV2, srcAddr *wire.NetAddressV2) {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
@ -611,7 +611,7 @@ func (a *AddrManager) AddAddresses(addrs []*wire.NetAddress, srcAddr *wire.NetAd
|
||||
// AddAddress adds a new address to the address manager. It enforces a max
|
||||
// number of addresses and silently ignores duplicate addresses. It is
|
||||
// safe for concurrent access.
|
||||
func (a *AddrManager) AddAddress(addr, srcAddr *wire.NetAddress) {
|
||||
func (a *AddrManager) AddAddress(addr, srcAddr *wire.NetAddressV2) {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
@ -635,7 +635,7 @@ func (a *AddrManager) AddAddressByIP(addrIP string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid port %s: %v", portStr, err)
|
||||
}
|
||||
na := wire.NewNetAddressIPPort(ip, uint16(port), 0)
|
||||
na := wire.NetAddressV2FromBytes(time.Now(), 0, ip, uint16(port))
|
||||
a.AddAddress(na, na) // XXX use correct src address
|
||||
return nil
|
||||
}
|
||||
@ -664,7 +664,7 @@ func (a *AddrManager) NeedMoreAddresses() bool {
|
||||
|
||||
// AddressCache returns the current address cache. It must be treated as
|
||||
// read-only (but since it is a copy now, this is not as dangerous).
|
||||
func (a *AddrManager) AddressCache() []*wire.NetAddress {
|
||||
func (a *AddrManager) AddressCache() []*wire.NetAddressV2 {
|
||||
allAddr := a.getAddresses()
|
||||
|
||||
numAddresses := len(allAddr) * getAddrPercent / 100
|
||||
@ -686,7 +686,7 @@ func (a *AddrManager) AddressCache() []*wire.NetAddress {
|
||||
|
||||
// getAddresses returns all of the addresses currently found within the
|
||||
// manager's address cache.
|
||||
func (a *AddrManager) getAddresses() []*wire.NetAddress {
|
||||
func (a *AddrManager) getAddresses() []*wire.NetAddressV2 {
|
||||
a.mtx.RLock()
|
||||
defer a.mtx.RUnlock()
|
||||
|
||||
@ -695,7 +695,7 @@ func (a *AddrManager) getAddresses() []*wire.NetAddress {
|
||||
return nil
|
||||
}
|
||||
|
||||
addrs := make([]*wire.NetAddress, 0, addrIndexLen)
|
||||
addrs := make([]*wire.NetAddressV2, 0, addrIndexLen)
|
||||
for _, v := range a.addrIndex {
|
||||
addrs = append(addrs, v.na)
|
||||
}
|
||||
@ -722,20 +722,43 @@ func (a *AddrManager) reset() {
|
||||
// HostToNetAddress returns a netaddress given a host address. If the address
|
||||
// is a Tor .onion address this will be taken care of. Else if the host is
|
||||
// not an IP address it will be resolved (via Tor if required).
|
||||
func (a *AddrManager) HostToNetAddress(host string, port uint16, services wire.ServiceFlag) (*wire.NetAddress, error) {
|
||||
// Tor address is 16 char base32 + ".onion"
|
||||
var ip net.IP
|
||||
if len(host) == 22 && host[16:] == ".onion" {
|
||||
func (a *AddrManager) HostToNetAddress(host string, port uint16,
|
||||
services wire.ServiceFlag) (*wire.NetAddressV2, error) {
|
||||
|
||||
var (
|
||||
na *wire.NetAddressV2
|
||||
ip net.IP
|
||||
)
|
||||
|
||||
// Tor v2 address is 16 char base32 + ".onion"
|
||||
if len(host) == wire.TorV2EncodedSize && host[wire.TorV2EncodedSize-6:] == ".onion" {
|
||||
// go base32 encoding uses capitals (as does the rfc
|
||||
// but Tor and bitcoind tend to user lowercase, so we switch
|
||||
// case here.
|
||||
data, err := base32.StdEncoding.DecodeString(
|
||||
strings.ToUpper(host[:16]))
|
||||
strings.ToUpper(host[:wire.TorV2EncodedSize-6]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prefix := []byte{0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43}
|
||||
ip = net.IP(append(prefix, data...))
|
||||
|
||||
na = wire.NetAddressV2FromBytes(
|
||||
time.Now(), services, data, port,
|
||||
)
|
||||
} else if len(host) == wire.TorV3EncodedSize && host[wire.TorV3EncodedSize-6:] == ".onion" {
|
||||
// Tor v3 addresses are 56 base32 characters with the 6 byte
|
||||
// onion suffix.
|
||||
data, err := base32.StdEncoding.DecodeString(
|
||||
strings.ToUpper(host[:wire.TorV3EncodedSize-6]),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The first 32 bytes is the ed25519 public key and is enough
|
||||
// to reconstruct the .onion address.
|
||||
na = wire.NetAddressV2FromBytes(
|
||||
time.Now(), services, data[:wire.TorV3Size], port,
|
||||
)
|
||||
} else if ip = net.ParseIP(host); ip == nil {
|
||||
ips, err := a.lookupFunc(host)
|
||||
if err != nil {
|
||||
@ -745,30 +768,23 @@ func (a *AddrManager) HostToNetAddress(host string, port uint16, services wire.S
|
||||
return nil, fmt.Errorf("no addresses found for %s", host)
|
||||
}
|
||||
ip = ips[0]
|
||||
|
||||
na = wire.NetAddressV2FromBytes(time.Now(), services, ip, port)
|
||||
} else {
|
||||
// This is an non-nil IP address that was parsed in the else if
|
||||
// above.
|
||||
na = wire.NetAddressV2FromBytes(time.Now(), services, ip, port)
|
||||
}
|
||||
|
||||
return wire.NewNetAddressIPPort(ip, port, services), nil
|
||||
}
|
||||
|
||||
// ipString returns a string for the ip from the provided NetAddress. If the
|
||||
// ip is in the range used for Tor addresses then it will be transformed into
|
||||
// the relevant .onion address.
|
||||
func ipString(na *wire.NetAddress) string {
|
||||
if IsOnionCatTor(na) {
|
||||
// We know now that na.IP is long enough.
|
||||
base32 := base32.StdEncoding.EncodeToString(na.IP[6:])
|
||||
return strings.ToLower(base32) + ".onion"
|
||||
}
|
||||
|
||||
return na.IP.String()
|
||||
return na, nil
|
||||
}
|
||||
|
||||
// NetAddressKey returns a string key in the form of ip:port for IPv4 addresses
|
||||
// or [ip]:port for IPv6 addresses.
|
||||
func NetAddressKey(na *wire.NetAddress) string {
|
||||
// or [ip]:port for IPv6 addresses. It also handles onion v2 and v3 addresses.
|
||||
func NetAddressKey(na *wire.NetAddressV2) string {
|
||||
port := strconv.FormatUint(uint64(na.Port), 10)
|
||||
|
||||
return net.JoinHostPort(ipString(na), port)
|
||||
return net.JoinHostPort(na.Addr.String(), port)
|
||||
}
|
||||
|
||||
// GetAddress returns a single address that should be routable. It picks a
|
||||
@ -842,13 +858,13 @@ func (a *AddrManager) GetAddress() *KnownAddress {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AddrManager) find(addr *wire.NetAddress) *KnownAddress {
|
||||
func (a *AddrManager) find(addr *wire.NetAddressV2) *KnownAddress {
|
||||
return a.addrIndex[NetAddressKey(addr)]
|
||||
}
|
||||
|
||||
// Attempt increases the given address' attempt counter and updates
|
||||
// the last attempt time.
|
||||
func (a *AddrManager) Attempt(addr *wire.NetAddress) {
|
||||
func (a *AddrManager) Attempt(addr *wire.NetAddressV2) {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
@ -869,7 +885,7 @@ func (a *AddrManager) Attempt(addr *wire.NetAddress) {
|
||||
// Connected Marks the given address as currently connected and working at the
|
||||
// current time. The address must already be known to AddrManager else it will
|
||||
// be ignored.
|
||||
func (a *AddrManager) Connected(addr *wire.NetAddress) {
|
||||
func (a *AddrManager) Connected(addr *wire.NetAddressV2) {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
@ -894,7 +910,7 @@ func (a *AddrManager) Connected(addr *wire.NetAddress) {
|
||||
// Good marks the given address as good. To be called after a successful
|
||||
// connection and version exchange. If the address is unknown to the address
|
||||
// manager it will be ignored.
|
||||
func (a *AddrManager) Good(addr *wire.NetAddress) {
|
||||
func (a *AddrManager) Good(addr *wire.NetAddressV2) {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
@ -983,7 +999,7 @@ func (a *AddrManager) Good(addr *wire.NetAddress) {
|
||||
}
|
||||
|
||||
// SetServices sets the services for the giiven address to the provided value.
|
||||
func (a *AddrManager) SetServices(addr *wire.NetAddress, services wire.ServiceFlag) {
|
||||
func (a *AddrManager) SetServices(addr *wire.NetAddressV2, services wire.ServiceFlag) {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
@ -1005,9 +1021,11 @@ func (a *AddrManager) SetServices(addr *wire.NetAddress, services wire.ServiceFl
|
||||
|
||||
// AddLocalAddress adds na to the list of known local addresses to advertise
|
||||
// with the given priority.
|
||||
func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPriority) error {
|
||||
func (a *AddrManager) AddLocalAddress(na *wire.NetAddressV2, priority AddressPriority) error {
|
||||
if !IsRoutable(na) {
|
||||
return fmt.Errorf("address %s is not routable", na.IP)
|
||||
return fmt.Errorf(
|
||||
"address %s is not routable", na.Addr.String(),
|
||||
)
|
||||
}
|
||||
|
||||
a.lamtx.Lock()
|
||||
@ -1030,7 +1048,7 @@ func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPrior
|
||||
|
||||
// getReachabilityFrom returns the relative reachability of the provided local
|
||||
// address to the provided remote address.
|
||||
func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
|
||||
func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddressV2) int {
|
||||
const (
|
||||
Unreachable = 0
|
||||
Default = iota
|
||||
@ -1045,36 +1063,66 @@ func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
|
||||
return Unreachable
|
||||
}
|
||||
|
||||
if IsOnionCatTor(remoteAddr) {
|
||||
if IsOnionCatTor(localAddr) {
|
||||
if remoteAddr.IsTorV3() {
|
||||
if localAddr.IsTorV3() {
|
||||
return Private
|
||||
}
|
||||
|
||||
if IsRoutable(localAddr) && IsIPv4(localAddr) {
|
||||
lna := localAddr.ToLegacy()
|
||||
if IsOnionCatTor(lna) {
|
||||
// Modern v3 clients should not be able to connect to
|
||||
// deprecated v2 hidden services.
|
||||
return Unreachable
|
||||
}
|
||||
|
||||
if IsRoutable(localAddr) && IsIPv4(lna) {
|
||||
return Ipv4
|
||||
}
|
||||
|
||||
return Default
|
||||
}
|
||||
|
||||
if IsRFC4380(remoteAddr) {
|
||||
// We can't be sure if the remote party can actually connect to this
|
||||
// address or not.
|
||||
if localAddr.IsTorV3() {
|
||||
return Default
|
||||
}
|
||||
|
||||
// Convert the V2 addresses into legacy to access the network
|
||||
// functions.
|
||||
remoteLna := remoteAddr.ToLegacy()
|
||||
localLna := localAddr.ToLegacy()
|
||||
|
||||
if IsOnionCatTor(remoteLna) {
|
||||
if IsOnionCatTor(localLna) {
|
||||
return Private
|
||||
}
|
||||
|
||||
if IsRoutable(localAddr) && IsIPv4(localLna) {
|
||||
return Ipv4
|
||||
}
|
||||
|
||||
return Default
|
||||
}
|
||||
|
||||
if IsRFC4380(remoteLna) {
|
||||
if !IsRoutable(localAddr) {
|
||||
return Default
|
||||
}
|
||||
|
||||
if IsRFC4380(localAddr) {
|
||||
if IsRFC4380(localLna) {
|
||||
return Teredo
|
||||
}
|
||||
|
||||
if IsIPv4(localAddr) {
|
||||
if IsIPv4(localLna) {
|
||||
return Ipv4
|
||||
}
|
||||
|
||||
return Ipv6Weak
|
||||
}
|
||||
|
||||
if IsIPv4(remoteAddr) {
|
||||
if IsRoutable(localAddr) && IsIPv4(localAddr) {
|
||||
if IsIPv4(remoteLna) {
|
||||
if IsRoutable(localAddr) && IsIPv4(localLna) {
|
||||
return Ipv4
|
||||
}
|
||||
return Unreachable
|
||||
@ -1083,7 +1131,7 @@ func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
|
||||
/* ipv6 */
|
||||
var tunnelled bool
|
||||
// Is our v6 is tunnelled?
|
||||
if IsRFC3964(localAddr) || IsRFC6052(localAddr) || IsRFC6145(localAddr) {
|
||||
if IsRFC3964(localLna) || IsRFC6052(localLna) || IsRFC6145(localLna) {
|
||||
tunnelled = true
|
||||
}
|
||||
|
||||
@ -1091,11 +1139,11 @@ func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
|
||||
return Default
|
||||
}
|
||||
|
||||
if IsRFC4380(localAddr) {
|
||||
if IsRFC4380(localLna) {
|
||||
return Teredo
|
||||
}
|
||||
|
||||
if IsIPv4(localAddr) {
|
||||
if IsIPv4(localLna) {
|
||||
return Ipv4
|
||||
}
|
||||
|
||||
@ -1109,13 +1157,13 @@ func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
|
||||
|
||||
// GetBestLocalAddress returns the most appropriate local address to use
|
||||
// for the given remote address.
|
||||
func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.NetAddress {
|
||||
func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddressV2) *wire.NetAddressV2 {
|
||||
a.lamtx.Lock()
|
||||
defer a.lamtx.Unlock()
|
||||
|
||||
bestreach := 0
|
||||
var bestscore AddressPriority
|
||||
var bestAddress *wire.NetAddress
|
||||
var bestAddress *wire.NetAddressV2
|
||||
for _, la := range a.localAddresses {
|
||||
reach := getReachabilityFrom(la.na, remoteAddr)
|
||||
if reach > bestreach ||
|
||||
@ -1126,21 +1174,29 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net
|
||||
}
|
||||
}
|
||||
if bestAddress != nil {
|
||||
log.Debugf("Suggesting address %s:%d for %s:%d", bestAddress.IP,
|
||||
bestAddress.Port, remoteAddr.IP, remoteAddr.Port)
|
||||
log.Debugf("Suggesting address %s:%d for %s:%d",
|
||||
bestAddress.Addr.String(), bestAddress.Port,
|
||||
remoteAddr.Addr.String(), remoteAddr.Port)
|
||||
} else {
|
||||
log.Debugf("No worthy address for %s:%d", remoteAddr.IP,
|
||||
remoteAddr.Port)
|
||||
log.Debugf("No worthy address for %s:%d",
|
||||
remoteAddr.Addr.String(), remoteAddr.Port)
|
||||
|
||||
// Send something unroutable if nothing suitable.
|
||||
var ip net.IP
|
||||
if !IsIPv4(remoteAddr) && !IsOnionCatTor(remoteAddr) {
|
||||
ip = net.IPv6zero
|
||||
} else {
|
||||
if remoteAddr.IsTorV3() {
|
||||
ip = net.IPv4zero
|
||||
} else {
|
||||
remoteLna := remoteAddr.ToLegacy()
|
||||
if !IsIPv4(remoteLna) && !IsOnionCatTor(remoteLna) {
|
||||
ip = net.IPv6zero
|
||||
} else {
|
||||
ip = net.IPv4zero
|
||||
}
|
||||
}
|
||||
services := wire.SFNodeNetwork | wire.SFNodeWitness | wire.SFNodeBloom
|
||||
bestAddress = wire.NewNetAddressIPPort(ip, 0, services)
|
||||
bestAddress = wire.NetAddressV2FromBytes(
|
||||
time.Now(), services, ip, 0,
|
||||
)
|
||||
}
|
||||
|
||||
return bestAddress
|
||||
|
@ -6,12 +6,14 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// randAddr generates a *wire.NetAddress backed by a random IPv4/IPv6 address.
|
||||
func randAddr(t *testing.T) *wire.NetAddress {
|
||||
// randAddr generates a *wire.NetAddressV2 backed by a random IPv4/IPv6
|
||||
// address.
|
||||
func randAddr(t *testing.T) *wire.NetAddressV2 {
|
||||
t.Helper()
|
||||
|
||||
ipv4 := rand.Intn(2) == 0
|
||||
@ -30,22 +32,26 @@ func randAddr(t *testing.T) *wire.NetAddress {
|
||||
ip = b[:]
|
||||
}
|
||||
|
||||
return &wire.NetAddress{
|
||||
Services: wire.ServiceFlag(rand.Uint64()),
|
||||
IP: ip,
|
||||
Port: uint16(rand.Uint32()),
|
||||
}
|
||||
services := wire.ServiceFlag(rand.Uint64())
|
||||
port := uint16(rand.Uint32())
|
||||
|
||||
return wire.NetAddressV2FromBytes(
|
||||
time.Now(), services, ip, port,
|
||||
)
|
||||
}
|
||||
|
||||
// assertAddr ensures that the two addresses match. The timestamp is not
|
||||
// checked as it does not affect uniquely identifying a specific address.
|
||||
func assertAddr(t *testing.T, got, expected *wire.NetAddress) {
|
||||
func assertAddr(t *testing.T, got, expected *wire.NetAddressV2) {
|
||||
if got.Services != expected.Services {
|
||||
t.Fatalf("expected address services %v, got %v",
|
||||
expected.Services, got.Services)
|
||||
}
|
||||
if !got.IP.Equal(expected.IP) {
|
||||
t.Fatalf("expected address IP %v, got %v", expected.IP, got.IP)
|
||||
gotAddr := got.Addr.String()
|
||||
expectedAddr := expected.Addr.String()
|
||||
if gotAddr != expectedAddr {
|
||||
t.Fatalf("expected address IP %v, got %v", expectedAddr,
|
||||
gotAddr)
|
||||
}
|
||||
if got.Port != expected.Port {
|
||||
t.Fatalf("expected address port %d, got %d", expected.Port,
|
||||
@ -56,7 +62,7 @@ func assertAddr(t *testing.T, got, expected *wire.NetAddress) {
|
||||
// assertAddrs ensures that the manager's address cache matches the given
|
||||
// expected addresses.
|
||||
func assertAddrs(t *testing.T, addrMgr *AddrManager,
|
||||
expectedAddrs map[string]*wire.NetAddress) {
|
||||
expectedAddrs map[string]*wire.NetAddressV2) {
|
||||
|
||||
t.Helper()
|
||||
|
||||
@ -96,7 +102,7 @@ func TestAddrManagerSerialization(t *testing.T) {
|
||||
// We'll be adding 5 random addresses to the manager.
|
||||
const numAddrs = 5
|
||||
|
||||
expectedAddrs := make(map[string]*wire.NetAddress, numAddrs)
|
||||
expectedAddrs := make(map[string]*wire.NetAddressV2, numAddrs)
|
||||
for i := 0; i < numAddrs; i++ {
|
||||
addr := randAddr(t)
|
||||
expectedAddrs[NetAddressKey(addr)] = addr
|
||||
@ -141,7 +147,7 @@ func TestAddrManagerV1ToV2(t *testing.T) {
|
||||
// each addresses' services will not be stored.
|
||||
const numAddrs = 5
|
||||
|
||||
expectedAddrs := make(map[string]*wire.NetAddress, numAddrs)
|
||||
expectedAddrs := make(map[string]*wire.NetAddressV2, numAddrs)
|
||||
for i := 0; i < numAddrs; i++ {
|
||||
addr := randAddr(t)
|
||||
expectedAddrs[NetAddressKey(addr)] = addr
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
// naTest is used to describe a test to be performed against the NetAddressKey
|
||||
// method.
|
||||
type naTest struct {
|
||||
in wire.NetAddress
|
||||
in wire.NetAddressV2
|
||||
want string
|
||||
}
|
||||
|
||||
@ -93,8 +93,10 @@ func addNaTests() {
|
||||
|
||||
func addNaTest(ip string, port uint16, want string) {
|
||||
nip := net.ParseIP(ip)
|
||||
na := *wire.NewNetAddressIPPort(nip, port, wire.SFNodeNetwork)
|
||||
test := naTest{na, want}
|
||||
na := wire.NetAddressV2FromBytes(
|
||||
time.Now(), wire.SFNodeNetwork, nip, port,
|
||||
)
|
||||
test := naTest{*na, want}
|
||||
naTests = append(naTests, test)
|
||||
}
|
||||
|
||||
@ -157,37 +159,49 @@ func TestAddAddressByIP(t *testing.T) {
|
||||
|
||||
func TestAddLocalAddress(t *testing.T) {
|
||||
var tests = []struct {
|
||||
address wire.NetAddress
|
||||
address wire.NetAddressV2
|
||||
priority addrmgr.AddressPriority
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
wire.NetAddress{IP: net.ParseIP("192.168.0.100")},
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("192.168.0.100"), 0,
|
||||
),
|
||||
addrmgr.InterfacePrio,
|
||||
false,
|
||||
},
|
||||
{
|
||||
wire.NetAddress{IP: net.ParseIP("204.124.1.1")},
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("204.124.1.1"), 0,
|
||||
),
|
||||
addrmgr.InterfacePrio,
|
||||
true,
|
||||
},
|
||||
{
|
||||
wire.NetAddress{IP: net.ParseIP("204.124.1.1")},
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("204.124.1.1"), 0,
|
||||
),
|
||||
addrmgr.BoundPrio,
|
||||
true,
|
||||
},
|
||||
{
|
||||
wire.NetAddress{IP: net.ParseIP("::1")},
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("::1"), 0,
|
||||
),
|
||||
addrmgr.InterfacePrio,
|
||||
false,
|
||||
},
|
||||
{
|
||||
wire.NetAddress{IP: net.ParseIP("fe80::1")},
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("fe80::1"), 0,
|
||||
),
|
||||
addrmgr.InterfacePrio,
|
||||
false,
|
||||
},
|
||||
{
|
||||
wire.NetAddress{IP: net.ParseIP("2620:100::1")},
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("2620:100::1"), 0,
|
||||
),
|
||||
addrmgr.InterfacePrio,
|
||||
true,
|
||||
},
|
||||
@ -197,12 +211,12 @@ func TestAddLocalAddress(t *testing.T) {
|
||||
result := amgr.AddLocalAddress(&test.address, test.priority)
|
||||
if result == nil && !test.valid {
|
||||
t.Errorf("TestAddLocalAddress test #%d failed: %s should have "+
|
||||
"been accepted", x, test.address.IP)
|
||||
"been accepted", x, test.address.Addr.String())
|
||||
continue
|
||||
}
|
||||
if result != nil && test.valid {
|
||||
t.Errorf("TestAddLocalAddress test #%d failed: %s should not have "+
|
||||
"been accepted", x, test.address.IP)
|
||||
"been accepted", x, test.address.Addr.String())
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -257,7 +271,7 @@ func TestNeedMoreAddresses(t *testing.T) {
|
||||
if !b {
|
||||
t.Errorf("Expected that we need more addresses")
|
||||
}
|
||||
addrs := make([]*wire.NetAddress, addrsToAdd)
|
||||
addrs := make([]*wire.NetAddressV2, addrsToAdd)
|
||||
|
||||
var err error
|
||||
for i := 0; i < addrsToAdd; i++ {
|
||||
@ -268,7 +282,9 @@ func TestNeedMoreAddresses(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
|
||||
srcAddr := wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.IPv4(173, 144, 173, 111), 8333,
|
||||
)
|
||||
|
||||
n.AddAddresses(addrs, srcAddr)
|
||||
numAddrs := n.NumAddresses()
|
||||
@ -285,7 +301,7 @@ func TestNeedMoreAddresses(t *testing.T) {
|
||||
func TestGood(t *testing.T) {
|
||||
n := addrmgr.New("testgood", lookupFunc)
|
||||
addrsToAdd := 64 * 64
|
||||
addrs := make([]*wire.NetAddress, addrsToAdd)
|
||||
addrs := make([]*wire.NetAddressV2, addrsToAdd)
|
||||
|
||||
var err error
|
||||
for i := 0; i < addrsToAdd; i++ {
|
||||
@ -296,7 +312,9 @@ func TestGood(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
|
||||
srcAddr := wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.IPv4(173, 144, 173, 111), 8333,
|
||||
)
|
||||
|
||||
n.AddAddresses(addrs, srcAddr)
|
||||
for _, addr := range addrs {
|
||||
@ -331,8 +349,8 @@ func TestGetAddress(t *testing.T) {
|
||||
if ka == nil {
|
||||
t.Fatalf("Did not get an address where there is one in the pool")
|
||||
}
|
||||
if ka.NetAddress().IP.String() != someIP {
|
||||
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP)
|
||||
if ka.NetAddress().Addr.String() != someIP {
|
||||
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().Addr.String(), someIP)
|
||||
}
|
||||
|
||||
// Mark this as a good address and get it
|
||||
@ -341,8 +359,8 @@ func TestGetAddress(t *testing.T) {
|
||||
if ka == nil {
|
||||
t.Fatalf("Did not get an address where there is one in the pool")
|
||||
}
|
||||
if ka.NetAddress().IP.String() != someIP {
|
||||
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP)
|
||||
if ka.NetAddress().Addr.String() != someIP {
|
||||
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().Addr.String(), someIP)
|
||||
}
|
||||
|
||||
numAddrs := n.NumAddresses()
|
||||
@ -352,43 +370,83 @@ func TestGetAddress(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetBestLocalAddress(t *testing.T) {
|
||||
localAddrs := []wire.NetAddress{
|
||||
{IP: net.ParseIP("192.168.0.100")},
|
||||
{IP: net.ParseIP("::1")},
|
||||
{IP: net.ParseIP("fe80::1")},
|
||||
{IP: net.ParseIP("2001:470::1")},
|
||||
localAddrs := []wire.NetAddressV2{
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("192.168.0.100"), 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("::1"), 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("fe80::1"), 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("2001:470::1"), 0,
|
||||
),
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
remoteAddr wire.NetAddress
|
||||
want0 wire.NetAddress
|
||||
want1 wire.NetAddress
|
||||
want2 wire.NetAddress
|
||||
want3 wire.NetAddress
|
||||
remoteAddr wire.NetAddressV2
|
||||
want0 wire.NetAddressV2
|
||||
want1 wire.NetAddressV2
|
||||
want2 wire.NetAddressV2
|
||||
want3 wire.NetAddressV2
|
||||
}{
|
||||
{
|
||||
// Remote connection from public IPv4
|
||||
wire.NetAddress{IP: net.ParseIP("204.124.8.1")},
|
||||
wire.NetAddress{IP: net.IPv4zero},
|
||||
wire.NetAddress{IP: net.IPv4zero},
|
||||
wire.NetAddress{IP: net.ParseIP("204.124.8.100")},
|
||||
wire.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")},
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("204.124.8.1"), 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.IPv4zero, 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.IPv4zero, 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("204.124.8.100"), 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0,
|
||||
net.ParseIP("fd87:d87e:eb43:25::1"), 0,
|
||||
),
|
||||
},
|
||||
{
|
||||
// Remote connection from private IPv4
|
||||
wire.NetAddress{IP: net.ParseIP("172.16.0.254")},
|
||||
wire.NetAddress{IP: net.IPv4zero},
|
||||
wire.NetAddress{IP: net.IPv4zero},
|
||||
wire.NetAddress{IP: net.IPv4zero},
|
||||
wire.NetAddress{IP: net.IPv4zero},
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("172.16.0.254"), 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.IPv4zero, 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.IPv4zero, 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.IPv4zero, 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.IPv4zero, 0,
|
||||
),
|
||||
},
|
||||
{
|
||||
// Remote connection from public IPv6
|
||||
wire.NetAddress{IP: net.ParseIP("2602:100:abcd::102")},
|
||||
wire.NetAddress{IP: net.IPv6zero},
|
||||
wire.NetAddress{IP: net.ParseIP("2001:470::1")},
|
||||
wire.NetAddress{IP: net.ParseIP("2001:470::1")},
|
||||
wire.NetAddress{IP: net.ParseIP("2001:470::1")},
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0,
|
||||
net.ParseIP("2602:100:abcd::102"), 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.IPv6zero, 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("2001:470::1"), 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("2001:470::1"), 0,
|
||||
),
|
||||
*wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("2001:470::1"), 0,
|
||||
),
|
||||
},
|
||||
/* XXX
|
||||
{
|
||||
@ -406,9 +464,12 @@ func TestGetBestLocalAddress(t *testing.T) {
|
||||
// Test against default when there's no address
|
||||
for x, test := range tests {
|
||||
got := amgr.GetBestLocalAddress(&test.remoteAddr)
|
||||
if !test.want0.IP.Equal(got.IP) {
|
||||
wantAddr := test.want0.Addr.String()
|
||||
gotAddr := got.Addr.String()
|
||||
if wantAddr != gotAddr {
|
||||
remoteAddr := test.remoteAddr.Addr.String()
|
||||
t.Errorf("TestGetBestLocalAddress test1 #%d failed for remote address %s: want %s got %s",
|
||||
x, test.remoteAddr.IP, test.want1.IP, got.IP)
|
||||
x, remoteAddr, wantAddr, gotAddr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -420,23 +481,31 @@ func TestGetBestLocalAddress(t *testing.T) {
|
||||
// Test against want1
|
||||
for x, test := range tests {
|
||||
got := amgr.GetBestLocalAddress(&test.remoteAddr)
|
||||
if !test.want1.IP.Equal(got.IP) {
|
||||
wantAddr := test.want1.Addr.String()
|
||||
gotAddr := got.Addr.String()
|
||||
if wantAddr != gotAddr {
|
||||
remoteAddr := test.remoteAddr.Addr.String()
|
||||
t.Errorf("TestGetBestLocalAddress test1 #%d failed for remote address %s: want %s got %s",
|
||||
x, test.remoteAddr.IP, test.want1.IP, got.IP)
|
||||
x, remoteAddr, wantAddr, gotAddr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Add a public IP to the list of local addresses.
|
||||
localAddr := wire.NetAddress{IP: net.ParseIP("204.124.8.100")}
|
||||
amgr.AddLocalAddress(&localAddr, addrmgr.InterfacePrio)
|
||||
localAddr := wire.NetAddressV2FromBytes(
|
||||
time.Now(), 0, net.ParseIP("204.124.8.100"), 0,
|
||||
)
|
||||
amgr.AddLocalAddress(localAddr, addrmgr.InterfacePrio)
|
||||
|
||||
// Test against want2
|
||||
for x, test := range tests {
|
||||
got := amgr.GetBestLocalAddress(&test.remoteAddr)
|
||||
if !test.want2.IP.Equal(got.IP) {
|
||||
wantAddr := test.want2.Addr.String()
|
||||
gotAddr := got.Addr.String()
|
||||
if wantAddr != gotAddr {
|
||||
remoteAddr := test.remoteAddr.Addr.String()
|
||||
t.Errorf("TestGetBestLocalAddress test2 #%d failed for remote address %s: want %s got %s",
|
||||
x, test.remoteAddr.IP, test.want2.IP, got.IP)
|
||||
x, remoteAddr, wantAddr, gotAddr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func TstKnownAddressChance(ka *KnownAddress) float64 {
|
||||
return ka.chance()
|
||||
}
|
||||
|
||||
func TstNewKnownAddress(na *wire.NetAddress, attempts int,
|
||||
func TstNewKnownAddress(na *wire.NetAddressV2, attempts int,
|
||||
lastattempt, lastsuccess time.Time, tried bool, refs int) *KnownAddress {
|
||||
return &KnownAddress{na: na, attempts: attempts, lastattempt: lastattempt,
|
||||
lastsuccess: lastsuccess, tried: tried, refs: refs}
|
||||
|
@ -15,8 +15,8 @@ import (
|
||||
// to determine how viable an address is.
|
||||
type KnownAddress struct {
|
||||
mtx sync.RWMutex // na and lastattempt
|
||||
na *wire.NetAddress
|
||||
srcAddr *wire.NetAddress
|
||||
na *wire.NetAddressV2
|
||||
srcAddr *wire.NetAddressV2
|
||||
attempts int
|
||||
lastattempt time.Time
|
||||
lastsuccess time.Time
|
||||
@ -24,9 +24,9 @@ type KnownAddress struct {
|
||||
refs int // reference count of new buckets
|
||||
}
|
||||
|
||||
// NetAddress returns the underlying wire.NetAddress associated with the
|
||||
// NetAddress returns the underlying wire.NetAddressV2 associated with the
|
||||
// known address.
|
||||
func (ka *KnownAddress) NetAddress() *wire.NetAddress {
|
||||
func (ka *KnownAddress) NetAddress() *wire.NetAddressV2 {
|
||||
ka.mtx.RLock()
|
||||
defer ka.mtx.RUnlock()
|
||||
return ka.na
|
||||
|
@ -21,27 +21,27 @@ func TestChance(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
//Test normal case
|
||||
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
|
||||
addrmgr.TstNewKnownAddress(&wire.NetAddressV2{Timestamp: now.Add(-35 * time.Second)},
|
||||
0, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
|
||||
1.0,
|
||||
}, {
|
||||
//Test case in which lastseen < 0
|
||||
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(20 * time.Second)},
|
||||
addrmgr.TstNewKnownAddress(&wire.NetAddressV2{Timestamp: now.Add(20 * time.Second)},
|
||||
0, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
|
||||
1.0,
|
||||
}, {
|
||||
//Test case in which lastattempt < 0
|
||||
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
|
||||
addrmgr.TstNewKnownAddress(&wire.NetAddressV2{Timestamp: now.Add(-35 * time.Second)},
|
||||
0, time.Now().Add(30*time.Minute), time.Now(), false, 0),
|
||||
1.0 * .01,
|
||||
}, {
|
||||
//Test case in which lastattempt < ten minutes
|
||||
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
|
||||
addrmgr.TstNewKnownAddress(&wire.NetAddressV2{Timestamp: now.Add(-35 * time.Second)},
|
||||
0, time.Now().Add(-5*time.Minute), time.Now(), false, 0),
|
||||
1.0 * .01,
|
||||
}, {
|
||||
//Test case with several failed attempts.
|
||||
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
|
||||
addrmgr.TstNewKnownAddress(&wire.NetAddressV2{Timestamp: now.Add(-35 * time.Second)},
|
||||
2, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
|
||||
1 / 1.5 / 1.5,
|
||||
},
|
||||
@ -65,10 +65,10 @@ func TestIsBad(t *testing.T) {
|
||||
hoursOld := now.Add(-5 * time.Hour)
|
||||
zeroTime := time.Time{}
|
||||
|
||||
futureNa := &wire.NetAddress{Timestamp: future}
|
||||
minutesOldNa := &wire.NetAddress{Timestamp: minutesOld}
|
||||
monthOldNa := &wire.NetAddress{Timestamp: monthOld}
|
||||
currentNa := &wire.NetAddress{Timestamp: secondsOld}
|
||||
futureNa := &wire.NetAddressV2{Timestamp: future}
|
||||
minutesOldNa := &wire.NetAddressV2{Timestamp: minutesOld}
|
||||
monthOldNa := &wire.NetAddressV2{Timestamp: monthOld}
|
||||
currentNa := &wire.NetAddressV2{Timestamp: secondsOld}
|
||||
|
||||
//Test addresses that have been tried in the last minute.
|
||||
if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(futureNa, 3, secondsOld, zeroTime, false, 0)) {
|
||||
|
@ -222,11 +222,20 @@ func IsValid(na *wire.NetAddress) bool {
|
||||
// IsRoutable returns whether or not the passed address is routable over
|
||||
// the public internet. This is true as long as the address is valid and is not
|
||||
// in any reserved ranges.
|
||||
func IsRoutable(na *wire.NetAddress) bool {
|
||||
return IsValid(na) && !(IsRFC1918(na) || IsRFC2544(na) ||
|
||||
IsRFC3927(na) || IsRFC4862(na) || IsRFC3849(na) ||
|
||||
IsRFC4843(na) || IsRFC5737(na) || IsRFC6598(na) ||
|
||||
IsLocal(na) || (IsRFC4193(na) && !IsOnionCatTor(na)))
|
||||
func IsRoutable(na *wire.NetAddressV2) bool {
|
||||
if na.IsTorV3() {
|
||||
// na is a torv3 address, return true.
|
||||
return true
|
||||
}
|
||||
|
||||
// Else na can be represented as a legacy NetAddress since i2p and
|
||||
// cjdns are unsupported.
|
||||
lna := na.ToLegacy()
|
||||
return IsValid(lna) && !(IsRFC1918(lna) || IsRFC2544(lna) ||
|
||||
IsRFC3927(lna) || IsRFC4862(lna) || IsRFC3849(lna) ||
|
||||
IsRFC4843(lna) || IsRFC5737(lna) || IsRFC6598(lna) ||
|
||||
IsLocal(lna) || (IsRFC4193(lna) &&
|
||||
!IsOnionCatTor(lna)))
|
||||
}
|
||||
|
||||
// GroupKey returns a string representing the network group an address is part
|
||||
@ -234,48 +243,56 @@ func IsRoutable(na *wire.NetAddress) bool {
|
||||
// "local" for a local address, the string "tor:key" where key is the /4 of the
|
||||
// onion address for Tor address, and the string "unroutable" for an unroutable
|
||||
// address.
|
||||
func GroupKey(na *wire.NetAddress) string {
|
||||
if IsLocal(na) {
|
||||
func GroupKey(na *wire.NetAddressV2) string {
|
||||
if na.IsTorV3() {
|
||||
// na is a torv3 address. Use the same network group keying as
|
||||
// for torv2.
|
||||
return fmt.Sprintf("tor:%d", na.TorV3Key()&((1<<4)-1))
|
||||
}
|
||||
|
||||
lna := na.ToLegacy()
|
||||
|
||||
if IsLocal(lna) {
|
||||
return "local"
|
||||
}
|
||||
if !IsRoutable(na) {
|
||||
return "unroutable"
|
||||
}
|
||||
if IsIPv4(na) {
|
||||
return na.IP.Mask(net.CIDRMask(16, 32)).String()
|
||||
if IsIPv4(lna) {
|
||||
return lna.IP.Mask(net.CIDRMask(16, 32)).String()
|
||||
}
|
||||
if IsRFC6145(na) || IsRFC6052(na) {
|
||||
if IsRFC6145(lna) || IsRFC6052(lna) {
|
||||
// last four bytes are the ip address
|
||||
ip := na.IP[12:16]
|
||||
ip := lna.IP[12:16]
|
||||
return ip.Mask(net.CIDRMask(16, 32)).String()
|
||||
}
|
||||
|
||||
if IsRFC3964(na) {
|
||||
ip := na.IP[2:6]
|
||||
if IsRFC3964(lna) {
|
||||
ip := lna.IP[2:6]
|
||||
return ip.Mask(net.CIDRMask(16, 32)).String()
|
||||
|
||||
}
|
||||
if IsRFC4380(na) {
|
||||
if IsRFC4380(lna) {
|
||||
// teredo tunnels have the last 4 bytes as the v4 address XOR
|
||||
// 0xff.
|
||||
ip := net.IP(make([]byte, 4))
|
||||
for i, byte := range na.IP[12:16] {
|
||||
for i, byte := range lna.IP[12:16] {
|
||||
ip[i] = byte ^ 0xff
|
||||
}
|
||||
return ip.Mask(net.CIDRMask(16, 32)).String()
|
||||
}
|
||||
if IsOnionCatTor(na) {
|
||||
if IsOnionCatTor(lna) {
|
||||
// group is keyed off the first 4 bits of the actual onion key.
|
||||
return fmt.Sprintf("tor:%d", na.IP[6]&((1<<4)-1))
|
||||
return fmt.Sprintf("tor:%d", lna.IP[6]&((1<<4)-1))
|
||||
}
|
||||
|
||||
// OK, so now we know ourselves to be a IPv6 address.
|
||||
// bitcoind uses /32 for everything, except for Hurricane Electric's
|
||||
// (he.net) IP range, which it uses /36 for.
|
||||
bits := 32
|
||||
if heNet.Contains(na.IP) {
|
||||
if heNet.Contains(lna.IP) {
|
||||
bits = 36
|
||||
}
|
||||
|
||||
return na.IP.Mask(net.CIDRMask(bits, 128)).String()
|
||||
return lna.IP.Mask(net.CIDRMask(bits, 128)).String()
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ package addrmgr_test
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/addrmgr"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
@ -136,7 +137,10 @@ func TestIPTypes(t *testing.T) {
|
||||
t.Errorf("IsValid %s\n got: %v want: %v", test.in.IP, rv, test.valid)
|
||||
}
|
||||
|
||||
if rv := addrmgr.IsRoutable(&test.in); rv != test.routable {
|
||||
currentNa := wire.NetAddressV2FromBytes(
|
||||
time.Now(), test.in.Services, test.in.IP, test.in.Port,
|
||||
)
|
||||
if rv := addrmgr.IsRoutable(currentNa); rv != test.routable {
|
||||
t.Errorf("IsRoutable %s\n got: %v want: %v", test.in.IP, rv, test.routable)
|
||||
}
|
||||
}
|
||||
@ -192,8 +196,10 @@ func TestGroupKey(t *testing.T) {
|
||||
|
||||
for i, test := range tests {
|
||||
nip := net.ParseIP(test.ip)
|
||||
na := *wire.NewNetAddressIPPort(nip, 8333, wire.SFNodeNetwork)
|
||||
if key := addrmgr.GroupKey(&na); key != test.expected {
|
||||
na := wire.NetAddressV2FromBytes(
|
||||
time.Now(), wire.SFNodeNetwork, nip, 8333,
|
||||
)
|
||||
if key := addrmgr.GroupKey(na); key != test.expected {
|
||||
t.Errorf("TestGroupKey #%d (%s): unexpected group key "+
|
||||
"- got '%s', want '%s'", i, test.name,
|
||||
key, test.expected)
|
||||
|
@ -24,7 +24,7 @@ const (
|
||||
|
||||
// OnSeed is the signature of the callback function which is invoked when DNS
|
||||
// seeding is succesfull.
|
||||
type OnSeed func(addrs []*wire.NetAddress)
|
||||
type OnSeed func(addrs []*wire.NetAddressV2)
|
||||
|
||||
// LookupFunc is the signature of the DNS lookup function.
|
||||
type LookupFunc func(string) ([]net.IP, error)
|
||||
@ -56,11 +56,11 @@ func SeedFromDNS(chainParams *chaincfg.Params, reqServices wire.ServiceFlag,
|
||||
if numPeers == 0 {
|
||||
return
|
||||
}
|
||||
addresses := make([]*wire.NetAddress, len(seedpeers))
|
||||
addresses := make([]*wire.NetAddressV2, len(seedpeers))
|
||||
// if this errors then we have *real* problems
|
||||
intPort, _ := strconv.Atoi(chainParams.DefaultPort)
|
||||
for i, peer := range seedpeers {
|
||||
addresses[i] = wire.NewNetAddressTimestamp(
|
||||
addresses[i] = wire.NetAddressV2FromBytes(
|
||||
// bitcoind seeds with addresses from
|
||||
// a time randomly selected between 3
|
||||
// and 7 days ago.
|
||||
|
231
peer/peer.go
231
peer/peer.go
@ -29,7 +29,7 @@ import (
|
||||
|
||||
const (
|
||||
// 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
|
||||
// inv message to a peer.
|
||||
@ -102,6 +102,9 @@ type MessageListeners struct {
|
||||
// OnAddr is invoked when a peer receives an addr bitcoin message.
|
||||
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 func(p *Peer, msg *wire.MsgPing)
|
||||
|
||||
@ -197,6 +200,9 @@ type MessageListeners struct {
|
||||
// message.
|
||||
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
|
||||
// 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
|
||||
@ -399,7 +405,7 @@ type AddrFunc func(remoteAddr *wire.NetAddress) *wire.NetAddress
|
||||
// HostToNetAddrFunc is a func which takes a host, port, services and returns
|
||||
// the netaddress.
|
||||
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
|
||||
// messages are read via the inHandler goroutine and generally dispatched to
|
||||
@ -445,7 +451,7 @@ type Peer struct {
|
||||
inbound bool
|
||||
|
||||
flagsMtx sync.Mutex // protects the peer flags below
|
||||
na *wire.NetAddress
|
||||
na *wire.NetAddressV2
|
||||
id int32
|
||||
userAgent string
|
||||
services wire.ServiceFlag
|
||||
@ -455,6 +461,7 @@ type Peer struct {
|
||||
sendHeadersPreferred bool // peer sent a sendheaders message
|
||||
verAckReceived bool
|
||||
witnessEnabled bool
|
||||
sendAddrV2 bool
|
||||
|
||||
wireEncoding wire.MessageEncoding
|
||||
|
||||
@ -585,7 +592,7 @@ func (p *Peer) ID() int32 {
|
||||
// NA returns the peer network address.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (p *Peer) NA() *wire.NetAddress {
|
||||
func (p *Peer) NA() *wire.NetAddressV2 {
|
||||
p.flagsMtx.Lock()
|
||||
na := p.na
|
||||
p.flagsMtx.Unlock()
|
||||
@ -820,6 +827,16 @@ func (p *Peer) IsWitnessEnabled() bool {
|
||||
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
|
||||
// addresses. This function is useful over manually sending the message via
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
// and stop hash. It will ignore back-to-back duplicate requests.
|
||||
//
|
||||
@ -1363,6 +1412,19 @@ out:
|
||||
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
|
||||
// local peer is not forcibly disconnecting and the
|
||||
// remote peer has not disconnected.
|
||||
@ -1404,6 +1466,11 @@ out:
|
||||
)
|
||||
break out
|
||||
|
||||
case *wire.MsgSendAddrV2:
|
||||
// Disconnect if peer sends this after the handshake is
|
||||
// completed.
|
||||
break out
|
||||
|
||||
case *wire.MsgGetAddr:
|
||||
if p.cfg.Listeners.OnGetAddr != nil {
|
||||
p.cfg.Listeners.OnGetAddr(p, msg)
|
||||
@ -1414,6 +1481,11 @@ out:
|
||||
p.cfg.Listeners.OnAddr(p, msg)
|
||||
}
|
||||
|
||||
case *wire.MsgAddrV2:
|
||||
if p.cfg.Listeners.OnAddrV2 != nil {
|
||||
p.cfg.Listeners.OnAddrV2(p, msg)
|
||||
}
|
||||
|
||||
case *wire.MsgPing:
|
||||
p.handlePingMsg(msg)
|
||||
if p.cfg.Listeners.OnPing != nil {
|
||||
@ -1986,29 +2058,8 @@ func (p *Peer) readRemoteVersionMsg() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// readRemoteVerAckMsg waits for the next message to arrive from the remote
|
||||
// peer. If this message is not a verack message, then an error is returned.
|
||||
// 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)
|
||||
}
|
||||
|
||||
// processRemoteVerAckMsg takes the verack from the remote peer and handles it.
|
||||
func (p *Peer) processRemoteVerAckMsg(msg *wire.MsgVerAck) {
|
||||
p.flagsMtx.Lock()
|
||||
p.verAckReceived = true
|
||||
p.flagsMtx.Unlock()
|
||||
@ -2016,8 +2067,6 @@ func (p *Peer) readRemoteVerAckMsg() error {
|
||||
if p.cfg.Listeners.OnVerAck != nil {
|
||||
p.cfg.Listeners.OnVerAck(p, msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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 != "" {
|
||||
proxyaddress, _, err := net.SplitHostPort(p.cfg.Proxy)
|
||||
// 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.Services)
|
||||
}
|
||||
@ -2092,14 +2149,71 @@ func (p *Peer) writeLocalVersionMsg() error {
|
||||
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
|
||||
// peer. The events should occur in the following order, otherwise an error is
|
||||
// returned:
|
||||
//
|
||||
// 1. Remote peer sends their version.
|
||||
// 2. We send our version.
|
||||
// 3. We send our verack.
|
||||
// 4. Remote peer sends their verack.
|
||||
// 3. We send sendaddrv2 if their version is >= 70016.
|
||||
// 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 {
|
||||
if err := p.readRemoteVersionMsg(); err != nil {
|
||||
return err
|
||||
@ -2109,12 +2223,22 @@ func (p *Peer) negotiateInboundProtocol() error {
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
@ -2123,8 +2247,11 @@ func (p *Peer) negotiateInboundProtocol() error {
|
||||
//
|
||||
// 1. We send our 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.
|
||||
// 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 {
|
||||
if err := p.writeLocalVersionMsg(); err != nil {
|
||||
return err
|
||||
@ -2134,11 +2261,22 @@ func (p *Peer) negotiateOutboundProtocol() error {
|
||||
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 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.
|
||||
@ -2201,7 +2339,12 @@ func (p *Peer) AssociateConnection(conn net.Conn) {
|
||||
p.Disconnect()
|
||||
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() {
|
||||
@ -2267,7 +2410,10 @@ func NewInboundPeer(cfg *Config) *Peer {
|
||||
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) {
|
||||
p := newPeerBase(cfg, false)
|
||||
p.addr = addr
|
||||
@ -2289,7 +2435,12 @@ func NewOutboundPeer(cfg *Config, addr string) (*Peer, error) {
|
||||
}
|
||||
p.na = na
|
||||
} 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
|
||||
|
@ -289,18 +289,16 @@ func TestPeerConnection(t *testing.T) {
|
||||
{
|
||||
"basic handshake",
|
||||
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.AssociateConnection(inConn)
|
||||
|
||||
outPeer, err := peer.NewOutboundPeer(peer2Cfg, "10.0.0.2:8333")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
outPeer.AssociateConnection(outConn)
|
||||
|
||||
err = setupPeerConnection(inPeer, outPeer)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
select {
|
||||
@ -315,18 +313,16 @@ func TestPeerConnection(t *testing.T) {
|
||||
{
|
||||
"socks proxy",
|
||||
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.AssociateConnection(inConn)
|
||||
|
||||
outPeer, err := peer.NewOutboundPeer(peer2Cfg, "10.0.0.2:8333")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
outPeer.AssociateConnection(outConn)
|
||||
|
||||
err = setupPeerConnection(inPeer, outPeer)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
select {
|
||||
@ -359,7 +355,7 @@ func TestPeerConnection(t *testing.T) {
|
||||
// TestPeerListeners tests that the peer listeners are called as expected.
|
||||
func TestPeerListeners(t *testing.T) {
|
||||
verack := make(chan struct{}, 1)
|
||||
ok := make(chan wire.Message, 20)
|
||||
ok := make(chan wire.Message, 22)
|
||||
peerCfg := &peer.Config{
|
||||
Listeners: peer.MessageListeners{
|
||||
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) {
|
||||
ok <- msg
|
||||
},
|
||||
OnSendAddrV2: func(p *peer.Peer, msg *wire.MsgSendAddrV2) {
|
||||
ok <- msg
|
||||
},
|
||||
OnAddrV2: func(p *peer.Peer, msg *wire.MsgAddrV2) {
|
||||
ok <- msg
|
||||
},
|
||||
},
|
||||
UserAgentName: "peer",
|
||||
UserAgentVersion: "1.0",
|
||||
@ -456,12 +458,7 @@ func TestPeerListeners(t *testing.T) {
|
||||
TrickleInterval: time.Second * 10,
|
||||
AllowSelfConns: true,
|
||||
}
|
||||
inConn, outConn := pipe(
|
||||
&conn{raddr: "10.0.0.1:8333"},
|
||||
&conn{raddr: "10.0.0.2:8333"},
|
||||
)
|
||||
inPeer := peer.NewInboundPeer(peerCfg)
|
||||
inPeer.AssociateConnection(inConn)
|
||||
|
||||
peerCfg.Listeners = peer.MessageListeners{
|
||||
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)
|
||||
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++ {
|
||||
select {
|
||||
@ -597,6 +599,14 @@ func TestPeerListeners(t *testing.T) {
|
||||
"OnSendHeaders",
|
||||
wire.NewMsgSendHeaders(),
|
||||
},
|
||||
{
|
||||
"OnSendAddrV2",
|
||||
wire.NewMsgSendAddrV2(),
|
||||
},
|
||||
{
|
||||
"OnAddrV2",
|
||||
wire.NewMsgAddrV2(),
|
||||
},
|
||||
}
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for _, test := range tests {
|
||||
@ -881,17 +891,17 @@ func TestDuplicateVersionMsg(t *testing.T) {
|
||||
Services: 0,
|
||||
AllowSelfConns: true,
|
||||
}
|
||||
inConn, outConn := pipe(
|
||||
&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)
|
||||
outPeer, err := peer.NewOutboundPeer(peerCfg, "10.0.0.2:8333")
|
||||
if err != nil {
|
||||
t.Fatalf("NewOutboundPeer: unexpected err: %v\n", err)
|
||||
}
|
||||
outPeer.AssociateConnection(outConn)
|
||||
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.
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
@ -947,17 +957,16 @@ func TestUpdateLastBlockHeight(t *testing.T) {
|
||||
remotePeerCfg.NewestBlock = func() (*chainhash.Hash, int32, error) {
|
||||
return &chainhash.Hash{}, remotePeerHeight, nil
|
||||
}
|
||||
inConn, outConn := pipe(
|
||||
&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)
|
||||
localPeer, err := peer.NewOutboundPeer(&peerCfg, "10.0.0.2:8333")
|
||||
if err != nil {
|
||||
t.Fatalf("NewOutboundPeer: unexpected err: %v\n", err)
|
||||
}
|
||||
localPeer.AssociateConnection(outConn)
|
||||
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.
|
||||
for i := 0; i < 2; i++ {
|
||||
@ -989,3 +998,214 @@ func TestUpdateLastBlockHeight(t *testing.T) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ func (cm *rpcConnManager) RelayTransactions(txns []*mempool.TxDesc) {
|
||||
//
|
||||
// This function is safe for concurrent access and is part of the
|
||||
// rpcserverConnManager interface implementation.
|
||||
func (cm *rpcConnManager) NodeAddresses() []*wire.NetAddress {
|
||||
func (cm *rpcConnManager) NodeAddresses() []*wire.NetAddressV2 {
|
||||
return cm.server.addrManager.AddressCache()
|
||||
}
|
||||
|
||||
|
@ -2521,7 +2521,7 @@ func handleGetNodeAddresses(s *rpcServer, cmd interface{}, closeChan <-chan stru
|
||||
address := &btcjson.GetNodeAddressesResult{
|
||||
Time: node.Timestamp.Unix(),
|
||||
Services: uint64(node.Services),
|
||||
Address: node.IP.String(),
|
||||
Address: node.Addr.String(),
|
||||
Port: node.Port,
|
||||
}
|
||||
addresses = append(addresses, address)
|
||||
@ -4527,7 +4527,7 @@ type rpcserverConnManager interface {
|
||||
|
||||
// NodeAddresses returns an array consisting node addresses which can
|
||||
// potentially be used to find new nodes in the network.
|
||||
NodeAddresses() []*wire.NetAddress
|
||||
NodeAddresses() []*wire.NetAddressV2
|
||||
}
|
||||
|
||||
// rpcserverSyncManager represents a sync manager for use with the RPC server.
|
||||
|
148
server.go
148
server.go
@ -25,6 +25,8 @@ import (
|
||||
"github.com/btcsuite/btcd/addrmgr"
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/blockchain/indexers"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/btcutil/bloom"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/connmgr"
|
||||
@ -36,8 +38,7 @@ import (
|
||||
"github.com/btcsuite/btcd/peer"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/btcutil/bloom"
|
||||
"github.com/decred/dcrd/lru"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -274,7 +275,7 @@ type serverPeer struct {
|
||||
isWhitelisted bool
|
||||
filter *bloom.Filter
|
||||
addressesMtx sync.RWMutex
|
||||
knownAddresses map[string]struct{}
|
||||
knownAddresses lru.Cache
|
||||
banScore connmgr.DynamicBanScore
|
||||
quit chan struct{}
|
||||
// The following chans are used to sync blockmanager and server.
|
||||
@ -289,7 +290,7 @@ func newServerPeer(s *server, isPersistent bool) *serverPeer {
|
||||
server: s,
|
||||
persistent: isPersistent,
|
||||
filter: bloom.LoadFilter(nil),
|
||||
knownAddresses: make(map[string]struct{}),
|
||||
knownAddresses: lru.NewCache(5000),
|
||||
quit: make(chan struct{}),
|
||||
txProcessed: make(chan struct{}, 1),
|
||||
blockProcessed: make(chan struct{}, 1),
|
||||
@ -305,18 +306,18 @@ func (sp *serverPeer) newestBlock() (*chainhash.Hash, int32, error) {
|
||||
|
||||
// addKnownAddresses adds the given addresses to the set of known addresses to
|
||||
// the peer to prevent sending duplicate addresses.
|
||||
func (sp *serverPeer) addKnownAddresses(addresses []*wire.NetAddress) {
|
||||
func (sp *serverPeer) addKnownAddresses(addresses []*wire.NetAddressV2) {
|
||||
sp.addressesMtx.Lock()
|
||||
for _, na := range addresses {
|
||||
sp.knownAddresses[addrmgr.NetAddressKey(na)] = struct{}{}
|
||||
sp.knownAddresses.Add(addrmgr.NetAddressKey(na))
|
||||
}
|
||||
sp.addressesMtx.Unlock()
|
||||
}
|
||||
|
||||
// addressKnown true if the given address is already known to the peer.
|
||||
func (sp *serverPeer) addressKnown(na *wire.NetAddress) bool {
|
||||
func (sp *serverPeer) addressKnown(na *wire.NetAddressV2) bool {
|
||||
sp.addressesMtx.RLock()
|
||||
_, exists := sp.knownAddresses[addrmgr.NetAddressKey(na)]
|
||||
exists := sp.knownAddresses.Contains(addrmgr.NetAddressKey(na))
|
||||
sp.addressesMtx.RUnlock()
|
||||
return exists
|
||||
}
|
||||
@ -340,23 +341,73 @@ func (sp *serverPeer) relayTxDisabled() bool {
|
||||
return isDisabled
|
||||
}
|
||||
|
||||
// pushAddrMsg sends an addr message to the connected peer using the provided
|
||||
// addresses.
|
||||
func (sp *serverPeer) pushAddrMsg(addresses []*wire.NetAddress) {
|
||||
// Filter addresses already known to the peer.
|
||||
addrs := make([]*wire.NetAddress, 0, len(addresses))
|
||||
for _, addr := range addresses {
|
||||
if !sp.addressKnown(addr) {
|
||||
// pushAddrMsg sends a legacy addr message to the connected peer using the
|
||||
// provided addresses.
|
||||
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))
|
||||
for _, addr := range addresses {
|
||||
// Filter addresses already known to the peer.
|
||||
if sp.addressKnown(addr) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Must skip the V3 addresses for legacy ADDR messages.
|
||||
if addr.IsTorV3() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert the NetAddressV2 to a legacy address.
|
||||
addrs = append(addrs, addr.ToLegacy())
|
||||
}
|
||||
|
||||
known, err := sp.PushAddrMsg(addrs)
|
||||
if err != nil {
|
||||
peerLog.Errorf("Can't push address message to %s: %v", sp.Peer, err)
|
||||
peerLog.Errorf(
|
||||
"Can't push address message to %s: %v", sp.Peer, err,
|
||||
)
|
||||
sp.Disconnect()
|
||||
return
|
||||
}
|
||||
sp.addKnownAddresses(known)
|
||||
|
||||
// Convert all of the known addresses to NetAddressV2 to add them to
|
||||
// the set of known addresses.
|
||||
knownAddrs := make([]*wire.NetAddressV2, 0, len(known))
|
||||
for _, knownAddr := range known {
|
||||
currentKna := wire.NetAddressV2FromBytes(
|
||||
knownAddr.Timestamp, knownAddr.Services,
|
||||
knownAddr.IP, knownAddr.Port,
|
||||
)
|
||||
knownAddrs = append(knownAddrs, currentKna)
|
||||
}
|
||||
sp.addKnownAddresses(knownAddrs)
|
||||
}
|
||||
|
||||
// addBanScore increases the persistent and decaying ban score fields by the
|
||||
@ -1272,6 +1323,7 @@ func (sp *serverPeer) OnAddr(_ *peer.Peer, msg *wire.MsgAddr) {
|
||||
return
|
||||
}
|
||||
|
||||
addrs := make([]*wire.NetAddressV2, 0, len(msg.AddrList))
|
||||
for _, na := range msg.AddrList {
|
||||
// Don't add more address if we're disconnecting.
|
||||
if !sp.Connected() {
|
||||
@ -1286,8 +1338,14 @@ func (sp *serverPeer) OnAddr(_ *peer.Peer, msg *wire.MsgAddr) {
|
||||
na.Timestamp = now.Add(-1 * time.Hour * 24 * 5)
|
||||
}
|
||||
|
||||
// Add address to known addresses for this peer.
|
||||
sp.addKnownAddresses([]*wire.NetAddress{na})
|
||||
// Add address to known addresses for this peer. This is
|
||||
// converted to NetAddressV2 since that's what the address
|
||||
// manager uses.
|
||||
currentNa := wire.NetAddressV2FromBytes(
|
||||
na.Timestamp, na.Services, na.IP, na.Port,
|
||||
)
|
||||
addrs = append(addrs, currentNa)
|
||||
sp.addKnownAddresses([]*wire.NetAddressV2{currentNa})
|
||||
}
|
||||
|
||||
// Add addresses to server address manager. The address manager handles
|
||||
@ -1295,6 +1353,45 @@ func (sp *serverPeer) OnAddr(_ *peer.Peer, msg *wire.MsgAddr) {
|
||||
// addresses, and last seen updates.
|
||||
// XXX bitcoind gives a 2 hour time penalty here, do we want to do the
|
||||
// same?
|
||||
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())
|
||||
}
|
||||
|
||||
@ -1700,7 +1797,7 @@ func (s *server) handleAddPeerMsg(state *peerState, sp *serverPeer) bool {
|
||||
lna := s.addrManager.GetBestLocalAddress(sp.NA())
|
||||
if addrmgr.IsRoutable(lna) {
|
||||
// Filter addresses the peer already knows about.
|
||||
addresses := []*wire.NetAddress{lna}
|
||||
addresses := []*wire.NetAddressV2{lna}
|
||||
sp.pushAddrMsg(addresses)
|
||||
}
|
||||
}
|
||||
@ -2044,6 +2141,7 @@ func newPeerConfig(sp *serverPeer) *peer.Config {
|
||||
OnFilterLoad: sp.OnFilterLoad,
|
||||
OnGetAddr: sp.OnGetAddr,
|
||||
OnAddr: sp.OnAddr,
|
||||
OnAddrV2: sp.OnAddrV2,
|
||||
OnRead: sp.OnRead,
|
||||
OnWrite: sp.OnWrite,
|
||||
OnNotFound: sp.OnNotFound,
|
||||
@ -2152,7 +2250,7 @@ func (s *server) peerHandler() {
|
||||
if !cfg.DisableDNSSeed {
|
||||
// Add peers discovered through DNS to the address manager.
|
||||
connmgr.SeedFromDNS(activeNetParams.Params, defaultRequiredServices,
|
||||
btcdLookup, func(addrs []*wire.NetAddress) {
|
||||
btcdLookup, func(addrs []*wire.NetAddressV2) {
|
||||
// Bitcoind uses a lookup of the dns seeder here. This
|
||||
// is rather strange since the values looked up by the
|
||||
// DNS seed lookups will vary quite a lot.
|
||||
@ -2543,8 +2641,8 @@ out:
|
||||
srvrLog.Warnf("UPnP can't get external address: %v", err)
|
||||
continue out
|
||||
}
|
||||
na := wire.NewNetAddressIPPort(externalip, uint16(listenPort),
|
||||
s.services)
|
||||
na := wire.NetAddressV2FromBytes(time.Now(), s.services,
|
||||
externalip, uint16(listenPort))
|
||||
err = s.addrManager.AddLocalAddress(na, addrmgr.UpnpPrio)
|
||||
if err != nil {
|
||||
// XXX DeletePortMapping?
|
||||
@ -3117,7 +3215,9 @@ func addLocalAddress(addrMgr *addrmgr.AddrManager, addr string, services wire.Se
|
||||
continue
|
||||
}
|
||||
|
||||
netAddr := wire.NewNetAddressIPPort(ifaceIP, uint16(port), services)
|
||||
netAddr := wire.NetAddressV2FromBytes(
|
||||
time.Now(), services, ifaceIP, uint16(port),
|
||||
)
|
||||
addrMgr.AddLocalAddress(netAddr, addrmgr.BoundPrio)
|
||||
}
|
||||
} else {
|
||||
|
@ -32,6 +32,7 @@ const (
|
||||
CmdVerAck = "verack"
|
||||
CmdGetAddr = "getaddr"
|
||||
CmdAddr = "addr"
|
||||
CmdAddrV2 = "addrv2"
|
||||
CmdGetBlocks = "getblocks"
|
||||
CmdInv = "inv"
|
||||
CmdGetData = "getdata"
|
||||
@ -78,6 +79,13 @@ const (
|
||||
// protocol.
|
||||
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
|
||||
// implements Message has complete control over the representation of its data
|
||||
// and may therefore contain additional or fewer fields than those which
|
||||
@ -109,6 +117,9 @@ func makeEmptyMessage(command string) (Message, error) {
|
||||
case CmdAddr:
|
||||
msg = &MsgAddr{}
|
||||
|
||||
case CmdAddrV2:
|
||||
msg = &MsgAddrV2{}
|
||||
|
||||
case CmdGetBlocks:
|
||||
msg = &MsgGetBlocks{}
|
||||
|
||||
@ -185,7 +196,7 @@ func makeEmptyMessage(command string) (Message, error) {
|
||||
msg = &MsgCFCheckpt{}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled command [%s]", command)
|
||||
return nil, ErrUnknownMessage
|
||||
}
|
||||
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.
|
||||
msg, err := makeEmptyMessage(command)
|
||||
if err != nil {
|
||||
// makeEmptyMessage can only return ErrUnknownMessage and it is
|
||||
// important that we bubble it up to the caller.
|
||||
discardInput(r, hdr.length)
|
||||
return totalBytes, nil, nil, messageError("ReadMessage",
|
||||
err.Error())
|
||||
return totalBytes, nil, nil, err
|
||||
}
|
||||
|
||||
// Check for maximum length based on the message type as a malicious client
|
||||
|
@ -295,7 +295,7 @@ func TestReadMessageWireErrors(t *testing.T) {
|
||||
pver,
|
||||
btcnet,
|
||||
len(unsupportedCommandBytes),
|
||||
&MessageError{},
|
||||
ErrUnknownMessage,
|
||||
24,
|
||||
},
|
||||
|
||||
@ -345,7 +345,7 @@ func TestReadMessageWireErrors(t *testing.T) {
|
||||
pver,
|
||||
btcnet,
|
||||
len(discardBytes),
|
||||
&MessageError{},
|
||||
ErrUnknownMessage,
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
588
wire/netaddressv2.go
Normal file
588
wire/netaddressv2.go
Normal file
@ -0,0 +1,588 @@
|
||||
package wire
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxAddrV2Size is the maximum size an address may be in the addrv2
|
||||
// message.
|
||||
maxAddrV2Size = 512
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidAddressSize is an error that means an incorrect address
|
||||
// size was decoded for a networkID or that the address exceeded the
|
||||
// maximum size for an unknown networkID.
|
||||
ErrInvalidAddressSize = fmt.Errorf("invalid address size")
|
||||
|
||||
// ErrSkippedNetworkID is returned when the cjdns, i2p, or unknown
|
||||
// networks are encountered during decoding. btcd does not support i2p
|
||||
// or cjdns addresses. In the case of an unknown networkID, this is so
|
||||
// that a future BIP reserving a new networkID does not cause older
|
||||
// addrv2-supporting btcd software to disconnect upon receiving the new
|
||||
// addresses. This error can also be returned when an OnionCat-encoded
|
||||
// torv2 address is received with the ipv6 networkID. This error
|
||||
// signals to the caller to continue reading.
|
||||
ErrSkippedNetworkID = fmt.Errorf("skipped networkID")
|
||||
)
|
||||
|
||||
// maxNetAddressV2Payload returns the max payload size for an address used in
|
||||
// the addrv2 message.
|
||||
func maxNetAddressV2Payload() uint32 {
|
||||
// The timestamp takes up four bytes.
|
||||
plen := uint32(4)
|
||||
|
||||
// The ServiceFlag is a varint and its maximum size is 9 bytes.
|
||||
plen += 9
|
||||
|
||||
// The netID is a single byte.
|
||||
plen += 1
|
||||
|
||||
// The largest address is 512 bytes. Even though it will not be a valid
|
||||
// address, we should read and ignore it. The preceeding varint to
|
||||
// store 512 bytes is 3 bytes long. This gives us a total of 515 bytes.
|
||||
plen += 515
|
||||
|
||||
// The port is 2 bytes.
|
||||
plen += 2
|
||||
|
||||
return plen
|
||||
}
|
||||
|
||||
// isOnionCatTor returns whether a given ip address is actually an encoded tor
|
||||
// v2 address. The wire package is unable to use the addrmgr's IsOnionCatTor as
|
||||
// doing so would give an import cycle.
|
||||
func isOnionCatTor(ip net.IP) bool {
|
||||
onionCatNet := net.IPNet{
|
||||
IP: net.ParseIP("fd87:d87e:eb43::"),
|
||||
Mask: net.CIDRMask(48, 128),
|
||||
}
|
||||
return onionCatNet.Contains(ip)
|
||||
}
|
||||
|
||||
// NetAddressV2 defines information about a peer on the network including the
|
||||
// last time it was seen, the services it supports, its address, and port. This
|
||||
// struct is used in the addrv2 message (MsgAddrV2) and can contain larger
|
||||
// addresses, like Tor. Additionally, it can contain any NetAddress address.
|
||||
type NetAddressV2 struct {
|
||||
// Last time the address was seen. This is, unfortunately, encoded as a
|
||||
// uint32 on the wire and therefore is limited to 2106. This field is
|
||||
// not present in the bitcoin version message (MsgVersion) nor was it
|
||||
// added until protocol version >= NetAddressTimeVersion.
|
||||
Timestamp time.Time
|
||||
|
||||
// Services is a bitfield which identifies the services supported by
|
||||
// the address. This is encoded in CompactSize.
|
||||
Services ServiceFlag
|
||||
|
||||
// Addr is the network address of the peer. This is a variable-length
|
||||
// address. Network() returns the BIP-155 networkID which is a uint8
|
||||
// encoded as a string. String() returns the address as a string.
|
||||
Addr net.Addr
|
||||
|
||||
// Port is the port of the address. This is 0 if the network doesn't
|
||||
// use ports.
|
||||
Port uint16
|
||||
}
|
||||
|
||||
// HasService returns whether the specified service is supported by the
|
||||
// address.
|
||||
func (na *NetAddressV2) HasService(service ServiceFlag) bool {
|
||||
return na.Services&service == service
|
||||
}
|
||||
|
||||
// AddService adds a service to the Services bitfield.
|
||||
func (na *NetAddressV2) AddService(service ServiceFlag) {
|
||||
na.Services |= service
|
||||
}
|
||||
|
||||
// ToLegacy attempts to convert a NetAddressV2 to a legacy NetAddress. This
|
||||
// only works for ipv4, ipv6, or torv2 addresses as they can be encoded with
|
||||
// the OnionCat encoding. If this method is called on a torv3 address, nil will
|
||||
// be returned.
|
||||
func (na *NetAddressV2) ToLegacy() *NetAddress {
|
||||
legacyNa := &NetAddress{
|
||||
Timestamp: na.Timestamp,
|
||||
Services: na.Services,
|
||||
Port: na.Port,
|
||||
}
|
||||
|
||||
switch a := na.Addr.(type) {
|
||||
case *ipv4Addr:
|
||||
legacyNa.IP = a.addr[:]
|
||||
case *ipv6Addr:
|
||||
legacyNa.IP = a.addr[:]
|
||||
case *torv2Addr:
|
||||
legacyNa.IP = a.onionCatEncoding()
|
||||
case *torv3Addr:
|
||||
return nil
|
||||
}
|
||||
|
||||
return legacyNa
|
||||
}
|
||||
|
||||
// IsTorV3 returns a bool that signals to the caller whether or not this is a
|
||||
// torv3 address.
|
||||
func (na *NetAddressV2) IsTorV3() bool {
|
||||
_, ok := na.Addr.(*torv3Addr)
|
||||
return ok
|
||||
}
|
||||
|
||||
// TorV3Key returns the first byte of the v3 public key. This is used in the
|
||||
// addrmgr to calculate a key from a network group.
|
||||
func (na *NetAddressV2) TorV3Key() byte {
|
||||
// This should never be called on a non-torv3 address.
|
||||
addr, ok := na.Addr.(*torv3Addr)
|
||||
if !ok {
|
||||
panic("unexpected TorV3Key call on non-torv3 address")
|
||||
}
|
||||
|
||||
return addr.addr[0]
|
||||
}
|
||||
|
||||
// NetAddressV2FromBytes creates a NetAddressV2 from a byte slice. It will
|
||||
// also handle a torv2 address using the OnionCat encoding.
|
||||
func NetAddressV2FromBytes(timestamp time.Time, services ServiceFlag,
|
||||
addrBytes []byte, port uint16) *NetAddressV2 {
|
||||
|
||||
var netAddr net.Addr
|
||||
switch len(addrBytes) {
|
||||
case ipv4Size:
|
||||
addr := &ipv4Addr{}
|
||||
addr.netID = ipv4
|
||||
copy(addr.addr[:], addrBytes)
|
||||
netAddr = addr
|
||||
case ipv6Size:
|
||||
if isOnionCatTor(addrBytes) {
|
||||
addr := &torv2Addr{}
|
||||
addr.netID = torv2
|
||||
copy(addr.addr[:], addrBytes[6:])
|
||||
netAddr = addr
|
||||
break
|
||||
}
|
||||
|
||||
addr := &ipv6Addr{}
|
||||
addr.netID = ipv6
|
||||
copy(addr.addr[:], addrBytes)
|
||||
netAddr = addr
|
||||
case torv2Size:
|
||||
addr := &torv2Addr{}
|
||||
addr.netID = torv2
|
||||
copy(addr.addr[:], addrBytes)
|
||||
netAddr = addr
|
||||
case TorV3Size:
|
||||
addr := &torv3Addr{}
|
||||
addr.netID = torv3
|
||||
copy(addr.addr[:], addrBytes)
|
||||
netAddr = addr
|
||||
}
|
||||
|
||||
return &NetAddressV2{
|
||||
Timestamp: timestamp,
|
||||
Services: services,
|
||||
Addr: netAddr,
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
// writeNetAddressV2 writes a NetAddressV2 to a writer.
|
||||
func writeNetAddressV2(w io.Writer, pver uint32, na *NetAddressV2) error {
|
||||
err := writeElement(w, uint32(na.Timestamp.Unix()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := WriteVarInt(w, pver, uint64(na.Services)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
netID networkID
|
||||
address []byte
|
||||
)
|
||||
|
||||
switch a := na.Addr.(type) {
|
||||
case *ipv4Addr:
|
||||
netID = a.netID
|
||||
address = a.addr[:]
|
||||
case *ipv6Addr:
|
||||
netID = a.netID
|
||||
address = a.addr[:]
|
||||
case *torv2Addr:
|
||||
netID = a.netID
|
||||
address = a.addr[:]
|
||||
case *torv3Addr:
|
||||
netID = a.netID
|
||||
address = a.addr[:]
|
||||
default:
|
||||
// This should not occur.
|
||||
return fmt.Errorf("unexpected address type")
|
||||
}
|
||||
|
||||
if err := writeElement(w, netID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addressSize := uint64(len(address))
|
||||
if err := WriteVarInt(w, pver, addressSize); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := writeElement(w, address); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return binary.Write(w, bigEndian, na.Port)
|
||||
}
|
||||
|
||||
// readNetAddressV2 reads a NetAddressV2 from a reader. This function has
|
||||
// checks that the corresponding write function doesn't. This is because
|
||||
// reading from the peer is untrusted whereas writing assumes we have already
|
||||
// validated the NetAddressV2.
|
||||
func readNetAddressV2(r io.Reader, pver uint32, na *NetAddressV2) error {
|
||||
err := readElement(r, (*uint32Time)(&na.Timestamp))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Services is encoded as a variable length integer in addrv2.
|
||||
services, err := ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
na.Services = ServiceFlag(services)
|
||||
|
||||
var netID uint8
|
||||
if err := readElement(r, &netID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
decodedSize, err := ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isKnownNetworkID(netID) {
|
||||
// In the case of an unknown networkID, we'll read the address
|
||||
// size and error with ErrInvalidAddressSize if it's greater
|
||||
// than maxAddrV2Size. If the address size is within the valid
|
||||
// range, we'll just read and discard the address. In this
|
||||
// case, ErrSkippedNetworkID will be returned to signal to the
|
||||
// caller to continue reading.
|
||||
if decodedSize > maxAddrV2Size {
|
||||
return ErrInvalidAddressSize
|
||||
}
|
||||
|
||||
// The +2 is the port field.
|
||||
discardedAddrPort := make([]byte, decodedSize+2)
|
||||
if err := readElement(r, &discardedAddrPort); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrSkippedNetworkID
|
||||
}
|
||||
|
||||
// If the netID is an i2p or cjdns address, we'll advance the reader
|
||||
// and return a special error to signal to the caller to not use the
|
||||
// passed NetAddressV2 struct. Otherwise, we'll just read the address
|
||||
// and port without returning an error.
|
||||
switch networkID(netID) {
|
||||
case ipv4:
|
||||
addr := &ipv4Addr{}
|
||||
addr.netID = ipv4
|
||||
if decodedSize != uint64(ipv4Size) {
|
||||
return ErrInvalidAddressSize
|
||||
}
|
||||
|
||||
if err := readElement(r, &addr.addr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
na.Port, err = binarySerializer.Uint16(r, bigEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
na.Addr = addr
|
||||
case ipv6:
|
||||
addr := &ipv6Addr{}
|
||||
addr.netID = ipv6
|
||||
if decodedSize != uint64(ipv6Size) {
|
||||
return ErrInvalidAddressSize
|
||||
}
|
||||
|
||||
if err := readElement(r, &addr.addr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
na.Port, err = binarySerializer.Uint16(r, bigEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
na.Addr = addr
|
||||
|
||||
// BIP-155 says to ignore OnionCat addresses in addrv2
|
||||
// messages.
|
||||
if isOnionCatTor(addr.addr[:]) {
|
||||
return ErrSkippedNetworkID
|
||||
}
|
||||
case torv2:
|
||||
addr := &torv2Addr{}
|
||||
addr.netID = torv2
|
||||
if decodedSize != uint64(torv2Size) {
|
||||
return ErrInvalidAddressSize
|
||||
}
|
||||
|
||||
if err := readElement(r, &addr.addr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
na.Port, err = binarySerializer.Uint16(r, bigEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
na.Addr = addr
|
||||
case torv3:
|
||||
addr := &torv3Addr{}
|
||||
addr.netID = torv3
|
||||
if decodedSize != uint64(TorV3Size) {
|
||||
return ErrInvalidAddressSize
|
||||
}
|
||||
|
||||
if err := readElement(r, &addr.addr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
na.Port, err = binarySerializer.Uint16(r, bigEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// BIP-155 does not specify to validate the public key here.
|
||||
// bitcoind does not validate the ed25519 pubkey.
|
||||
na.Addr = addr
|
||||
case i2p:
|
||||
addr := &i2pAddr{}
|
||||
addr.netID = i2p
|
||||
if decodedSize != uint64(i2pSize) {
|
||||
return ErrInvalidAddressSize
|
||||
}
|
||||
|
||||
if err := readElement(r, &addr.addr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
na.Port, err = binarySerializer.Uint16(r, bigEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrSkippedNetworkID
|
||||
case cjdns:
|
||||
addr := &cjdnsAddr{}
|
||||
addr.netID = cjdns
|
||||
if decodedSize != uint64(cjdnsSize) {
|
||||
return ErrInvalidAddressSize
|
||||
}
|
||||
|
||||
if err := readElement(r, &addr.addr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
na.Port, err = binarySerializer.Uint16(r, bigEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrSkippedNetworkID
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// networkID represents the network that a given address is in. CJDNS and I2P
|
||||
// addresses are not included.
|
||||
type networkID uint8
|
||||
|
||||
const (
|
||||
// ipv4 means the following address is ipv4.
|
||||
ipv4 networkID = iota + 1
|
||||
|
||||
// ipv6 means the following address is ipv6.
|
||||
ipv6
|
||||
|
||||
// torv2 means the following address is a torv2 hidden service address.
|
||||
torv2
|
||||
|
||||
// torv3 means the following address is a torv3 hidden service address.
|
||||
torv3
|
||||
|
||||
// i2p means the following address is an i2p address.
|
||||
i2p
|
||||
|
||||
// cjdns means the following address is a cjdns address.
|
||||
cjdns
|
||||
)
|
||||
|
||||
const (
|
||||
// ipv4Size is the size of an ipv4 address.
|
||||
ipv4Size = 4
|
||||
|
||||
// ipv6Size is the size of an ipv6 address.
|
||||
ipv6Size = 16
|
||||
|
||||
// torv2Size is the size of a torv2 address.
|
||||
torv2Size = 10
|
||||
|
||||
// TorV3Size is the size of a torv3 address in bytes.
|
||||
TorV3Size = 32
|
||||
|
||||
// i2pSize is the size of an i2p address.
|
||||
i2pSize = 32
|
||||
|
||||
// cjdnsSize is the size of a cjdns address.
|
||||
cjdnsSize = 16
|
||||
)
|
||||
|
||||
const (
|
||||
// TorV2EncodedSize is the size of a torv2 address encoded in base32
|
||||
// with the ".onion" suffix.
|
||||
TorV2EncodedSize = 22
|
||||
|
||||
// TorV3EncodedSize is the size of a torv3 address encoded in base32
|
||||
// with the ".onion" suffix.
|
||||
TorV3EncodedSize = 62
|
||||
)
|
||||
|
||||
// isKnownNetworkID returns true if the networkID is one listed above and false
|
||||
// otherwise.
|
||||
func isKnownNetworkID(netID uint8) bool {
|
||||
return uint8(ipv4) <= netID && netID <= uint8(cjdns)
|
||||
}
|
||||
|
||||
type ipv4Addr struct {
|
||||
addr [ipv4Size]byte
|
||||
netID networkID
|
||||
}
|
||||
|
||||
// Part of the net.Addr interface.
|
||||
func (a *ipv4Addr) String() string {
|
||||
return net.IP(a.addr[:]).String()
|
||||
}
|
||||
|
||||
// Part of the net.Addr interface.
|
||||
func (a *ipv4Addr) Network() string {
|
||||
return string(a.netID)
|
||||
}
|
||||
|
||||
// Compile-time constraints to check that ipv4Addr meets the net.Addr
|
||||
// interface.
|
||||
var _ net.Addr = (*ipv4Addr)(nil)
|
||||
|
||||
type ipv6Addr struct {
|
||||
addr [ipv6Size]byte
|
||||
netID networkID
|
||||
}
|
||||
|
||||
// Part of the net.Addr interface.
|
||||
func (a *ipv6Addr) String() string {
|
||||
return net.IP(a.addr[:]).String()
|
||||
}
|
||||
|
||||
// Part of the net.Addr interface.
|
||||
func (a *ipv6Addr) Network() string {
|
||||
return string(a.netID)
|
||||
}
|
||||
|
||||
// Compile-time constraints to check that ipv6Addr meets the net.Addr
|
||||
// interface.
|
||||
var _ net.Addr = (*ipv4Addr)(nil)
|
||||
|
||||
type torv2Addr struct {
|
||||
addr [torv2Size]byte
|
||||
netID networkID
|
||||
}
|
||||
|
||||
// Part of the net.Addr interface.
|
||||
func (a *torv2Addr) String() string {
|
||||
base32Hash := base32.StdEncoding.EncodeToString(a.addr[:])
|
||||
return strings.ToLower(base32Hash) + ".onion"
|
||||
}
|
||||
|
||||
// Part of the net.Addr interface.
|
||||
func (a *torv2Addr) Network() string {
|
||||
return string(a.netID)
|
||||
}
|
||||
|
||||
// onionCatEncoding returns a torv2 address as an ipv6 address.
|
||||
func (a *torv2Addr) onionCatEncoding() net.IP {
|
||||
prefix := []byte{0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43}
|
||||
return net.IP(append(prefix, a.addr[:]...))
|
||||
}
|
||||
|
||||
// Compile-time constraints to check that torv2Addr meets the net.Addr
|
||||
// interface.
|
||||
var _ net.Addr = (*torv2Addr)(nil)
|
||||
|
||||
type torv3Addr struct {
|
||||
addr [TorV3Size]byte
|
||||
netID networkID
|
||||
}
|
||||
|
||||
// Part of the net.Addr interface.
|
||||
func (a *torv3Addr) String() string {
|
||||
// BIP-155 describes the torv3 address format:
|
||||
// onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
|
||||
// CHECKSUM = H(".onion checksum" | PUBKEY | VERSION)[:2]
|
||||
// PUBKEY = addr, which is the ed25519 pubkey of the hidden service.
|
||||
// VERSION = '\x03'
|
||||
// H() is the SHA3-256 cryptographic hash function.
|
||||
|
||||
torV3Version := []byte("\x03")
|
||||
checksumConst := []byte(".onion checksum")
|
||||
|
||||
// Write never returns an error so there is no need to handle it.
|
||||
h := sha3.New256()
|
||||
h.Write(checksumConst)
|
||||
h.Write(a.addr[:])
|
||||
h.Write(torV3Version)
|
||||
truncatedChecksum := h.Sum(nil)[:2]
|
||||
|
||||
var base32Input [35]byte
|
||||
copy(base32Input[:32], a.addr[:])
|
||||
copy(base32Input[32:34], truncatedChecksum)
|
||||
copy(base32Input[34:], torV3Version)
|
||||
|
||||
base32Hash := base32.StdEncoding.EncodeToString(base32Input[:])
|
||||
return strings.ToLower(base32Hash) + ".onion"
|
||||
}
|
||||
|
||||
// Part of the net.Addr interface.
|
||||
func (a *torv3Addr) Network() string {
|
||||
return string(a.netID)
|
||||
}
|
||||
|
||||
// Compile-time constraints to check that torv3Addr meets the net.Addr
|
||||
// interface.
|
||||
var _ net.Addr = (*torv3Addr)(nil)
|
||||
|
||||
type i2pAddr struct {
|
||||
addr [i2pSize]byte
|
||||
netID networkID
|
||||
}
|
||||
|
||||
type cjdnsAddr struct {
|
||||
addr [cjdnsSize]byte
|
||||
netID networkID
|
||||
}
|
285
wire/netaddressv2_test.go
Normal file
285
wire/netaddressv2_test.go
Normal file
@ -0,0 +1,285 @@
|
||||
package wire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestNetAddressV2FromBytes tests that NetAddressV2FromBytes works as
|
||||
// expected.
|
||||
func TestNetAddressV2FromBytes(t *testing.T) {
|
||||
tests := []struct {
|
||||
addrBytes []byte
|
||||
expectedString string
|
||||
expectedNetwork string
|
||||
}{
|
||||
// Ipv4 encoding
|
||||
{
|
||||
[]byte{0x7f, 0x00, 0x00, 0x01},
|
||||
"127.0.0.1",
|
||||
string(ipv4),
|
||||
},
|
||||
|
||||
// Ipv6 encoding
|
||||
{
|
||||
[]byte{
|
||||
0x20, 0x01, 0x09, 0xe8, 0x26, 0x15, 0x73, 0x00,
|
||||
0x09, 0x54, 0x12, 0x63, 0xef, 0xc8, 0x2e, 0x34,
|
||||
},
|
||||
"2001:9e8:2615:7300:954:1263:efc8:2e34",
|
||||
string(ipv6),
|
||||
},
|
||||
|
||||
// OnionCat encoding
|
||||
{
|
||||
[]byte{
|
||||
0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43, 0xff, 0xfe,
|
||||
0xcc, 0x39, 0xa8, 0x73, 0x69, 0x15, 0xff, 0xff,
|
||||
},
|
||||
"777myonionurl777.onion",
|
||||
string(torv2),
|
||||
},
|
||||
|
||||
// Torv2 encoding
|
||||
{
|
||||
[]byte{
|
||||
0xff, 0xfe, 0xcc, 0x39, 0xa8, 0x73, 0x69, 0x15,
|
||||
0xff, 0xff,
|
||||
},
|
||||
"777myonionurl777.onion",
|
||||
string(torv2),
|
||||
},
|
||||
|
||||
// Torv3 encoding
|
||||
{
|
||||
[]byte{
|
||||
0xca, 0xd2, 0xd3, 0xc8, 0xdc, 0x9c, 0xc4, 0xd3,
|
||||
0x70, 0x33, 0x30, 0xc5, 0x23, 0xaf, 0x02, 0xed,
|
||||
0xc4, 0x9d, 0xf8, 0xc6, 0xb0, 0x4e, 0x74, 0x6d,
|
||||
0x3b, 0x51, 0x57, 0xa7, 0x15, 0xfe, 0x98, 0x35,
|
||||
},
|
||||
"zljnhsg4ttcng4btgdcshlyc5xcj36ggwbhhi3j3kfl2ofp6ta26jlid.onion",
|
||||
string(torv3),
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
na := NetAddressV2FromBytes(time.Time{}, 0, test.addrBytes, 0)
|
||||
|
||||
if test.expectedNetwork != string(torv3) {
|
||||
if na.ToLegacy() == nil {
|
||||
t.Errorf("Test #%d has nil legacy encoding", i)
|
||||
}
|
||||
} else {
|
||||
if !na.IsTorV3() {
|
||||
t.Errorf("Test #%d is not torv3 address", i)
|
||||
}
|
||||
}
|
||||
|
||||
if na.Addr.String() != test.expectedString {
|
||||
t.Errorf("Test #%d did not match expected string", i)
|
||||
}
|
||||
|
||||
if na.Addr.Network() != test.expectedNetwork {
|
||||
t.Errorf("Test #%d did not match expected network", i)
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
if err := writeNetAddressV2(&b, 0, na); err != nil {
|
||||
t.Errorf("Test #%d failed writing address %v", i, err)
|
||||
}
|
||||
|
||||
// Assert that the written netID is equivalent to the above.
|
||||
if string(b.Bytes()[5]) != test.expectedNetwork {
|
||||
t.Errorf("Test #%d did not match expected network", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestReadNetAddressV2 tests that readNetAddressV2 behaves as expected in
|
||||
// different scenarios.
|
||||
func TestReadNetAddressV2(t *testing.T) {
|
||||
tests := []struct {
|
||||
buf []byte
|
||||
expectedNetwork string
|
||||
expectedError error
|
||||
}{
|
||||
// Invalid address size for unknown netID.
|
||||
{
|
||||
[]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xff,
|
||||
0xff,
|
||||
},
|
||||
"",
|
||||
ErrInvalidAddressSize,
|
||||
},
|
||||
|
||||
// Valid address size for unknown netID.
|
||||
{
|
||||
[]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x20, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x22,
|
||||
0x22,
|
||||
},
|
||||
"",
|
||||
ErrSkippedNetworkID,
|
||||
},
|
||||
|
||||
// Invalid ipv4 size.
|
||||
{
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05},
|
||||
"",
|
||||
ErrInvalidAddressSize,
|
||||
},
|
||||
|
||||
// Valid ipv4 encoding.
|
||||
{
|
||||
[]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x7f,
|
||||
0x00, 0x00, 0x01, 0x22, 0x22,
|
||||
},
|
||||
string(ipv4),
|
||||
nil,
|
||||
},
|
||||
|
||||
// Invalid ipv6 size.
|
||||
{
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfc},
|
||||
"",
|
||||
ErrInvalidAddressSize,
|
||||
},
|
||||
|
||||
// OnionCat encoding is skipped.
|
||||
{
|
||||
[]byte{
|
||||
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,
|
||||
},
|
||||
"",
|
||||
ErrSkippedNetworkID,
|
||||
},
|
||||
|
||||
// Valid ipv6 encoding.
|
||||
{
|
||||
[]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
|
||||
0x22,
|
||||
},
|
||||
string(ipv6),
|
||||
nil,
|
||||
},
|
||||
|
||||
// Invalid torv2 size.
|
||||
{
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02},
|
||||
"",
|
||||
ErrInvalidAddressSize,
|
||||
},
|
||||
|
||||
// Valid torv2 encoding.
|
||||
{
|
||||
[]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x22, 0x22,
|
||||
},
|
||||
string(torv2),
|
||||
nil,
|
||||
},
|
||||
|
||||
// Invalid torv3 size.
|
||||
{
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02},
|
||||
"",
|
||||
ErrInvalidAddressSize,
|
||||
},
|
||||
|
||||
// Valid torv3 encoding.
|
||||
{
|
||||
[]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x22,
|
||||
0x22,
|
||||
},
|
||||
string(torv3),
|
||||
nil,
|
||||
},
|
||||
|
||||
// Invalid i2p size.
|
||||
{
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02},
|
||||
"",
|
||||
ErrInvalidAddressSize,
|
||||
},
|
||||
|
||||
// Valid i2p encoding.
|
||||
{
|
||||
[]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x22,
|
||||
0x22,
|
||||
},
|
||||
string(i2p),
|
||||
ErrSkippedNetworkID,
|
||||
},
|
||||
|
||||
// Invalid cjdns size.
|
||||
{
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02},
|
||||
"",
|
||||
ErrInvalidAddressSize,
|
||||
},
|
||||
|
||||
// Valid cjdns encoding.
|
||||
{
|
||||
[]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
|
||||
0x22,
|
||||
},
|
||||
string(cjdns),
|
||||
ErrSkippedNetworkID,
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
r := bytes.NewReader(test.buf)
|
||||
na := &NetAddressV2{}
|
||||
|
||||
err := readNetAddressV2(r, 0, na)
|
||||
if err != test.expectedError {
|
||||
t.Errorf("Test #%d had unexpected error %v", i, err)
|
||||
} else if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// 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 na.Addr.Network() != test.expectedNetwork {
|
||||
t.Errorf("Test #%d had unexpected network %v", i,
|
||||
na.Addr.Network())
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ import (
|
||||
// XXX pedro: we will probably need to bump this.
|
||||
const (
|
||||
// ProtocolVersion is the latest protocol version this package supports.
|
||||
ProtocolVersion uint32 = 70013
|
||||
ProtocolVersion uint32 = 70016
|
||||
|
||||
// MultipleAddressVersion is the protocol version which added multiple
|
||||
// addresses per message (pver >= MultipleAddressVersion).
|
||||
@ -51,6 +51,13 @@ const (
|
||||
// FeeFilterVersion is the protocol version which added a new
|
||||
// feefilter message.
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user