2019-10-16 19:18:30 -07:00
|
|
|
package zcash
|
|
|
|
|
|
|
|
import (
|
2020-05-20 19:16:41 -07:00
|
|
|
mrand "math/rand"
|
2019-10-16 19:18:30 -07:00
|
|
|
"net"
|
|
|
|
"strconv"
|
2019-10-17 19:47:40 -07:00
|
|
|
"sync"
|
2019-10-16 19:18:30 -07:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Address struct {
|
2020-05-21 15:39:10 -07:00
|
|
|
netaddr *wire.NetAddress
|
|
|
|
lastUpdate time.Time
|
2019-10-16 19:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Address) String() string {
|
|
|
|
portString := strconv.Itoa(int(a.netaddr.Port))
|
|
|
|
return net.JoinHostPort(a.netaddr.IP.String(), portString)
|
|
|
|
}
|
|
|
|
|
2019-10-17 19:47:40 -07:00
|
|
|
func (a *Address) asPeerKey() PeerKey {
|
|
|
|
return PeerKey(a.String())
|
|
|
|
}
|
|
|
|
|
2020-05-20 15:08:34 -07:00
|
|
|
func (a *Address) fromPeerKey(s PeerKey) (*Address, error) {
|
|
|
|
host, portString, err := net.SplitHostPort(s.String())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
portInt, err := strconv.ParseUint(portString, 10, 16)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
na := wire.NewNetAddressTimestamp(
|
|
|
|
time.Now(),
|
|
|
|
0,
|
|
|
|
net.ParseIP(host),
|
|
|
|
uint16(portInt),
|
|
|
|
)
|
|
|
|
|
|
|
|
a.netaddr = na
|
|
|
|
a.lastUpdate = na.Timestamp
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Address) asNetAddress() *wire.NetAddress {
|
|
|
|
newNA := *a.netaddr
|
|
|
|
newNA.Timestamp = a.lastUpdate
|
|
|
|
return &newNA
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Address) fromNetAddress(na *wire.NetAddress) (*Address, error) {
|
|
|
|
a.netaddr = na
|
|
|
|
a.lastUpdate = na.Timestamp
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
2019-10-17 19:47:40 -07:00
|
|
|
func (a *Address) MarshalText() (text []byte, err error) {
|
|
|
|
return []byte(a.String()), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type AddressBook struct {
|
2020-05-21 15:39:10 -07:00
|
|
|
peers map[PeerKey]*Address
|
|
|
|
blacklist map[PeerKey]*Address
|
|
|
|
|
2019-10-17 19:47:40 -07:00
|
|
|
addrState sync.RWMutex
|
|
|
|
addrRecvCond *sync.Cond
|
|
|
|
}
|
|
|
|
|
2020-05-20 15:08:34 -07:00
|
|
|
func NewAddressBook() *AddressBook {
|
|
|
|
addrBook := &AddressBook{
|
2020-05-21 15:39:10 -07:00
|
|
|
peers: make(map[PeerKey]*Address),
|
|
|
|
blacklist: make(map[PeerKey]*Address),
|
2020-05-20 15:08:34 -07:00
|
|
|
}
|
|
|
|
addrBook.addrRecvCond = sync.NewCond(&addrBook.addrState)
|
|
|
|
return addrBook
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bk *AddressBook) Add(s PeerKey) {
|
|
|
|
newAddr, err := (&Address{}).fromPeerKey(s)
|
|
|
|
if err != nil {
|
|
|
|
// XXX effectively NOP bogus peer strings
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-10-17 19:47:40 -07:00
|
|
|
bk.addrState.Lock()
|
2020-05-21 15:39:10 -07:00
|
|
|
bk.peers[s] = newAddr
|
2019-10-17 19:47:40 -07:00
|
|
|
bk.addrState.Unlock()
|
2019-12-30 17:54:38 -08:00
|
|
|
|
2020-05-20 15:08:34 -07:00
|
|
|
// Wake anyone who was waiting on us to receive an address.
|
2019-12-30 17:54:38 -08:00
|
|
|
bk.addrRecvCond.Broadcast()
|
2019-10-17 19:47:40 -07:00
|
|
|
}
|
|
|
|
|
2020-05-20 15:08:34 -07:00
|
|
|
func (bk *AddressBook) Remove(s PeerKey) {
|
2019-10-17 19:47:40 -07:00
|
|
|
bk.addrState.Lock()
|
2020-05-20 15:08:34 -07:00
|
|
|
defer bk.addrState.Unlock()
|
2019-10-16 19:18:30 -07:00
|
|
|
|
2020-05-21 15:39:10 -07:00
|
|
|
if _, ok := bk.peers[s]; ok {
|
|
|
|
delete(bk.peers, s)
|
2019-10-17 19:47:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-20 15:08:34 -07:00
|
|
|
func (bk *AddressBook) Blacklist(s PeerKey) {
|
|
|
|
bk.addrState.Lock()
|
|
|
|
defer bk.addrState.Unlock()
|
2019-10-17 19:47:40 -07:00
|
|
|
|
2020-05-21 15:39:10 -07:00
|
|
|
if target, ok := bk.peers[s]; ok {
|
|
|
|
bk.blacklist[s] = target
|
|
|
|
delete(bk.peers, s)
|
2020-05-20 15:08:34 -07:00
|
|
|
} else {
|
|
|
|
// Create a new Address just to be blacklisted
|
|
|
|
addr, err := (&Address{}).fromPeerKey(s)
|
|
|
|
if err != nil {
|
|
|
|
// XXX effectively NOP bogus peer strings
|
|
|
|
return
|
2019-10-17 19:47:40 -07:00
|
|
|
}
|
2020-05-21 15:39:10 -07:00
|
|
|
bk.blacklist[s] = addr
|
2019-10-17 19:47:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-20 15:08:34 -07:00
|
|
|
// Touch updates the last-seen timestamp if the peer is in the address book or does nothing if not.
|
|
|
|
func (bk *AddressBook) Touch(s PeerKey) {
|
2019-10-17 19:47:40 -07:00
|
|
|
bk.addrState.Lock()
|
|
|
|
defer bk.addrState.Unlock()
|
|
|
|
|
2020-05-21 15:39:10 -07:00
|
|
|
if target, ok := bk.peers[s]; ok {
|
2020-05-20 15:08:34 -07:00
|
|
|
target.lastUpdate = time.Now()
|
2019-10-17 19:47:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-20 15:08:34 -07:00
|
|
|
// IsKnown returns true if the peer is already in our address book, false if not.
|
|
|
|
func (bk *AddressBook) IsKnown(s PeerKey) bool {
|
|
|
|
bk.addrState.RLock()
|
|
|
|
defer bk.addrState.RUnlock()
|
|
|
|
|
2020-05-21 15:39:10 -07:00
|
|
|
_, known := bk.peers[s]
|
2020-05-20 15:08:34 -07:00
|
|
|
return known
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bk *AddressBook) IsBlacklisted(s PeerKey) bool {
|
|
|
|
bk.addrState.RLock()
|
|
|
|
defer bk.addrState.RUnlock()
|
|
|
|
|
2020-05-21 15:39:10 -07:00
|
|
|
_, blacklisted := bk.blacklist[s]
|
|
|
|
return blacklisted
|
2019-10-17 19:47:40 -07:00
|
|
|
}
|
2019-10-16 19:18:30 -07:00
|
|
|
|
2019-12-30 17:54:38 -08:00
|
|
|
// WaitForAddresses waits for n addresses to be received and their initial
|
|
|
|
// connection attempts to resolve. There is no escape if that does not happen -
|
|
|
|
// this is intended for test runners or goroutines with a timeout.
|
|
|
|
func (bk *AddressBook) waitForAddresses(n int, done chan struct{}) {
|
|
|
|
bk.addrState.Lock()
|
|
|
|
for {
|
2020-05-21 15:39:10 -07:00
|
|
|
addrCount := len(bk.peers)
|
2019-12-30 17:54:38 -08:00
|
|
|
if addrCount < n {
|
|
|
|
bk.addrRecvCond.Wait()
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bk.addrState.Unlock()
|
|
|
|
done <- struct{}{}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-20 19:16:41 -07:00
|
|
|
// GetAddressList returns a slice of n valid addresses in random order.
|
|
|
|
// If there aren't enough known addresses, it returns as many as we have.
|
|
|
|
func (bk *AddressBook) shuffleAddressList(n int) []net.IP {
|
|
|
|
bk.addrState.RLock()
|
|
|
|
defer bk.addrState.RUnlock()
|
|
|
|
|
2020-05-21 15:39:10 -07:00
|
|
|
resp := make([]net.IP, 0, len(bk.peers))
|
2020-05-20 15:08:34 -07:00
|
|
|
|
2020-05-21 15:39:10 -07:00
|
|
|
for k, v := range bk.peers {
|
|
|
|
if _, blacklisted := bk.blacklist[k]; blacklisted {
|
2020-05-20 19:16:41 -07:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
resp = append(resp, v.netaddr.IP)
|
|
|
|
}
|
|
|
|
|
|
|
|
mrand.Seed(time.Now().UnixNano())
|
|
|
|
mrand.Shuffle(len(resp), func(i, j int) {
|
|
|
|
resp[i], resp[j] = resp[j], resp[i]
|
|
|
|
})
|
|
|
|
|
|
|
|
if len(resp) > n {
|
|
|
|
return resp[:n]
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp
|
|
|
|
}
|