ceremonyclient/go-libp2p-blossomsub/blossomsub_connmgr_test.go
2024-10-12 11:48:25 -07:00

183 lines
5.0 KiB
Go

package blossomsub
import (
"context"
"testing"
"time"
"github.com/benbjohnson/clock"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/p2p/net/connmgr"
)
func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
t.Skip("Test disabled with go-libp2p v0.22.0") // TODO: reenable test when updating to v0.23.0
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
oldBlossomSubD := BlossomSubD
oldBlossomSubDlo := BlossomSubDlo
oldBlossomSubDHi := BlossomSubDhi
oldBlossomSubConnTagDecayInterval := BlossomSubConnTagDecayInterval
oldBlossomSubConnTagMessageDeliveryCap := BlossomSubConnTagMessageDeliveryCap
// set the BlossomSub D parameters low, so that we have some peers outside the mesh
BlossomSubDlo = 3
BlossomSubD = 3
BlossomSubDhi = 3
// also set the tag decay interval so we don't have to wait forever for tests
BlossomSubConnTagDecayInterval = time.Second
// set the cap for deliveries above BlossomSubConnTagValueMeshPeer, so the sybils
// will be forced out even if they end up in someone's mesh
BlossomSubConnTagMessageDeliveryCap = 50
// reset globals after test
defer func() {
BlossomSubD = oldBlossomSubD
BlossomSubDlo = oldBlossomSubDlo
BlossomSubDhi = oldBlossomSubDHi
BlossomSubConnTagDecayInterval = oldBlossomSubConnTagDecayInterval
BlossomSubConnTagMessageDeliveryCap = oldBlossomSubConnTagMessageDeliveryCap
}()
decayClock := clock.NewMock()
decayCfg := connmgr.DecayerCfg{
Resolution: time.Second,
Clock: decayClock,
}
nHonest := 5
nSquatter := 10
connLimit := 10
connmgrs := make([]*connmgr.BasicConnMgr, nHonest)
honestHosts := make([]host.Host, nHonest)
honestPeers := make(map[peer.ID]struct{})
for i := 0; i < nHonest; i++ {
var err error
connmgrs[i], err = connmgr.NewConnManager(nHonest, connLimit,
connmgr.WithGracePeriod(0),
connmgr.WithSilencePeriod(time.Millisecond),
connmgr.DecayerConfig(&decayCfg),
)
if err != nil {
t.Fatal(err)
}
h, err := libp2p.New(
libp2p.ResourceManager(&network.NullResourceManager{}),
libp2p.ConnectionManager(connmgrs[i]),
)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { h.Close() })
honestHosts[i] = h
honestPeers[h.ID()] = struct{}{}
}
// use flood publishing, so non-mesh peers will still be delivering messages
// to everyone
psubs := getBlossomSubs(ctx, honestHosts,
WithFloodPublish(true))
// sybil squatters to be connected later
sybilHosts := getDefaultHosts(t, nSquatter)
for _, h := range sybilHosts {
squatter := &sybilSquatter{h: h}
h.SetStreamHandler(BlossomSubID_v2, squatter.handleStream)
}
// connect the honest hosts
connectAll(t, honestHosts)
for _, h := range honestHosts {
if len(h.Network().Conns()) < nHonest-1 {
t.Errorf("expected to have conns to all honest peers, have %d", len(h.Network().Conns()))
}
}
// subscribe everyone to the bitmask
bitmask := []byte{0x00, 0x80, 0x00, 0x00}
for _, ps := range psubs {
_, err := ps.Subscribe(bitmask)
if err != nil {
t.Fatal(err)
}
}
// sleep to allow meshes to form
time.Sleep(2 * time.Second)
// have all the hosts publish enough messages to ensure that they get some delivery credit
nMessages := BlossomSubConnTagMessageDeliveryCap * 2
for _, ps := range psubs {
b, err := ps.Join(bitmask)
if err != nil {
t.Fatal(err)
}
for i := 0; i < nMessages; i++ {
b[0].Publish(ctx, b[0].bitmask, []byte("hello"))
}
}
// advance the fake time for the tag decay
decayClock.Add(time.Second)
// verify that they've given each other delivery connection tags
tag := "pubsub-deliveries:" + string([]byte{0x00, 0x80, 0x00, 0x00})
for _, h := range honestHosts {
for _, h2 := range honestHosts {
if h.ID() == h2.ID() {
continue
}
val := getTagValue(h.ConnManager(), h2.ID(), tag)
if val == 0 {
t.Errorf("Expected non-zero delivery tag value for peer %s", h2.ID())
}
}
}
// now connect the sybils to put pressure on the real hosts' connection managers
allHosts := honestHosts
connectAll(t, allHosts)
// verify that we have a bunch of connections
for _, h := range honestHosts {
if len(h.Network().Conns()) < nHonest-1 {
t.Errorf("expected to have conns to all peers, have %d", len(h.Network().Conns()))
}
}
// force the connection managers to trim, so we don't need to muck about with timing as much
for _, cm := range connmgrs {
cm.TrimOpenConns(ctx)
}
// we should still have conns to all the honest peers, but not the sybils
for _, h := range honestHosts {
nHonestConns := 0
nDishonestConns := 0
for _, conn := range h.Network().Conns() {
if _, ok := honestPeers[conn.RemotePeer()]; !ok {
nDishonestConns++
} else {
nHonestConns++
}
}
if nDishonestConns > connLimit-nHonest {
t.Errorf("expected most dishonest conns to be pruned, have %d", nDishonestConns)
}
if nHonestConns < nHonest-1 {
t.Errorf("expected all honest conns to be preserved, have %d", nHonestConns)
}
}
}