addrmgr: store address' supported service bits

In this commit, we update the serialized version of the AddressManager
to also store each address' service bits. This allows an address'
service bits to be properly set when read from disk, which allows
external callers to reliably filter out addresses which do not support
their required services.

Since the service bits were not previously stored, the serialization
version needed to be bumped. A test has been added to test the behavior
of upgrading from version 1 to 2.
This commit is contained in:
Wilmer Paulino 2019-02-06 13:32:52 -08:00
parent 10f17a8bb0
commit bcd50ea838
No known key found for this signature in database
GPG key ID: 6DF57B9F9514972F
3 changed files with 114 additions and 10 deletions

View file

@ -46,6 +46,7 @@ type AddrManager struct {
nNew int nNew int
lamtx sync.Mutex lamtx sync.Mutex
localAddresses map[string]*localAddress localAddresses map[string]*localAddress
version int
} }
type serializedKnownAddress struct { type serializedKnownAddress struct {
@ -55,6 +56,8 @@ type serializedKnownAddress struct {
TimeStamp int64 TimeStamp int64
LastAttempt int64 LastAttempt int64
LastSuccess int64 LastSuccess int64
Services wire.ServiceFlag
SrcServices wire.ServiceFlag
// no refcount or tried, that is available from context. // no refcount or tried, that is available from context.
} }
@ -155,7 +158,7 @@ const (
getAddrPercent = 23 getAddrPercent = 23
// serialisationVersion is the current version of the on-disk format. // serialisationVersion is the current version of the on-disk format.
serialisationVersion = 1 serialisationVersion = 2
) )
// updateAddress is a helper function to either update an address already known // updateAddress is a helper function to either update an address already known
@ -362,7 +365,7 @@ func (a *AddrManager) savePeers() {
// First we make a serialisable datastructure so we can encode it to // First we make a serialisable datastructure so we can encode it to
// json. // json.
sam := new(serializedAddrManager) sam := new(serializedAddrManager)
sam.Version = serialisationVersion sam.Version = a.version
copy(sam.Key[:], a.key[:]) copy(sam.Key[:], a.key[:])
sam.Addresses = make([]*serializedKnownAddress, len(a.addrIndex)) sam.Addresses = make([]*serializedKnownAddress, len(a.addrIndex))
@ -375,6 +378,10 @@ func (a *AddrManager) savePeers() {
ska.Attempts = v.attempts ska.Attempts = v.attempts
ska.LastAttempt = v.lastattempt.Unix() ska.LastAttempt = v.lastattempt.Unix()
ska.LastSuccess = v.lastsuccess.Unix() ska.LastSuccess = v.lastsuccess.Unix()
if a.version > 1 {
ska.Services = v.na.Services
ska.SrcServices = v.srcAddr.Services
}
// Tried and refs are implicit in the rest of the structure // Tried and refs are implicit in the rest of the structure
// and will be worked out from context on unserialisation. // and will be worked out from context on unserialisation.
sam.Addresses[i] = ska sam.Addresses[i] = ska
@ -451,24 +458,43 @@ func (a *AddrManager) deserializePeers(filePath string) error {
return fmt.Errorf("error reading %s: %v", filePath, err) return fmt.Errorf("error reading %s: %v", filePath, err)
} }
if sam.Version != serialisationVersion { // Since decoding JSON is backwards compatible (i.e., only decodes
// fields it understands), we'll only return an error upon seeing a
// version past our latest supported version.
if sam.Version > serialisationVersion {
return fmt.Errorf("unknown version %v in serialized "+ return fmt.Errorf("unknown version %v in serialized "+
"addrmanager", sam.Version) "addrmanager", sam.Version)
} }
copy(a.key[:], sam.Key[:]) copy(a.key[:], sam.Key[:])
for _, v := range sam.Addresses { for _, v := range sam.Addresses {
ka := new(KnownAddress) ka := new(KnownAddress)
ka.na, err = a.DeserializeNetAddress(v.Addr)
// The first version of the serialized address manager was not
// aware of the service bits associated with this address, so
// we'll assign a default of SFNodeNetwork to it.
if sam.Version == 1 {
v.Services = wire.SFNodeNetwork
}
ka.na, err = a.DeserializeNetAddress(v.Addr, v.Services)
if err != nil { if err != nil {
return fmt.Errorf("failed to deserialize netaddress "+ return fmt.Errorf("failed to deserialize netaddress "+
"%s: %v", v.Addr, err) "%s: %v", v.Addr, err)
} }
ka.srcAddr, err = a.DeserializeNetAddress(v.Src)
// The first version of the serialized address manager was not
// aware of the service bits associated with the source address,
// so we'll assign a default of SFNodeNetwork to it.
if sam.Version == 1 {
v.SrcServices = wire.SFNodeNetwork
}
ka.srcAddr, err = a.DeserializeNetAddress(v.Src, v.SrcServices)
if err != nil { if err != nil {
return fmt.Errorf("failed to deserialize netaddress "+ return fmt.Errorf("failed to deserialize netaddress "+
"%s: %v", v.Src, err) "%s: %v", v.Src, err)
} }
ka.attempts = v.Attempts ka.attempts = v.Attempts
ka.lastattempt = time.Unix(v.LastAttempt, 0) ka.lastattempt = time.Unix(v.LastAttempt, 0)
ka.lastsuccess = time.Unix(v.LastSuccess, 0) ka.lastsuccess = time.Unix(v.LastSuccess, 0)
@ -520,8 +546,10 @@ func (a *AddrManager) deserializePeers(filePath string) error {
return nil return nil
} }
// DeserializeNetAddress converts a given address string to a *wire.NetAddress // DeserializeNetAddress converts a given address string to a *wire.NetAddress.
func (a *AddrManager) DeserializeNetAddress(addr string) (*wire.NetAddress, error) { func (a *AddrManager) DeserializeNetAddress(addr string,
services wire.ServiceFlag) (*wire.NetAddress, error) {
host, portStr, err := net.SplitHostPort(addr) host, portStr, err := net.SplitHostPort(addr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -531,7 +559,7 @@ func (a *AddrManager) DeserializeNetAddress(addr string) (*wire.NetAddress, erro
return nil, err return nil, err
} }
return a.HostToNetAddress(host, uint16(port), wire.SFNodeNetwork) return a.HostToNetAddress(host, uint16(port), services)
} }
// Start begins the core address handler which manages a pool of known // Start begins the core address handler which manages a pool of known
@ -1116,6 +1144,7 @@ func New(dataDir string, lookupFunc func(string) ([]net.IP, error)) *AddrManager
rand: rand.New(rand.NewSource(time.Now().UnixNano())), rand: rand.New(rand.NewSource(time.Now().UnixNano())),
quit: make(chan struct{}), quit: make(chan struct{}),
localAddresses: make(map[string]*localAddress), localAddresses: make(map[string]*localAddress),
version: serialisationVersion,
} }
am.reset() am.reset()
return &am return &am

