dnsseed,zcash: occasionally retry blacklisted addresses

This commit is contained in:
George Tankersley 2020-05-24 21:20:28 -04:00
parent bba9f4436e
commit e57eebd627
4 changed files with 73 additions and 0 deletions

View File

@ -1,6 +1,7 @@
package dnsseed
import (
crypto_rand "crypto/rand"
"net"
"net/url"
"time"
@ -73,10 +74,18 @@ func setup(c *caddy.Controller) error {
// Start the update timer
go func() {
log.Infof("Starting update timer. Will crawl every %.0f minutes.", updateInterval.Minutes())
randByte := []byte{0}
for {
select {
case <-time.After(updateInterval):
runCrawl(seeder)
crypto_rand.Read(randByte[:])
if randByte[0] >= byte(192) {
// About 25% of the time, retry the blacklist.
// This stops us from losing peers forever due to
// temporary downtime.
seeder.RetryBlacklist()
}
}
}
}()

View File

@ -104,6 +104,7 @@ func (bk *AddressBook) Remove(s PeerKey) {
}
}
// 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()
@ -122,6 +123,16 @@ func (bk *AddressBook) Blacklist(s PeerKey) {
}
}
// Redeem removes an address from the blacklist.
func (bk *AddressBook) Redeem(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()

View File

@ -426,6 +426,48 @@ func (s *Seeder) RefreshAddresses(disconnect bool) {
s.logger.Printf("RefreshAddresses() finished.")
}
// RetryBlacklist checks if the addresses in our blacklist are usable again.
// If the trial connection succeeds, they're removed from the blacklist.
func (s *Seeder) RetryBlacklist() {
s.logger.Printf("Giving the blacklist another chance")
var blacklistQueue chan *Address
var wg sync.WaitGroup
// XXX lil awkward to allocate a channel whose size we can't determine without a lock here
s.addrBook.enqueueAddrs(&blacklistQueue)
for i := 0; i < crawlerGoroutineCount; i++ {
wg.Add(1)
go func() {
for len(blacklistQueue) > 0 {
// Pull the next address off the queue
next := <-blacklistQueue
na := next.netaddr
ipString := na.IP.String()
portString := strconv.Itoa(int(na.Port))
err := s.Connect(ipString, portString)
if err != nil {
// Connection failed. Peer remains blacklisted.
continue
}
s.DisconnectPeer(next.asPeerKey())
// This would deadlock if enqueueAddrs still held the RLock.
s.addrBook.Redeem(next.asPeerKey())
}
wg.Done()
}()
}
wg.Wait()
s.logger.Printf("RetryBlacklist() finished.")
}
// WaitForAddresses waits for n addresses to be confirmed and available in the address book.
func (s *Seeder) WaitForAddresses(n int, timeout time.Duration) error {
done := make(chan struct{})
@ -462,3 +504,8 @@ func (s *Seeder) GetPeerCount() int {
func (s *Seeder) testBlacklist(pk PeerKey) {
s.addrBook.Blacklist(pk)
}
// testRedeen adds a peer to the blacklist directly, for testing.
func (s *Seeder) testRedeem(pk PeerKey) {
s.addrBook.Redeem(pk)
}

View File

@ -231,4 +231,10 @@ func TestBlacklist(t *testing.T) {
if err != ErrBlacklistedPeer {
t.Errorf("Blacklist did not prevent connection")
}
regSeeder.testRedeem(PeerKey("127.0.0.1:12345"))
err = regSeeder.Connect("127.0.0.1", "12345")
if err != nil {
t.Errorf("Redeem didn't allow reconnecting")
}
}