2023-08-21 03:50:38 +00:00
|
|
|
package swarm_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-06-07 06:25:43 +00:00
|
|
|
"sync"
|
2023-08-21 03:50:38 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/libp2p/go-libp2p/core/event"
|
|
|
|
"github.com/libp2p/go-libp2p/core/network"
|
|
|
|
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
|
|
|
|
. "github.com/libp2p/go-libp2p/p2p/net/swarm"
|
|
|
|
swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing"
|
|
|
|
|
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
2024-06-07 06:25:43 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2023-08-21 03:50:38 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func newSwarmWithSubscription(t *testing.T) (*Swarm, event.Subscription) {
|
|
|
|
t.Helper()
|
|
|
|
bus := eventbus.NewBus()
|
|
|
|
sw := swarmt.GenSwarm(t, swarmt.EventBus(bus))
|
|
|
|
t.Cleanup(func() { sw.Close() })
|
|
|
|
sub, err := bus.Subscribe(new(event.EvtPeerConnectednessChanged))
|
|
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() { sub.Close() })
|
|
|
|
return sw, sub
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkEvent(t *testing.T, sub event.Subscription, expected event.EvtPeerConnectednessChanged) {
|
|
|
|
t.Helper()
|
|
|
|
select {
|
|
|
|
case ev, ok := <-sub.Out():
|
|
|
|
require.True(t, ok)
|
|
|
|
evt := ev.(event.EvtPeerConnectednessChanged)
|
|
|
|
require.Equal(t, expected.Connectedness, evt.Connectedness, "wrong connectedness state")
|
|
|
|
require.Equal(t, expected.Peer, evt.Peer)
|
|
|
|
case <-time.After(time.Second):
|
|
|
|
t.Fatal("didn't get PeerConnectedness event")
|
|
|
|
}
|
|
|
|
|
|
|
|
// check that there are no more events
|
|
|
|
select {
|
|
|
|
case <-sub.Out():
|
|
|
|
t.Fatal("didn't expect any more events")
|
|
|
|
case <-time.After(100 * time.Millisecond):
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestConnectednessEventsSingleConn(t *testing.T) {
|
|
|
|
s1, sub1 := newSwarmWithSubscription(t)
|
|
|
|
s2, sub2 := newSwarmWithSubscription(t)
|
|
|
|
|
|
|
|
s1.Peerstore().AddAddrs(s2.LocalPeer(), []ma.Multiaddr{s2.ListenAddresses()[0]}, time.Hour)
|
|
|
|
_, err := s1.DialPeer(context.Background(), s2.LocalPeer())
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
checkEvent(t, sub1, event.EvtPeerConnectednessChanged{Peer: s2.LocalPeer(), Connectedness: network.Connected})
|
|
|
|
checkEvent(t, sub2, event.EvtPeerConnectednessChanged{Peer: s1.LocalPeer(), Connectedness: network.Connected})
|
|
|
|
|
|
|
|
for _, c := range s2.ConnsToPeer(s1.LocalPeer()) {
|
|
|
|
require.NoError(t, c.Close())
|
|
|
|
}
|
|
|
|
checkEvent(t, sub1, event.EvtPeerConnectednessChanged{Peer: s2.LocalPeer(), Connectedness: network.NotConnected})
|
|
|
|
checkEvent(t, sub2, event.EvtPeerConnectednessChanged{Peer: s1.LocalPeer(), Connectedness: network.NotConnected})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNoDeadlockWhenConsumingConnectednessEvents(t *testing.T) {
|
2024-06-07 06:25:43 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
|
|
defer cancel()
|
|
|
|
|
2023-08-21 03:50:38 +00:00
|
|
|
dialerEventBus := eventbus.NewBus()
|
|
|
|
dialer := swarmt.GenSwarm(t, swarmt.OptDialOnly, swarmt.EventBus(dialerEventBus))
|
|
|
|
defer dialer.Close()
|
|
|
|
|
|
|
|
listener := swarmt.GenSwarm(t, swarmt.OptDialOnly)
|
|
|
|
addrsToListen := []ma.Multiaddr{
|
|
|
|
ma.StringCast("/ip4/127.0.0.1/udp/0/quic-v1"),
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := listener.Listen(addrsToListen...); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
listenedAddrs := listener.ListenAddresses()
|
|
|
|
|
|
|
|
dialer.Peerstore().AddAddrs(listener.LocalPeer(), listenedAddrs, time.Hour)
|
|
|
|
|
|
|
|
sub, err := dialerEventBus.Subscribe(new(event.EvtPeerConnectednessChanged))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// A slow consumer
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
case <-sub.Out():
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Do something with the swarm that needs the conns lock
|
|
|
|
_ = dialer.ConnsToPeer(listener.LocalPeer())
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
// Connect and disconnect to trigger a bunch of events
|
|
|
|
_, err := dialer.DialPeer(context.Background(), listener.LocalPeer())
|
|
|
|
require.NoError(t, err)
|
|
|
|
dialer.ClosePeer(listener.LocalPeer())
|
|
|
|
}
|
|
|
|
|
|
|
|
// The test should finish without deadlocking
|
|
|
|
}
|
2024-06-07 06:25:43 +00:00
|
|
|
|
|
|
|
func TestConnectednessEvents(t *testing.T) {
|
|
|
|
s1, sub1 := newSwarmWithSubscription(t)
|
|
|
|
const N = 100
|
|
|
|
peers := make([]*Swarm, N)
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
peers[i] = swarmt.GenSwarm(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// First check all connected events
|
|
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
defer close(done)
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
e := <-sub1.Out()
|
|
|
|
evt, ok := e.(event.EvtPeerConnectednessChanged)
|
|
|
|
if !ok {
|
|
|
|
t.Error("invalid event received", e)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if evt.Connectedness != network.Connected {
|
|
|
|
t.Errorf("invalid event received: expected: Connected, got: %s", evt)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
s1.Peerstore().AddAddrs(peers[i].LocalPeer(), []ma.Multiaddr{peers[i].ListenAddresses()[0]}, time.Hour)
|
|
|
|
_, err := s1.DialPeer(context.Background(), peers[i].LocalPeer())
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
case <-time.After(10 * time.Second):
|
|
|
|
t.Fatal("expected all connectedness events to be completed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disconnect some peers
|
|
|
|
done = make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
defer close(done)
|
|
|
|
for i := 0; i < N/2; i++ {
|
|
|
|
e := <-sub1.Out()
|
|
|
|
evt, ok := e.(event.EvtPeerConnectednessChanged)
|
|
|
|
if !ok {
|
|
|
|
t.Error("invalid event received", e)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if evt.Connectedness != network.NotConnected {
|
|
|
|
t.Errorf("invalid event received: expected: NotConnected, got: %s", evt)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
for i := 0; i < N/2; i++ {
|
|
|
|
err := s1.ClosePeer(peers[i].LocalPeer())
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
case <-time.After(10 * time.Second):
|
|
|
|
t.Fatal("expected all disconnected events to be completed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for disconnected events on swarm close
|
|
|
|
done = make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
defer close(done)
|
|
|
|
for i := N / 2; i < N; i++ {
|
|
|
|
e := <-sub1.Out()
|
|
|
|
evt, ok := e.(event.EvtPeerConnectednessChanged)
|
|
|
|
if !ok {
|
|
|
|
t.Error("invalid event received", e)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if evt.Connectedness != network.NotConnected {
|
|
|
|
t.Errorf("invalid event received: expected: NotConnected, got: %s", evt)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
s1.Close()
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
case <-time.After(10 * time.Second):
|
|
|
|
t.Fatal("expected all disconnected events after swarm close to be completed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestConnectednessEventDeadlock(t *testing.T) {
|
|
|
|
s1, sub1 := newSwarmWithSubscription(t)
|
|
|
|
const N = 100
|
|
|
|
peers := make([]*Swarm, N)
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
peers[i] = swarmt.GenSwarm(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// First check all connected events
|
|
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
defer close(done)
|
|
|
|
count := 0
|
|
|
|
for count < N {
|
|
|
|
e := <-sub1.Out()
|
|
|
|
// sleep to simulate a slow consumer
|
|
|
|
evt, ok := e.(event.EvtPeerConnectednessChanged)
|
|
|
|
if !ok {
|
|
|
|
t.Error("invalid event received", e)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if evt.Connectedness != network.Connected {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
count++
|
|
|
|
s1.ClosePeer(evt.Peer)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
s1.Peerstore().AddAddrs(peers[i].LocalPeer(), []ma.Multiaddr{peers[i].ListenAddresses()[0]}, time.Hour)
|
|
|
|
go func(i int) {
|
|
|
|
_, err := s1.DialPeer(context.Background(), peers[i].LocalPeer())
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}(i)
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
case <-time.After(100 * time.Second):
|
|
|
|
t.Fatal("expected all connectedness events to be completed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestConnectednessEventDeadlockWithDial(t *testing.T) {
|
|
|
|
s1, sub1 := newSwarmWithSubscription(t)
|
|
|
|
const N = 200
|
|
|
|
peers := make([]*Swarm, N)
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
peers[i] = swarmt.GenSwarm(t)
|
|
|
|
}
|
|
|
|
peers2 := make([]*Swarm, N)
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
peers2[i] = swarmt.GenSwarm(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// First check all connected events
|
|
|
|
done := make(chan struct{})
|
|
|
|
var subWG sync.WaitGroup
|
|
|
|
subWG.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer subWG.Done()
|
|
|
|
count := 0
|
|
|
|
for {
|
|
|
|
var e interface{}
|
|
|
|
select {
|
|
|
|
case e = <-sub1.Out():
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// sleep to simulate a slow consumer
|
|
|
|
evt, ok := e.(event.EvtPeerConnectednessChanged)
|
|
|
|
if !ok {
|
|
|
|
t.Error("invalid event received", e)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if evt.Connectedness != network.Connected {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if count < N {
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
|
|
|
|
s1.Peerstore().AddAddrs(peers2[count].LocalPeer(), []ma.Multiaddr{peers2[count].ListenAddresses()[0]}, time.Hour)
|
|
|
|
s1.DialPeer(ctx, peers2[count].LocalPeer())
|
|
|
|
count++
|
|
|
|
cancel()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(N)
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
s1.Peerstore().AddAddrs(peers[i].LocalPeer(), []ma.Multiaddr{peers[i].ListenAddresses()[0]}, time.Hour)
|
|
|
|
go func(i int) {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
|
|
s1.DialPeer(ctx, peers[i].LocalPeer())
|
|
|
|
cancel()
|
|
|
|
wg.Done()
|
|
|
|
}(i)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
s1.Close()
|
|
|
|
|
|
|
|
close(done)
|
|
|
|
subWG.Wait()
|
|
|
|
}
|