package blossomsub import ( "context" "testing" "time" "github.com/benbjohnson/clock" "github.com/libp2p/go-libp2p/core/host" swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing" "github.com/libp2p/go-libp2p/core/peer" bhost "github.com/libp2p/go-libp2p/p2p/host/blank" "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) } netw := swarmt.GenSwarm(t) defer netw.Close() h := bhost.NewBlankHost(netw, bhost.WithConnectionManager(connmgrs[i])) 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 := getNetHosts(t, ctx, nSquatter) for _, h := range sybilHosts { squatter := &sybilSquatter{h: h} h.SetStreamHandler(BlossomSubID_v12, 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{0xff, 0x00, 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 { for i := 0; i < nMessages; i++ { ps.Publish(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:test" 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 := append(honestHosts, sybilHosts...) connectAll(t, allHosts) // verify that we have a bunch of connections for _, h := range honestHosts { if len(h.Network().Conns()) != nHonest+nSquatter-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) } } }