mirror of
https://github.com/libp2p/go-libp2p-peerstore.git
synced 2025-03-22 12:50:07 +08:00
This test is flaking on windows when using the leveldb datastore. Hopefully, this extra check will help us narrow it down.
505 lines
15 KiB
Go
505 lines
15 KiB
Go
package test
|
|
|
|
import (
|
|
"github.com/libp2p/go-libp2p-core/crypto"
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
"github.com/libp2p/go-libp2p-core/record"
|
|
"github.com/libp2p/go-libp2p-core/test"
|
|
"github.com/multiformats/go-multiaddr"
|
|
"testing"
|
|
"time"
|
|
|
|
pstore "github.com/libp2p/go-libp2p-core/peerstore"
|
|
)
|
|
|
|
var addressBookSuite = map[string]func(book pstore.AddrBook) func(*testing.T){
|
|
"AddAddress": testAddAddress,
|
|
"Clear": testClearWorks,
|
|
"SetNegativeTTLClears": testSetNegativeTTLClears,
|
|
"UpdateTTLs": testUpdateTTLs,
|
|
"NilAddrsDontBreak": testNilAddrsDontBreak,
|
|
"AddressesExpire": testAddressesExpire,
|
|
"ClearWithIter": testClearWithIterator,
|
|
"PeersWithAddresses": testPeersWithAddrs,
|
|
"CertifiedAddresses": testCertifiedAddresses,
|
|
}
|
|
|
|
type AddrBookFactory func() (pstore.AddrBook, func())
|
|
|
|
func TestAddrBook(t *testing.T, factory AddrBookFactory) {
|
|
for name, test := range addressBookSuite {
|
|
// Create a new peerstore.
|
|
ab, closeFunc := factory()
|
|
|
|
// Run the test.
|
|
t.Run(name, test(ab))
|
|
|
|
// Cleanup.
|
|
if closeFunc != nil {
|
|
closeFunc()
|
|
}
|
|
}
|
|
}
|
|
|
|
func testAddAddress(ab pstore.AddrBook) func(*testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Run("add a single address", func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
addrs := GenerateAddrs(1)
|
|
|
|
ab.AddAddr(id, addrs[0], time.Hour)
|
|
|
|
AssertAddressesEqual(t, addrs, ab.Addrs(id))
|
|
})
|
|
|
|
t.Run("idempotent add single address", func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
addrs := GenerateAddrs(1)
|
|
|
|
ab.AddAddr(id, addrs[0], time.Hour)
|
|
ab.AddAddr(id, addrs[0], time.Hour)
|
|
|
|
AssertAddressesEqual(t, addrs, ab.Addrs(id))
|
|
})
|
|
|
|
t.Run("add multiple addresses", func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
addrs := GenerateAddrs(3)
|
|
|
|
ab.AddAddrs(id, addrs, time.Hour)
|
|
AssertAddressesEqual(t, addrs, ab.Addrs(id))
|
|
})
|
|
|
|
t.Run("idempotent add multiple addresses", func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
addrs := GenerateAddrs(3)
|
|
|
|
ab.AddAddrs(id, addrs, time.Hour)
|
|
ab.AddAddrs(id, addrs, time.Hour)
|
|
|
|
AssertAddressesEqual(t, addrs, ab.Addrs(id))
|
|
})
|
|
|
|
t.Run("adding an existing address with a later expiration extends its ttl", func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
addrs := GenerateAddrs(3)
|
|
|
|
ab.AddAddrs(id, addrs, time.Second)
|
|
|
|
// same address as before but with a higher TTL
|
|
ab.AddAddrs(id, addrs[2:], time.Hour)
|
|
|
|
// after the initial TTL has expired, check that only the third address is present.
|
|
time.Sleep(1200 * time.Millisecond)
|
|
AssertAddressesEqual(t, addrs[2:], ab.Addrs(id))
|
|
|
|
// make sure we actually set the TTL
|
|
ab.UpdateAddrs(id, time.Hour, 0)
|
|
AssertAddressesEqual(t, nil, ab.Addrs(id))
|
|
})
|
|
|
|
t.Run("adding an existing address with an earlier expiration never reduces the expiration", func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
addrs := GenerateAddrs(3)
|
|
|
|
ab.AddAddrs(id, addrs, time.Hour)
|
|
|
|
// same address as before but with a lower TTL
|
|
ab.AddAddrs(id, addrs[2:], time.Second)
|
|
|
|
// after the initial TTL has expired, check that all three addresses are still present (i.e. the TTL on
|
|
// the modified one was not shortened).
|
|
time.Sleep(2100 * time.Millisecond)
|
|
AssertAddressesEqual(t, addrs, ab.Addrs(id))
|
|
})
|
|
|
|
t.Run("adding an existing address with an earlier expiration never reduces the TTL", func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
addrs := GenerateAddrs(1)
|
|
|
|
ab.AddAddrs(id, addrs, 4*time.Second)
|
|
// 4 seconds left
|
|
time.Sleep(3 * time.Second)
|
|
// 1 second left
|
|
ab.AddAddrs(id, addrs, 3*time.Second)
|
|
// 3 seconds left
|
|
time.Sleep(1 * time.Second)
|
|
// 2 seconds left.
|
|
|
|
// We still have the address.
|
|
AssertAddressesEqual(t, addrs, ab.Addrs(id))
|
|
|
|
// The TTL wasn't reduced
|
|
ab.UpdateAddrs(id, 4*time.Second, 0)
|
|
AssertAddressesEqual(t, nil, ab.Addrs(id))
|
|
})
|
|
}
|
|
}
|
|
|
|
func testClearWorks(ab pstore.AddrBook) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
ids := GeneratePeerIDs(2)
|
|
addrs := GenerateAddrs(5)
|
|
|
|
ab.AddAddrs(ids[0], addrs[0:3], time.Hour)
|
|
ab.AddAddrs(ids[1], addrs[3:], time.Hour)
|
|
|
|
AssertAddressesEqual(t, addrs[0:3], ab.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs[3:], ab.Addrs(ids[1]))
|
|
|
|
ab.ClearAddrs(ids[0])
|
|
AssertAddressesEqual(t, nil, ab.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs[3:], ab.Addrs(ids[1]))
|
|
|
|
ab.ClearAddrs(ids[1])
|
|
AssertAddressesEqual(t, nil, ab.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, nil, ab.Addrs(ids[1]))
|
|
}
|
|
}
|
|
|
|
func testSetNegativeTTLClears(m pstore.AddrBook) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
addrs := GenerateAddrs(100)
|
|
|
|
m.SetAddrs(id, addrs, time.Hour)
|
|
AssertAddressesEqual(t, addrs, m.Addrs(id))
|
|
|
|
// remove two addresses.
|
|
m.SetAddr(id, addrs[50], -1)
|
|
m.SetAddr(id, addrs[75], -1)
|
|
|
|
// calculate the survivors
|
|
survivors := append(addrs[0:50], addrs[51:]...)
|
|
survivors = append(survivors[0:74], survivors[75:]...)
|
|
|
|
AssertAddressesEqual(t, survivors, m.Addrs(id))
|
|
|
|
// remove _all_ the addresses
|
|
m.SetAddrs(id, survivors, -1)
|
|
if len(m.Addrs(id)) != 0 {
|
|
t.Error("expected empty address list after clearing all addresses")
|
|
}
|
|
|
|
// add half, but try to remove more than we added
|
|
m.SetAddrs(id, addrs[:50], time.Hour)
|
|
m.SetAddrs(id, addrs, -1)
|
|
if len(m.Addrs(id)) != 0 {
|
|
t.Error("expected empty address list after clearing all addresses")
|
|
}
|
|
|
|
// try to remove the same addr multiple times
|
|
m.SetAddrs(id, addrs[:5], time.Hour)
|
|
repeated := make([]multiaddr.Multiaddr, 10)
|
|
for i := 0; i < len(repeated); i++ {
|
|
repeated[i] = addrs[0]
|
|
}
|
|
m.SetAddrs(id, repeated, -1)
|
|
if len(m.Addrs(id)) != 4 {
|
|
t.Errorf("expected 4 addrs after removing one, got %d", len(m.Addrs(id)))
|
|
}
|
|
}
|
|
}
|
|
|
|
func testUpdateTTLs(m pstore.AddrBook) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Run("update ttl of peer with no addrs", func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
|
|
// Shouldn't panic.
|
|
m.UpdateAddrs(id, time.Hour, time.Minute)
|
|
})
|
|
|
|
t.Run("update to 0 clears addrs", func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
addrs := GenerateAddrs(1)
|
|
|
|
// Shouldn't panic.
|
|
m.SetAddrs(id, addrs, time.Hour)
|
|
m.UpdateAddrs(id, time.Hour, 0)
|
|
if len(m.Addrs(id)) != 0 {
|
|
t.Error("expected no addresses")
|
|
}
|
|
})
|
|
|
|
t.Run("update ttls successfully", func(t *testing.T) {
|
|
ids := GeneratePeerIDs(2)
|
|
addrs1, addrs2 := GenerateAddrs(2), GenerateAddrs(2)
|
|
|
|
// set two keys with different ttls for each peer.
|
|
m.SetAddr(ids[0], addrs1[0], time.Hour)
|
|
m.SetAddr(ids[0], addrs1[1], time.Minute)
|
|
m.SetAddr(ids[1], addrs2[0], time.Hour)
|
|
m.SetAddr(ids[1], addrs2[1], time.Minute)
|
|
|
|
// Sanity check.
|
|
AssertAddressesEqual(t, addrs1, m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs2, m.Addrs(ids[1]))
|
|
|
|
// Will only affect addrs1[0].
|
|
// Badger does not support subsecond TTLs.
|
|
// https://github.com/dgraph-io/badger/issues/339
|
|
m.UpdateAddrs(ids[0], time.Hour, 1*time.Second)
|
|
|
|
// No immediate effect.
|
|
AssertAddressesEqual(t, addrs1, m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs2, m.Addrs(ids[1]))
|
|
|
|
// After a wait, addrs[0] is gone.
|
|
time.Sleep(2 * time.Second)
|
|
AssertAddressesEqual(t, addrs1[1:2], m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs2, m.Addrs(ids[1]))
|
|
|
|
// Will only affect addrs2[0].
|
|
m.UpdateAddrs(ids[1], time.Hour, 1*time.Second)
|
|
|
|
// No immediate effect.
|
|
AssertAddressesEqual(t, addrs1[1:2], m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs2, m.Addrs(ids[1]))
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
// First addrs is gone in both.
|
|
AssertAddressesEqual(t, addrs1[1:], m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs2[1:], m.Addrs(ids[1]))
|
|
})
|
|
|
|
}
|
|
}
|
|
|
|
func testNilAddrsDontBreak(m pstore.AddrBook) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
id := GeneratePeerIDs(1)[0]
|
|
|
|
m.SetAddr(id, nil, time.Hour)
|
|
m.AddAddr(id, nil, time.Hour)
|
|
}
|
|
}
|
|
|
|
func testAddressesExpire(m pstore.AddrBook) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
ids := GeneratePeerIDs(2)
|
|
addrs1 := GenerateAddrs(3)
|
|
addrs2 := GenerateAddrs(2)
|
|
|
|
m.AddAddrs(ids[0], addrs1, time.Hour)
|
|
m.AddAddrs(ids[1], addrs2, time.Hour)
|
|
|
|
AssertAddressesEqual(t, addrs1, m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs2, m.Addrs(ids[1]))
|
|
|
|
m.AddAddrs(ids[0], addrs1, 2*time.Hour)
|
|
m.AddAddrs(ids[1], addrs2, 2*time.Hour)
|
|
|
|
AssertAddressesEqual(t, addrs1, m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs2, m.Addrs(ids[1]))
|
|
|
|
m.SetAddr(ids[0], addrs1[0], 100*time.Microsecond)
|
|
<-time.After(100 * time.Millisecond)
|
|
AssertAddressesEqual(t, addrs1[1:3], m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs2, m.Addrs(ids[1]))
|
|
|
|
m.SetAddr(ids[0], addrs1[2], 100*time.Microsecond)
|
|
<-time.After(100 * time.Millisecond)
|
|
AssertAddressesEqual(t, addrs1[1:2], m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs2, m.Addrs(ids[1]))
|
|
|
|
m.SetAddr(ids[1], addrs2[0], 100*time.Microsecond)
|
|
<-time.After(100 * time.Millisecond)
|
|
AssertAddressesEqual(t, addrs1[1:2], m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, addrs2[1:], m.Addrs(ids[1]))
|
|
|
|
m.SetAddr(ids[1], addrs2[1], 100*time.Microsecond)
|
|
<-time.After(100 * time.Millisecond)
|
|
AssertAddressesEqual(t, addrs1[1:2], m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, nil, m.Addrs(ids[1]))
|
|
|
|
m.SetAddr(ids[0], addrs1[1], 100*time.Microsecond)
|
|
<-time.After(100 * time.Millisecond)
|
|
AssertAddressesEqual(t, nil, m.Addrs(ids[0]))
|
|
AssertAddressesEqual(t, nil, m.Addrs(ids[1]))
|
|
}
|
|
}
|
|
|
|
func testClearWithIterator(m pstore.AddrBook) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
ids := GeneratePeerIDs(2)
|
|
addrs := GenerateAddrs(100)
|
|
|
|
// Add the peers with 50 addresses each.
|
|
m.AddAddrs(ids[0], addrs[:50], pstore.PermanentAddrTTL)
|
|
m.AddAddrs(ids[1], addrs[50:], pstore.PermanentAddrTTL)
|
|
|
|
if all := append(m.Addrs(ids[0]), m.Addrs(ids[1])...); len(all) != 100 {
|
|
t.Fatal("expected pstore to contain both peers with all their maddrs")
|
|
}
|
|
|
|
// Since we don't fetch these peers, they won't be present in cache.
|
|
|
|
m.ClearAddrs(ids[0])
|
|
if all := append(m.Addrs(ids[0]), m.Addrs(ids[1])...); len(all) != 50 {
|
|
t.Fatal("expected pstore to contain only addrs of peer 2")
|
|
}
|
|
|
|
m.ClearAddrs(ids[1])
|
|
if all := append(m.Addrs(ids[0]), m.Addrs(ids[1])...); len(all) != 0 {
|
|
t.Fatal("expected pstore to contain no addresses")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testPeersWithAddrs(m pstore.AddrBook) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
// cannot run in parallel as the store is modified.
|
|
// go runs sequentially in the specified order
|
|
// see https://blog.golang.org/subtests
|
|
|
|
t.Run("empty addrbook", func(t *testing.T) {
|
|
if peers := m.PeersWithAddrs(); len(peers) != 0 {
|
|
t.Fatal("expected to find no peers")
|
|
}
|
|
})
|
|
|
|
t.Run("non-empty addrbook", func(t *testing.T) {
|
|
ids := GeneratePeerIDs(2)
|
|
addrs := GenerateAddrs(10)
|
|
|
|
m.AddAddrs(ids[0], addrs[:5], pstore.PermanentAddrTTL)
|
|
m.AddAddrs(ids[1], addrs[5:], pstore.PermanentAddrTTL)
|
|
|
|
if peers := m.PeersWithAddrs(); len(peers) != 2 {
|
|
t.Fatal("expected to find 2 peers")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func testCertifiedAddresses(m pstore.AddrBook) func(*testing.T) {
|
|
return func(t *testing.T) {
|
|
cab := m.(pstore.CertifiedAddrBook)
|
|
|
|
priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 256)
|
|
if err != nil {
|
|
t.Errorf("error generating testing keys: %v", err)
|
|
}
|
|
|
|
id, _ := peer.IDFromPrivateKey(priv)
|
|
allAddrs := GenerateAddrs(10)
|
|
certifiedAddrs := allAddrs[:5]
|
|
uncertifiedAddrs := allAddrs[5:]
|
|
rec1 := peer.NewPeerRecord()
|
|
rec1.PeerID = id
|
|
rec1.Addrs = certifiedAddrs
|
|
signedRec1, err := record.Seal(rec1, priv)
|
|
if err != nil {
|
|
t.Errorf("error creating signed routing record: %v", err)
|
|
}
|
|
|
|
rec2 := peer.NewPeerRecord()
|
|
rec2.PeerID = id
|
|
rec2.Addrs = certifiedAddrs
|
|
signedRec2, err := record.Seal(rec2, priv)
|
|
if err != nil {
|
|
t.Errorf("error creating signed routing record: %v", err)
|
|
}
|
|
|
|
// add a few non-certified addrs
|
|
m.AddAddrs(id, uncertifiedAddrs, time.Hour)
|
|
|
|
// make sure they're present
|
|
AssertAddressesEqual(t, uncertifiedAddrs, m.Addrs(id))
|
|
|
|
// add the signed record to addr book
|
|
accepted, err := cab.ConsumePeerRecord(signedRec2, time.Hour)
|
|
if err != nil {
|
|
t.Errorf("error adding signed routing record to addrbook: %v", err)
|
|
}
|
|
if !accepted {
|
|
t.Errorf("should have accepted signed peer record")
|
|
}
|
|
|
|
// the non-certified addrs should be gone & we should get only certified addrs back from Addrs
|
|
// AssertAddressesEqual(t, certifiedAddrs, m.Addrs(id))
|
|
AssertAddressesEqual(t, allAddrs, m.Addrs(id))
|
|
|
|
// PeersWithAddrs should return a single peer
|
|
if len(m.PeersWithAddrs()) != 1 {
|
|
t.Errorf("expected PeersWithAddrs to return 1, got %d", len(m.PeersWithAddrs()))
|
|
}
|
|
|
|
// Adding an old record should fail
|
|
accepted, err = cab.ConsumePeerRecord(signedRec1, time.Hour)
|
|
if accepted {
|
|
t.Error("We should have failed to accept a record with an old sequence number")
|
|
}
|
|
if err != nil {
|
|
t.Errorf("expected no error, got: %s", err)
|
|
}
|
|
|
|
// once certified addrs exist, trying to add non-certified addrs should have no effect
|
|
// m.AddAddrs(id, uncertifiedAddrs, time.Hour)
|
|
// AssertAddressesEqual(t, certifiedAddrs, m.Addrs(id))
|
|
// XXX: Disabled until signed records are required
|
|
m.AddAddrs(id, uncertifiedAddrs, time.Hour)
|
|
AssertAddressesEqual(t, allAddrs, m.Addrs(id))
|
|
|
|
// we should be able to retrieve the signed peer record
|
|
rec3 := cab.GetPeerRecord(id)
|
|
if rec3 == nil || !signedRec2.Equal(rec3) {
|
|
t.Error("unable to retrieve signed routing record from addrbook")
|
|
}
|
|
|
|
// Adding a new envelope should clear existing certified addresses.
|
|
// Only the newly-added ones should remain
|
|
certifiedAddrs = certifiedAddrs[:3]
|
|
rec4 := peer.NewPeerRecord()
|
|
rec4.PeerID = id
|
|
rec4.Addrs = certifiedAddrs
|
|
signedRec4, err := record.Seal(rec4, priv)
|
|
test.AssertNilError(t, err)
|
|
accepted, err = cab.ConsumePeerRecord(signedRec4, time.Hour)
|
|
test.AssertNilError(t, err)
|
|
if !accepted {
|
|
t.Error("expected peer record to be accepted")
|
|
}
|
|
// AssertAddressesEqual(t, certifiedAddrs, m.Addrs(id))
|
|
AssertAddressesEqual(t, allAddrs, m.Addrs(id))
|
|
|
|
// update TTL on signed addrs to -1 to remove them.
|
|
// the signed routing record should be deleted
|
|
// m.SetAddrs(id, certifiedAddrs, -1)
|
|
// XXX: Disabled until signed records are required
|
|
m.SetAddrs(id, allAddrs, -1)
|
|
if len(m.Addrs(id)) != 0 {
|
|
t.Error("expected zero certified addrs after setting TTL to -1")
|
|
}
|
|
if cab.GetPeerRecord(id) != nil {
|
|
t.Error("expected signed peer record to be removed when addresses expire")
|
|
}
|
|
|
|
// Test that natural TTL expiration clears signed peer records
|
|
accepted, err = cab.ConsumePeerRecord(signedRec4, time.Second)
|
|
if !accepted {
|
|
t.Error("expected peer record to be accepted")
|
|
}
|
|
test.AssertNilError(t, err)
|
|
AssertAddressesEqual(t, certifiedAddrs, m.Addrs(id))
|
|
|
|
time.Sleep(2 * time.Second)
|
|
if cab.GetPeerRecord(id) != nil {
|
|
t.Error("expected signed peer record to be removed when addresses expire")
|
|
}
|
|
|
|
// adding a peer record that's signed with the wrong key should fail
|
|
priv2, _, err := test.RandTestKeyPair(crypto.Ed25519, 256)
|
|
test.AssertNilError(t, err)
|
|
env, err := record.Seal(rec4, priv2)
|
|
test.AssertNilError(t, err)
|
|
|
|
accepted, err = cab.ConsumePeerRecord(env, time.Second)
|
|
if accepted || err == nil {
|
|
t.Error("expected adding a PeerRecord that's signed with the wrong key to fail")
|
|
}
|
|
}
|
|
}
|