Skip to content

Commit 5d58009

Browse files
committed
test: add integration test for PEX network bootstrapping with dynamic node connections
1 parent 5585246 commit 5d58009

File tree

1 file changed

+118
-0
lines changed

1 file changed

+118
-0
lines changed

p2p/pex/pex_reactor_test.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,3 +919,121 @@ func TestPEXReactorWhenAddressBookIsSmallerThanMaxDials(t *testing.T) {
919919
assert.Equal(t, 1, pexR.AttemptsToDial(peer))
920920
}
921921
}
922+
923+
// TestPexFromScratch simulates establishing a working P2P network from scratch with a single bootstrap node.
924+
//
925+
// To further test reactor-specific peer disconnections and bootstrap node accept capabilities, each switch is running
926+
// not only PEX, but also some test reactors.
927+
// It is asserted that nodes can connect to each other and the address books contain other peers.
928+
func TestPexFromScratch(t *testing.T) {
929+
testCases := []struct {
930+
name string
931+
bootstrap int // n - number of bootstrap nodes
932+
joining int // m - number of joining nodes
933+
}{
934+
{"1 bootstrap, 3 joining", 1, 3},
935+
{"2 bootstrap, 5 joining", 2, 5},
936+
{"3 bootstrap, 20 joining", 3, 20},
937+
}
938+
939+
for _, tc := range testCases {
940+
t.Run(tc.name, func(t *testing.T) {
941+
// directory to store address books
942+
dir, err := os.MkdirTemp("", "pex_from_scratch")
943+
require.NoError(t, err)
944+
defer os.RemoveAll(dir)
945+
946+
totalNodes := tc.bootstrap + tc.joining
947+
switches := make([]*p2p.Switch, totalNodes)
948+
books := make([]AddrBook, totalNodes)
949+
logger := log.TestingLogger()
950+
951+
// Create all switches (bootstrap + joining nodes)
952+
for i := 0; i < totalNodes; i++ {
953+
switches[i] = p2p.MakeSwitch(cfg, i, func(i int, sw *p2p.Switch) *p2p.Switch {
954+
books[i] = NewAddrBook(filepath.Join(dir, fmt.Sprintf("addrbook%d.json", i)), false)
955+
books[i].SetLogger(logger.With("pex", i))
956+
sw.SetAddrBook(books[i])
957+
958+
sw.SetLogger(logger.With("pex", i))
959+
960+
r := NewReactor(books[i], &ReactorConfig{SeedMode: true})
961+
r.SetLogger(logger.With("pex", i))
962+
r.SetEnsurePeersPeriod(250 * time.Millisecond)
963+
sw.AddReactor("pex", r)
964+
965+
return sw
966+
})
967+
}
968+
969+
// Bootstrap nodes know about each other
970+
for i := 0; i < tc.bootstrap; i++ {
971+
for j := 0; j < tc.bootstrap; j++ {
972+
if i != j {
973+
addr := switches[j].NetAddress()
974+
err := books[i].AddAddress(addr, addr)
975+
require.NoError(t, err)
976+
}
977+
}
978+
}
979+
980+
// Joining nodes each know about a single bootstrap node
981+
for i := tc.bootstrap; i < totalNodes; i++ {
982+
// Each joining node connects to a bootstrap node at index (i-tc.bootstrap) % tc.bootstrap
983+
bootstrapIndex := (i - tc.bootstrap) % tc.bootstrap
984+
addr := switches[bootstrapIndex].NetAddress()
985+
err := books[i].AddAddress(addr, addr)
986+
require.NoError(t, err)
987+
}
988+
989+
// Start bootstrappers
990+
for i := 0; i < tc.bootstrap; i++ {
991+
err := switches[i].Start()
992+
require.NoError(t, err)
993+
}
994+
995+
// Wait a bit and start joining nodes
996+
time.Sleep(100 * time.Millisecond)
997+
for i := tc.bootstrap; i < totalNodes; i++ {
998+
err := switches[i].Start()
999+
require.NoError(t, err)
1000+
}
1001+
1002+
assertFullAddressBooks(t, totalNodes, books)
1003+
})
1004+
}
1005+
}
1006+
1007+
// assertFullAddressBooks checks if all address books have the expected number of peer entries within a timeout period.
1008+
func assertFullAddressBooks(t *testing.T, totalNodes int, books []AddrBook) {
1009+
var (
1010+
ticker = time.NewTicker(50 * time.Millisecond)
1011+
timeoutCh = time.After(5 * time.Second)
1012+
)
1013+
defer ticker.Stop()
1014+
1015+
allGood := false
1016+
expected := totalNodes - 1
1017+
for !allGood {
1018+
select {
1019+
case <-ticker.C:
1020+
// PEX is responsible for address exchange, so we need to check address books, not the number of connections
1021+
// let's make a strong assumption - each node knows of all other nodes
1022+
cnt := 0
1023+
for i := 0; i < totalNodes; i++ {
1024+
total := len(books[i].GetSelection())
1025+
if total == expected {
1026+
cnt++
1027+
}
1028+
}
1029+
t.Logf("%d nodes with full address book", cnt)
1030+
if cnt == totalNodes {
1031+
allGood = true
1032+
}
1033+
case <-timeoutCh:
1034+
t.Errorf("expected all nodes to connect to each other")
1035+
t.Fail()
1036+
}
1037+
}
1038+
assert.True(t, allGood, "Not all nodes connected to each other")
1039+
}

0 commit comments

Comments
 (0)