func (a *AddrBook) loadFromFile(filePath string) bool {
	_, err := os.Stat(filePath)
	if os.IsNotExist(err) {
		return false
	}
	r, err := os.Open(filePath)
	if err != nil {
		PanicCrisis(Fmt("Error opening file %s: %v", filePath, err))
	}
	defer r.Close()
	aJSON := &addrBookJSON{}
	dec := json.NewDecoder(r)
	err = dec.Decode(aJSON)
	if err != nil {
		PanicCrisis(Fmt("Error reading file %s: %v", filePath, err))
	}
	a.key = aJSON.Key
	for _, ka := range aJSON.Addrs {
		for _, bucketIndex := range ka.Buckets {
			bucket := a.getBucket(ka.BucketType, bucketIndex)
			bucket[ka.Addr.String()] = ka
		}
		a.addrLookup[ka.Addr.String()] = ka
		if ka.BucketType == bucketTypeNew {
			a.nNew++
		} else {
			a.nOld++
		}
	}
	return true
}
Returns false if file does not exist.
Panics if file is corrupt.
package p2p
import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math"
	"math/rand"
	"net"
	"os"
	"sync"
	"time"
	"go.uber.org/zap"
	. "github.com/annchain/annchain/module/lib/go-common"
	"github.com/annchain/annchain/module/lib/go-crypto"
)
const (
	needAddressThreshold = 1000
	dumpAddressInterval = time.Minute * 2
	oldBucketSize = 64
	oldBucketCount = 64
	newBucketSize = 64
	newBucketCount = 256
	oldBucketsPerGroup = 4
	newBucketsPerGroup = 32
	maxNewBucketsPerAddress = 4
	numMissingDays = 30
	numRetries = 3
	maxFailures = 10
	minBadDays = 7
	getSelectionPercent = 23
	minGetSelection = 32
	maxGetSelection = 250
	serializationVersion = 1
)
type AddrBook struct {
	BaseService
	mtx               sync.Mutex
	filePath          string
	routabilityStrict bool
	rand              *rand.Rand
	key               string
	ourAddrs          map[string]*NetAddress
	addrLookup        map[string]*knownAddress  
	addrNew           []map[string]*knownAddress
	addrOld           []map[string]*knownAddress
	wg                sync.WaitGroup
	nOld              int
	nNew              int
	logger  *zap.Logger
	slogger *zap.SugaredLogger
}
const (
	bucketTypeNew = 0x01
	bucketTypeOld = 0x02
)
func NewAddrBook(logger *zap.Logger, filePath string, routabilityStrict bool) *AddrBook {
	am := &AddrBook{
		rand:              rand.New(rand.NewSource(time.Now().UnixNano())),
		ourAddrs:          make(map[string]*NetAddress),
		addrLookup:        make(map[string]*knownAddress),
		filePath:          filePath,
		routabilityStrict: routabilityStrict,
		logger:            logger,
		slogger:           logger.Sugar(),
	}
	am.init()
	am.BaseService = *NewBaseService(logger, "AddrBook", am)
	return am
}
func (a *AddrBook) init() {
	a.key = crypto.CRandHex(24)  
	a.addrNew = make([]map[string]*knownAddress, newBucketCount)
	for i := range a.addrNew {
		a.addrNew[i] = make(map[string]*knownAddress)
	}
	a.addrOld = make([]map[string]*knownAddress, oldBucketCount)
	for i := range a.addrOld {
		a.addrOld[i] = make(map[string]*knownAddress)
	}
}
func (a *AddrBook) OnStart() error {
	a.BaseService.OnStart()
	a.loadFromFile(a.filePath)
	a.wg.Add(1)
	go a.saveRoutine()
	return nil
}
func (a *AddrBook) OnStop() {
	a.BaseService.OnStop()
	a.wg.Wait()
}
func (a *AddrBook) AddOurAddress(addr *NetAddress) {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	a.slogger.Debugw("Add our address to book", "addr", addr)
	a.ourAddrs[addr.String()] = addr
}
func (a *AddrBook) OurAddresses() []*NetAddress {
	addrs := []*NetAddress{}
	for _, addr := range a.ourAddrs {
		addrs = append(addrs, addr)
	}
	return addrs
}
func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	a.slogger.Debugw("Add address to book", "addr", addr, "src", src)
	a.addAddress(addr, src)
}
func (a *AddrBook) NeedMoreAddrs() bool {
	return a.Size() < needAddressThreshold
}
func (a *AddrBook) Size() int {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	return a.size()
}
func (a *AddrBook) size() int {
	return a.nNew + a.nOld
}
func (a *AddrBook) PickAddress(newBias int) *NetAddress {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	if a.size() == 0 {
		return nil
	}
	if newBias > 100 {
		newBias = 100
	}
	if newBias < 0 {
		newBias = 0
	}
	oldCorrelation := math.Sqrt(float64(a.nOld)) * (100.0 - float64(newBias))
	newCorrelation := math.Sqrt(float64(a.nNew)) * float64(newBias)
	if (newCorrelation+oldCorrelation)*a.rand.Float64() < oldCorrelation {
		var bucket map[string]*knownAddress = nil
		for len(bucket) == 0 {
			bucket = a.addrOld[a.rand.Intn(len(a.addrOld))]
		}
		randIndex := a.rand.Intn(len(bucket))
		for _, ka := range bucket {
			if randIndex == 0 {
				return ka.Addr
			}
			randIndex--
		}
		PanicSanity("Should not happen")
	} else {
		var bucket map[string]*knownAddress = nil
		for len(bucket) == 0 {
			bucket = a.addrNew[a.rand.Intn(len(a.addrNew))]
		}
		randIndex := a.rand.Intn(len(bucket))
		for _, ka := range bucket {
			if randIndex == 0 {
				return ka.Addr
			}
			randIndex--
		}
		PanicSanity("Should not happen")
	}
	return nil
}
func (a *AddrBook) MarkGood(addr *NetAddress) {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	ka := a.addrLookup[addr.String()]
	if ka == nil {
		return
	}
	ka.markGood()
	if ka.isNew() {
		a.moveToOld(ka)
	}
}
func (a *AddrBook) MarkAttempt(addr *NetAddress) {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	ka := a.addrLookup[addr.String()]
	if ka == nil {
		return
	}
	ka.markAttempt()
}
func (a *AddrBook) MarkBad(addr *NetAddress) {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	ka := a.addrLookup[addr.String()]
	if ka == nil {
		return
	}
	a.removeFromAllBuckets(ka)
}
func (a *AddrBook) GetSelection() []*NetAddress {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	if a.size() == 0 {
		return nil
	}
	allAddr := make([]*NetAddress, a.size())
	i := 0
	for _, v := range a.addrLookup {
		allAddr[i] = v.Addr
		i++
	}
	numAddresses := MaxInt(
		MinInt(minGetSelection, len(allAddr)),
		len(allAddr)*getSelectionPercent/100)
	numAddresses = MinInt(maxGetSelection, numAddresses)
	for i := 0; i < numAddresses; i++ {
		j := rand.Intn(len(allAddr)-i) + i
		allAddr[i], allAddr[j] = allAddr[j], allAddr[i]
	}
	return allAddr[:numAddresses]
}
type addrBookJSON struct {
	Key   string
	Addrs []*knownAddress
}
func (a *AddrBook) saveToFile(filePath string) {
	addrs := []*knownAddress{}
	for _, ka := range a.addrLookup {
		addrs = append(addrs, ka)
	}
	aJSON := &addrBookJSON{
		Key:   a.key,
		Addrs: addrs,
	}
	jsonBytes, err := json.MarshalIndent(aJSON, "", "\t")
	if err != nil {
		a.logger.Error("Failed to save AddrBook to file", zap.String("err", err.Error()))
		return
	}
	err = WriteFileAtomic(filePath, jsonBytes, 0644)
	if err != nil {
		a.logger.Error("Failed to save AddrBook to file", zap.String("file", filePath), zap.String("error", err.Error()))
	}
}