View file

@ -117,3 +117,78 @@ func TestAddrManagerSerialization(t *testing.T) {
addrMgr.loadPeers() addrMgr.loadPeers()
assertAddrs(t, addrMgr, expectedAddrs) assertAddrs(t, addrMgr, expectedAddrs)
} }
// TestAddrManagerV1ToV2 ensures that we can properly upgrade the serialized
// version of the address manager from v1 to v2.
func TestAddrManagerV1ToV2(t *testing.T) {
t.Parallel()
// We'll start by creating our address manager backed by a temporary
// directory.
tempDir, err := ioutil.TempDir("", "addrmgr")
if err != nil {
t.Fatalf("unable to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)
addrMgr := New(tempDir, nil)
// As we're interested in testing the upgrade path from v1 to v2, we'll
// override the manager's current version.
addrMgr.version = 1
// We'll be adding 5 random addresses to the manager. Since this is v1,
// each addresses' services will not be stored.
const numAddrs = 5
expectedAddrs := make(map[string]*wire.NetAddress, numAddrs)
for i := 0; i < numAddrs; i++ {
addr := randAddr(t)
expectedAddrs[NetAddressKey(addr)] = addr
addrMgr.AddAddress(addr, randAddr(t))
}
// Then, we'll persist these addresses to disk and restart the address
// manager - overriding its version back to v1.
addrMgr.savePeers()
addrMgr = New(tempDir, nil)
addrMgr.version = 1
// When we read all of the addresses back from disk, we should expect to
// find all of them, but their services will be set to a default of
// SFNodeNetwork since they were not previously stored. After ensuring
// that this default is set, we'll override each addresses' services
// with the original value from when they were created.
addrMgr.loadPeers()
addrs := addrMgr.getAddresses()
if len(addrs) != len(expectedAddrs) {
t.Fatalf("expected to find %d adddresses, found %d",
len(expectedAddrs), len(addrs))
}
for _, addr := range addrs {
addrStr := NetAddressKey(addr)
expectedAddr, ok := expectedAddrs[addrStr]
if !ok {
t.Fatalf("expected to find address %v", addrStr)
}
if addr.Services != wire.SFNodeNetwork {
t.Fatalf("expected address services to be %v, got %v",
wire.SFNodeNetwork, addr.Services)
}
addrMgr.SetServices(addr, expectedAddr.Services)
}
// We'll also bump up the manager's version to v2, which should signal
// that it should include the address services when persisting its
// state.
addrMgr.version = 2
addrMgr.savePeers()
// Finally, we'll recreate the manager and ensure that the services were
// persisted correctly.
addrMgr = New(tempDir, nil)
addrMgr.loadPeers()
assertAddrs(t, addrMgr, expectedAddrs)
}

View file

@ -262,7 +262,7 @@ func TestNeedMoreAddresses(t *testing.T) {
var err error var err error
for i := 0; i < addrsToAdd; i++ { for i := 0; i < addrsToAdd; i++ {
s := fmt.Sprintf("%d.%d.173.147:8333", i/128+60, i%128+60) s := fmt.Sprintf("%d.%d.173.147:8333", i/128+60, i%128+60)
addrs[i], err = n.DeserializeNetAddress(s) addrs[i], err = n.DeserializeNetAddress(s, wire.SFNodeNetwork)
if err != nil { if err != nil {
t.Errorf("Failed to turn %s into an address: %v", s, err) t.Errorf("Failed to turn %s into an address: %v", s, err)
} }
@ -290,7 +290,7 @@ func TestGood(t *testing.T) {
var err error var err error
for i := 0; i < addrsToAdd; i++ { for i := 0; i < addrsToAdd; i++ {
s := fmt.Sprintf("%d.173.147.%d:8333", i/64+60, i%64+60) s := fmt.Sprintf("%d.173.147.%d:8333", i/64+60, i%64+60)
addrs[i], err = n.DeserializeNetAddress(s) addrs[i], err = n.DeserializeNetAddress(s, wire.SFNodeNetwork)
if err != nil { if err != nil {
t.Errorf("Failed to turn %s into an address: %v", s, err) t.Errorf("Failed to turn %s into an address: %v", s, err)
} }