270 lines
6.0 KiB
Go
270 lines
6.0 KiB
Go
package zcash
|
|
|
|
import (
|
|
mrand "math/rand"
|
|
"net"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
)
|
|
|
|
type Address struct {
|
|
netaddr *wire.NetAddress
|
|
lastUpdate time.Time
|
|
}
|
|
|
|
func (a *Address) String() string {
|
|
portString := strconv.Itoa(int(a.netaddr.Port))
|
|
return net.JoinHostPort(a.netaddr.IP.String(), portString)
|
|
}
|
|
|
|
func (a *Address) asPeerKey() PeerKey {
|
|
return PeerKey(a.String())
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (a *Address) MarshalText() (text []byte, err error) {
|
|
return []byte(a.String()), nil
|
|
}
|
|
|
|
type AddressBook struct {
|
|
peers map[PeerKey]*Address
|
|
blacklist map[PeerKey]*Address
|
|
|
|
addrState sync.RWMutex
|
|
addrRecvCond *sync.Cond
|
|
}
|
|
|
|
func NewAddressBook() *AddressBook {
|
|
addrBook := &AddressBook{
|
|
peers: make(map[PeerKey]*Address),
|
|
blacklist: make(map[PeerKey]*Address),
|
|
}
|
|
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
|
|
}
|
|
|
|
bk.addrState.Lock()
|
|
bk.peers[s] = newAddr
|
|
bk.addrState.Unlock()
|
|
|
|
// Wake anyone who was waiting on us to receive an address.
|
|
bk.addrRecvCond.Broadcast()
|
|
}
|
|
|
|
func (bk *AddressBook) Remove(s PeerKey) {
|
|
bk.addrState.Lock()
|
|
defer bk.addrState.Unlock()
|
|
|
|
if _, ok := bk.peers[s]; ok {
|
|
delete(bk.peers, s)
|
|
}
|
|
}
|
|
|
|
// Blacklist adds an address to the blacklist so we won't try to connect to it again.
|
|
func (bk *AddressBook) Blacklist(s PeerKey) {
|
|
bk.addrState.Lock()
|
|
defer bk.addrState.Unlock()
|
|
|
|
if target, ok := bk.peers[s]; ok {
|
|
bk.blacklist[s] = target
|
|
delete(bk.peers, s)
|
|
} else {
|
|
// Create a new Address just to be blacklisted
|
|
addr, err := (&Address{}).fromPeerKey(s)
|
|
if err != nil {
|
|
// XXX effectively NOP bogus peer strings
|
|
return
|
|
}
|
|
bk.blacklist[s] = addr
|
|
}
|
|
}
|
|
|
|
// Redeem removes an address from the blacklist and adds it to the peer list.
|
|
func (bk *AddressBook) Redeem(s PeerKey) {
|
|
bk.addrState.Lock()
|
|
if addr, ok := bk.blacklist[s]; ok {
|
|
delete(bk.blacklist, s)
|
|
addr.lastUpdate = time.Now()
|
|
bk.peers[s] = addr
|
|
}
|
|
bk.addrState.Unlock()
|
|
|
|
// Wake anyone who was waiting on us to receive an address.
|
|
bk.addrRecvCond.Broadcast()
|
|
}
|
|
|
|
// DropFromBlacklist removes an address from the blacklist.
|
|
func (bk *AddressBook) DropFromBlacklist(s PeerKey) {
|
|
bk.addrState.Lock()
|
|
defer bk.addrState.Unlock()
|
|
|
|
if _, ok := bk.blacklist[s]; ok {
|
|
delete(bk.blacklist, s)
|
|
}
|
|
}
|
|
|
|
// Touch updates the last-seen timestamp if the peer is in the valid address book or does nothing if not.
|
|
func (bk *AddressBook) Touch(s PeerKey) {
|
|
bk.addrState.Lock()
|
|
defer bk.addrState.Unlock()
|
|
|
|
if target, ok := bk.peers[s]; ok {
|
|
target.lastUpdate = time.Now()
|
|
}
|
|
}
|
|
|
|
func (bk *AddressBook) Count() int {
|
|
bk.addrState.RLock()
|
|
defer bk.addrState.RUnlock()
|
|
|
|
return len(bk.peers)
|
|
}
|
|
|
|
// 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()
|
|
|
|
_, knownGood := bk.peers[s]
|
|
_, knownBad := bk.blacklist[s]
|
|
return knownGood || knownBad
|
|
}
|
|
|
|
func (bk *AddressBook) IsBlacklisted(s PeerKey) bool {
|
|
bk.addrState.RLock()
|
|
defer bk.addrState.RUnlock()
|
|
|
|
_, blacklisted := bk.blacklist[s]
|
|
return blacklisted
|
|
}
|
|
|
|
// enqueueAddrs puts all of our known valid peers to a channel for processing.
|
|
func (bk *AddressBook) enqueueAddrs(addrQueue *chan *Address) {
|
|
bk.addrState.RLock()
|
|
defer bk.addrState.RUnlock()
|
|
|
|
*addrQueue = make(chan *Address, len(bk.peers))
|
|
for _, v := range bk.peers {
|
|
*addrQueue <- v
|
|
}
|
|
}
|
|
|
|
// enqueueBlacklist puts all of our blacklisted peers into a channel.
|
|
func (bk *AddressBook) enqueueBlacklist(addrQueue *chan *Address) {
|
|
bk.addrState.RLock()
|
|
defer bk.addrState.RUnlock()
|
|
|
|
*addrQueue = make(chan *Address, len(bk.blacklist))
|
|
for _, v := range bk.blacklist {
|
|
*addrQueue <- v
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
addrCount := len(bk.peers)
|
|
if addrCount < n {
|
|
bk.addrRecvCond.Wait()
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
bk.addrState.Unlock()
|
|
done <- struct{}{}
|
|
return
|
|
}
|
|
|
|
// 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, v6 bool, defaultPort string) []net.IP {
|
|
bk.addrState.RLock()
|
|
defer bk.addrState.RUnlock()
|
|
|
|
resp := make([]net.IP, 0, len(bk.peers))
|
|
|
|
for k, v := range bk.peers {
|
|
if _, blacklisted := bk.blacklist[k]; blacklisted {
|
|
// Check in case we've accidentally registered a bad peer
|
|
continue
|
|
}
|
|
|
|
if v6 && v.netaddr.IP.To4() != nil {
|
|
// skip IPv4 addresses if we're asked for v6
|
|
continue
|
|
}
|
|
|
|
if !v6 && v.netaddr.IP.To4() == nil {
|
|
// skip IPv6 addresses if we're asked for v4
|
|
continue
|
|
}
|
|
|
|
if strconv.Itoa(int(v.netaddr.Port)) != defaultPort {
|
|
// The DNS seeder is only able to return IP addresses, and it can't report
|
|
// ports. For this reason, we can only return addresses that are using
|
|
// the standard port.
|
|
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
|
|
}
|