From 5554bf380a7b1c1dd4d7a863de3d17ddebe56c0a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 9 Nov 2022 16:07:02 -0800 Subject: [PATCH 001/138] upgrades forked version of gossipsub --- insecure/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/insecure/go.mod b/insecure/go.mod index 7d18fae735d..c30821f0e15 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -13,7 +13,7 @@ require ( github.com/onflow/flow-go/crypto v0.24.4 github.com/rs/zerolog v1.28.0 github.com/stretchr/testify v1.8.0 - github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11 + github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110000148-5e7a6828a453 google.golang.org/grpc v1.50.1 google.golang.org/protobuf v1.28.1 ) From 2878bf5ab799321e70a00298cbe746dc3d0b5271 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 9 Nov 2022 16:37:40 -0800 Subject: [PATCH 002/138] adjusts libp2p version --- insecure/go.mod | 2 +- insecure/go.sum | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/insecure/go.mod b/insecure/go.mod index c30821f0e15..d6590c6a2eb 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -13,7 +13,7 @@ require ( github.com/onflow/flow-go/crypto v0.24.4 github.com/rs/zerolog v1.28.0 github.com/stretchr/testify v1.8.0 - github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110000148-5e7a6828a453 + github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110003401-b897e22c3c7c google.golang.org/grpc v1.50.1 google.golang.org/protobuf v1.28.1 ) diff --git a/insecure/go.sum b/insecure/go.sum index 9be15ddc0a4..3da8a62db5e 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -872,6 +872,8 @@ github.com/libp2p/go-libp2p-peerstore v0.8.0 h1:bzTG693TA1Ju/zKmUCQzDLSqiJnyRFVw github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= github.com/libp2p/go-libp2p-pubsub v0.8.1-0.20220908052023-8866ca88a105 h1:EAAgUl0EnSk4Z/ct1xsHTYSy9JYDdcTazrC6phSdlIY= github.com/libp2p/go-libp2p-pubsub v0.8.1-0.20220908052023-8866ca88a105/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= +github.com/libp2p/go-libp2p-pubsub v0.8.1 h1:hSw09NauFUaA0FLgQPBJp6QOy0a2n+HSkb8IeOx8OnY= +github.com/libp2p/go-libp2p-pubsub v0.8.1/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= @@ -1491,6 +1493,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11 h1:WYWfp6ydU0v6ev+amaIRIfxJEoEnf9HP0o523qlg/1o= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11/go.mod h1:GwZ5VBU63KlM8wZsnVvSGDIMeGMOO9Cb3uLXDP8WFYM= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110003401-b897e22c3c7c h1:Za50fWzRA8RfwWvy434k+HpTFUuL5Tlx3SvhoEcv1CA= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110003401-b897e22c3c7c/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 6221e0695f2c20161651da4083c77ae52c2d932a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 9 Nov 2022 17:03:43 -0800 Subject: [PATCH 003/138] nit --- insecure/corruptlibp2p/fixtures.go | 1 + insecure/corruptlibp2p/spammerGossipSub.go | 1 + insecure/corruptnet/libp2p_node_factory.go | 15 ++++++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 insecure/corruptlibp2p/fixtures.go create mode 100644 insecure/corruptlibp2p/spammerGossipSub.go diff --git a/insecure/corruptlibp2p/fixtures.go b/insecure/corruptlibp2p/fixtures.go new file mode 100644 index 00000000000..3804f232e90 --- /dev/null +++ b/insecure/corruptlibp2p/fixtures.go @@ -0,0 +1 @@ +package corruptlibp2p diff --git a/insecure/corruptlibp2p/spammerGossipSub.go b/insecure/corruptlibp2p/spammerGossipSub.go new file mode 100644 index 00000000000..3804f232e90 --- /dev/null +++ b/insecure/corruptlibp2p/spammerGossipSub.go @@ -0,0 +1 @@ +package corruptlibp2p diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptnet/libp2p_node_factory.go index aa0e0144945..7b2e3d55ad8 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptnet/libp2p_node_factory.go @@ -36,7 +36,20 @@ func NewCorruptLibP2PNodeFactory( panic("illegal chain id for using corruptible conduit factory") } - builder := p2pbuilder.DefaultNodeBuilder(log, address, flowKey, sporkId, idProvider, metrics, resolver, role, onInterceptPeerDialFilters, onInterceptSecuredFilters, peerScoringEnabled, connectionPruning, updateInterval) + builder := p2pbuilder.DefaultNodeBuilder( + log, + address, + flowKey, + sporkId, + idProvider, + metrics, + resolver, + role, + onInterceptPeerDialFilters, + onInterceptSecuredFilters, + peerScoringEnabled, + connectionPruning, + updateInterval) builder.SetCreateNode(NewCorruptLibP2PNode) return builder.Build() } From 59fa751f7dfbdbbfc303ab4a6ab2e941f414f509 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 9 Nov 2022 17:04:04 -0800 Subject: [PATCH 004/138] adds ihave fixture --- insecure/corruptlibp2p/fixtures.go | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/insecure/corruptlibp2p/fixtures.go b/insecure/corruptlibp2p/fixtures.go index 3804f232e90..feccab00fd1 100644 --- a/insecure/corruptlibp2p/fixtures.go +++ b/insecure/corruptlibp2p/fixtures.go @@ -1 +1,49 @@ package corruptlibp2p + +import ( + pubsubpb "github.com/libp2p/go-libp2p-pubsub/pb" + + "github.com/onflow/flow-go/utils/unittest" +) + +type GossipSubCtrlOption func(*pubsubpb.ControlMessage) + +func GossipSubCtrlFixture(opts ...GossipSubCtrlOption) *pubsubpb.ControlMessage { + msg := &pubsubpb.ControlMessage{} + for _, opt := range opts { + opt(msg) + } + return msg +} + +func WithIHave(msgCount int, msgSize int) GossipSubCtrlOption { + return func(msg *pubsubpb.ControlMessage) { + iHaves := make([]*pubsubpb.ControlIHave, msgCount) + for i := 0; i < msgCount; i++ { + topicId := topicIdFixture() + iHaves[i] = &pubsubpb.ControlIHave{ + TopicID: &topicId, + MessageIDs: messageIdsFixture(msgSize), + } + } + msg.Ihave = iHaves + } +} + +func messageIdFixture() string { + // TODO: messageID length should be a parameter. + return unittest.GenerateRandomStringWithLen(10) +} + +func topicIdFixture() string { + // TODO: topicID length should be a parameter. + return unittest.GenerateRandomStringWithLen(10) +} + +func messageIdsFixture(count int) []string { + msgIds := make([]string, count) + for i := 0; i < count; i++ { + msgIds[i] = messageIdFixture() + } + return msgIds +} From fea68e12edbe26d073c4d1e9c7a0deb3852cdbe1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 10 Nov 2022 12:42:25 -0800 Subject: [PATCH 005/138] updates go module --- insecure/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/insecure/go.mod b/insecure/go.mod index d6590c6a2eb..15341746bf4 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -8,12 +8,12 @@ require ( github.com/ipfs/go-datastore v0.6.0 github.com/libp2p/go-libp2p v0.23.3 github.com/libp2p/go-libp2p-pubsub v0.8.1-0.20220908052023-8866ca88a105 + github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/onflow/flow-go v0.0.0-00010101000000-000000000000 github.com/onflow/flow-go/crypto v0.24.4 github.com/rs/zerolog v1.28.0 github.com/stretchr/testify v1.8.0 - github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110003401-b897e22c3c7c google.golang.org/grpc v1.50.1 google.golang.org/protobuf v1.28.1 ) From d0480e6e152e2a9d99359e4208d212ad1c99f012 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 10 Nov 2022 12:43:39 -0800 Subject: [PATCH 006/138] adds spammer module --- insecure/corruptlibp2p/spammerGossipSub.go | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/insecure/corruptlibp2p/spammerGossipSub.go b/insecure/corruptlibp2p/spammerGossipSub.go index 3804f232e90..cdc70715f01 100644 --- a/insecure/corruptlibp2p/spammerGossipSub.go +++ b/insecure/corruptlibp2p/spammerGossipSub.go @@ -1 +1,28 @@ package corruptlibp2p + +import ( + "github.com/libp2p/go-libp2p/core/peer" + pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" +) + +type ControlMessage int + +type SpammerGossipSub struct { + router *pubsub.GossipSubRouter +} + +func NewSpammerGossipSub(router *pubsub.GossipSubRouter) *SpammerGossipSub { + return &SpammerGossipSub{ + router: router, + } +} + +// SpamIHave spams the victim with junk iHave messages. +// msgCount is the number of iHave messages to send. +// msgSize is the number of messageIDs to include in each iHave message. +func (s *SpammerGossipSub) SpamIHave(victim peer.ID, msgCount int, msgSize int) { + for i := 0; i < msgCount; i++ { + ctlIHave := GossipSubCtrlFixture(WithIHave(msgCount, msgSize)) + s.router.SendControl(victim, ctlIHave) + } +} From cb12ce3453a5ac7432f514cbd7ad978765e6cf9e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 10 Nov 2022 14:02:57 -0800 Subject: [PATCH 007/138] migrates p2p fixtures --- .../corruptlibp2p/spammerGossipSub-test.go | 91 ++++++ network/internal/p2pfixtures/fixtures.go | 234 --------------- .../p2p/connection/connection_gater_test.go | 41 +-- .../peerManager_integration_test.go | 8 +- network/p2p/dht/dht_test.go | 18 +- network/p2p/p2pnode/libp2pNode_test.go | 31 +- network/p2p/p2pnode/libp2pStream_test.go | 85 +++--- network/p2p/scoring/app_score_test.go | 43 +-- .../scoring/subscription_validator_test.go | 33 +-- .../subscription/subscription_filter_test.go | 5 +- network/p2p/test/fixtures.go | 271 ++++++++++++++++++ network/p2p/test/sporking_test.go | 63 ++-- network/p2p/test/topic_validator_test.go | 73 ++--- 13 files changed, 566 insertions(+), 430 deletions(-) create mode 100644 insecure/corruptlibp2p/spammerGossipSub-test.go create mode 100644 network/p2p/test/fixtures.go diff --git a/insecure/corruptlibp2p/spammerGossipSub-test.go b/insecure/corruptlibp2p/spammerGossipSub-test.go new file mode 100644 index 00000000000..0451453222c --- /dev/null +++ b/insecure/corruptlibp2p/spammerGossipSub-test.go @@ -0,0 +1,91 @@ +package corruptlibp2p + +import ( + "fmt" + "time" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/utils/unittest" +) + +func TestSpammerGossipSub(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + sporkId := unittest.IdentifierFixture() + defer cancel() + + count := 5 + nodes := make([]p2p.LibP2PNode, 0, 5) + ids := flow.IdentityList{} + inbounds := make([]chan string, 0, 5) + + disallowedList := map[*flow.Identity]struct{}{} + + for i := 0; i < count; i++ { + handler, inbound := p2pfixtures.StreamHandlerFixture(t) + node, id := p2pfixtures.NodeFixture( + t, + sporkId, + t.Name(), + p2pfixtures.WithRole(flow.RoleConsensus), + p2pfixtures.WithDefaultStreamHandler(handler), + // enable peer manager, with a 1-second refresh rate, and connection pruning enabled. + p2pfixtures.WithPeerManagerEnabled(true, 1*time.Second, func() peer.IDSlice { + list := make(peer.IDSlice, 0) + for _, id := range ids { + if _, ok := disallowedList[id]; ok { + continue + } + + pid, err := unittest.PeerIDFromFlowID(id) + require.NoError(t, err) + + list = append(list, pid) + } + return list + }), + p2pfixtures.WithConnectionGater(testutils.NewConnectionGater(func(pid peer.ID) error { + for id := range disallowedList { + bid, err := unittest.PeerIDFromFlowID(id) + require.NoError(t, err) + if bid == pid { + return fmt.Errorf("disallow-listed") + } + } + + return nil + }))) + + nodes = append(nodes, node) + ids = append(ids, &id) + inbounds = append(inbounds, inbound) + } + + p2pfixtures.StartNodes(t, signalerCtx, nodes, 1*time.Second) + defer p2pfixtures.StopNodes(t, nodes, cancel, 1*time.Second) + + p2pfixtures.LetNodesDiscoverEachOther(t, ctx, nodes, ids) + + // ensures that all nodes are connected to each other, and they can exchange messages over the pubsub and unicast. + ensureCommunicationOverAllProtocols(t, ctx, sporkId, nodes, inbounds) + + // now we add one of the nodes (the last node) to the disallow-list. + disallowedList[ids[len(ids)-1]] = struct{}{} + // let peer manager prune the connections to the disallow-listed node. + time.Sleep(1 * time.Second) + // ensures no connection, unicast, or pubsub going to or coming from the disallow-listed node. + ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:count-1], nodes[count-1:]) + + // now we add another node (the second last node) to the disallowed list. + disallowedList[ids[len(ids)-2]] = struct{}{} + // let peer manager prune the connections to the disallow-listed node. + time.Sleep(1 * time.Second) + // ensures no connection, unicast, or pubsub going to and coming from the disallow-listed nodes. + ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:count-2], nodes[count-2:]) + // ensures that all nodes are other non-disallow-listed nodes can exchange messages over the pubsub and unicast. + ensureCommunicationOverAllProtocols(t, ctx, sporkId, nodes[:count-2], inbounds[:count-2]) +} diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index c1ed02d7c73..cfed964c90b 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -10,14 +10,11 @@ import ( "time" addrutil "github.com/libp2p/go-addr-util" - dht "github.com/libp2p/go-libp2p-kad-dht" pubsub "github.com/libp2p/go-libp2p-pubsub" - "github.com/libp2p/go-libp2p/core/connmgr" "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/core/peerstore" - "github.com/libp2p/go-libp2p/core/protocol" "github.com/libp2p/go-libp2p/core/routing" "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" @@ -26,8 +23,6 @@ import ( "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module" - "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" @@ -35,16 +30,13 @@ import ( "github.com/onflow/flow-go/network/internal/testutils" "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/network/p2p/connection" p2pdht "github.com/onflow/flow-go/network/p2p/dht" "github.com/onflow/flow-go/network/p2p/keyutils" "github.com/onflow/flow-go/network/p2p/p2pbuilder" validator "github.com/onflow/flow-go/network/validator/pubsub" - "github.com/onflow/flow-go/network/p2p/scoring" "github.com/onflow/flow-go/network/p2p/unicast" "github.com/onflow/flow-go/network/p2p/utils" - "github.com/onflow/flow-go/utils/logging" "github.com/onflow/flow-go/utils/unittest" ) @@ -61,214 +53,6 @@ func NetworkingKeyFixtures(t *testing.T) crypto.PrivateKey { return key } -// NodeFixture is a test fixture that creates a single libp2p node with the given key, spork id, and options. -// It returns the node and its identity. -func NodeFixture( - t *testing.T, - sporkID flow.Identifier, - dhtPrefix string, - opts ...NodeFixtureParameterOption, -) (p2p.LibP2PNode, flow.Identity) { - // default parameters - parameters := &NodeFixtureParameters{ - HandlerFunc: func(network.Stream) {}, - Unicasts: nil, - Key: NetworkingKeyFixtures(t), - Address: defaultAddress, - Logger: unittest.Logger().Level(zerolog.ErrorLevel), - Role: flow.RoleCollection, - } - - for _, opt := range opts { - opt(parameters) - } - - identity := unittest.IdentityFixture( - unittest.WithNetworkingKey(parameters.Key.PublicKey()), - unittest.WithAddress(parameters.Address), - unittest.WithRole(parameters.Role)) - - logger := parameters.Logger.With().Hex("node_id", logging.ID(identity.NodeID)).Logger() - - noopMetrics := metrics.NewNoopCollector() - connManager := connection.NewConnManager(logger, noopMetrics) - resourceManager := testutils.NewResourceManager(t) - - builder := p2pbuilder.NewNodeBuilder(logger, parameters.Address, parameters.Key, sporkID). - SetConnectionManager(connManager). - SetRoutingSystem(func(c context.Context, h host.Host) (routing.Routing, error) { - return p2pdht.NewDHT(c, h, - protocol.ID(unicast.FlowDHTProtocolIDPrefix+sporkID.String()+"/"+dhtPrefix), - logger, - noopMetrics, - parameters.DhtOptions..., - ) - }). - SetResourceManager(resourceManager). - SetCreateNode(p2pbuilder.DefaultCreateNodeFunc) - - if parameters.ConnGater != nil { - builder.SetConnectionGater(parameters.ConnGater) - } - - if parameters.PeerScoringEnabled { - scoreOptionParams := make([]scoring.PeerScoreParamsOption, 0) - if parameters.AppSpecificScore != nil { - scoreOptionParams = append(scoreOptionParams, scoring.WithAppSpecificScoreFunction(parameters.AppSpecificScore)) - } - builder.EnableGossipSubPeerScoring(parameters.IdProvider, scoreOptionParams...) - } - - if parameters.UpdateInterval != 0 { - require.NotNil(t, parameters.PeerProvider) - builder.SetPeerManagerOptions(parameters.ConnectionPruning, parameters.UpdateInterval) - } - - n, err := builder.Build() - require.NoError(t, err) - - err = n.WithDefaultUnicastProtocol(parameters.HandlerFunc, parameters.Unicasts) - require.NoError(t, err) - - // get the actual IP and port that have been assigned by the subsystem - ip, port, err := n.GetIPPort() - require.NoError(t, err) - identity.Address = ip + ":" + port - - if parameters.PeerProvider != nil { - n.WithPeersProvider(parameters.PeerProvider) - } - return n, *identity -} - -type NodeFixtureParameters struct { - HandlerFunc network.StreamHandler - Unicasts []unicast.ProtocolName - Key crypto.PrivateKey - Address string - DhtOptions []dht.Option - Role flow.Role - Logger zerolog.Logger - PeerScoringEnabled bool - IdProvider module.IdentityProvider - AppSpecificScore func(peer.ID) float64 // overrides GossipSub scoring for sake of testing. - ConnectionPruning bool // peer manager parameter - UpdateInterval time.Duration // peer manager parameter - PeerProvider p2p.PeersProvider // peer manager parameter - ConnGater connmgr.ConnectionGater -} - -type NodeFixtureParameterOption func(*NodeFixtureParameters) - -func WithPeerScoringEnabled(idProvider module.IdentityProvider) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.PeerScoringEnabled = true - p.IdProvider = idProvider - } -} - -func WithDefaultStreamHandler(handler network.StreamHandler) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.HandlerFunc = handler - } -} - -func WithPeerManagerEnabled(connectionPruning bool, updateInterval time.Duration, peerProvider p2p.PeersProvider) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.ConnectionPruning = connectionPruning - p.UpdateInterval = updateInterval - p.PeerProvider = peerProvider - } -} - -func WithPreferredUnicasts(unicasts []unicast.ProtocolName) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.Unicasts = unicasts - } -} - -func WithNetworkingPrivateKey(key crypto.PrivateKey) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.Key = key - } -} - -func WithNetworkingAddress(address string) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.Address = address - } -} - -func WithDHTOptions(opts ...dht.Option) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.DhtOptions = opts - } -} - -func WithConnectionGater(connGater connmgr.ConnectionGater) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.ConnGater = connGater - } -} - -func WithRole(role flow.Role) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.Role = role - } -} - -func WithAppSpecificScore(score func(peer.ID) float64) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.AppSpecificScore = score - } -} - -func WithLogger(logger zerolog.Logger) NodeFixtureParameterOption { - return func(p *NodeFixtureParameters) { - p.Logger = logger - } -} - -// StartNodes start all nodes in the input slice using the provided context, timing out if nodes are -// not all Ready() before duration expires -func StartNodes(t *testing.T, ctx irrecoverable.SignalerContext, nodes []p2p.LibP2PNode, timeout time.Duration) { - rdas := make([]module.ReadyDoneAware, 0, len(nodes)) - for _, node := range nodes { - node.Start(ctx) - rdas = append(rdas, node) - - if peerManager := node.PeerManagerComponent(); peerManager != (*connection.PeerManager)(nil) { - // we need to start the peer manager post the node startup (if such component exists). - peerManager.Start(ctx) - rdas = append(rdas, peerManager) - } - } - unittest.RequireComponentsReadyBefore(t, timeout, rdas...) -} - -// StartNode start a single node using the provided context, timing out if nodes are not all Ready() -// before duration expires -func StartNode(t *testing.T, ctx irrecoverable.SignalerContext, node p2p.LibP2PNode, timeout time.Duration) { - node.Start(ctx) - unittest.RequireComponentsReadyBefore(t, timeout, node) -} - -// StopNodes stops all nodes in the input slice using the provided cancel func, timing out if nodes are -// not all Done() before duration expires -func StopNodes(t *testing.T, nodes []p2p.LibP2PNode, cancel context.CancelFunc, timeout time.Duration) { - cancel() - for _, node := range nodes { - unittest.RequireComponentsDoneBefore(t, timeout, node) - } -} - -// StopNode stops a single node using the provided cancel func, timing out if nodes are not all Done() -// before duration expires -func StopNode(t *testing.T, node p2p.LibP2PNode, cancel context.CancelFunc, timeout time.Duration) { - cancel() - unittest.RequireComponentsDoneBefore(t, timeout, node) -} - // SilentNodeFixture returns a TCP listener and a node which never replies func SilentNodeFixture(t *testing.T) (net.Listener, flow.Identity) { key := NetworkingKeyFixtures(t) @@ -308,24 +92,6 @@ func acceptAndHang(t *testing.T, l net.Listener) { } } -// NodesFixture is a test fixture that creates a number of libp2p nodes with the given callback function for stream handling. -// It returns the nodes and their identities. -func NodesFixture(t *testing.T, sporkID flow.Identifier, dhtPrefix string, count int, opts ...NodeFixtureParameterOption) ([]p2p.LibP2PNode, - flow.IdentityList) { - var nodes []p2p.LibP2PNode - - // creating nodes - var identities flow.IdentityList - for i := 0; i < count; i++ { - // create a node on localhost with a random port assigned by the OS - node, identity := NodeFixture(t, sporkID, dhtPrefix, opts...) - nodes = append(nodes, node) - identities = append(identities, &identity) - } - - return nodes, identities -} - type nodeOpt func(p2pbuilder.NodeBuilder) func WithSubscriptionFilter(filter pubsub.SubscriptionFilter) nodeOpt { diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index 751c49cc048..3510bbce7c4 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -21,6 +21,7 @@ import ( "github.com/onflow/flow-go/network/internal/testutils" "github.com/onflow/flow-go/network/p2p" mockp2p "github.com/onflow/flow-go/network/p2p/mock" + p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/utils/unittest" ) @@ -33,11 +34,11 @@ func TestConnectionGating(t *testing.T) { // create 2 nodes node1Peers := make(map[peer.ID]struct{}) - node1, node1Id := p2pfixtures.NodeFixture( + node1, node1Id := p2ptest.NodeFixture( t, sporkID, t.Name(), - p2pfixtures.WithConnectionGater(testutils.NewConnectionGater(func(p peer.ID) error { + p2ptest.WithConnectionGater(testutils.NewConnectionGater(func(p peer.ID) error { if _, ok := node1Peers[p]; !ok { return fmt.Errorf("id not found: %s", p.String()) } @@ -45,11 +46,11 @@ func TestConnectionGating(t *testing.T) { }))) node2Peers := make(map[peer.ID]struct{}) - node2, node2Id := p2pfixtures.NodeFixture( + node2, node2Id := p2ptest.NodeFixture( t, sporkID, t.Name(), - p2pfixtures.WithConnectionGater(testutils.NewConnectionGater(func(p peer.ID) error { + p2ptest.WithConnectionGater(testutils.NewConnectionGater(func(p peer.ID) error { if _, ok := node2Peers[p]; !ok { return fmt.Errorf("id not found: %s", p.String()) } @@ -58,8 +59,8 @@ func TestConnectionGating(t *testing.T) { nodes := []p2p.LibP2PNode{node1, node2} ids := flow.IdentityList{&node1Id, &node2Id} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) p2pfixtures.AddNodesToEachOthersPeerStore(t, nodes, ids) @@ -117,14 +118,14 @@ func TestConnectionGater_InterceptUpgrade(t *testing.T) { connectionGater := mockp2p.NewConnectionGater(t) for i := 0; i < count; i++ { handler, inbound := p2pfixtures.StreamHandlerFixture(t) - node, _ := p2pfixtures.NodeFixture( + node, _ := p2ptest.NodeFixture( t, sporkId, t.Name(), - p2pfixtures.WithRole(flow.RoleConsensus), - p2pfixtures.WithDefaultStreamHandler(handler), + p2ptest.WithRole(flow.RoleConsensus), + p2ptest.WithDefaultStreamHandler(handler), // enable peer manager, with a 1-second refresh rate, and connection pruning enabled. - p2pfixtures.WithPeerManagerEnabled(true, 1*time.Second, func() peer.IDSlice { + p2ptest.WithPeerManagerEnabled(true, 1*time.Second, func() peer.IDSlice { list := make(peer.IDSlice, 0) for _, pid := range allPeerIds { if _, ok := disallowedPeerIds[pid]; !ok { @@ -133,7 +134,7 @@ func TestConnectionGater_InterceptUpgrade(t *testing.T) { } return list }), - p2pfixtures.WithConnectionGater(connectionGater)) + p2ptest.WithConnectionGater(connectionGater)) nodes = append(nodes, node) allPeerIds = append(allPeerIds, node.Host().ID()) @@ -159,8 +160,8 @@ func TestConnectionGater_InterceptUpgrade(t *testing.T) { disallowedPeerIds[nodes[0].Host().ID()] = struct{}{} // starts the nodes - p2pfixtures.StartNodes(t, signalerCtx, nodes, 1*time.Second) - defer p2pfixtures.StopNodes(t, nodes, cancel, 1*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) + defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:1], nodes[1:]) @@ -201,14 +202,14 @@ func TestConnectionGater_Disallow_Integration(t *testing.T) { for i := 0; i < count; i++ { handler, inbound := p2pfixtures.StreamHandlerFixture(t) - node, id := p2pfixtures.NodeFixture( + node, id := p2ptest.NodeFixture( t, sporkId, t.Name(), - p2pfixtures.WithRole(flow.RoleConsensus), - p2pfixtures.WithDefaultStreamHandler(handler), + p2ptest.WithRole(flow.RoleConsensus), + p2ptest.WithDefaultStreamHandler(handler), // enable peer manager, with a 1-second refresh rate, and connection pruning enabled. - p2pfixtures.WithPeerManagerEnabled(true, 1*time.Second, func() peer.IDSlice { + p2ptest.WithPeerManagerEnabled(true, 1*time.Second, func() peer.IDSlice { list := make(peer.IDSlice, 0) for _, id := range ids { if _, ok := disallowedList[id]; ok { @@ -222,7 +223,7 @@ func TestConnectionGater_Disallow_Integration(t *testing.T) { } return list }), - p2pfixtures.WithConnectionGater(testutils.NewConnectionGater(func(pid peer.ID) error { + p2ptest.WithConnectionGater(testutils.NewConnectionGater(func(pid peer.ID) error { for id := range disallowedList { bid, err := unittest.PeerIDFromFlowID(id) require.NoError(t, err) @@ -239,8 +240,8 @@ func TestConnectionGater_Disallow_Integration(t *testing.T) { inbounds = append(inbounds, inbound) } - p2pfixtures.StartNodes(t, signalerCtx, nodes, 1*time.Second) - defer p2pfixtures.StopNodes(t, nodes, cancel, 1*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) + defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) p2pfixtures.LetNodesDiscoverEachOther(t, ctx, nodes, ids) diff --git a/network/p2p/connection/peerManager_integration_test.go b/network/p2p/connection/peerManager_integration_test.go index ebe6b699204..b711c62ba65 100644 --- a/network/p2p/connection/peerManager_integration_test.go +++ b/network/p2p/connection/peerManager_integration_test.go @@ -14,9 +14,9 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/connection" + p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/network/p2p/translator" "github.com/onflow/flow-go/network/p2p/utils" "github.com/onflow/flow-go/utils/unittest" @@ -33,10 +33,10 @@ func TestPeerManager_Integration(t *testing.T) { signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) // create nodes - nodes, identities := p2pfixtures.NodesFixture(t, unittest.IdentifierFixture(), "test_peer_manager", count) + nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_peer_manager", count) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) thisNode := nodes[0] topologyPeers := identities[1:] diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index 6549f6d7a85..9a05b0d54a2 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -16,9 +16,9 @@ import ( "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p/dht" + p2ptest "github.com/onflow/flow-go/network/p2p/test" flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" "github.com/onflow/flow-go/utils/unittest" ) @@ -36,14 +36,14 @@ func TestFindPeerWithDHT(t *testing.T) { golog.SetAllLoggers(golog.LevelFatal) // change this to Debug if libp2p logs are needed sporkId := unittest.IdentifierFixture() - dhtServerNodes, _ := p2pfixtures.NodesFixture(t, sporkId, "dht_test", 2, p2pfixtures.WithDHTOptions(dht.AsServer())) + dhtServerNodes, _ := p2ptest.NodesFixture(t, sporkId, "dht_test", 2, p2ptest.WithDHTOptions(dht.AsServer())) require.Len(t, dhtServerNodes, 2) - dhtClientNodes, _ := p2pfixtures.NodesFixture(t, sporkId, "dht_test", count-2, p2pfixtures.WithDHTOptions(dht.AsClient())) + dhtClientNodes, _ := p2ptest.NodesFixture(t, sporkId, "dht_test", count-2, p2ptest.WithDHTOptions(dht.AsClient())) nodes := append(dhtServerNodes, dhtClientNodes...) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) getDhtServerAddr := func(i uint) peer.AddrInfo { return peer.AddrInfo{ID: dhtServerNodes[i].Host().ID(), Addrs: dhtServerNodes[i].Host().Addrs()} @@ -120,16 +120,16 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { sporkId := unittest.IdentifierFixture() // create one node running the DHT Server (mimicking the staked AN) - dhtServerNodes, _ := p2pfixtures.NodesFixture(t, sporkId, "dht_test", 1, p2pfixtures.WithDHTOptions(dht.AsServer())) + dhtServerNodes, _ := p2ptest.NodesFixture(t, sporkId, "dht_test", 1, p2ptest.WithDHTOptions(dht.AsServer())) require.Len(t, dhtServerNodes, 1) dhtServerNode := dhtServerNodes[0] // crate other nodes running the DHT Client (mimicking the unstaked ANs) - dhtClientNodes, _ := p2pfixtures.NodesFixture(t, sporkId, "dht_test", count-1, p2pfixtures.WithDHTOptions(dht.AsClient())) + dhtClientNodes, _ := p2ptest.NodesFixture(t, sporkId, "dht_test", count-1, p2ptest.WithDHTOptions(dht.AsClient())) nodes := append(dhtServerNodes, dhtClientNodes...) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) // Step 2: Connect all nodes running a DHT client to the node running the DHT server // This has to be done before subscribing to any topic, otherwise the node gives up on advertising diff --git a/network/p2p/p2pnode/libp2pNode_test.go b/network/p2p/p2pnode/libp2pNode_test.go index 59e5bb399f2..6917739ed6b 100644 --- a/network/p2p/p2pnode/libp2pNode_test.go +++ b/network/p2p/p2pnode/libp2pNode_test.go @@ -17,6 +17,7 @@ import ( "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/internal/p2putils" "github.com/onflow/flow-go/network/internal/testutils" + p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/network/p2p/utils" "github.com/onflow/flow-go/utils/unittest" ) @@ -59,7 +60,7 @@ func TestSingleNodeLifeCycle(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - node, _ := p2pfixtures.NodeFixture( + node, _ := p2ptest.NodeFixture( t, unittest.IdentifierFixture(), "test_single_node_life_cycle", @@ -102,9 +103,9 @@ func TestAddPeers(t *testing.T) { signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) // create nodes - nodes, identities := p2pfixtures.NodesFixture(t, unittest.IdentifierFixture(), "test_add_peers", count) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_add_peers", count) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) // add the remaining nodes to the first node as its set of peers for _, identity := range identities[1:] { @@ -124,12 +125,12 @@ func TestRemovePeers(t *testing.T) { signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) // create nodes - nodes, identities := p2pfixtures.NodesFixture(t, unittest.IdentifierFixture(), "test_remove_peers", count) + nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_remove_peers", count) peerInfos, errs := utils.PeerInfosFromIDs(identities) assert.Len(t, errs, 0) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) // add nodes two and three to the first node as its peers for _, pInfo := range peerInfos[1:] { @@ -153,35 +154,35 @@ func TestConnGater(t *testing.T) { sporkID := unittest.IdentifierFixture() node1Peers := make(map[peer.ID]struct{}) - node1, identity1 := p2pfixtures.NodeFixture( + node1, identity1 := p2ptest.NodeFixture( t, sporkID, t.Name(), - p2pfixtures.WithConnectionGater(testutils.NewConnectionGater(func(pid peer.ID) error { + p2ptest.WithConnectionGater(testutils.NewConnectionGater(func(pid peer.ID) error { if _, ok := node1Peers[pid]; !ok { return fmt.Errorf("peer id not found: %s", pid.String()) } return nil }))) - p2pfixtures.StartNode(t, signalerCtx, node1, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, node1, cancel, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx, node1, 100*time.Millisecond) + defer p2ptest.StopNode(t, node1, cancel, 100*time.Millisecond) node1Info, err := utils.PeerAddressInfo(identity1) assert.NoError(t, err) node2Peers := make(map[peer.ID]struct{}) - node2, identity2 := p2pfixtures.NodeFixture( + node2, identity2 := p2ptest.NodeFixture( t, sporkID, t.Name(), - p2pfixtures.WithConnectionGater(testutils.NewConnectionGater(func(pid peer.ID) error { + p2ptest.WithConnectionGater(testutils.NewConnectionGater(func(pid peer.ID) error { if _, ok := node2Peers[pid]; !ok { return fmt.Errorf("id not found: %s", pid.String()) } return nil }))) - p2pfixtures.StartNode(t, signalerCtx, node2, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, node2, cancel, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx, node2, 100*time.Millisecond) + defer p2ptest.StopNode(t, node2, cancel, 100*time.Millisecond) node2Info, err := utils.PeerAddressInfo(identity2) assert.NoError(t, err) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index 4e3212da660..fff9358ddd1 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -12,6 +12,7 @@ import ( "time" "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/libp2p/go-libp2p/core" "github.com/libp2p/go-libp2p/core/network" @@ -41,14 +42,14 @@ func TestStreamClosing(t *testing.T) { handler, streamCloseWG := mockStreamHandlerForMessages(t, ctx, count, msgRegex) // Creates nodes - nodes, identities := p2pfixtures.NodesFixture(t, + nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_stream_closing", 2, - p2pfixtures.WithDefaultStreamHandler(handler)) + p2ptest.WithDefaultStreamHandler(handler)) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) nodeInfo1, err := utils.PeerAddressInfo(*identities[1]) require.NoError(t, err) @@ -147,14 +148,14 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []unicast. ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - nodes, identities := p2pfixtures.NodesFixture(t, + nodes, identities := p2ptest.NodesFixture(t, sporkId, "test_create_stream", count, - p2pfixtures.WithPreferredUnicasts(unicasts)) + p2ptest.WithPreferredUnicasts(unicasts)) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) id2 := identities[1] @@ -206,15 +207,15 @@ func TestCreateStream_FallBack(t *testing.T) { // Creates two nodes: one with preferred gzip, and other one with default protocol sporkId := unittest.IdentifierFixture() - thisNode, _ := p2pfixtures.NodeFixture(t, + thisNode, _ := p2ptest.NodeFixture(t, sporkId, "test_create_stream_fallback", - p2pfixtures.WithPreferredUnicasts([]unicast.ProtocolName{unicast.GzipCompressionUnicast})) - otherNode, otherId := p2pfixtures.NodeFixture(t, sporkId, "test_create_stream_fallback") + p2ptest.WithPreferredUnicasts([]unicast.ProtocolName{unicast.GzipCompressionUnicast})) + otherNode, otherId := p2ptest.NodeFixture(t, sporkId, "test_create_stream_fallback") nodes := []p2p.LibP2PNode{thisNode, otherNode} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) // Assert that there is no outbound stream to the target yet (neither default nor preferred) defaultProtocolId := unicast.FlowProtocolID(sporkId) @@ -270,11 +271,11 @@ func TestCreateStreamIsConcurrencySafe(t *testing.T) { signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) // create two nodes - nodes, identities := p2pfixtures.NodesFixture(t, unittest.IdentifierFixture(), "test_create_stream_is_concurrency_safe", 2) + nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_create_stream_is_concurrency_safe", 2) require.Len(t, identities, 2) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) nodeInfo1, err := utils.PeerAddressInfo(*identities[1]) require.NoError(t, err) @@ -319,7 +320,7 @@ func TestNoBackoffWhenCreatingStream(t *testing.T) { count := 2 // Creates nodes - nodes, identities := p2pfixtures.NodesFixture(t, + nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_no_backoff_when_create_stream", count, @@ -327,12 +328,12 @@ func TestNoBackoffWhenCreatingStream(t *testing.T) { node1 := nodes[0] node2 := nodes[1] - p2pfixtures.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) - p2pfixtures.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) // stop node 2 immediately - p2pfixtures.StopNode(t, node2, cancel2, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, node1, cancel1, 100*time.Millisecond) + p2ptest.StopNode(t, node2, cancel2, 100*time.Millisecond) + defer p2ptest.StopNode(t, node1, cancel1, 100*time.Millisecond) id2 := identities[1] pInfo, err := utils.PeerAddressInfo(*id2) @@ -380,11 +381,11 @@ func TestUnicastOverStream_WithPlainStream(t *testing.T) { // TestUnicastOverStream_WithGzipStreamCompression checks two nodes can send and receive unicast messages on gzip compressed streams // when both nodes have gzip stream compression enabled. func TestUnicastOverStream_WithGzipStreamCompression(t *testing.T) { - testUnicastOverStream(t, p2pfixtures.WithPreferredUnicasts([]unicast.ProtocolName{unicast.GzipCompressionUnicast})) + testUnicastOverStream(t, p2ptest.WithPreferredUnicasts([]unicast.ProtocolName{unicast.GzipCompressionUnicast})) } // testUnicastOverStream sends a message from node 1 to node 2 and then from node 2 to node 1 over a unicast stream. -func testUnicastOverStream(t *testing.T, opts ...p2pfixtures.NodeFixtureParameterOption) { +func testUnicastOverStream(t *testing.T, opts ...p2ptest.NodeFixtureParameterOption) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) @@ -392,23 +393,23 @@ func testUnicastOverStream(t *testing.T, opts ...p2pfixtures.NodeFixtureParamete sporkId := unittest.IdentifierFixture() streamHandler1, inbound1 := p2pfixtures.StreamHandlerFixture(t) - node1, id1 := p2pfixtures.NodeFixture( + node1, id1 := p2ptest.NodeFixture( t, sporkId, t.Name(), - append(opts, p2pfixtures.WithDefaultStreamHandler(streamHandler1))...) + append(opts, p2ptest.WithDefaultStreamHandler(streamHandler1))...) streamHandler2, inbound2 := p2pfixtures.StreamHandlerFixture(t) - node2, id2 := p2pfixtures.NodeFixture( + node2, id2 := p2ptest.NodeFixture( t, sporkId, t.Name(), - append(opts, p2pfixtures.WithDefaultStreamHandler(streamHandler2))...) + append(opts, p2ptest.WithDefaultStreamHandler(streamHandler2))...) nodes := []p2p.LibP2PNode{node1, node2} ids := flow.IdentityList{&id1, &id2} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) p2pfixtures.LetNodesDiscoverEachOther(t, ctx, nodes, ids) @@ -432,26 +433,26 @@ func TestUnicastOverStream_Fallback(t *testing.T) { sporkId := unittest.IdentifierFixture() streamHandler1, inbound1 := p2pfixtures.StreamHandlerFixture(t) - node1, id1 := p2pfixtures.NodeFixture( + node1, id1 := p2ptest.NodeFixture( t, sporkId, t.Name(), - p2pfixtures.WithDefaultStreamHandler(streamHandler1), + p2ptest.WithDefaultStreamHandler(streamHandler1), ) streamHandler2, inbound2 := p2pfixtures.StreamHandlerFixture(t) - node2, id2 := p2pfixtures.NodeFixture( + node2, id2 := p2ptest.NodeFixture( t, sporkId, t.Name(), - p2pfixtures.WithDefaultStreamHandler(streamHandler2), - p2pfixtures.WithPreferredUnicasts([]unicast.ProtocolName{unicast.GzipCompressionUnicast}), + p2ptest.WithDefaultStreamHandler(streamHandler2), + p2ptest.WithPreferredUnicasts([]unicast.ProtocolName{unicast.GzipCompressionUnicast}), ) nodes := []p2p.LibP2PNode{node1, node2} ids := flow.IdentityList{&id1, &id2} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) p2pfixtures.LetNodesDiscoverEachOther(t, ctx, nodes, ids) p2pfixtures.EnsureMessageExchangeOverUnicast(t, ctx, nodes, []chan string{inbound1, inbound2}, p2pfixtures.LongStringMessageFactoryFixture(t)) @@ -464,15 +465,15 @@ func TestCreateStreamTimeoutWithUnresponsiveNode(t *testing.T) { signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) // creates a regular node - nodes, identities := p2pfixtures.NodesFixture(t, + nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_create_stream_timeout_with_unresponsive_node", 1, ) require.Len(t, identities, 1) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) // create a silent node which never replies listener, silentNodeId := p2pfixtures.SilentNodeFixture(t) @@ -505,15 +506,15 @@ func TestCreateStreamIsConcurrent(t *testing.T) { signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) // create two regular node - goodNodes, goodNodeIds := p2pfixtures.NodesFixture(t, + goodNodes, goodNodeIds := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_create_stream_is_concurrent", 2, ) require.Len(t, goodNodeIds, 2) - p2pfixtures.StartNodes(t, signalerCtx, goodNodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, goodNodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, goodNodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, goodNodes, cancel, 100*time.Millisecond) goodNodeInfo1, err := utils.PeerAddressInfo(*goodNodeIds[1]) require.NoError(t, err) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 459d6e7ad9d..e9af19fe7c7 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -19,6 +19,7 @@ import ( "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/scoring" + p2ptest "github.com/onflow/flow-go/network/p2p/test" flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" "github.com/onflow/flow-go/utils/unittest" ) @@ -33,15 +34,15 @@ func TestFullGossipSubConnectivity(t *testing.T) { idProvider := mock.NewIdentityProvider(t) // two groups of non-access nodes and one group of access nodes. - groupOneNodes, groupOneIds := p2pfixtures.NodesFixture(t, sporkId, t.Name(), 5, - p2pfixtures.WithRole(flow.RoleConsensus), - p2pfixtures.WithPeerScoringEnabled(idProvider)) - groupTwoNodes, groupTwoIds := p2pfixtures.NodesFixture(t, sporkId, t.Name(), 5, - p2pfixtures.WithRole(flow.RoleCollection), - p2pfixtures.WithPeerScoringEnabled(idProvider)) - accessNodeGroup, accessNodeIds := p2pfixtures.NodesFixture(t, sporkId, t.Name(), 5, - p2pfixtures.WithRole(flow.RoleAccess), - p2pfixtures.WithPeerScoringEnabled(idProvider)) + groupOneNodes, groupOneIds := p2ptest.NodesFixture(t, sporkId, t.Name(), 5, + p2ptest.WithRole(flow.RoleConsensus), + p2ptest.WithPeerScoringEnabled(idProvider)) + groupTwoNodes, groupTwoIds := p2ptest.NodesFixture(t, sporkId, t.Name(), 5, + p2ptest.WithRole(flow.RoleCollection), + p2ptest.WithPeerScoringEnabled(idProvider)) + accessNodeGroup, accessNodeIds := p2ptest.NodesFixture(t, sporkId, t.Name(), 5, + p2ptest.WithRole(flow.RoleAccess), + p2ptest.WithPeerScoringEnabled(idProvider)) ids := append(append(groupOneIds, groupTwoIds...), accessNodeIds...) nodes := append(append(groupOneNodes, groupTwoNodes...), accessNodeGroup...) @@ -55,8 +56,8 @@ func TestFullGossipSubConnectivity(t *testing.T) { _, ok := provider.ByPeerID(peerId) return ok }) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 2*time.Second) blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) slashingViolationsConsumer := unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector()) @@ -146,22 +147,22 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS idProvider := mock.NewIdentityProvider(t) // two (honest) consensus nodes - opts := []p2pfixtures.NodeFixtureParameterOption{p2pfixtures.WithRole(flow.RoleConsensus)} + opts := []p2ptest.NodeFixtureParameterOption{p2ptest.WithRole(flow.RoleConsensus)} if honestPeerScoring { - opts = append(opts, p2pfixtures.WithPeerScoringEnabled(idProvider)) + opts = append(opts, p2ptest.WithPeerScoringEnabled(idProvider)) } - con1Node, con1Id := p2pfixtures.NodeFixture(t, sporkId, t.Name(), opts...) - con2Node, con2Id := p2pfixtures.NodeFixture(t, sporkId, t.Name(), opts...) + con1Node, con1Id := p2ptest.NodeFixture(t, sporkId, t.Name(), opts...) + con2Node, con2Id := p2ptest.NodeFixture(t, sporkId, t.Name(), opts...) // create > 2 * 12 malicious access nodes // 12 is the maximum size of default GossipSub mesh. // We want to make sure that it is unlikely for honest nodes to be in the same mesh (hence messages from // one honest node to the other is routed through the malicious nodes). - accessNodeGroup, accessNodeIds := p2pfixtures.NodesFixture(t, sporkId, t.Name(), 30, - p2pfixtures.WithRole(flow.RoleAccess), - p2pfixtures.WithPeerScoringEnabled(idProvider), + accessNodeGroup, accessNodeIds := p2ptest.NodesFixture(t, sporkId, t.Name(), 30, + p2ptest.WithRole(flow.RoleAccess), + p2ptest.WithPeerScoringEnabled(idProvider), // overrides the default peer scoring parameters to mute GossipSub traffic from/to honest nodes. - p2pfixtures.WithAppSpecificScore(maliciousAppSpecificScore(flow.IdentityList{&con1Id, &con2Id}))) + p2ptest.WithAppSpecificScore(maliciousAppSpecificScore(flow.IdentityList{&con1Id, &con2Id}))) allNodes := append([]p2p.LibP2PNode{con1Node, con2Node}, accessNodeGroup...) allIds := append([]*flow.Identity{&con1Id, &con2Id}, accessNodeIds...) @@ -176,8 +177,8 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS return ok }).Maybe() - p2pfixtures.StartNodes(t, signalerCtx, allNodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, allNodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, allNodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, allNodes, cancel, 2*time.Second) blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) slashingViolationsConsumer := unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector()) diff --git a/network/p2p/scoring/subscription_validator_test.go b/network/p2p/scoring/subscription_validator_test.go index 9f11f8ec3b2..75f9321914d 100644 --- a/network/p2p/scoring/subscription_validator_test.go +++ b/network/p2p/scoring/subscription_validator_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" pubsub "github.com/libp2p/go-libp2p-pubsub" @@ -131,8 +132,8 @@ func TestSubscriptionValidator_InvalidSubscriptions(t *testing.T) { for _, role := range flow.Roles() { peerId := p2pfixtures.PeerIdFixture(t) unauthorizedChannels := channels.Channels(). // all channels - ExcludeChannels(channels.ChannelsByRole(role)). // excluding the channels for the role - ExcludePattern(regexp.MustCompile("^(test).*")) // excluding the test channels. + ExcludeChannels(channels.ChannelsByRole(role)). // excluding the channels for the role + ExcludePattern(regexp.MustCompile("^(test).*")) // excluding the test channels. sporkID := unittest.IdentifierFixture() unauthorizedTopics := make([]string, 0, len(unauthorizedChannels)) for _, channel := range unauthorizedChannels { @@ -176,21 +177,21 @@ func TestSubscriptionValidator_Integration(t *testing.T) { idProvider := mock.NewIdentityProvider(t) // one consensus node. - conNode, conId := p2pfixtures.NodeFixture(t, sporkId, t.Name(), - p2pfixtures.WithLogger(unittest.Logger()), - p2pfixtures.WithPeerScoringEnabled(idProvider), - p2pfixtures.WithRole(flow.RoleConsensus)) + conNode, conId := p2ptest.NodeFixture(t, sporkId, t.Name(), + p2ptest.WithLogger(unittest.Logger()), + p2ptest.WithPeerScoringEnabled(idProvider), + p2ptest.WithRole(flow.RoleConsensus)) // two verification node. - verNode1, verId1 := p2pfixtures.NodeFixture(t, sporkId, t.Name(), - p2pfixtures.WithLogger(unittest.Logger()), - p2pfixtures.WithPeerScoringEnabled(idProvider), - p2pfixtures.WithRole(flow.RoleVerification)) + verNode1, verId1 := p2ptest.NodeFixture(t, sporkId, t.Name(), + p2ptest.WithLogger(unittest.Logger()), + p2ptest.WithPeerScoringEnabled(idProvider), + p2ptest.WithRole(flow.RoleVerification)) - verNode2, verId2 := p2pfixtures.NodeFixture(t, sporkId, t.Name(), - p2pfixtures.WithLogger(unittest.Logger()), - p2pfixtures.WithPeerScoringEnabled(idProvider), - p2pfixtures.WithRole(flow.RoleVerification)) + verNode2, verId2 := p2ptest.NodeFixture(t, sporkId, t.Name(), + p2ptest.WithLogger(unittest.Logger()), + p2ptest.WithPeerScoringEnabled(idProvider), + p2ptest.WithRole(flow.RoleVerification)) ids := flow.IdentityList{&conId, &verId1, &verId2} nodes := []p2p.LibP2PNode{conNode, verNode1, verNode2} @@ -205,8 +206,8 @@ func TestSubscriptionValidator_Integration(t *testing.T) { return ok }) - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) slashingViolationsConsumer := unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector()) diff --git a/network/p2p/subscription/subscription_filter_test.go b/network/p2p/subscription/subscription_filter_test.go index 10ddae136e1..98e1c2a2aa3 100644 --- a/network/p2p/subscription/subscription_filter_test.go +++ b/network/p2p/subscription/subscription_filter_test.go @@ -19,6 +19,7 @@ import ( "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/subscription" + p2ptest "github.com/onflow/flow-go/network/p2p/test" flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" "github.com/onflow/flow-go/utils/unittest" ) @@ -117,8 +118,8 @@ func TestCanSubscribe(t *testing.T) { collectionNode := p2pfixtures.CreateNode(t, identity.NodeID, privateKey, sporkId, zerolog.Nop(), p2pfixtures.WithSubscriptionFilter(subscriptionFilter(identity, flow.IdentityList{identity}))) - p2pfixtures.StartNode(t, signalerCtx, collectionNode, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, collectionNode, cancel, 1*time.Second) + p2ptest.StartNode(t, signalerCtx, collectionNode, 100*time.Millisecond) + defer p2ptest.StopNode(t, collectionNode, cancel, 1*time.Second) logger := unittest.Logger() topicValidator := flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), unittest.NetworkSlashingViolationsConsumer(logger, metrics.NewNoopCollector()), unittest.AllowAllPeerFilter()) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go new file mode 100644 index 00000000000..bf2265630f4 --- /dev/null +++ b/network/p2p/test/fixtures.go @@ -0,0 +1,271 @@ +package p2ptest + +import ( + "context" + "testing" + "time" + + dht "github.com/libp2p/go-libp2p-kad-dht" + "github.com/libp2p/go-libp2p/core/connmgr" + "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/core/protocol" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/crypto" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/network/internal/testutils" + "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network/p2p/connection" + p2pdht "github.com/onflow/flow-go/network/p2p/dht" + "github.com/onflow/flow-go/network/p2p/p2pbuilder" + "github.com/onflow/flow-go/network/p2p/scoring" + "github.com/onflow/flow-go/network/p2p/unicast" + "github.com/onflow/flow-go/utils/logging" + "github.com/onflow/flow-go/utils/unittest" +) + +// Creating a node fixture with defaultAddress lets libp2p runs it on an +// allocated port by OS. So after fixture created, its address would be +// "0.0.0.0: +const defaultAddress = "0.0.0.0:0" + +// NetworkingKeyFixtures is a test helper that generates a ECDSA flow key pair. +func NetworkingKeyFixtures(t *testing.T) crypto.PrivateKey { + seed := unittest.SeedFixture(48) + key, err := crypto.GeneratePrivateKey(crypto.ECDSASecp256k1, seed) + require.NoError(t, err) + return key +} + +// NodeFixture is a test fixture that creates a single libp2p node with the given key, spork id, and options. +// It returns the node and its identity. +func NodeFixture( + t *testing.T, + sporkID flow.Identifier, + dhtPrefix string, + opts ...NodeFixtureParameterOption, +) (p2p.LibP2PNode, flow.Identity) { + // default parameters + parameters := &NodeFixtureParameters{ + HandlerFunc: func(network.Stream) {}, + Unicasts: nil, + Key: NetworkingKeyFixtures(t), + Address: defaultAddress, + Logger: unittest.Logger().Level(zerolog.ErrorLevel), + Role: flow.RoleCollection, + } + + for _, opt := range opts { + opt(parameters) + } + + identity := unittest.IdentityFixture( + unittest.WithNetworkingKey(parameters.Key.PublicKey()), + unittest.WithAddress(parameters.Address), + unittest.WithRole(parameters.Role)) + + logger := parameters.Logger.With().Hex("node_id", logging.ID(identity.NodeID)).Logger() + + noopMetrics := metrics.NewNoopCollector() + connManager := connection.NewConnManager(logger, noopMetrics) + resourceManager := testutils.NewResourceManager(t) + + builder := p2pbuilder.NewNodeBuilder(logger, parameters.Address, parameters.Key, sporkID). + SetConnectionManager(connManager). + SetRoutingSystem(func(c context.Context, h host.Host) (routing.Routing, error) { + return p2pdht.NewDHT(c, h, + protocol.ID(unicast.FlowDHTProtocolIDPrefix+sporkID.String()+"/"+dhtPrefix), + logger, + noopMetrics, + parameters.DhtOptions..., + ) + }). + SetResourceManager(resourceManager). + SetCreateNode(p2pbuilder.DefaultCreateNodeFunc) + + if parameters.ConnGater != nil { + builder.SetConnectionGater(parameters.ConnGater) + } + + if parameters.PeerScoringEnabled { + scoreOptionParams := make([]scoring.PeerScoreParamsOption, 0) + if parameters.AppSpecificScore != nil { + scoreOptionParams = append(scoreOptionParams, scoring.WithAppSpecificScoreFunction(parameters.AppSpecificScore)) + } + builder.EnableGossipSubPeerScoring(parameters.IdProvider, scoreOptionParams...) + } + + if parameters.UpdateInterval != 0 { + require.NotNil(t, parameters.PeerProvider) + builder.SetPeerManagerOptions(parameters.ConnectionPruning, parameters.UpdateInterval) + } + + n, err := builder.Build() + require.NoError(t, err) + + err = n.WithDefaultUnicastProtocol(parameters.HandlerFunc, parameters.Unicasts) + require.NoError(t, err) + + // get the actual IP and port that have been assigned by the subsystem + ip, port, err := n.GetIPPort() + require.NoError(t, err) + identity.Address = ip + ":" + port + + if parameters.PeerProvider != nil { + n.WithPeersProvider(parameters.PeerProvider) + } + return n, *identity +} + +type NodeFixtureParameterOption func(*NodeFixtureParameters) + +type NodeFixtureParameters struct { + HandlerFunc network.StreamHandler + Unicasts []unicast.ProtocolName + Key crypto.PrivateKey + Address string + DhtOptions []dht.Option + Role flow.Role + Logger zerolog.Logger + PeerScoringEnabled bool + IdProvider module.IdentityProvider + AppSpecificScore func(peer.ID) float64 // overrides GossipSub scoring for sake of testing. + ConnectionPruning bool // peer manager parameter + UpdateInterval time.Duration // peer manager parameter + PeerProvider p2p.PeersProvider // peer manager parameter + ConnGater connmgr.ConnectionGater +} + +func WithPeerScoringEnabled(idProvider module.IdentityProvider) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.PeerScoringEnabled = true + p.IdProvider = idProvider + } +} + +func WithDefaultStreamHandler(handler network.StreamHandler) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.HandlerFunc = handler + } +} + +func WithPeerManagerEnabled(connectionPruning bool, updateInterval time.Duration, peerProvider p2p.PeersProvider) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.ConnectionPruning = connectionPruning + p.UpdateInterval = updateInterval + p.PeerProvider = peerProvider + } +} + +func WithPreferredUnicasts(unicasts []unicast.ProtocolName) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.Unicasts = unicasts + } +} + +func WithNetworkingPrivateKey(key crypto.PrivateKey) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.Key = key + } +} + +func WithNetworkingAddress(address string) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.Address = address + } +} + +func WithDHTOptions(opts ...dht.Option) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.DhtOptions = opts + } +} + +func WithConnectionGater(connGater connmgr.ConnectionGater) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.ConnGater = connGater + } +} + +func WithRole(role flow.Role) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.Role = role + } +} + +func WithAppSpecificScore(score func(peer.ID) float64) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.AppSpecificScore = score + } +} + +func WithLogger(logger zerolog.Logger) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.Logger = logger + } +} + +// NodesFixture is a test fixture that creates a number of libp2p nodes with the given callback function for stream handling. +// It returns the nodes and their identities. +func NodesFixture(t *testing.T, sporkID flow.Identifier, dhtPrefix string, count int, opts ...NodeFixtureParameterOption) ([]p2p.LibP2PNode, + flow.IdentityList) { + var nodes []p2p.LibP2PNode + + // creating nodes + var identities flow.IdentityList + for i := 0; i < count; i++ { + // create a node on localhost with a random port assigned by the OS + node, identity := NodeFixture(t, sporkID, dhtPrefix, opts...) + nodes = append(nodes, node) + identities = append(identities, &identity) + } + + return nodes, identities +} + +// StartNodes start all nodes in the input slice using the provided context, timing out if nodes are +// not all Ready() before duration expires +func StartNodes(t *testing.T, ctx irrecoverable.SignalerContext, nodes []p2p.LibP2PNode, timeout time.Duration) { + rdas := make([]module.ReadyDoneAware, 0, len(nodes)) + for _, node := range nodes { + node.Start(ctx) + rdas = append(rdas, node) + + if peerManager := node.PeerManagerComponent(); peerManager != (*connection.PeerManager)(nil) { + // we need to start the peer manager post the node startup (if such component exists). + peerManager.Start(ctx) + rdas = append(rdas, peerManager) + } + } + unittest.RequireComponentsReadyBefore(t, timeout, rdas...) +} + +// StartNode start a single node using the provided context, timing out if nodes are not all Ready() +// before duration expires +func StartNode(t *testing.T, ctx irrecoverable.SignalerContext, node p2p.LibP2PNode, timeout time.Duration) { + node.Start(ctx) + unittest.RequireComponentsReadyBefore(t, timeout, node) +} + +// StopNodes stops all nodes in the input slice using the provided cancel func, timing out if nodes are +// not all Done() before duration expires +func StopNodes(t *testing.T, nodes []p2p.LibP2PNode, cancel context.CancelFunc, timeout time.Duration) { + cancel() + for _, node := range nodes { + unittest.RequireComponentsDoneBefore(t, timeout, node) + } +} + +// StopNode stops a single node using the provided cancel func, timing out if nodes are not all Done() +// before duration expires +func StopNode(t *testing.T, node p2p.LibP2PNode, cancel context.CancelFunc, timeout time.Duration) { + cancel() + unittest.RequireComponentsDoneBefore(t, timeout, node) +} diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index b3ebabbac2c..18fbfd479a7 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -1,4 +1,4 @@ -package test_test +package p2ptest_test import ( "context" @@ -6,6 +6,7 @@ import ( "time" "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" @@ -54,26 +55,26 @@ func TestCrosstalkPreventionOnNetworkKeyChange(t *testing.T) { node1key := p2pfixtures.NetworkingKeyFixtures(t) sporkId := unittest.IdentifierFixture() - node1, id1 := p2pfixtures.NodeFixture(t, + node1, id1 := p2ptest.NodeFixture(t, sporkId, "test_crosstalk_prevention_on_network_key_change", - p2pfixtures.WithNetworkingPrivateKey(node1key), + p2ptest.WithNetworkingPrivateKey(node1key), ) - p2pfixtures.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, node1, cancel1, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) + defer p2ptest.StopNode(t, node1, cancel1, 100*time.Millisecond) t.Logf(" %s node started on %s", id1.NodeID.String(), id1.Address) t.Logf("libp2p ID for %s: %s", id1.NodeID.String(), node1.Host().ID()) // create and start node 2 on localhost and random port - node2key := p2pfixtures.NetworkingKeyFixtures(t) - node2, id2 := p2pfixtures.NodeFixture(t, + node2key := p2ptest.NetworkingKeyFixtures(t) + node2, id2 := p2ptest.NodeFixture(t, sporkId, "test_crosstalk_prevention_on_network_key_change", - p2pfixtures.WithNetworkingPrivateKey(node2key), + p2ptest.WithNetworkingPrivateKey(node2key), ) - p2pfixtures.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) peerInfo2, err := utils.PeerAddressInfo(id2) require.NoError(t, err) @@ -84,20 +85,20 @@ func TestCrosstalkPreventionOnNetworkKeyChange(t *testing.T) { // Simulate a hard-spoon: node1 is on the old chain, but node2 is moved from the old chain to the new chain // stop node 2 and start it again with a different networking key but on the same IP and port - p2pfixtures.StopNode(t, node2, cancel2, 100*time.Millisecond) + p2ptest.StopNode(t, node2, cancel2, 100*time.Millisecond) // start node2 with the same name, ip and port but with the new key node2keyNew := p2pfixtures.NetworkingKeyFixtures(t) assert.False(t, node2key.Equals(node2keyNew)) - node2, id2New := p2pfixtures.NodeFixture(t, + node2, id2New := p2ptest.NodeFixture(t, sporkId, "test_crosstalk_prevention_on_network_key_change", - p2pfixtures.WithNetworkingPrivateKey(node2keyNew), - p2pfixtures.WithNetworkingAddress(id2.Address), + p2ptest.WithNetworkingPrivateKey(node2keyNew), + p2ptest.WithNetworkingAddress(id2.Address), ) - p2pfixtures.StartNode(t, signalerCtx2a, node2, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, node2, cancel2a, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2a, node2, 100*time.Millisecond) + defer p2ptest.StopNode(t, node2, cancel2a, 100*time.Millisecond) // make sure the node2 indeed came up on the old ip and port assert.Equal(t, id2New.Address, id2.Address) @@ -126,35 +127,35 @@ func TestOneToOneCrosstalkPrevention(t *testing.T) { sporkId1 := unittest.IdentifierFixture() // create and start node 1 on localhost and random port - node1, id1 := p2pfixtures.NodeFixture(t, sporkId1, "test_one_to_one_crosstalk_prevention") + node1, id1 := p2ptest.NodeFixture(t, sporkId1, "test_one_to_one_crosstalk_prevention") - p2pfixtures.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, node1, cancel1, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) + defer p2ptest.StopNode(t, node1, cancel1, 100*time.Millisecond) peerInfo1, err := utils.PeerAddressInfo(id1) require.NoError(t, err) // create and start node 2 on localhost and random port - node2, id2 := p2pfixtures.NodeFixture(t, sporkId1, "test_one_to_one_crosstalk_prevention") + node2, id2 := p2ptest.NodeFixture(t, sporkId1, "test_one_to_one_crosstalk_prevention") - p2pfixtures.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) // create stream from node 2 to node 1 testOneToOneMessagingSucceeds(t, node2, peerInfo1) // Simulate a hard-spoon: node1 is on the old chain, but node2 is moved from the old chain to the new chain // stop node 2 and start it again with a different libp2p protocol id to listen for - p2pfixtures.StopNode(t, node2, cancel2, time.Second) + p2ptest.StopNode(t, node2, cancel2, time.Second) // start node2 with the same address and root key but different root block id - node2, id2New := p2pfixtures.NodeFixture(t, + node2, id2New := p2ptest.NodeFixture(t, unittest.IdentifierFixture(), // update the flow root id for node 2. node1 is still listening on the old protocol "test_one_to_one_crosstalk_prevention", - p2pfixtures.WithNetworkingAddress(id2.Address), + p2ptest.WithNetworkingAddress(id2.Address), ) - p2pfixtures.StartNode(t, signalerCtx2a, node2, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, node2, cancel2a, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2a, node2, 100*time.Millisecond) + defer p2ptest.StopNode(t, node2, cancel2a, 100*time.Millisecond) // make sure the node2 indeed came up on the old ip and port assert.Equal(t, id2New.Address, id2.Address) @@ -180,22 +181,22 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { previousSporkId := unittest.IdentifierFixture() // create and start node 1 on localhost and random port - node1, _ := p2pfixtures.NodeFixture(t, + node1, _ := p2ptest.NodeFixture(t, previousSporkId, "test_one_to_k_crosstalk_prevention", ) - p2pfixtures.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, node1, cancel1, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) + defer p2ptest.StopNode(t, node1, cancel1, 100*time.Millisecond) // create and start node 2 on localhost and random port with the same root block ID - node2, id2 := p2pfixtures.NodeFixture(t, + node2, id2 := p2ptest.NodeFixture(t, previousSporkId, "test_one_to_k_crosstalk_prevention", ) - p2pfixtures.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, node2, cancel2, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) + defer p2ptest.StopNode(t, node2, cancel2, 100*time.Millisecond) pInfo2, err := utils.PeerAddressInfo(id2) require.NoError(t, err) diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index 54bb37a5a43..0bf96f1f286 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -1,4 +1,4 @@ -package test_test +package p2ptest_test import ( "context" @@ -8,6 +8,7 @@ import ( "time" "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" @@ -39,12 +40,12 @@ func TestTopicValidator_Unstaked(t *testing.T) { sporkId := unittest.IdentifierFixture() - sn1, identity1 := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleConsensus), p2pfixtures.WithLogger(logger)) - sn2, identity2 := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleConsensus), p2pfixtures.WithLogger(logger)) + sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) + sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) nodes := []p2p.LibP2PNode{sn1, sn2} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) channel := channels.ConsensusCommittee topic := channels.TopicFromChannel(channel, sporkId) @@ -114,12 +115,12 @@ func TestTopicValidator_PublicChannel(t *testing.T) { sporkId := unittest.IdentifierFixture() logger := unittest.Logger() - sn1, _ := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleConsensus), p2pfixtures.WithLogger(logger)) - sn2, identity2 := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleConsensus), p2pfixtures.WithLogger(logger)) + sn1, _ := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) + sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) nodes := []p2p.LibP2PNode{sn1, sn2} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) // unauthenticated messages should not be dropped on public channels channel := channels.PublicSyncCommittee @@ -175,12 +176,12 @@ func TestTopicValidator_TopicMismatch(t *testing.T) { sporkId := unittest.IdentifierFixture() - sn1, _ := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleConsensus), p2pfixtures.WithLogger(logger)) - sn2, identity2 := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleConsensus), p2pfixtures.WithLogger(logger)) + sn1, _ := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) + sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) nodes := []p2p.LibP2PNode{sn1, sn2} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) channel := channels.ConsensusCommittee topic := channels.TopicFromChannel(channel, sporkId) @@ -231,12 +232,12 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { sporkId := unittest.IdentifierFixture() - sn1, _ := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleConsensus), p2pfixtures.WithLogger(logger)) - sn2, identity2 := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleConsensus), p2pfixtures.WithLogger(logger)) + sn1, _ := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) + sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) nodes := []p2p.LibP2PNode{sn1, sn2} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) topic := channels.Topic("invalid-topic") @@ -285,13 +286,13 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { sporkId := unittest.IdentifierFixture() - sn1, identity1 := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleConsensus)) - sn2, identity2 := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleConsensus)) - an1, identity3 := p2pfixtures.NodeFixture(t, sporkId, t.Name(), p2pfixtures.WithRole(flow.RoleAccess)) + sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleConsensus)) + sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleConsensus)) + an1, identity3 := p2ptest.NodeFixture(t, sporkId, t.Name(), p2ptest.WithRole(flow.RoleAccess)) nodes := []p2p.LibP2PNode{sn1, sn2, an1} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) channel := channels.ConsensusCommittee topic := channels.TopicFromChannel(channel, sporkId) @@ -397,12 +398,12 @@ func TestAuthorizedSenderValidator_InvalidMsg(t *testing.T) { sporkId := unittest.IdentifierFixture() - sn1, identity1 := p2pfixtures.NodeFixture(t, sporkId, "consensus_1", p2pfixtures.WithRole(flow.RoleConsensus)) - sn2, identity2 := p2pfixtures.NodeFixture(t, sporkId, "consensus_2", p2pfixtures.WithRole(flow.RoleConsensus)) + sn1, identity1 := p2ptest.NodeFixture(t, sporkId, "consensus_1", p2ptest.WithRole(flow.RoleConsensus)) + sn2, identity2 := p2ptest.NodeFixture(t, sporkId, "consensus_2", p2ptest.WithRole(flow.RoleConsensus)) nodes := []p2p.LibP2PNode{sn1, sn2} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) // try to publish BlockProposal on invalid SyncCommittee channel channel := channels.SyncCommittee @@ -470,13 +471,13 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { sporkId := unittest.IdentifierFixture() - sn1, identity1 := p2pfixtures.NodeFixture(t, sporkId, "consensus_1", p2pfixtures.WithRole(flow.RoleConsensus)) - sn2, identity2 := p2pfixtures.NodeFixture(t, sporkId, "consensus_2", p2pfixtures.WithRole(flow.RoleConsensus)) - an1, identity3 := p2pfixtures.NodeFixture(t, sporkId, "access_1", p2pfixtures.WithRole(flow.RoleAccess)) + sn1, identity1 := p2ptest.NodeFixture(t, sporkId, "consensus_1", p2ptest.WithRole(flow.RoleConsensus)) + sn2, identity2 := p2ptest.NodeFixture(t, sporkId, "consensus_2", p2ptest.WithRole(flow.RoleConsensus)) + an1, identity3 := p2ptest.NodeFixture(t, sporkId, "access_1", p2ptest.WithRole(flow.RoleAccess)) nodes := []p2p.LibP2PNode{sn1, sn2, an1} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) channel := channels.ConsensusCommittee topic := channels.TopicFromChannel(channel, sporkId) @@ -565,13 +566,13 @@ func TestAuthorizedSenderValidator_ClusterChannel(t *testing.T) { sporkId := unittest.IdentifierFixture() - ln1, identity1 := p2pfixtures.NodeFixture(t, sporkId, "collection_1", p2pfixtures.WithRole(flow.RoleCollection)) - ln2, identity2 := p2pfixtures.NodeFixture(t, sporkId, "collection_2", p2pfixtures.WithRole(flow.RoleCollection)) - ln3, identity3 := p2pfixtures.NodeFixture(t, sporkId, "collection_3", p2pfixtures.WithRole(flow.RoleCollection)) + ln1, identity1 := p2ptest.NodeFixture(t, sporkId, "collection_1", p2ptest.WithRole(flow.RoleCollection)) + ln2, identity2 := p2ptest.NodeFixture(t, sporkId, "collection_2", p2ptest.WithRole(flow.RoleCollection)) + ln3, identity3 := p2ptest.NodeFixture(t, sporkId, "collection_3", p2ptest.WithRole(flow.RoleCollection)) nodes := []p2p.LibP2PNode{ln1, ln2, ln3} - p2pfixtures.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2pfixtures.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) channel := channels.SyncCluster(flow.Testnet) topic := channels.TopicFromChannel(channel, sporkId) From ac6bac7b2bc1134a26da22039b51f9bc8650373e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 10 Nov 2022 15:54:44 -0800 Subject: [PATCH 008/138] migrates stream handling logic --- insecure/corruptlibp2p/spammerGossipSub-test.go | 5 ++++- network/internal/p2pfixtures/fixtures.go | 13 ------------- network/p2p/connection/connection_gater_test.go | 4 ++-- network/p2p/p2pnode/libp2pStream_test.go | 8 ++++---- network/p2p/test/fixtures.go | 13 +++++++++++++ 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/insecure/corruptlibp2p/spammerGossipSub-test.go b/insecure/corruptlibp2p/spammerGossipSub-test.go index 0451453222c..b1318c85985 100644 --- a/insecure/corruptlibp2p/spammerGossipSub-test.go +++ b/insecure/corruptlibp2p/spammerGossipSub-test.go @@ -1,7 +1,9 @@ package corruptlibp2p import ( + "context" "fmt" + "testing" "time" "github.com/stretchr/testify/require" @@ -9,6 +11,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/utils/unittest" ) @@ -26,7 +29,7 @@ func TestSpammerGossipSub(t *testing.T) { disallowedList := map[*flow.Identity]struct{}{} for i := 0; i < count; i++ { - handler, inbound := p2pfixtures.StreamHandlerFixture(t) + handler, inbound := p2ptest.StreamHandlerFixture(t) node, id := p2pfixtures.NodeFixture( t, sporkId, diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index cfed964c90b..87a051eb228 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -12,7 +12,6 @@ import ( addrutil "github.com/libp2p/go-addr-util" pubsub "github.com/libp2p/go-libp2p-pubsub" "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/core/peerstore" "github.com/libp2p/go-libp2p/core/routing" @@ -480,18 +479,6 @@ func EnsureStreamCreationInBothDirections(t *testing.T, ctx context.Context, nod } } -// StreamHandlerFixture returns a stream handler that writes the received message to the given channel. -func StreamHandlerFixture(t *testing.T) (func(s network.Stream), chan string) { - ch := make(chan string, 1) // channel to receive messages - - return func(s network.Stream) { - rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s)) - str, err := rw.ReadString('\n') - require.NoError(t, err) - ch <- str - }, ch -} - // LongStringMessageFactoryFixture returns a function that creates a long unique string message. func LongStringMessageFactoryFixture(t *testing.T) func() string { return func() string { diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index 3510bbce7c4..6fa4e198aa7 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -117,7 +117,7 @@ func TestConnectionGater_InterceptUpgrade(t *testing.T) { connectionGater := mockp2p.NewConnectionGater(t) for i := 0; i < count; i++ { - handler, inbound := p2pfixtures.StreamHandlerFixture(t) + handler, inbound := p2ptest.StreamHandlerFixture(t) node, _ := p2ptest.NodeFixture( t, sporkId, @@ -201,7 +201,7 @@ func TestConnectionGater_Disallow_Integration(t *testing.T) { disallowedList := map[*flow.Identity]struct{}{} for i := 0; i < count; i++ { - handler, inbound := p2pfixtures.StreamHandlerFixture(t) + handler, inbound := p2ptest.StreamHandlerFixture(t) node, id := p2ptest.NodeFixture( t, sporkId, diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index fff9358ddd1..f6545d0bd91 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -392,14 +392,14 @@ func testUnicastOverStream(t *testing.T, opts ...p2ptest.NodeFixtureParameterOpt // Creates nodes sporkId := unittest.IdentifierFixture() - streamHandler1, inbound1 := p2pfixtures.StreamHandlerFixture(t) + streamHandler1, inbound1 := p2ptest.StreamHandlerFixture(t) node1, id1 := p2ptest.NodeFixture( t, sporkId, t.Name(), append(opts, p2ptest.WithDefaultStreamHandler(streamHandler1))...) - streamHandler2, inbound2 := p2pfixtures.StreamHandlerFixture(t) + streamHandler2, inbound2 := p2ptest.StreamHandlerFixture(t) node2, id2 := p2ptest.NodeFixture( t, sporkId, @@ -432,7 +432,7 @@ func TestUnicastOverStream_Fallback(t *testing.T) { // node2: supports plain and gzip sporkId := unittest.IdentifierFixture() - streamHandler1, inbound1 := p2pfixtures.StreamHandlerFixture(t) + streamHandler1, inbound1 := p2ptest.StreamHandlerFixture(t) node1, id1 := p2ptest.NodeFixture( t, sporkId, @@ -440,7 +440,7 @@ func TestUnicastOverStream_Fallback(t *testing.T) { p2ptest.WithDefaultStreamHandler(streamHandler1), ) - streamHandler2, inbound2 := p2pfixtures.StreamHandlerFixture(t) + streamHandler2, inbound2 := p2ptest.StreamHandlerFixture(t) node2, id2 := p2ptest.NodeFixture( t, sporkId, diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index bf2265630f4..2d63cc9e997 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -1,6 +1,7 @@ package p2ptest import ( + "bufio" "context" "testing" "time" @@ -269,3 +270,15 @@ func StopNode(t *testing.T, node p2p.LibP2PNode, cancel context.CancelFunc, time cancel() unittest.RequireComponentsDoneBefore(t, timeout, node) } + +// StreamHandlerFixture returns a stream handler that writes the received message to the given channel. +func StreamHandlerFixture(t *testing.T) (func(s network.Stream), chan string) { + ch := make(chan string, 1) // channel to receive messages + + return func(s network.Stream) { + rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s)) + str, err := rw.ReadString('\n') + require.NoError(t, err) + ch <- str + }, ch +} From 94a7d0ad9e47d469cd2cbcd1556e2c1925a085c2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 10 Nov 2022 16:04:38 -0800 Subject: [PATCH 009/138] migrates discovery logic --- .../corruptlibp2p/spammerGossipSub-test.go | 94 ------------------- .../corruptlibp2p/spammerGossipSub_test.go | 45 +++++++++ insecure/go.sum | 2 + network/internal/p2pfixtures/fixtures.go | 14 --- .../p2p/connection/connection_gater_test.go | 2 +- network/p2p/p2pnode/libp2pStream_test.go | 4 +- network/p2p/scoring/app_score_test.go | 6 +- .../scoring/subscription_validator_test.go | 6 +- network/p2p/test/fixtures.go | 15 +++ 9 files changed, 71 insertions(+), 117 deletions(-) delete mode 100644 insecure/corruptlibp2p/spammerGossipSub-test.go create mode 100644 insecure/corruptlibp2p/spammerGossipSub_test.go diff --git a/insecure/corruptlibp2p/spammerGossipSub-test.go b/insecure/corruptlibp2p/spammerGossipSub-test.go deleted file mode 100644 index b1318c85985..00000000000 --- a/insecure/corruptlibp2p/spammerGossipSub-test.go +++ /dev/null @@ -1,94 +0,0 @@ -package corruptlibp2p - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/network/p2p" - p2ptest "github.com/onflow/flow-go/network/p2p/test" - "github.com/onflow/flow-go/utils/unittest" -) - -func TestSpammerGossipSub(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - sporkId := unittest.IdentifierFixture() - defer cancel() - - count := 5 - nodes := make([]p2p.LibP2PNode, 0, 5) - ids := flow.IdentityList{} - inbounds := make([]chan string, 0, 5) - - disallowedList := map[*flow.Identity]struct{}{} - - for i := 0; i < count; i++ { - handler, inbound := p2ptest.StreamHandlerFixture(t) - node, id := p2pfixtures.NodeFixture( - t, - sporkId, - t.Name(), - p2pfixtures.WithRole(flow.RoleConsensus), - p2pfixtures.WithDefaultStreamHandler(handler), - // enable peer manager, with a 1-second refresh rate, and connection pruning enabled. - p2pfixtures.WithPeerManagerEnabled(true, 1*time.Second, func() peer.IDSlice { - list := make(peer.IDSlice, 0) - for _, id := range ids { - if _, ok := disallowedList[id]; ok { - continue - } - - pid, err := unittest.PeerIDFromFlowID(id) - require.NoError(t, err) - - list = append(list, pid) - } - return list - }), - p2pfixtures.WithConnectionGater(testutils.NewConnectionGater(func(pid peer.ID) error { - for id := range disallowedList { - bid, err := unittest.PeerIDFromFlowID(id) - require.NoError(t, err) - if bid == pid { - return fmt.Errorf("disallow-listed") - } - } - - return nil - }))) - - nodes = append(nodes, node) - ids = append(ids, &id) - inbounds = append(inbounds, inbound) - } - - p2pfixtures.StartNodes(t, signalerCtx, nodes, 1*time.Second) - defer p2pfixtures.StopNodes(t, nodes, cancel, 1*time.Second) - - p2pfixtures.LetNodesDiscoverEachOther(t, ctx, nodes, ids) - - // ensures that all nodes are connected to each other, and they can exchange messages over the pubsub and unicast. - ensureCommunicationOverAllProtocols(t, ctx, sporkId, nodes, inbounds) - - // now we add one of the nodes (the last node) to the disallow-list. - disallowedList[ids[len(ids)-1]] = struct{}{} - // let peer manager prune the connections to the disallow-listed node. - time.Sleep(1 * time.Second) - // ensures no connection, unicast, or pubsub going to or coming from the disallow-listed node. - ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:count-1], nodes[count-1:]) - - // now we add another node (the second last node) to the disallowed list. - disallowedList[ids[len(ids)-2]] = struct{}{} - // let peer manager prune the connections to the disallow-listed node. - time.Sleep(1 * time.Second) - // ensures no connection, unicast, or pubsub going to and coming from the disallow-listed nodes. - ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:count-2], nodes[count-2:]) - // ensures that all nodes are other non-disallow-listed nodes can exchange messages over the pubsub and unicast. - ensureCommunicationOverAllProtocols(t, ctx, sporkId, nodes[:count-2], inbounds[:count-2]) -} diff --git a/insecure/corruptlibp2p/spammerGossipSub_test.go b/insecure/corruptlibp2p/spammerGossipSub_test.go new file mode 100644 index 00000000000..f29e3d5cebd --- /dev/null +++ b/insecure/corruptlibp2p/spammerGossipSub_test.go @@ -0,0 +1,45 @@ +package corruptlibp2p + +import ( + "context" + "testing" + "time" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" + "github.com/onflow/flow-go/utils/unittest" +) + +func TestSpammerGossipSub(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + sporkId := unittest.IdentifierFixture() + defer cancel() + + count := 5 + nodes := make([]p2p.LibP2PNode, 0, 5) + ids := flow.IdentityList{} + inbounds := make([]chan string, 0, 5) + + for i := 0; i < count; i++ { + handler, inbound := p2ptest.StreamHandlerFixture(t) + node, id := p2ptest.NodeFixture( + t, + sporkId, + t.Name(), + p2ptest.WithRole(flow.RoleConsensus), + p2ptest.WithDefaultStreamHandler(handler), + ) + + nodes = append(nodes, node) + ids = append(ids, &id) + inbounds = append(inbounds, inbound) + } + + p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) + defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + + p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) +} diff --git a/insecure/go.sum b/insecure/go.sum index 3da8a62db5e..2fc4d21fce8 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -1495,6 +1495,8 @@ github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11 h1:WYWfp6ydU0v6ev+amaIRIfxJEo github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11/go.mod h1:GwZ5VBU63KlM8wZsnVvSGDIMeGMOO9Cb3uLXDP8WFYM= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110003401-b897e22c3c7c h1:Za50fWzRA8RfwWvy434k+HpTFUuL5Tlx3SvhoEcv1CA= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110003401-b897e22c3c7c/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 h1:c6Ns5fDv1quWqDgIVw+hRRsLfCVTz7tqhSwRP5wjRRo= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 87a051eb228..1e9667389fa 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -233,20 +233,6 @@ func SubsMustNeverReceiveAnyMessage(t *testing.T, ctx context.Context, subs []*p } } -// LetNodesDiscoverEachOther connects all nodes to each other on the pubsub mesh. -func LetNodesDiscoverEachOther(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode, ids flow.IdentityList) { - for _, node := range nodes { - for i, other := range nodes { - if node == other { - continue - } - otherPInfo, err := utils.PeerAddressInfo(*ids[i]) - require.NoError(t, err) - require.NoError(t, node.AddPeer(ctx, otherPInfo)) - } - } -} - // AddNodesToEachOthersPeerStore adds the dialing address of all nodes to the peer store of all other nodes. // However, it does not connect them to each other. func AddNodesToEachOthersPeerStore(t *testing.T, nodes []p2p.LibP2PNode, ids flow.IdentityList) { diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index 6fa4e198aa7..0ae0d473111 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -243,7 +243,7 @@ func TestConnectionGater_Disallow_Integration(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) - p2pfixtures.LetNodesDiscoverEachOther(t, ctx, nodes, ids) + p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) // ensures that all nodes are connected to each other, and they can exchange messages over the pubsub and unicast. ensureCommunicationOverAllProtocols(t, ctx, sporkId, nodes, inbounds) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index f6545d0bd91..8b1899c2788 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -411,7 +411,7 @@ func testUnicastOverStream(t *testing.T, opts ...p2ptest.NodeFixtureParameterOpt p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) - p2pfixtures.LetNodesDiscoverEachOther(t, ctx, nodes, ids) + p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) p2pfixtures.EnsureMessageExchangeOverUnicast( t, @@ -454,7 +454,7 @@ func TestUnicastOverStream_Fallback(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) - p2pfixtures.LetNodesDiscoverEachOther(t, ctx, nodes, ids) + p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) p2pfixtures.EnsureMessageExchangeOverUnicast(t, ctx, nodes, []chan string{inbound1, inbound2}, p2pfixtures.LongStringMessageFactoryFixture(t)) } diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index e9af19fe7c7..9953380f470 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -86,8 +86,8 @@ func TestFullGossipSubConnectivity(t *testing.T) { // creates a topology as follows: // groupOneNodes <--> accessNodeGroup <--> groupTwoNodes - p2pfixtures.LetNodesDiscoverEachOther(t, ctx, append(groupOneNodes, accessNodeGroup...), append(groupOneIds, accessNodeIds...)) - p2pfixtures.LetNodesDiscoverEachOther(t, ctx, append(groupTwoNodes, accessNodeGroup...), append(groupTwoIds, accessNodeIds...)) + p2ptest.LetNodesDiscoverEachOther(t, ctx, append(groupOneNodes, accessNodeGroup...), append(groupOneIds, accessNodeIds...)) + p2ptest.LetNodesDiscoverEachOther(t, ctx, append(groupTwoNodes, accessNodeGroup...), append(groupTwoIds, accessNodeIds...)) // checks end-to-end message delivery works // each node sends a distinct message to all and checks that all nodes receive it. @@ -199,7 +199,7 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS } // let nodes reside on a full topology, hence no partition is caused by the topology. - p2pfixtures.LetNodesDiscoverEachOther(t, ctx, allNodes, allIds) + p2ptest.LetNodesDiscoverEachOther(t, ctx, allNodes, allIds) proposalMsg := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channels.PushBlocks) require.NoError(t, con1Node.Publish(ctx, blockTopic, proposalMsg)) diff --git a/network/p2p/scoring/subscription_validator_test.go b/network/p2p/scoring/subscription_validator_test.go index 75f9321914d..45e51503022 100644 --- a/network/p2p/scoring/subscription_validator_test.go +++ b/network/p2p/scoring/subscription_validator_test.go @@ -132,8 +132,8 @@ func TestSubscriptionValidator_InvalidSubscriptions(t *testing.T) { for _, role := range flow.Roles() { peerId := p2pfixtures.PeerIdFixture(t) unauthorizedChannels := channels.Channels(). // all channels - ExcludeChannels(channels.ChannelsByRole(role)). // excluding the channels for the role - ExcludePattern(regexp.MustCompile("^(test).*")) // excluding the test channels. + ExcludeChannels(channels.ChannelsByRole(role)). // excluding the channels for the role + ExcludePattern(regexp.MustCompile("^(test).*")) // excluding the test channels. sporkID := unittest.IdentifierFixture() unauthorizedTopics := make([]string, 0, len(unauthorizedChannels)) for _, channel := range unauthorizedChannels { @@ -215,7 +215,7 @@ func TestSubscriptionValidator_Integration(t *testing.T) { topicValidator := flowpubsub.TopicValidator(unittest.Logger(), unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter()) // wait for the subscriptions to be established - p2pfixtures.LetNodesDiscoverEachOther(t, ctx, nodes, ids) + p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) // consensus node subscribes to the block topic. conSub, err := conNode.Subscribe(blockTopic, topicValidator) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 2d63cc9e997..9f1be2f7428 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -28,6 +28,7 @@ import ( "github.com/onflow/flow-go/network/p2p/p2pbuilder" "github.com/onflow/flow-go/network/p2p/scoring" "github.com/onflow/flow-go/network/p2p/unicast" + "github.com/onflow/flow-go/network/p2p/utils" "github.com/onflow/flow-go/utils/logging" "github.com/onflow/flow-go/utils/unittest" ) @@ -282,3 +283,17 @@ func StreamHandlerFixture(t *testing.T) (func(s network.Stream), chan string) { ch <- str }, ch } + +// LetNodesDiscoverEachOther connects all nodes to each other on the pubsub mesh. +func LetNodesDiscoverEachOther(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode, ids flow.IdentityList) { + for _, node := range nodes { + for i, other := range nodes { + if node == other { + continue + } + otherPInfo, err := utils.PeerAddressInfo(*ids[i]) + require.NoError(t, err) + require.NoError(t, node.AddPeer(ctx, otherPInfo)) + } + } +} From 555a7b8531fe35736a4aa64179dabaf6e0469e18 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 10 Nov 2022 16:05:00 -0800 Subject: [PATCH 010/138] tidy --- insecure/go.mod | 2 +- insecure/go.sum | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/insecure/go.mod b/insecure/go.mod index 15341746bf4..8a9660afe5b 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -8,12 +8,12 @@ require ( github.com/ipfs/go-datastore v0.6.0 github.com/libp2p/go-libp2p v0.23.3 github.com/libp2p/go-libp2p-pubsub v0.8.1-0.20220908052023-8866ca88a105 - github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/onflow/flow-go v0.0.0-00010101000000-000000000000 github.com/onflow/flow-go/crypto v0.24.4 github.com/rs/zerolog v1.28.0 github.com/stretchr/testify v1.8.0 + github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 google.golang.org/grpc v1.50.1 google.golang.org/protobuf v1.28.1 ) diff --git a/insecure/go.sum b/insecure/go.sum index 2fc4d21fce8..caa69b171f3 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -872,8 +872,6 @@ github.com/libp2p/go-libp2p-peerstore v0.8.0 h1:bzTG693TA1Ju/zKmUCQzDLSqiJnyRFVw github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= github.com/libp2p/go-libp2p-pubsub v0.8.1-0.20220908052023-8866ca88a105 h1:EAAgUl0EnSk4Z/ct1xsHTYSy9JYDdcTazrC6phSdlIY= github.com/libp2p/go-libp2p-pubsub v0.8.1-0.20220908052023-8866ca88a105/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= -github.com/libp2p/go-libp2p-pubsub v0.8.1 h1:hSw09NauFUaA0FLgQPBJp6QOy0a2n+HSkb8IeOx8OnY= -github.com/libp2p/go-libp2p-pubsub v0.8.1/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= @@ -1491,10 +1489,6 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11 h1:WYWfp6ydU0v6ev+amaIRIfxJEoEnf9HP0o523qlg/1o= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11/go.mod h1:GwZ5VBU63KlM8wZsnVvSGDIMeGMOO9Cb3uLXDP8WFYM= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110003401-b897e22c3c7c h1:Za50fWzRA8RfwWvy434k+HpTFUuL5Tlx3SvhoEcv1CA= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110003401-b897e22c3c7c/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 h1:c6Ns5fDv1quWqDgIVw+hRRsLfCVTz7tqhSwRP5wjRRo= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 6296c6b6b7782b9a69f308d03914e545b73c8426 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 10 Nov 2022 16:19:15 -0800 Subject: [PATCH 011/138] adds gossipsub router fixture --- .../internal/mockGossipSubRouter.go | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 insecure/corruptlibp2p/internal/mockGossipSubRouter.go diff --git a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go b/insecure/corruptlibp2p/internal/mockGossipSubRouter.go new file mode 100644 index 00000000000..342545d1ae3 --- /dev/null +++ b/insecure/corruptlibp2p/internal/mockGossipSubRouter.go @@ -0,0 +1,95 @@ +package internal + +import ( + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" + pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" +) + +type GossipSubRouterFixture struct { + router *pubsub.GossipSubRouter +} + +var _ pubsub.GossipPubSubRouter = (*GossipSubRouterFixture)(nil) + +func NewGossipSubRouterFixture() *GossipSubRouterFixture { + return &GossipSubRouterFixture{ + router: pubsub.DefaultGossipSubRouter(), + } +} + +func (m *GossipSubRouterFixture) Protocols() []protocol.ID { + return m.router.Protocols() +} + +func (m *GossipSubRouterFixture) Attach(sub *pubsub.PubSub) { + m.router.Attach(sub) +} + +func (m *GossipSubRouterFixture) AddPeer(pid peer.ID, protocolId protocol.ID) { + m.AddPeer(pid, protocolId) +} + +func (m *GossipSubRouterFixture) RemovePeer(pid peer.ID) { + m.RemovePeer(pid) +} + +func (m *GossipSubRouterFixture) EnoughPeers(topic string, suggested int) bool { + return m.router.EnoughPeers(topic, suggested) +} + +func (m *GossipSubRouterFixture) AcceptFrom(pid peer.ID) pubsub.AcceptStatus { + return m.router.AcceptFrom(pid) +} + +func (m *GossipSubRouterFixture) HandleRPC(rpc *pubsub.RPC) { + m.router.HandleRPC(rpc) +} + +func (m *GossipSubRouterFixture) Publish(message *pubsub.Message) { + m.router.Publish(message) +} + +func (m *GossipSubRouterFixture) Join(topic string) { + m.router.Join(topic) +} + +func (m *GossipSubRouterFixture) Leave(topic string) { + m.router.Leave(topic) +} + +func (m *GossipSubRouterFixture) SetPeerScore(score *pubsub.PeerScore) { + m.router.SetPeerScore(score) +} + +func (m *GossipSubRouterFixture) GetPeerScore() *pubsub.PeerScore { + return m.router.GetPeerScore() +} + +func (m *GossipSubRouterFixture) SetPeerScoreThresholds(thresholds *pubsub.PeerScoreThresholds) { + m.router.SetPeerScoreThresholds(thresholds) +} + +func (m *GossipSubRouterFixture) SetGossipTracer(tracer *pubsub.GossipTracer) { + m.router.SetGossipTracer(tracer) +} + +func (m *GossipSubRouterFixture) GetGossipTracer() *pubsub.GossipTracer { + return m.router.GetGossipTracer() +} + +func (m *GossipSubRouterFixture) GetTagTracer() *pubsub.TagTracer { + return m.router.GetTagTracer() +} + +func (m *GossipSubRouterFixture) SetDirectPeers(direct map[peer.ID]struct{}) { + m.router.SetDirectPeers(direct) +} + +func (m *GossipSubRouterFixture) SetPeerGater(gater *pubsub.PeerGater) { + m.router.SetPeerGater(gater) +} + +func (m *GossipSubRouterFixture) GetPeerGater() *pubsub.PeerGater { + return m.router.GetPeerGater() +} From 993cf02ad6be044767c95334dee1b925ed2ef77e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 10 Nov 2022 18:01:10 -0800 Subject: [PATCH 012/138] adds pubsub factory to libp2p node builder --- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 30 ++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 7bf1112373f..989722e5835 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -44,6 +44,7 @@ import ( // LibP2PFactoryFunc is a factory function type for generating libp2p Node instances. type LibP2PFactoryFunc func() (p2p.LibP2PNode, error) +type GossipSubFactoryFuc func(context.Context, host.Host, ...pubsub.Option) (*pubsub.PubSub, error) // DefaultLibP2PNodeFactory returns a LibP2PFactoryFunc which generates the libp2p host initialized with the // default options for the host, the pubsub and the ping service. @@ -87,6 +88,7 @@ type NodeBuilder interface { SetPeerManagerOptions(connectionPruning bool, updateInterval time.Duration) NodeBuilder EnableGossipSubPeerScoring(provider module.IdentityProvider, ops ...scoring.PeerScoreParamsOption) NodeBuilder SetCreateNode(CreateNodeFunc) NodeBuilder + SetGossipSubFactory(f GossipSubFactoryFuc) NodeBuilder Build() (p2p.LibP2PNode, error) } @@ -101,6 +103,7 @@ type LibP2PNodeBuilder struct { connManager connmgr.ConnManager connGater connmgr.ConnectionGater idProvider module.IdentityProvider + gossipSubFactory GossipSubFactoryFuc gossipSubPeerScoring bool // whether to enable gossipsub peer scoring routingFactory func(context.Context, host.Host) (routing.Routing, error) peerManagerEnablePruning bool @@ -180,6 +183,11 @@ func (builder *LibP2PNodeBuilder) SetCreateNode(f CreateNodeFunc) NodeBuilder { return builder } +func (builder *LibP2PNodeBuilder) SetGossipSubFactory(f GossipSubFactoryFuc) NodeBuilder { + builder.gossipSubFactory = f + return builder +} + // Build creates a new libp2p node using the configured options. func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { if builder.routingFactory == nil { @@ -264,15 +272,25 @@ func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { psOpts = append(psOpts, scoreOpt.BuildFlowPubSubScoreOption()) } - pubSub, err := pubsub.NewGossipSub(ctx, h, psOpts...) - if err != nil { - ctx.Throw(fmt.Errorf("could not create gossipsub: %w", err)) + var ps *pubsub.PubSub + if builder.gossipSubFactory != nil { + // builds GossipSub with the given factory + ps, err = builder.gossipSubFactory(ctx, h, psOpts...) + if err != nil { + ctx.Throw(fmt.Errorf("could not create gossipsub using injected factory: %w", err)) + } + } else { + // builds GossipSub with the default factory + ps, err = pubsub.NewGossipSub(ctx, h, psOpts...) + if err != nil { + ctx.Throw(fmt.Errorf("could not create gossipsub: %w", err)) + } } + if scoreOpt != nil { - scoreOpt.SetSubscriptionProvider(scoring.NewSubscriptionProvider(builder.logger, pubSub)) + scoreOpt.SetSubscriptionProvider(scoring.NewSubscriptionProvider(builder.logger, ps)) } - - node.SetPubSub(pubSub) + node.SetPubSub(ps) ready() <-ctx.Done() From 38c29bcdea5dc56b0793b3d3f41124cd1fd18e23 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 14 Nov 2022 15:53:09 -0500 Subject: [PATCH 013/138] added `TestSpam` skeleton test --- .../internal/mockGossipSubRouter.go | 10 +-- insecure/corruptlibp2p/libp2p_test.go | 65 +++++++++++++++++++ 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go b/insecure/corruptlibp2p/internal/mockGossipSubRouter.go index 342545d1ae3..da636febe0b 100644 --- a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go +++ b/insecure/corruptlibp2p/internal/mockGossipSubRouter.go @@ -12,11 +12,11 @@ type GossipSubRouterFixture struct { var _ pubsub.GossipPubSubRouter = (*GossipSubRouterFixture)(nil) -func NewGossipSubRouterFixture() *GossipSubRouterFixture { - return &GossipSubRouterFixture{ - router: pubsub.DefaultGossipSubRouter(), - } -} +//func NewGossipSubRouterFixture() *GossipSubRouterFixture { +// return &GossipSubRouterFixture{ +// router: pubsub.DefaultGossipSubRouter(), +// } +//} func (m *GossipSubRouterFixture) Protocols() []protocol.ID { return m.router.Protocols() diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index a5ae4024eee..f7100a1f531 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -1,7 +1,14 @@ package corruptlibp2p import ( + "context" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" + "github.com/onflow/flow-go/utils/unittest" "testing" + "time" "github.com/stretchr/testify/require" ) @@ -11,3 +18,61 @@ func TestGetGossipSubParams(t *testing.T) { require.Equal(t, gossipSubParams, gossipSubParams) } + +func TestSpam(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + sporkId := unittest.IdentifierFixture() + defer cancel() + + count := 5 + nodes := make([]p2p.LibP2PNode, 0, 5) + ids := flow.IdentityList{} + inbounds := make([]chan string, 0, 5) + + //disallowedList := map[*flow.Identity]struct{}{} + + for i := 0; i < count; i++ { + handler, inbound := p2ptest.StreamHandlerFixture(t) + node, id := p2ptest.NodeFixture( + t, + sporkId, + t.Name(), + p2ptest.WithRole(flow.RoleConsensus), + p2ptest.WithDefaultStreamHandler(handler), + // enable peer manager, with a 1-second refresh rate, and connection pruning enabled. + //p2ptest.WithPeerManagerEnabled(true, 1*time.Second, func() peer.IDSlice { + // list := make(peer.IDSlice, 0) + // for _, id := range ids { + // if _, ok := disallowedList[id]; ok { + // continue + // } + // + // pid, err := unittest.PeerIDFromFlowID(id) + // require.NoError(t, err) + // + // list = append(list, pid) + // } + // return list + //}), + //p2ptest.WithConnectionGater(testutils.NewConnectionGater(func(pid peer.ID) error { + // for id := range disallowedList { + // bid, err := unittest.PeerIDFromFlowID(id) + // require.NoError(t, err) + // if bid == pid { + // return fmt.Errorf("disallow-listed") + // } + // } + // + // return nil + //})), + ) + + nodes = append(nodes, node) + ids = append(ids, &id) + inbounds = append(inbounds, inbound) + } + + p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) + defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) +} From 1ac5f1c98136aea403d204009c6b0c119f21893d Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 14 Nov 2022 16:27:08 -0500 Subject: [PATCH 014/138] `TestSpam` uses spammer with `NewGossipSubRouterFixture` --- .../internal/mockGossipSubRouter.go | 46 +++++++++---------- insecure/corruptlibp2p/libp2p_test.go | 41 ++++++----------- 2 files changed, 36 insertions(+), 51 deletions(-) diff --git a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go b/insecure/corruptlibp2p/internal/mockGossipSubRouter.go index da636febe0b..9c7acbcc3bf 100644 --- a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go +++ b/insecure/corruptlibp2p/internal/mockGossipSubRouter.go @@ -7,23 +7,23 @@ import ( ) type GossipSubRouterFixture struct { - router *pubsub.GossipSubRouter + Router *pubsub.GossipSubRouter } var _ pubsub.GossipPubSubRouter = (*GossipSubRouterFixture)(nil) -//func NewGossipSubRouterFixture() *GossipSubRouterFixture { -// return &GossipSubRouterFixture{ -// router: pubsub.DefaultGossipSubRouter(), -// } -//} +func NewGossipSubRouterFixture() *GossipSubRouterFixture { + return &GossipSubRouterFixture{ + Router: pubsub.DefaultGossipSubRouter(), + } +} func (m *GossipSubRouterFixture) Protocols() []protocol.ID { - return m.router.Protocols() + return m.Router.Protocols() } func (m *GossipSubRouterFixture) Attach(sub *pubsub.PubSub) { - m.router.Attach(sub) + m.Router.Attach(sub) } func (m *GossipSubRouterFixture) AddPeer(pid peer.ID, protocolId protocol.ID) { @@ -35,61 +35,61 @@ func (m *GossipSubRouterFixture) RemovePeer(pid peer.ID) { } func (m *GossipSubRouterFixture) EnoughPeers(topic string, suggested int) bool { - return m.router.EnoughPeers(topic, suggested) + return m.Router.EnoughPeers(topic, suggested) } func (m *GossipSubRouterFixture) AcceptFrom(pid peer.ID) pubsub.AcceptStatus { - return m.router.AcceptFrom(pid) + return m.Router.AcceptFrom(pid) } func (m *GossipSubRouterFixture) HandleRPC(rpc *pubsub.RPC) { - m.router.HandleRPC(rpc) + m.Router.HandleRPC(rpc) } func (m *GossipSubRouterFixture) Publish(message *pubsub.Message) { - m.router.Publish(message) + m.Router.Publish(message) } func (m *GossipSubRouterFixture) Join(topic string) { - m.router.Join(topic) + m.Router.Join(topic) } func (m *GossipSubRouterFixture) Leave(topic string) { - m.router.Leave(topic) + m.Router.Leave(topic) } func (m *GossipSubRouterFixture) SetPeerScore(score *pubsub.PeerScore) { - m.router.SetPeerScore(score) + m.Router.SetPeerScore(score) } func (m *GossipSubRouterFixture) GetPeerScore() *pubsub.PeerScore { - return m.router.GetPeerScore() + return m.Router.GetPeerScore() } func (m *GossipSubRouterFixture) SetPeerScoreThresholds(thresholds *pubsub.PeerScoreThresholds) { - m.router.SetPeerScoreThresholds(thresholds) + m.Router.SetPeerScoreThresholds(thresholds) } func (m *GossipSubRouterFixture) SetGossipTracer(tracer *pubsub.GossipTracer) { - m.router.SetGossipTracer(tracer) + m.Router.SetGossipTracer(tracer) } func (m *GossipSubRouterFixture) GetGossipTracer() *pubsub.GossipTracer { - return m.router.GetGossipTracer() + return m.Router.GetGossipTracer() } func (m *GossipSubRouterFixture) GetTagTracer() *pubsub.TagTracer { - return m.router.GetTagTracer() + return m.Router.GetTagTracer() } func (m *GossipSubRouterFixture) SetDirectPeers(direct map[peer.ID]struct{}) { - m.router.SetDirectPeers(direct) + m.Router.SetDirectPeers(direct) } func (m *GossipSubRouterFixture) SetPeerGater(gater *pubsub.PeerGater) { - m.router.SetPeerGater(gater) + m.Router.SetPeerGater(gater) } func (m *GossipSubRouterFixture) GetPeerGater() *pubsub.PeerGater { - return m.router.GetPeerGater() + return m.Router.GetPeerGater() } diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index f7100a1f531..279f1b35f5c 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -2,6 +2,8 @@ package corruptlibp2p import ( "context" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/onflow/flow-go/insecure/corruptlibp2p/internal" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network/p2p" @@ -29,8 +31,7 @@ func TestSpam(t *testing.T) { nodes := make([]p2p.LibP2PNode, 0, 5) ids := flow.IdentityList{} inbounds := make([]chan string, 0, 5) - - //disallowedList := map[*flow.Identity]struct{}{} + peerIds := make([]peer.ID, 5) for i := 0; i < count; i++ { handler, inbound := p2ptest.StreamHandlerFixture(t) @@ -40,39 +41,23 @@ func TestSpam(t *testing.T) { t.Name(), p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithDefaultStreamHandler(handler), - // enable peer manager, with a 1-second refresh rate, and connection pruning enabled. - //p2ptest.WithPeerManagerEnabled(true, 1*time.Second, func() peer.IDSlice { - // list := make(peer.IDSlice, 0) - // for _, id := range ids { - // if _, ok := disallowedList[id]; ok { - // continue - // } - // - // pid, err := unittest.PeerIDFromFlowID(id) - // require.NoError(t, err) - // - // list = append(list, pid) - // } - // return list - //}), - //p2ptest.WithConnectionGater(testutils.NewConnectionGater(func(pid peer.ID) error { - // for id := range disallowedList { - // bid, err := unittest.PeerIDFromFlowID(id) - // require.NoError(t, err) - // if bid == pid { - // return fmt.Errorf("disallow-listed") - // } - // } - // - // return nil - //})), ) + peerId, err := unittest.PeerIDFromFlowID(&id) + require.NoError(t, err) nodes = append(nodes, node) ids = append(ids, &id) inbounds = append(inbounds, inbound) + peerIds = append(peerIds, peerId) } p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + + // create new spammer + gsr := internal.NewGossipSubRouterFixture() + spammer := NewSpammerGossipSub(gsr.Router) + + // start spamming the first peer + spammer.SpamIHave(peerIds[0], 10, 1) } From 4ef59ad7e6e936651fb5d687055b3344aa7f4223 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:54:18 -0800 Subject: [PATCH 015/138] adds pubsub adapter --- insecure/corruptlibp2p/internal/topic.go | 1 + insecure/corruptlibp2p/pubsubAdapter.go | 1 + network/p2p/p2pnode/gossipSubAdapter.go | 1 + network/p2p/p2pnode/gossipSubTopic.go | 1 + network/p2p/pubsub.go | 31 ++++++++++++++++++++++++ 5 files changed, 35 insertions(+) create mode 100644 insecure/corruptlibp2p/internal/topic.go create mode 100644 insecure/corruptlibp2p/pubsubAdapter.go create mode 100644 network/p2p/p2pnode/gossipSubAdapter.go create mode 100644 network/p2p/p2pnode/gossipSubTopic.go create mode 100644 network/p2p/pubsub.go diff --git a/insecure/corruptlibp2p/internal/topic.go b/insecure/corruptlibp2p/internal/topic.go new file mode 100644 index 00000000000..5bf0569ce8c --- /dev/null +++ b/insecure/corruptlibp2p/internal/topic.go @@ -0,0 +1 @@ +package internal diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go new file mode 100644 index 00000000000..3804f232e90 --- /dev/null +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -0,0 +1 @@ +package corruptlibp2p diff --git a/network/p2p/p2pnode/gossipSubAdapter.go b/network/p2p/p2pnode/gossipSubAdapter.go new file mode 100644 index 00000000000..3abbbf1cd60 --- /dev/null +++ b/network/p2p/p2pnode/gossipSubAdapter.go @@ -0,0 +1 @@ +package p2pnode diff --git a/network/p2p/p2pnode/gossipSubTopic.go b/network/p2p/p2pnode/gossipSubTopic.go new file mode 100644 index 00000000000..3abbbf1cd60 --- /dev/null +++ b/network/p2p/p2pnode/gossipSubTopic.go @@ -0,0 +1 @@ +package p2pnode diff --git a/network/p2p/pubsub.go b/network/p2p/pubsub.go new file mode 100644 index 00000000000..aad29f08bb0 --- /dev/null +++ b/network/p2p/pubsub.go @@ -0,0 +1,31 @@ +package p2p + +import ( + "context" + + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p/core/peer" +) + +type PubSubAdapter interface { + RegisterTopicValidator(topic string, val interface{}) error + UnregisterTopicValidator(topic string) error + Join(topic string) (Topic, error) + GetTopics() []string + ListPeers(topic string) []peer.ID +} + +type Topic interface { + String() string + Close() error + Publish(context.Context, []byte) error + Subscribe() (Subscription, error) +} + +type Subscription interface { + Cancel() + Next(context.Context) (*pubsub.Message, error) +} + +type Message struct { +} From 1a50e472d4fbeec71af0ce7685f08ed5a889d943 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:54:45 -0800 Subject: [PATCH 016/138] adds type assertion fixture --- network/internal/p2pfixtures/fixtures.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 1e9667389fa..17d202a67d6 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -287,9 +287,8 @@ func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p. subs := make([]*pubsub.Subscription, len(nodes)) slashingViolationsConsumer := unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector()) - var err error for i, node := range nodes { - subs[i], err = node.Subscribe( + ps, err := node.Subscribe( topic, validator.TopicValidator( unittest.Logger(), @@ -297,6 +296,7 @@ func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p. slashingViolationsConsumer, unittest.AllowAllPeerFilter())) require.NoError(t, err) + subs[i] = MustBePubSubSubscription(t, ps) } // let subscriptions propagate @@ -336,8 +336,9 @@ func EnsureNoPubsubMessageExchange(t *testing.T, ctx context.Context, from []p2p } for i, node := range to { - subs[i], err = node.Subscribe(topic, tv) + s, err := node.Subscribe(topic, tv) require.NoError(t, err) + subs[i] = MustBePubSubSubscription(t, s) } // let subscriptions propagate @@ -473,3 +474,9 @@ func LongStringMessageFactoryFixture(t *testing.T) func() string { return fmt.Sprintf("%s %d \n", msg, time.Now().UnixNano()) // add timestamp to make sure we don't send the same message twice } } + +func MustBePubSubSubscription(t *testing.T, subscription p2p.Subscription) *pubsub.Subscription { + ps, ok := subscription.(*pubsub.Subscription) + require.True(t, ok) + return ps +} From 171a2beaa93100d49e9913778a642f41b810bd3d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:55:14 -0800 Subject: [PATCH 017/138] fixes tests --- network/p2p/dht/dht_test.go | 4 ++- network/p2p/scoring/app_score_test.go | 15 ++++---- .../scoring/subscription_validator_test.go | 15 +++++--- .../subscription/subscription_filter_test.go | 4 +-- network/p2p/test/sporking_test.go | 10 +++--- network/p2p/test/topic_validator_test.go | 34 +++++++++---------- 6 files changed, 47 insertions(+), 35 deletions(-) diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index 9a05b0d54a2..bc3320f69b5 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -16,6 +16,7 @@ import ( "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p/dht" p2ptest "github.com/onflow/flow-go/network/p2p/test" @@ -160,8 +161,9 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { topicValidator := flowpubsub.TopicValidator(logger, codec, unittest.NetworkSlashingViolationsConsumer(logger, metrics.NewNoopCollector()), unittest.AllowAllPeerFilter()) for _, n := range nodes { - s, err := n.Subscribe(topic, topicValidator) + ps, err := n.Subscribe(topic, topicValidator) require.NoError(t, err) + s := p2pfixtures.MustBePubSubSubscription(t, ps) go func(s *pubsub.Subscription, nodeID peer.ID) { msg, err := s.Next(ctx) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 9953380f470..a681c021409 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -66,21 +66,23 @@ func TestFullGossipSubConnectivity(t *testing.T) { // all nodes subscribe to block topic (common topic among all roles) // group one groupOneSubs := make([]*pubsub.Subscription, len(groupOneNodes)) - var err error for i, node := range groupOneNodes { - groupOneSubs[i], err = node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) + sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) + groupOneSubs[i] = p2pfixtures.MustBePubSubSubscription(t, sub) require.NoError(t, err) } // group two groupTwoSubs := make([]*pubsub.Subscription, len(groupTwoNodes)) for i, node := range groupTwoNodes { - groupTwoSubs[i], err = node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) + sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) + groupTwoSubs[i] = p2pfixtures.MustBePubSubSubscription(t, sub) require.NoError(t, err) } // access node group accessNodeSubs := make([]*pubsub.Subscription, len(accessNodeGroup)) for i, node := range accessNodeGroup { - accessNodeSubs[i], err = node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) + sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) + accessNodeSubs[i] = p2pfixtures.MustBePubSubSubscription(t, sub) require.NoError(t, err) } @@ -194,7 +196,8 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS // access node group accessNodeSubs := make([]*pubsub.Subscription, len(accessNodeGroup)) for i, node := range accessNodeGroup { - accessNodeSubs[i], err = node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) + sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) + accessNodeSubs[i] = p2pfixtures.MustBePubSubSubscription(t, sub) require.NoError(t, err) } @@ -213,7 +216,7 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS // If honest peer scoring is enabled, then con1Node and con2Node are certainly in the same mesh, and hence the message is delivered. ctx1s, cancel1s := context.WithTimeout(ctx, 1*time.Second) defer cancel1s() - return p2pfixtures.HasSubReceivedMessage(t, ctx1s, proposalMsg, con2Sub) + return p2pfixtures.HasSubReceivedMessage(t, ctx1s, proposalMsg, p2pfixtures.MustBePubSubSubscription(t, con2Sub)) } // maliciousAppSpecificScore returns a malicious app specific score function that rewards the malicious node and diff --git a/network/p2p/scoring/subscription_validator_test.go b/network/p2p/scoring/subscription_validator_test.go index 45e51503022..2e1fa3f957b 100644 --- a/network/p2p/scoring/subscription_validator_test.go +++ b/network/p2p/scoring/subscription_validator_test.go @@ -244,7 +244,10 @@ func TestSubscriptionValidator_Integration(t *testing.T) { // checks that the message is received by all nodes. ctx1s, cancel1s := context.WithTimeout(ctx, 1*time.Second) defer cancel1s() - p2pfixtures.SubsMustReceiveMessage(t, ctx1s, proposalMsg, []*pubsub.Subscription{conSub, ver1SubBlocks, ver2SubBlocks}) + p2pfixtures.SubsMustReceiveMessage(t, ctx1s, proposalMsg, []*pubsub.Subscription{ + p2pfixtures.MustBePubSubSubscription(t, conSub), + p2pfixtures.MustBePubSubSubscription(t, ver1SubBlocks), + p2pfixtures.MustBePubSubSubscription(t, ver2SubBlocks)}) // now consensus node is doing something very bad! // it is subscribing to a channel that it is not supposed to subscribe to. @@ -263,7 +266,9 @@ func TestSubscriptionValidator_Integration(t *testing.T) { ctx5s, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - p2pfixtures.SubsMustNeverReceiveAnyMessage(t, ctx5s, []*pubsub.Subscription{ver1SubBlocks, ver2SubBlocks}) + p2pfixtures.SubsMustNeverReceiveAnyMessage(t, ctx5s, []*pubsub.Subscription{ + p2pfixtures.MustBePubSubSubscription(t, ver1SubBlocks), + p2pfixtures.MustBePubSubSubscription(t, ver2SubBlocks)}) // moreover, a verification node publishing a message to the request chunk topic should not reach consensus node. // however, both verification nodes should receive the message. @@ -275,9 +280,11 @@ func TestSubscriptionValidator_Integration(t *testing.T) { ctx1s, cancel1s = context.WithTimeout(ctx, 1*time.Second) defer cancel1s() - p2pfixtures.SubsMustReceiveMessage(t, ctx1s, chunkDataPackRequestMsg, []*pubsub.Subscription{ver1SubChunks, ver2SubChunks}) + p2pfixtures.SubsMustReceiveMessage(t, ctx1s, chunkDataPackRequestMsg, []*pubsub.Subscription{ + p2pfixtures.MustBePubSubSubscription(t, ver1SubChunks), + p2pfixtures.MustBePubSubSubscription(t, ver2SubChunks)}) ctx5s, cancel5s = context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - p2pfixtures.SubsMustNeverReceiveAnyMessage(t, ctx5s, []*pubsub.Subscription{conSubChunks}) + p2pfixtures.SubsMustNeverReceiveAnyMessage(t, ctx5s, []*pubsub.Subscription{p2pfixtures.MustBePubSubSubscription(t, conSubChunks)}) } diff --git a/network/p2p/subscription/subscription_filter_test.go b/network/p2p/subscription/subscription_filter_test.go index 98e1c2a2aa3..d419b60ba18 100644 --- a/network/p2p/subscription/subscription_filter_test.go +++ b/network/p2p/subscription/subscription_filter_test.go @@ -99,10 +99,10 @@ func TestFilterSubscribe(t *testing.T) { } // publish a message from node 1 and check that only node2 receives - testPublish(&wg, node1, sub2) + testPublish(&wg, node1, p2pfixtures.MustBePubSubSubscription(t, sub2)) // publish a message from node 2 and check that only node1 receives - testPublish(&wg, node2, sub1) + testPublish(&wg, node2, p2pfixtures.MustBePubSubSubscription(t, sub1)) unittest.RequireReturnsBefore(t, wg.Wait, 1*time.Second, "timeout performing publish test") } diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index 18fbfd479a7..763f7914248 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - "github.com/onflow/flow-go/network/p2p" - p2ptest "github.com/onflow/flow-go/network/p2p/test" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" + "github.com/onflow/flow-go/network/p2p/utils" "github.com/onflow/flow-go/module/irrecoverable" @@ -221,7 +221,7 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { time.Sleep(time.Second) // assert that node 1 can successfully send a message to node 2 via PubSub - testOneToKMessagingSucceeds(ctx, t, node1, sub2, topicBeforeSpork) + testOneToKMessagingSucceeds(ctx, t, node1, p2pfixtures.MustBePubSubSubscription(t, sub2), topicBeforeSpork) // new root id after spork rootIDAfterSpork := unittest.IdentifierFixture() @@ -238,7 +238,7 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { require.NoError(t, err) // assert that node 1 can no longer send a message to node 2 via PubSub - testOneToKMessagingFails(ctx, t, node1, sub2, topicAfterSpork) + testOneToKMessagingFails(ctx, t, node1, p2pfixtures.MustBePubSubSubscription(t, sub2), topicAfterSpork) } func testOneToOneMessagingSucceeds(t *testing.T, sourceNode p2p.LibP2PNode, peerInfo peer.AddrInfo) { diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index 0bf96f1f286..9ac07d124b7 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -101,7 +101,7 @@ func TestTopicValidator_Unstaked(t *testing.T) { // sn1 should not receive message from sn2 because sn2 is unstaked timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) defer cancel1s() - p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) + p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, p2pfixtures.MustBePubSubSubscription(t, sub1)) // ensure the correct error is contained in the logged error require.Contains(t, hook.Logs(), "filtering message from un-allowed peer") @@ -158,10 +158,10 @@ func TestTopicValidator_PublicChannel(t *testing.T) { defer cancel1s() // sn1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub1) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub1)) // sn2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub2) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub2)) unittest.RequireReturnsBefore(t, wg.Wait, 5*time.Second, "could not receive message on time") } @@ -349,13 +349,13 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { require.NoError(t, err) // sn1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub1) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub1)) // sn2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub2) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub2)) // an1 also gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub3) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub3)) timedCtx, cancel2s := context.WithTimeout(ctx, 2*time.Second) defer cancel2s() @@ -368,19 +368,19 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { require.NoError(t, err) // an1 receives its own message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data2, sub3) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data2, p2pfixtures.MustBePubSubSubscription(t, sub3)) var wg sync.WaitGroup // sn1 does NOT receive the message due to the topic validator timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) defer cancel1s() - p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) + p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, p2pfixtures.MustBePubSubSubscription(t, sub1)) // sn2 also does not receive the message via gossip from the sn1 (event after the 1 second hearbeat) timedCtx, cancel2s = context.WithTimeout(ctx, 2*time.Second) defer cancel2s() - p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub2) + p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, p2pfixtures.MustBePubSubSubscription(t, sub2)) unittest.RequireReturnsBefore(t, wg.Wait, 5*time.Second, "could not receive message on time") @@ -455,7 +455,7 @@ func TestAuthorizedSenderValidator_InvalidMsg(t *testing.T) { // sn1 should not receive message from sn2 timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) defer cancel1s() - p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) + p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, p2pfixtures.MustBePubSubSubscription(t, sub1)) // ensure the correct error is contained in the logged error require.Contains(t, hook.Logs(), message.ErrUnauthorizedMessageOnChannel.Error()) @@ -533,13 +533,13 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { require.NoError(t, err) // sn1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub1) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub1)) // sn2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub2) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub2)) // an1 also gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub3) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub3)) // "eject" sn2 to ensure messages published by ejected nodes get rejected identity2.Ejected = true @@ -553,7 +553,7 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { // sn1 should not receive rejected message from ejected sn2 timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) defer cancel1s() - p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) + p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, p2pfixtures.MustBePubSubSubscription(t, sub1)) // ensure the correct error is contained in the logged error require.Contains(t, hook.Logs(), validator.ErrSenderEjected.Error()) @@ -625,11 +625,11 @@ func TestAuthorizedSenderValidator_ClusterChannel(t *testing.T) { require.NoError(t, err) // ln1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, sub1) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, p2pfixtures.MustBePubSubSubscription(t, sub1)) // ln2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, sub2) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, p2pfixtures.MustBePubSubSubscription(t, sub2)) // ln3 also gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, sub3) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, p2pfixtures.MustBePubSubSubscription(t, sub3)) } From 66ea215e0596bdec17fde0ac36ec79dfa2d3be9e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:55:35 -0800 Subject: [PATCH 018/138] refactors libp2p interface --- network/p2p/libp2pNode.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 3819a003909..3a6e98c8287 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -43,7 +43,7 @@ type LibP2PNode interface { // ListPeers returns list of peer IDs for peers subscribed to the topic. ListPeers(topic string) []peer.ID // Subscribe subscribes the node to the given topic and returns the subscription - Subscribe(topic channels.Topic, topicValidator pubsub.ValidatorEx) (*pubsub.Subscription, error) + Subscribe(topic channels.Topic, topicValidator pubsub.ValidatorEx) (Subscription, error) // UnSubscribe cancels the subscriber and closes the topic. UnSubscribe(topic channels.Topic) error // Publish publishes the given payload on the topic. @@ -68,7 +68,7 @@ type LibP2PNode interface { Routing() routing.Routing // SetPubSub sets the node's pubsub implementation. // SetPubSub may be called at most once. - SetPubSub(ps *pubsub.PubSub) + SetPubSub(ps PubSubAdapter) // SetComponentManager sets the component manager for the node. // SetComponentManager may be called at most once. SetComponentManager(cm *component.ComponentManager) From 1a99697516e93b757fb6052157f64be24aaf5559 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:55:53 -0800 Subject: [PATCH 019/138] implements adapter --- network/p2p/p2pnode/gossipSubAdapter.go | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/network/p2p/p2pnode/gossipSubAdapter.go b/network/p2p/p2pnode/gossipSubAdapter.go index 3abbbf1cd60..ede8e89291f 100644 --- a/network/p2p/p2pnode/gossipSubAdapter.go +++ b/network/p2p/p2pnode/gossipSubAdapter.go @@ -1 +1,38 @@ package p2pnode + +import ( + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p/core/peer" + + "github.com/onflow/flow-go/network/p2p" +) + +type GossipSubAdapter struct { + gossipSub *pubsub.PubSub +} + +var _ p2p.PubSubAdapter = (*GossipSubAdapter)(nil) + +func (g *GossipSubAdapter) RegisterTopicValidator(topic string, val interface{}) error { + return g.gossipSub.RegisterTopicValidator(topic, val, pubsub.WithValidatorInline(true)) +} + +func (g *GossipSubAdapter) UnregisterTopicValidator(topic string) error { + return g.gossipSub.UnregisterTopicValidator(topic) +} + +func (g *GossipSubAdapter) Join(topic string) (p2p.Topic, error) { + t, err := g.gossipSub.Join(topic) + if err != nil { + return nil, err + } + return NewGossipSubTopic(t), nil +} + +func (g *GossipSubAdapter) GetTopics() []string { + return g.gossipSub.GetTopics() +} + +func (g *GossipSubAdapter) ListPeers(topic string) []peer.ID { + return g.gossipSub.ListPeers(topic) +} From 13ed60cac15cb8642329433ca528141e2936b223 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:55:59 -0800 Subject: [PATCH 020/138] implements topic --- network/p2p/p2pnode/gossipSubTopic.go | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/network/p2p/p2pnode/gossipSubTopic.go b/network/p2p/p2pnode/gossipSubTopic.go index 3abbbf1cd60..3f2f031bee8 100644 --- a/network/p2p/p2pnode/gossipSubTopic.go +++ b/network/p2p/p2pnode/gossipSubTopic.go @@ -1 +1,37 @@ package p2pnode + +import ( + "context" + + pubsub "github.com/libp2p/go-libp2p-pubsub" + + "github.com/onflow/flow-go/network/p2p" +) + +type GossipSubTopic struct { + t *pubsub.Topic +} + +var _ p2p.Topic = (*GossipSubTopic)(nil) + +func NewGossipSubTopic(topic *pubsub.Topic) *GossipSubTopic { + return &GossipSubTopic{ + t: topic, + } +} + +func (g *GossipSubTopic) String() string { + return g.t.String() +} + +func (g *GossipSubTopic) Close() error { + return g.t.Close() +} + +func (g *GossipSubTopic) Publish(ctx context.Context, bytes []byte) error { + return g.t.Publish(ctx, bytes) +} + +func (g *GossipSubTopic) Subscribe() (p2p.Subscription, error) { + return g.t.Subscribe() +} From e906029f1e20ad648e518b79f4ba3ebbf2fbb3b9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:56:14 -0800 Subject: [PATCH 021/138] refactors changes on libp2p node --- network/p2p/p2pnode/libp2pNode.go | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index f45f36684f2..066a059287a 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -53,11 +53,11 @@ type Node struct { component.Component sync.Mutex uniMgr *unicast.Manager - host host.Host // reference to the libp2p host (https://godoc.org/github.com/libp2p/go-libp2p/core/host) - pubSub *pubsub.PubSub // reference to the libp2p PubSub component - logger zerolog.Logger // used to provide logging - topics map[channels.Topic]*pubsub.Topic // map of a topic string to an actual topic instance - subs map[channels.Topic]*pubsub.Subscription // map of a topic string to an actual subscription + host host.Host // reference to the libp2p host (https://godoc.org/github.com/libp2p/go-libp2p/core/host) + pubSub p2p.PubSubAdapter + logger zerolog.Logger // used to provide logging + topics map[channels.Topic]p2p.Topic // map of a topic string to an actual topic instance + subs map[channels.Topic]p2p.Subscription // map of a topic string to an actual subscription routing routing.Routing pCache *ProtocolPeerCache peerManager *connection.PeerManager @@ -75,8 +75,8 @@ func NewNode( uniMgr: uniMgr, host: host, logger: logger.With().Str("component", "libp2p-node").Logger(), - topics: make(map[channels.Topic]*pubsub.Topic), - subs: make(map[channels.Topic]*pubsub.Subscription), + topics: make(map[channels.Topic]p2p.Topic), + subs: make(map[channels.Topic]p2p.Subscription), pCache: pCache, peerManager: peerManager, } @@ -220,7 +220,7 @@ func (n *Node) ListPeers(topic string) []peer.ID { // Subscribe subscribes the node to the given topic and returns the subscription // All errors returned from this function can be considered benign. -func (n *Node) Subscribe(topic channels.Topic, topicValidator pubsub.ValidatorEx) (*pubsub.Subscription, error) { +func (n *Node) Subscribe(topic channels.Topic, topicValidator pubsub.ValidatorEx) (p2p.Subscription, error) { n.Lock() defer n.Unlock() @@ -229,9 +229,7 @@ func (n *Node) Subscribe(topic channels.Topic, topicValidator pubsub.ValidatorEx tp, found := n.topics[topic] var err error if !found { - if err := n.pubSub.RegisterTopicValidator( - topic.String(), topicValidator, pubsub.WithValidatorInline(true), - ); err != nil { + if err := n.pubSub.RegisterTopicValidator(topic.String(), topicValidator); err != nil { n.logger.Err(err).Str("topic", topic.String()).Msg("failed to register topic validator, aborting subscription") return nil, fmt.Errorf("failed to register topic validator: %w", err) } @@ -375,7 +373,7 @@ func (n *Node) Routing() routing.Routing { // SetPubSub sets the node's pubsub implementation. // SetPubSub may be called at most once. -func (n *Node) SetPubSub(ps *pubsub.PubSub) { +func (n *Node) SetPubSub(ps p2p.PubSubAdapter) { if n.pubSub != nil { n.logger.Fatal().Msg("pubSub already set") } From a2858443fad4eed627f5776112fa5ad17b043735 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:56:32 -0800 Subject: [PATCH 022/138] refactors libp2p builder --- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 989722e5835..47fc13bcf12 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -44,7 +44,7 @@ import ( // LibP2PFactoryFunc is a factory function type for generating libp2p Node instances. type LibP2PFactoryFunc func() (p2p.LibP2PNode, error) -type GossipSubFactoryFuc func(context.Context, host.Host, ...pubsub.Option) (*pubsub.PubSub, error) +type GossipSubFactoryFuc func(context.Context, host.Host, ...pubsub.Option) (p2p.PubSub, error) // DefaultLibP2PNodeFactory returns a LibP2PFactoryFunc which generates the libp2p host initialized with the // default options for the host, the pubsub and the ping service. @@ -272,7 +272,7 @@ func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { psOpts = append(psOpts, scoreOpt.BuildFlowPubSubScoreOption()) } - var ps *pubsub.PubSub + var ps p2p.PubSub if builder.gossipSubFactory != nil { // builds GossipSub with the given factory ps, err = builder.gossipSubFactory(ctx, h, psOpts...) From 9e16bd97791510950930bd274966a3856930ac68 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:56:43 -0800 Subject: [PATCH 023/138] adds strict type assertion to middleware --- network/p2p/middleware/middleware.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 13128df1c59..97005d0ce28 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -12,6 +12,7 @@ import ( ggio "github.com/gogo/protobuf/io" "github.com/ipfs/go-datastore" + pubsub "github.com/libp2p/go-libp2p-pubsub" libp2pnetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" @@ -568,9 +569,14 @@ func (m *Middleware) Subscribe(channel channels.Channel) error { if err != nil { return fmt.Errorf("could not subscribe to topic (%s): %w", topic, err) } + sub, ok := s.(*pubsub.Subscription) + if !ok { + // from this point on, we assume that the subscription is a pubsub.Subscription + m.log.Fatal().Str("topic", topic.String()).Msg("could not cast subscription to pubsub.Subscription") + } // create a new readSubscription with the context of the middleware - rs := newReadSubscription(m.ctx, s, m.processAuthenticatedMessage, m.log, m.metrics) + rs := newReadSubscription(m.ctx, sub, m.processAuthenticatedMessage, m.log, m.metrics) m.wg.Add(1) // kick off the receive loop to continuously receive messages From 30b4321b6a8e6953e5dbd13c053d415cdb13d091 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:57:08 -0800 Subject: [PATCH 024/138] implements corrupt adapter --- insecure/corruptlibp2p/pubsubAdapter.go | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index 3804f232e90..9e652c2361e 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -1 +1,45 @@ package corruptlibp2p + +import ( + "github.com/libp2p/go-libp2p/core/peer" + corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" + + "github.com/onflow/flow-go/insecure/corruptlibp2p/internal" + "github.com/onflow/flow-go/network/p2p" +) + +type CorruptPubSubAdapter struct { + gossipSub *corrupt.PubSub +} + +func (c *CorruptPubSubAdapter) RegisterTopicValidator(topic string, val interface{}) error { + return c.gossipSub.RegisterTopicValidator(topic, val, corrupt.WithValidatorInline(true)) +} + +func (c *CorruptPubSubAdapter) UnregisterTopicValidator(topic string) error { + return c.gossipSub.UnregisterTopicValidator(topic) +} + +func (c *CorruptPubSubAdapter) Join(topic string) (p2p.Topic, error) { + t, err := c.gossipSub.Join(topic) + if err != nil { + return nil, err + } + return internal.NewCorruptTopic(t), nil +} + +func (c *CorruptPubSubAdapter) GetTopics() []string { + return c.gossipSub.GetTopics() +} + +func (c *CorruptPubSubAdapter) ListPeers(topic string) []peer.ID { + return c.ListPeers(topic) +} + +func NewCorruptPubSubAdapter() p2p.PubSubAdapter { + return &CorruptPubSubAdapter{ + gossipSub: corrupt.NewGossipSub(), + } +} + +var _ p2p.PubSubAdapter = (*CorruptPubSubAdapter)(nil) From 51c79914f0aaf59da814d4d6e5d1b5ddba3efddd Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:57:16 -0800 Subject: [PATCH 025/138] implements corrupt topic --- insecure/corruptlibp2p/internal/topic.go | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/insecure/corruptlibp2p/internal/topic.go b/insecure/corruptlibp2p/internal/topic.go index 5bf0569ce8c..b287eed4578 100644 --- a/insecure/corruptlibp2p/internal/topic.go +++ b/insecure/corruptlibp2p/internal/topic.go @@ -1 +1,33 @@ package internal + +import ( + "context" + + corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" + + "github.com/onflow/flow-go/network/p2p" +) + +type CorruptTopic struct { + t *corrupt.Topic +} + +func NewCorruptTopic(t *corrupt.Topic) p2p.Topic { + return &CorruptTopic{ + t: t, + } +} + +var _ p2p.Topic = (*CorruptTopic)(nil) + +func (c *CorruptTopic) String() string { + return c.t.String() +} + +func (c *CorruptTopic) Close() error { + return c.t.Close() +} + +func (c *CorruptTopic) Publish(ctx context.Context, bytes []byte) error { + return c.t.Publish(ctx, bytes) +} From eaf99aec3c681774a626e36104aa7415d6937f8a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:57:31 -0800 Subject: [PATCH 026/138] refactors corrupt libp2p node --- insecure/corruptnet/p2p_node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/insecure/corruptnet/p2p_node.go b/insecure/corruptnet/p2p_node.go index 73f93c7e675..f4398cc39d1 100644 --- a/insecure/corruptnet/p2p_node.go +++ b/insecure/corruptnet/p2p_node.go @@ -60,7 +60,7 @@ type CorruptP2PNode struct { // Subscribe subscribes the node to the given topic with a noop topic validator. // All errors returned from this function can be considered benign. -func (n *CorruptP2PNode) Subscribe(topic channels.Topic, _ pubsub.ValidatorEx) (*pubsub.Subscription, error) { +func (n *CorruptP2PNode) Subscribe(topic channels.Topic, _ pubsub.ValidatorEx) (p2p.Subscription, error) { return n.Node.Subscribe(topic, AcceptAllTopicValidator(n.logger, n.codec)) } From 7fe635a1a59b01838f3930d255cc7e8bccfe16f2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Nov 2022 12:57:39 -0800 Subject: [PATCH 027/138] refactors corrupt factory --- insecure/corruptnet/libp2p_node_factory.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptnet/libp2p_node_factory.go index 7b2e3d55ad8..63c9f20825c 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptnet/libp2p_node_factory.go @@ -1,8 +1,13 @@ package corruptnet import ( + "context" "time" + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p/core/host" + corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" + "github.com/onflow/flow-go/network/p2p" madns "github.com/multiformats/go-multiaddr-dns" @@ -51,6 +56,13 @@ func NewCorruptLibP2PNodeFactory( connectionPruning, updateInterval) builder.SetCreateNode(NewCorruptLibP2PNode) + builder.SetGossipSubFactory() return builder.Build() } } + +func corruptibleGossipSubFactory() p2pbuilder.GossipSubFactoryFuc { + return func(ctx context.Context, host host.Host, option ...pubsub.Option) (p2p.PubSub, error) { + return corrupt.NewGossipSubWithRouter(ctx, host, router, option...) + } +} From b5bf14a512f1817aecc193aa0d72ddfbed89963f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 09:41:17 -0800 Subject: [PATCH 028/138] adds corrupt subscription --- .../corruptlibp2p/internal/subscription.go | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 insecure/corruptlibp2p/internal/subscription.go diff --git a/insecure/corruptlibp2p/internal/subscription.go b/insecure/corruptlibp2p/internal/subscription.go new file mode 100644 index 00000000000..888dab4c17b --- /dev/null +++ b/insecure/corruptlibp2p/internal/subscription.go @@ -0,0 +1,40 @@ +package internal + +import ( + "context" + + pubsub "github.com/libp2p/go-libp2p-pubsub" + corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" + + "github.com/onflow/flow-go/network/p2p" +) + +type CorruptSubscription struct { + s *corrupt.Subscription +} + +var _ p2p.Subscription = (*CorruptSubscription)(nil) + +func NewCorruptSubscription(s *corrupt.Subscription) p2p.Subscription { + return &CorruptSubscription{ + s: s, + } +} + +func (c *CorruptSubscription) Cancel() { + c.s.Cancel() +} + +func (c *CorruptSubscription) Next(ctx context.Context) (*pubsub.Message, error) { + m, err := c.s.Next(ctx) + if err != nil { + return nil, err + } + return &pubsub.Message{ + Message: m.Message, + ID: m.ID, + ReceivedFrom: m.ReceivedFrom, + ValidatorData: m.ValidatorData, + Local: m.Local, + }, nil +} From db57a1792ec05f8d98d0c6960becbdbf002f926b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 09:41:38 -0800 Subject: [PATCH 029/138] refactors corrupt topic --- insecure/corruptlibp2p/internal/topic.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/insecure/corruptlibp2p/internal/topic.go b/insecure/corruptlibp2p/internal/topic.go index b287eed4578..4b6103942f3 100644 --- a/insecure/corruptlibp2p/internal/topic.go +++ b/insecure/corruptlibp2p/internal/topic.go @@ -12,6 +12,14 @@ type CorruptTopic struct { t *corrupt.Topic } +func (c *CorruptTopic) Subscribe() (p2p.Subscription, error) { + sub, err := c.t.Subscribe() + if err != nil { + return nil, err + } + return NewCorruptSubscription(sub), nil +} + func NewCorruptTopic(t *corrupt.Topic) p2p.Topic { return &CorruptTopic{ t: t, From 8d37fce49a93b0237e7b1ad7a5fee6552deeeb47 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 09:41:53 -0800 Subject: [PATCH 030/138] adds corrupt pubsub adapter --- insecure/corruptlibp2p/pubsubAdapter.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index 9e652c2361e..b010eb9830b 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -12,6 +12,8 @@ type CorruptPubSubAdapter struct { gossipSub *corrupt.PubSub } +var _ p2p.PubSubAdapter = (*CorruptPubSubAdapter)(nil) + func (c *CorruptPubSubAdapter) RegisterTopicValidator(topic string, val interface{}) error { return c.gossipSub.RegisterTopicValidator(topic, val, corrupt.WithValidatorInline(true)) } @@ -36,9 +38,9 @@ func (c *CorruptPubSubAdapter) ListPeers(topic string) []peer.ID { return c.ListPeers(topic) } -func NewCorruptPubSubAdapter() p2p.PubSubAdapter { +func NewCorruptPubSubAdapter(gossipSub *corrupt.PubSub) p2p.PubSubAdapter { return &CorruptPubSubAdapter{ - gossipSub: corrupt.NewGossipSub(), + gossipSub: gossipSub, } } From 8e2446b523f2a466ae3361288a76c33f97086c93 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 09:42:20 -0800 Subject: [PATCH 031/138] wip --- network/p2p/p2pnode/gossipSubAdapter.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/network/p2p/p2pnode/gossipSubAdapter.go b/network/p2p/p2pnode/gossipSubAdapter.go index ede8e89291f..d7e72a0ad23 100644 --- a/network/p2p/p2pnode/gossipSubAdapter.go +++ b/network/p2p/p2pnode/gossipSubAdapter.go @@ -1,18 +1,33 @@ package p2pnode import ( + "context" + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" "github.com/onflow/flow-go/network/p2p" ) type GossipSubAdapter struct { gossipSub *pubsub.PubSub + cr routing.ContentRouting } var _ p2p.PubSubAdapter = (*GossipSubAdapter)(nil) +func NewGossipSubAdapter(ctx context.Context, h host.Host, opts ...pubsub.Option) (p2p.PubSubAdapter, error) { + gossipSub, err := pubsub.NewGossipSub(ctx, h, opts...) + if err != nil { + return nil, err + } + return &GossipSubAdapter{ + gossipSub: gossipSub, + }, nil +} + func (g *GossipSubAdapter) RegisterTopicValidator(topic string, val interface{}) error { return g.gossipSub.RegisterTopicValidator(topic, val, pubsub.WithValidatorInline(true)) } @@ -36,3 +51,7 @@ func (g *GossipSubAdapter) GetTopics() []string { func (g *GossipSubAdapter) ListPeers(topic string) []peer.ID { return g.gossipSub.ListPeers(topic) } + +func defaultPubsubOptions() []pubsub.Option { + +} \ No newline at end of file From bf35b428dbb1b23d2e10afe6e5a7394fed977dc9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 09:42:31 -0800 Subject: [PATCH 032/138] wip --- insecure/corruptnet/libp2p_node_factory.go | 15 ++++++++++++--- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 6 +++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptnet/libp2p_node_factory.go index 63c9f20825c..41434417ad4 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptnet/libp2p_node_factory.go @@ -8,6 +8,7 @@ import ( "github.com/libp2p/go-libp2p/core/host" corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" + "github.com/onflow/flow-go/insecure/corruptlibp2p" "github.com/onflow/flow-go/network/p2p" madns "github.com/multiformats/go-multiaddr-dns" @@ -56,13 +57,21 @@ func NewCorruptLibP2PNodeFactory( connectionPruning, updateInterval) builder.SetCreateNode(NewCorruptLibP2PNode) - builder.SetGossipSubFactory() + builder.SetGossipSubFactory(corruptibleGossipSubFactory()) return builder.Build() } } func corruptibleGossipSubFactory() p2pbuilder.GossipSubFactoryFuc { - return func(ctx context.Context, host host.Host, option ...pubsub.Option) (p2p.PubSub, error) { - return corrupt.NewGossipSubWithRouter(ctx, host, router, option...) + return func(ctx context.Context, host host.Host, options ...pubsub.Option) (p2p.PubSubAdapter, error) { + for _, option := range options { + + } + + ps, err := corrupt.NewGossipSubWithRouter(ctx, host, option...) + if err != nil { + return nil, err + } + return corruptlibp2p.NewCorruptPubSubAdapter(ps), nil } } diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 47fc13bcf12..1162c51f16d 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -44,7 +44,7 @@ import ( // LibP2PFactoryFunc is a factory function type for generating libp2p Node instances. type LibP2PFactoryFunc func() (p2p.LibP2PNode, error) -type GossipSubFactoryFuc func(context.Context, host.Host, ...pubsub.Option) (p2p.PubSub, error) +type GossipSubFactoryFuc func(context.Context, host.Host, ...pubsub.Option) (p2p.PubSubAdapter, error) // DefaultLibP2PNodeFactory returns a LibP2PFactoryFunc which generates the libp2p host initialized with the // default options for the host, the pubsub and the ping service. @@ -272,7 +272,7 @@ func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { psOpts = append(psOpts, scoreOpt.BuildFlowPubSubScoreOption()) } - var ps p2p.PubSub + var ps p2p.PubSubAdapter if builder.gossipSubFactory != nil { // builds GossipSub with the given factory ps, err = builder.gossipSubFactory(ctx, h, psOpts...) @@ -281,7 +281,7 @@ func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { } } else { // builds GossipSub with the default factory - ps, err = pubsub.NewGossipSub(ctx, h, psOpts...) + ps, err = p2pnode.NewGossipSubAdapter(ctx, h, psOpts...) if err != nil { ctx.Throw(fmt.Errorf("could not create gossipsub: %w", err)) } From 1c6a822a71226a79a96906d0f9d207262510b2d0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:19:53 -0800 Subject: [PATCH 033/138] adds config and subscription filter interfaces --- insecure/corruptlibp2p/internal/pubsubAdapterConfig.go | 1 + network/p2p/p2pnode/gossipSubAdapterConfig.go | 1 + 2 files changed, 2 insertions(+) create mode 100644 insecure/corruptlibp2p/internal/pubsubAdapterConfig.go create mode 100644 network/p2p/p2pnode/gossipSubAdapterConfig.go diff --git a/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go b/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go new file mode 100644 index 00000000000..5bf0569ce8c --- /dev/null +++ b/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go @@ -0,0 +1 @@ +package internal diff --git a/network/p2p/p2pnode/gossipSubAdapterConfig.go b/network/p2p/p2pnode/gossipSubAdapterConfig.go new file mode 100644 index 00000000000..3abbbf1cd60 --- /dev/null +++ b/network/p2p/p2pnode/gossipSubAdapterConfig.go @@ -0,0 +1 @@ +package p2pnode From f1204d9c4119b6a491eaa551eaab0837f7278652 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:19:53 -0800 Subject: [PATCH 034/138] adds config and subscription filter interfaces --- .../internal/pubsubAdapterConfig.go | 1 + network/p2p/p2pnode/gossipSubAdapterConfig.go | 1 + network/p2p/pubsub.go | 19 ++++++++++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 insecure/corruptlibp2p/internal/pubsubAdapterConfig.go create mode 100644 network/p2p/p2pnode/gossipSubAdapterConfig.go diff --git a/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go b/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go new file mode 100644 index 00000000000..5bf0569ce8c --- /dev/null +++ b/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go @@ -0,0 +1 @@ +package internal diff --git a/network/p2p/p2pnode/gossipSubAdapterConfig.go b/network/p2p/p2pnode/gossipSubAdapterConfig.go new file mode 100644 index 00000000000..3abbbf1cd60 --- /dev/null +++ b/network/p2p/p2pnode/gossipSubAdapterConfig.go @@ -0,0 +1 @@ +package p2pnode diff --git a/network/p2p/pubsub.go b/network/p2p/pubsub.go index aad29f08bb0..89f68d4d443 100644 --- a/network/p2p/pubsub.go +++ b/network/p2p/pubsub.go @@ -4,7 +4,11 @@ import ( "context" pubsub "github.com/libp2p/go-libp2p-pubsub" + pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" + + "github.com/onflow/flow-go/network/p2p/scoring" ) type PubSubAdapter interface { @@ -15,6 +19,13 @@ type PubSubAdapter interface { ListPeers(topic string) []peer.ID } +type PubSubAdapterConfig interface { + WithRoutingDiscovery(routing.ContentRouting) + WithSubscriptionFilter(SubscriptionFilter) + WithScoreOption(*scoring.ScoreOption) + WithMessageIdFunction(f func([]byte) string) +} + type Topic interface { String() string Close() error @@ -27,5 +38,11 @@ type Subscription interface { Next(context.Context) (*pubsub.Message, error) } -type Message struct { +type BasePubSubAdapterConfig struct { + MaxMessageSize int +} + +type SubscriptionFilter interface { + CanSubscribe(string) bool + FilterIncomingSubscriptions(from peer.ID, opts []*pb.RPC_SubOpts) ([]*pb.RPC_SubOpts, error) } From 9c4c2c272982c275325e617c0352979454e807cb Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:20:36 -0800 Subject: [PATCH 035/138] moves message id to utils --- network/p2p/utils/p2putils.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/network/p2p/utils/p2putils.go b/network/p2p/utils/p2putils.go index 3845747eed7..552aa5c99a6 100644 --- a/network/p2p/utils/p2putils.go +++ b/network/p2p/utils/p2putils.go @@ -7,6 +7,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" + "github.com/onflow/flow-go/crypto/hash" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2putils" @@ -91,3 +92,10 @@ func AllowedSubscription(role flow.Role, topic string) bool { return false } } + +// MessageID returns the hash of the given data (used to generate the message ID for pubsub messages). +func MessageID(data []byte) string { + h := hash.NewSHA3_384() + _, _ = h.Write(data) + return h.SumHash().Hex() +} From 99b36dabcf4415f25f0a90d325577e484c9b095c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:20:50 -0800 Subject: [PATCH 036/138] adds pubsub adapter config --- network/p2p/p2pnode/gossipSubAdapterConfig.go | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/network/p2p/p2pnode/gossipSubAdapterConfig.go b/network/p2p/p2pnode/gossipSubAdapterConfig.go index 3abbbf1cd60..adc9bb3f6b1 100644 --- a/network/p2p/p2pnode/gossipSubAdapterConfig.go +++ b/network/p2p/p2pnode/gossipSubAdapterConfig.go @@ -1 +1,55 @@ package p2pnode + +import ( + pubsub "github.com/libp2p/go-libp2p-pubsub" + pb "github.com/libp2p/go-libp2p-pubsub/pb" + "github.com/libp2p/go-libp2p/core/routing" + discoveryrouting "github.com/libp2p/go-libp2p/p2p/discovery/routing" + + "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network/p2p/scoring" +) + +type GossipSubAdapterConfig struct { + options []pubsub.Option +} + +var _ p2p.PubSubAdapterConfig = (*GossipSubAdapterConfig)(nil) + +func NewGossipSubAdapterConfig(base *p2p.BasePubSubAdapterConfig) *GossipSubAdapterConfig { + return &GossipSubAdapterConfig{ + options: append(defaultPubsubOptions(base)), + } +} + +func (g *GossipSubAdapterConfig) WithRoutingDiscovery(routing routing.ContentRouting) { + g.options = append(g.options, pubsub.WithDiscovery(discoveryrouting.NewRoutingDiscovery(routing))) +} + +func (g *GossipSubAdapterConfig) WithSubscriptionFilter(filter p2p.SubscriptionFilter) { + g.options = append(g.options, pubsub.WithSubscriptionFilter(filter)) +} + +func (g *GossipSubAdapterConfig) WithScoreOption(option *scoring.ScoreOption) { + g.options = append(g.options, option.BuildFlowPubSubScoreOption()) +} + +func (g *GossipSubAdapterConfig) WithMessageIdFunction(f func([]byte) string) { + g.options = append(g.options, pubsub.WithMessageIdFn(func(pmsg *pb.Message) string { + return f(pmsg.Data) + })) +} + +func (g *GossipSubAdapterConfig) Build() []pubsub.Option { + return g.options +} + +func defaultPubsubOptions(base *p2p.BasePubSubAdapterConfig) []pubsub.Option { + return []pubsub.Option{ + pubsub.WithMessageSigning(true), + pubsub.WithStrictSignatureVerification(true), + pubsub.WithMaxMessageSize(base.MaxMessageSize), + } +} + +var _ p2p.PubSubAdapterConfig = (*GossipSubAdapterConfig)(nil) From a4bb8a76bb1a24e6efe53a85384db7f4f61bdec1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:21:04 -0800 Subject: [PATCH 037/138] refactors gossip sub adapter with config --- network/p2p/p2pnode/gossipSubAdapter.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/network/p2p/p2pnode/gossipSubAdapter.go b/network/p2p/p2pnode/gossipSubAdapter.go index d7e72a0ad23..0ff67a3b7ca 100644 --- a/network/p2p/p2pnode/gossipSubAdapter.go +++ b/network/p2p/p2pnode/gossipSubAdapter.go @@ -2,24 +2,28 @@ package p2pnode import ( "context" + "fmt" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/routing" "github.com/onflow/flow-go/network/p2p" ) type GossipSubAdapter struct { gossipSub *pubsub.PubSub - cr routing.ContentRouting } var _ p2p.PubSubAdapter = (*GossipSubAdapter)(nil) -func NewGossipSubAdapter(ctx context.Context, h host.Host, opts ...pubsub.Option) (p2p.PubSubAdapter, error) { - gossipSub, err := pubsub.NewGossipSub(ctx, h, opts...) +func NewGossipSubAdapter(ctx context.Context, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { + gossipSubConfig, ok := cfg.(*GossipSubAdapterConfig) + if !ok { + return nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) + } + + gossipSub, err := pubsub.NewGossipSub(ctx, h, gossipSubConfig.Build()...) if err != nil { return nil, err } @@ -51,7 +55,3 @@ func (g *GossipSubAdapter) GetTopics() []string { func (g *GossipSubAdapter) ListPeers(topic string) []peer.ID { return g.gossipSub.ListPeers(topic) } - -func defaultPubsubOptions() []pubsub.Option { - -} \ No newline at end of file From e20edeb7113fb48dd394b4593e7c6a20e9bb8cd3 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:21:53 -0800 Subject: [PATCH 038/138] refactors process of building a libp2p node --- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 86 +++++++++------------ 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 1162c51f16d..2c3830c4e1b 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -16,7 +16,6 @@ import ( "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/routing" "github.com/libp2p/go-libp2p/core/transport" - discoveryRouting "github.com/libp2p/go-libp2p/p2p/discovery/routing" "github.com/libp2p/go-libp2p/p2p/transport/tcp" "github.com/multiformats/go-multiaddr" madns "github.com/multiformats/go-multiaddr-dns" @@ -32,7 +31,6 @@ import ( "github.com/onflow/flow-go/network/p2p/dht" fcrypto "github.com/onflow/flow-go/crypto" - "github.com/onflow/flow-go/crypto/hash" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" @@ -44,7 +42,9 @@ import ( // LibP2PFactoryFunc is a factory function type for generating libp2p Node instances. type LibP2PFactoryFunc func() (p2p.LibP2PNode, error) -type GossipSubFactoryFuc func(context.Context, host.Host, ...pubsub.Option) (p2p.PubSubAdapter, error) +type GossipSubFactoryFuc func(context.Context, host.Host, p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) +type CreateNodeFunc func(logger zerolog.Logger, host host.Host, pCache *p2pnode.ProtocolPeerCache, uniMgr *unicast.Manager, peerManager *connection.PeerManager) p2p.LibP2PNode +type GossipSubAdapterConfigFunc func(*p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig // DefaultLibP2PNodeFactory returns a LibP2PFactoryFunc which generates the libp2p host initialized with the // default options for the host, the pubsub and the ping service. @@ -71,13 +71,9 @@ func DefaultLibP2PNodeFactory( // DefaultMessageIDFunction returns a default message ID function based on the message's data func DefaultMessageIDFunction(msg *pb.Message) string { - h := hash.NewSHA3_384() - _, _ = h.Write(msg.Data) - return h.SumHash().Hex() + return utils.MessageID(msg.Data) } -type CreateNodeFunc func(logger zerolog.Logger, host host.Host, pCache *p2pnode.ProtocolPeerCache, uniMgr *unicast.Manager, peerManager *connection.PeerManager) p2p.LibP2PNode - type NodeBuilder interface { SetBasicResolver(madns.BasicResolver) NodeBuilder SetSubscriptionFilter(pubsub.SubscriptionFilter) NodeBuilder @@ -104,6 +100,7 @@ type LibP2PNodeBuilder struct { connGater connmgr.ConnectionGater idProvider module.IdentityProvider gossipSubFactory GossipSubFactoryFuc + gossipSubConfigFunc GossipSubAdapterConfigFunc gossipSubPeerScoring bool // whether to enable gossipsub peer scoring routingFactory func(context.Context, host.Host) (routing.Routing, error) peerManagerEnablePruning bool @@ -119,12 +116,27 @@ func NewNodeBuilder( sporkID flow.Identifier, ) *LibP2PNodeBuilder { return &LibP2PNodeBuilder{ - logger: logger, - sporkID: sporkID, - addr: addr, - networkKey: networkKey, - createNode: DefaultCreateNodeFunc, + logger: logger, + sporkID: sporkID, + addr: addr, + networkKey: networkKey, + createNode: DefaultCreateNodeFunc, + gossipSubFactory: defaultGossipSubFactory(), + gossipSubConfigFunc: defaultGossipSubAdapterConfig(), + } +} + +func defaultGossipSubFactory() GossipSubFactoryFuc { + return func(ctx context.Context, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { + return p2pnode.NewGossipSubAdapter(ctx, h, cfg) + } +} + +func defaultGossipSubAdapterConfig() GossipSubAdapterConfigFunc { + return func(cfg *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { + return p2pnode.NewGossipSubAdapterConfig(cfg) } + } // SetBasicResolver sets the DNS resolver for the node. @@ -256,41 +268,31 @@ func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { node.SetRouting(rsys) - psOpts := append( - DefaultPubsubOptions(p2pnode.DefaultMaxPubSubMsgSize), - pubsub.WithDiscovery(discoveryRouting.NewRoutingDiscovery(rsys)), - pubsub.WithMessageIdFn(DefaultMessageIDFunction), - ) - + gossipSubConfigs := builder.gossipSubConfigFunc(&p2p.BasePubSubAdapterConfig{ + MaxMessageSize: p2pnode.DefaultMaxPubSubMsgSize, + }) + gossipSubConfigs.WithMessageIdFunction(utils.MessageID) + gossipSubConfigs.WithRoutingDiscovery(rsys) if builder.subscriptionFilter != nil { - psOpts = append(psOpts, pubsub.WithSubscriptionFilter(builder.subscriptionFilter)) + gossipSubConfigs.WithSubscriptionFilter(builder.subscriptionFilter) } var scoreOpt *scoring.ScoreOption if builder.gossipSubPeerScoring { scoreOpt = scoring.NewScoreOption(builder.logger, builder.idProvider, builder.peerScoringParameterOptions...) - psOpts = append(psOpts, scoreOpt.BuildFlowPubSubScoreOption()) + gossipSubConfigs.WithScoreOption(scoreOpt) } - var ps p2p.PubSubAdapter - if builder.gossipSubFactory != nil { - // builds GossipSub with the given factory - ps, err = builder.gossipSubFactory(ctx, h, psOpts...) - if err != nil { - ctx.Throw(fmt.Errorf("could not create gossipsub using injected factory: %w", err)) - } - } else { - // builds GossipSub with the default factory - ps, err = p2pnode.NewGossipSubAdapter(ctx, h, psOpts...) - if err != nil { - ctx.Throw(fmt.Errorf("could not create gossipsub: %w", err)) - } + // builds GossipSub with the given factory + gossipSub, err := builder.gossipSubFactory(ctx, h, gossipSubConfigs) + if err != nil { + ctx.Throw(fmt.Errorf("could not create gossipsub: %w", err)) } if scoreOpt != nil { - scoreOpt.SetSubscriptionProvider(scoring.NewSubscriptionProvider(builder.logger, ps)) + scoreOpt.SetSubscriptionProvider(scoring.NewSubscriptionProvider(builder.logger, gossipSub)) } - node.SetPubSub(ps) + node.SetPubSub(gossipSub) ready() <-ctx.Done() @@ -366,18 +368,6 @@ func defaultLibP2POptions(address string, key fcrypto.PrivateKey) ([]config.Opti return options, nil } -func DefaultPubsubOptions(maxPubSubMsgSize int) []pubsub.Option { - return []pubsub.Option{ - // enforce message signing - pubsub.WithMessageSigning(true), - // enforce message signature verification - pubsub.WithStrictSignatureVerification(true), - // set max message size limit for 1-k PubSub messaging - pubsub.WithMaxMessageSize(maxPubSubMsgSize), - // no discovery - } -} - // DefaultCreateNodeFunc returns new libP2P node. func DefaultCreateNodeFunc(logger zerolog.Logger, host host.Host, pCache *p2pnode.ProtocolPeerCache, uniMgr *unicast.Manager, peerManager *connection.PeerManager) p2p.LibP2PNode { return p2pnode.NewNode(logger, host, pCache, uniMgr, peerManager) From 7fa9dc399ec192b8693d1b24002bc3fe99ef1ea4 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:24:57 -0800 Subject: [PATCH 039/138] refactors pubsub adapter corruptible --- insecure/corruptlibp2p/pubsubAdapter.go | 36 +++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index b010eb9830b..77a07e38f15 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -1,6 +1,10 @@ package corruptlibp2p import ( + "context" + "fmt" + + "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" @@ -8,21 +12,21 @@ import ( "github.com/onflow/flow-go/network/p2p" ) -type CorruptPubSubAdapter struct { +type CorruptGossipSubAdapter struct { gossipSub *corrupt.PubSub } -var _ p2p.PubSubAdapter = (*CorruptPubSubAdapter)(nil) +var _ p2p.PubSubAdapter = (*CorruptGossipSubAdapter)(nil) -func (c *CorruptPubSubAdapter) RegisterTopicValidator(topic string, val interface{}) error { +func (c *CorruptGossipSubAdapter) RegisterTopicValidator(topic string, val interface{}) error { return c.gossipSub.RegisterTopicValidator(topic, val, corrupt.WithValidatorInline(true)) } -func (c *CorruptPubSubAdapter) UnregisterTopicValidator(topic string) error { +func (c *CorruptGossipSubAdapter) UnregisterTopicValidator(topic string) error { return c.gossipSub.UnregisterTopicValidator(topic) } -func (c *CorruptPubSubAdapter) Join(topic string) (p2p.Topic, error) { +func (c *CorruptGossipSubAdapter) Join(topic string) (p2p.Topic, error) { t, err := c.gossipSub.Join(topic) if err != nil { return nil, err @@ -30,18 +34,28 @@ func (c *CorruptPubSubAdapter) Join(topic string) (p2p.Topic, error) { return internal.NewCorruptTopic(t), nil } -func (c *CorruptPubSubAdapter) GetTopics() []string { +func (c *CorruptGossipSubAdapter) GetTopics() []string { return c.gossipSub.GetTopics() } -func (c *CorruptPubSubAdapter) ListPeers(topic string) []peer.ID { +func (c *CorruptGossipSubAdapter) ListPeers(topic string) []peer.ID { return c.ListPeers(topic) } -func NewCorruptPubSubAdapter(gossipSub *corrupt.PubSub) p2p.PubSubAdapter { - return &CorruptPubSubAdapter{ - gossipSub: gossipSub, +func NewCorruptGossipSubAdapter(ctx context.Context, router *corrupt.GossipSubRouter, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { + gossipSubConfig, ok := cfg.(*internal.CorruptPubSubAdapterConfig) + if !ok { + return nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) + } + + gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, router, gossipSubConfig.Build()...) + if err != nil { + return nil, err } + + return &CorruptGossipSubAdapter{ + gossipSub: gossipSub, + }, nil } -var _ p2p.PubSubAdapter = (*CorruptPubSubAdapter)(nil) +var _ p2p.PubSubAdapter = (*CorruptGossipSubAdapter)(nil) From 4513a605601793914974e738d643dfb461f1002b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:25:05 -0800 Subject: [PATCH 040/138] refactors spammer gossipsub --- insecure/corruptlibp2p/spammerGossipSub.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/insecure/corruptlibp2p/spammerGossipSub.go b/insecure/corruptlibp2p/spammerGossipSub.go index cdc70715f01..5aa7a22dbaf 100644 --- a/insecure/corruptlibp2p/spammerGossipSub.go +++ b/insecure/corruptlibp2p/spammerGossipSub.go @@ -11,7 +11,7 @@ type SpammerGossipSub struct { router *pubsub.GossipSubRouter } -func NewSpammerGossipSub(router *pubsub.GossipSubRouter) *SpammerGossipSub { +func NewSpammerGossipSubRouter(router *pubsub.GossipSubRouter) *SpammerGossipSub { return &SpammerGossipSub{ router: router, } From ef64c9ea6e8e84e5c8125bf1eb12b9fc90dfdf10 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:25:18 -0800 Subject: [PATCH 041/138] refactors corrupted libp2p factory --- insecure/corruptnet/libp2p_node_factory.go | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptnet/libp2p_node_factory.go index 41434417ad4..26e0d6bdb4e 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptnet/libp2p_node_factory.go @@ -4,7 +4,6 @@ import ( "context" "time" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/host" corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" @@ -30,6 +29,7 @@ func NewCorruptLibP2PNodeFactory( idProvider module.IdentityProvider, metrics module.NetworkMetrics, resolver madns.BasicResolver, + router *corrupt.GossipSubRouter, peerScoringEnabled bool, role string, onInterceptPeerDialFilters, @@ -57,21 +57,13 @@ func NewCorruptLibP2PNodeFactory( connectionPruning, updateInterval) builder.SetCreateNode(NewCorruptLibP2PNode) - builder.SetGossipSubFactory(corruptibleGossipSubFactory()) + builder.SetGossipSubFactory(corruptibleGossipSubFactory(router)) return builder.Build() } } -func corruptibleGossipSubFactory() p2pbuilder.GossipSubFactoryFuc { - return func(ctx context.Context, host host.Host, options ...pubsub.Option) (p2p.PubSubAdapter, error) { - for _, option := range options { - - } - - ps, err := corrupt.NewGossipSubWithRouter(ctx, host, option...) - if err != nil { - return nil, err - } - return corruptlibp2p.NewCorruptPubSubAdapter(ps), nil +func corruptibleGossipSubFactory(router *corrupt.GossipSubRouter) p2pbuilder.GossipSubFactoryFuc { + return func(ctx context.Context, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { + return corruptlibp2p.NewCorruptGossipSubAdapter(ctx, router, host, cfg) } } From 81ec0728eaa6db23ff62a1e4d2014e6e8eacf33b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:26:24 -0800 Subject: [PATCH 042/138] make tidy --- integration/go.mod | 1 + integration/go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/integration/go.mod b/integration/go.mod index 9c5dd20269b..971b0d3c92b 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -258,6 +258,7 @@ require ( github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect + github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/zeebo/blake3 v0.2.3 // indirect go.mongodb.org/mongo-driver v1.5.1 // indirect diff --git a/integration/go.sum b/integration/go.sum index 9a4c68bfd83..0d472d3646b 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -1733,6 +1733,8 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 h1:c6Ns5fDv1quWqDgIVw+hRRsLfCVTz7tqhSwRP5wjRRo= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 08780cbf661a32d76898e29aaf8e11e64e2231e7 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:32:47 -0800 Subject: [PATCH 043/138] lint fix --- network/internal/p2putils/utils.go | 8 -------- network/p2p/connector.go | 7 +++++++ network/p2p/middleware/middleware.go | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/network/internal/p2putils/utils.go b/network/internal/p2putils/utils.go index d7bbb90e777..dc98540870d 100644 --- a/network/internal/p2putils/utils.go +++ b/network/internal/p2putils/utils.go @@ -13,7 +13,6 @@ import ( "github.com/rs/zerolog" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/keyutils" "github.com/onflow/flow-go/network/p2p/unicast" ) @@ -164,10 +163,3 @@ func IPPortFromMultiAddress(addrs ...multiaddr.Multiaddr) (string, string, error } return "", "", fmt.Errorf("ip address or hostname not found") } - -// AllowAllPeerFilter returns a peer filter that does not do any filtering. -func AllowAllPeerFilter() p2p.PeerFilter { - return func(p peer.ID) error { - return nil - } -} diff --git a/network/p2p/connector.go b/network/p2p/connector.go index 987edac4d93..3bc4dd3df74 100644 --- a/network/p2p/connector.go +++ b/network/p2p/connector.go @@ -16,3 +16,10 @@ type Connector interface { } type PeerFilter func(peer.ID) error + +// AllowAllPeerFilter returns a peer filter that does not do any filtering. +func AllowAllPeerFilter() PeerFilter { + return func(p peer.ID) error { + return nil + } +} diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 97005d0ce28..bceac4cfc0d 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -552,7 +552,7 @@ func (m *Middleware) Subscribe(channel channels.Channel) error { if channels.IsPublicChannel(channel) { // NOTE: for public channels the callback used to check if a node is staked will // return true for every node. - peerFilter = p2putils.AllowAllPeerFilter() + peerFilter = p2p.AllowAllPeerFilter() } else { // for channels used by the staked nodes, add the topic validator to filter out messages from non-staked nodes validators = append(validators, From 8c5655a58c2ad53823ba26c7ac2bc2475f5b3bb9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:42:23 -0800 Subject: [PATCH 044/138] lint fix --- insecure/corruptlibp2p/internal/pubsubAdapterConfig.go | 3 +-- network/p2p/p2pnode/gossipSubAdapterConfig.go | 3 +-- network/p2p/pubsub.go | 8 +++++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go b/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go index 74ed91a08bf..8c93f11d0f9 100644 --- a/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go +++ b/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go @@ -7,7 +7,6 @@ import ( corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/network/p2p/scoring" ) type CorruptPubSubAdapterConfig struct { @@ -22,7 +21,7 @@ func (c *CorruptPubSubAdapterConfig) WithSubscriptionFilter(filter p2p.Subscript c.options = append(c.options, corrupt.WithSubscriptionFilter(filter)) } -func (c *CorruptPubSubAdapterConfig) WithScoreOption(_ *scoring.ScoreOption) { +func (c *CorruptPubSubAdapterConfig) WithScoreOption(_ p2p.ScoreOption) { panic("courrpted gossipsub does not support score option") } diff --git a/network/p2p/p2pnode/gossipSubAdapterConfig.go b/network/p2p/p2pnode/gossipSubAdapterConfig.go index adc9bb3f6b1..ac9474b0f76 100644 --- a/network/p2p/p2pnode/gossipSubAdapterConfig.go +++ b/network/p2p/p2pnode/gossipSubAdapterConfig.go @@ -7,7 +7,6 @@ import ( discoveryrouting "github.com/libp2p/go-libp2p/p2p/discovery/routing" "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/network/p2p/scoring" ) type GossipSubAdapterConfig struct { @@ -30,7 +29,7 @@ func (g *GossipSubAdapterConfig) WithSubscriptionFilter(filter p2p.SubscriptionF g.options = append(g.options, pubsub.WithSubscriptionFilter(filter)) } -func (g *GossipSubAdapterConfig) WithScoreOption(option *scoring.ScoreOption) { +func (g *GossipSubAdapterConfig) WithScoreOption(option p2p.ScoreOption) { g.options = append(g.options, option.BuildFlowPubSubScoreOption()) } diff --git a/network/p2p/pubsub.go b/network/p2p/pubsub.go index 89f68d4d443..654d7a29014 100644 --- a/network/p2p/pubsub.go +++ b/network/p2p/pubsub.go @@ -7,8 +7,6 @@ import ( pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" - - "github.com/onflow/flow-go/network/p2p/scoring" ) type PubSubAdapter interface { @@ -22,7 +20,7 @@ type PubSubAdapter interface { type PubSubAdapterConfig interface { WithRoutingDiscovery(routing.ContentRouting) WithSubscriptionFilter(SubscriptionFilter) - WithScoreOption(*scoring.ScoreOption) + WithScoreOption(ScoreOption) WithMessageIdFunction(f func([]byte) string) } @@ -33,6 +31,10 @@ type Topic interface { Subscribe() (Subscription, error) } +type ScoreOption interface { + BuildFlowPubSubScoreOption() pubsub.Option +} + type Subscription interface { Cancel() Next(context.Context) (*pubsub.Message, error) From 7817d0e27bfcbe6ef71b8ad2e5abec0999eda5bd Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:44:00 -0800 Subject: [PATCH 045/138] lint fix --- network/internal/p2pfixtures/fixtures.go | 5 ----- network/p2p/p2pnode/gossipSubAdapterConfig.go | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 17d202a67d6..0a3fcc98dbe 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -39,11 +39,6 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// Creating a node fixture with defaultAddress lets libp2p runs it on an -// allocated port by OS. So after fixture created, its address would be -// "0.0.0.0: -const defaultAddress = "0.0.0.0:0" - // NetworkingKeyFixtures is a test helper that generates a ECDSA flow key pair. func NetworkingKeyFixtures(t *testing.T) crypto.PrivateKey { seed := unittest.SeedFixture(48) diff --git a/network/p2p/p2pnode/gossipSubAdapterConfig.go b/network/p2p/p2pnode/gossipSubAdapterConfig.go index ac9474b0f76..aa57c04919f 100644 --- a/network/p2p/p2pnode/gossipSubAdapterConfig.go +++ b/network/p2p/p2pnode/gossipSubAdapterConfig.go @@ -17,7 +17,7 @@ var _ p2p.PubSubAdapterConfig = (*GossipSubAdapterConfig)(nil) func NewGossipSubAdapterConfig(base *p2p.BasePubSubAdapterConfig) *GossipSubAdapterConfig { return &GossipSubAdapterConfig{ - options: append(defaultPubsubOptions(base)), + options: defaultPubsubOptions(base), } } From 82f84195c4067d27938f8135fc709de1db0132ec Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:45:22 -0800 Subject: [PATCH 046/138] generates mock --- network/p2p/mock/lib_p2_p_node.go | 10 +- network/p2p/mock/pub_sub_adapter.go | 113 +++++++++++++++++++++ network/p2p/mock/pub_sub_adapter_config.go | 50 +++++++++ network/p2p/mock/score_option.go | 45 ++++++++ network/p2p/mock/subscription.go | 59 +++++++++++ network/p2p/mock/subscription_filter.go | 68 +++++++++++++ network/p2p/mock/topic.go | 95 +++++++++++++++++ 7 files changed, 435 insertions(+), 5 deletions(-) create mode 100644 network/p2p/mock/pub_sub_adapter.go create mode 100644 network/p2p/mock/pub_sub_adapter_config.go create mode 100644 network/p2p/mock/score_option.go create mode 100644 network/p2p/mock/subscription.go create mode 100644 network/p2p/mock/subscription_filter.go create mode 100644 network/p2p/mock/topic.go diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index f77915718ab..21a7bad24cf 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -289,7 +289,7 @@ func (_m *LibP2PNode) SetComponentManager(cm *component.ComponentManager) { } // SetPubSub provides a mock function with given fields: ps -func (_m *LibP2PNode) SetPubSub(ps *pubsub.PubSub) { +func (_m *LibP2PNode) SetPubSub(ps p2p.PubSubAdapter) { _m.Called(ps) } @@ -318,15 +318,15 @@ func (_m *LibP2PNode) Stop() error { } // Subscribe provides a mock function with given fields: topic, topicValidator -func (_m *LibP2PNode) Subscribe(topic channels.Topic, topicValidator pubsub.ValidatorEx) (*pubsub.Subscription, error) { +func (_m *LibP2PNode) Subscribe(topic channels.Topic, topicValidator pubsub.ValidatorEx) (p2p.Subscription, error) { ret := _m.Called(topic, topicValidator) - var r0 *pubsub.Subscription - if rf, ok := ret.Get(0).(func(channels.Topic, pubsub.ValidatorEx) *pubsub.Subscription); ok { + var r0 p2p.Subscription + if rf, ok := ret.Get(0).(func(channels.Topic, pubsub.ValidatorEx) p2p.Subscription); ok { r0 = rf(topic, topicValidator) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*pubsub.Subscription) + r0 = ret.Get(0).(p2p.Subscription) } } diff --git a/network/p2p/mock/pub_sub_adapter.go b/network/p2p/mock/pub_sub_adapter.go new file mode 100644 index 00000000000..95ad2409dd7 --- /dev/null +++ b/network/p2p/mock/pub_sub_adapter.go @@ -0,0 +1,113 @@ +// Code generated by mockery v2.13.1. DO NOT EDIT. + +package mockp2p + +import ( + p2p "github.com/onflow/flow-go/network/p2p" + mock "github.com/stretchr/testify/mock" + + peer "github.com/libp2p/go-libp2p/core/peer" +) + +// PubSubAdapter is an autogenerated mock type for the PubSubAdapter type +type PubSubAdapter struct { + mock.Mock +} + +// GetTopics provides a mock function with given fields: +func (_m *PubSubAdapter) GetTopics() []string { + ret := _m.Called() + + var r0 []string + if rf, ok := ret.Get(0).(func() []string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + return r0 +} + +// Join provides a mock function with given fields: topic +func (_m *PubSubAdapter) Join(topic string) (p2p.Topic, error) { + ret := _m.Called(topic) + + var r0 p2p.Topic + if rf, ok := ret.Get(0).(func(string) p2p.Topic); ok { + r0 = rf(topic) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(p2p.Topic) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(topic) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListPeers provides a mock function with given fields: topic +func (_m *PubSubAdapter) ListPeers(topic string) []peer.ID { + ret := _m.Called(topic) + + var r0 []peer.ID + if rf, ok := ret.Get(0).(func(string) []peer.ID); ok { + r0 = rf(topic) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]peer.ID) + } + } + + return r0 +} + +// RegisterTopicValidator provides a mock function with given fields: topic, val +func (_m *PubSubAdapter) RegisterTopicValidator(topic string, val interface{}) error { + ret := _m.Called(topic, val) + + var r0 error + if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { + r0 = rf(topic, val) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UnregisterTopicValidator provides a mock function with given fields: topic +func (_m *PubSubAdapter) UnregisterTopicValidator(topic string) error { + ret := _m.Called(topic) + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(topic) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewPubSubAdapter interface { + mock.TestingT + Cleanup(func()) +} + +// NewPubSubAdapter creates a new instance of PubSubAdapter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewPubSubAdapter(t mockConstructorTestingTNewPubSubAdapter) *PubSubAdapter { + mock := &PubSubAdapter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/pub_sub_adapter_config.go b/network/p2p/mock/pub_sub_adapter_config.go new file mode 100644 index 00000000000..452c3bf1c0d --- /dev/null +++ b/network/p2p/mock/pub_sub_adapter_config.go @@ -0,0 +1,50 @@ +// Code generated by mockery v2.13.1. DO NOT EDIT. + +package mockp2p + +import ( + p2p "github.com/onflow/flow-go/network/p2p" + mock "github.com/stretchr/testify/mock" + + routing "github.com/libp2p/go-libp2p/core/routing" +) + +// PubSubAdapterConfig is an autogenerated mock type for the PubSubAdapterConfig type +type PubSubAdapterConfig struct { + mock.Mock +} + +// WithMessageIdFunction provides a mock function with given fields: f +func (_m *PubSubAdapterConfig) WithMessageIdFunction(f func([]byte) string) { + _m.Called(f) +} + +// WithRoutingDiscovery provides a mock function with given fields: _a0 +func (_m *PubSubAdapterConfig) WithRoutingDiscovery(_a0 routing.ContentRouting) { + _m.Called(_a0) +} + +// WithScoreOption provides a mock function with given fields: _a0 +func (_m *PubSubAdapterConfig) WithScoreOption(_a0 p2p.ScoreOption) { + _m.Called(_a0) +} + +// WithSubscriptionFilter provides a mock function with given fields: _a0 +func (_m *PubSubAdapterConfig) WithSubscriptionFilter(_a0 p2p.SubscriptionFilter) { + _m.Called(_a0) +} + +type mockConstructorTestingTNewPubSubAdapterConfig interface { + mock.TestingT + Cleanup(func()) +} + +// NewPubSubAdapterConfig creates a new instance of PubSubAdapterConfig. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewPubSubAdapterConfig(t mockConstructorTestingTNewPubSubAdapterConfig) *PubSubAdapterConfig { + mock := &PubSubAdapterConfig{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/score_option.go b/network/p2p/mock/score_option.go new file mode 100644 index 00000000000..476154d5a2b --- /dev/null +++ b/network/p2p/mock/score_option.go @@ -0,0 +1,45 @@ +// Code generated by mockery v2.13.1. DO NOT EDIT. + +package mockp2p + +import ( + mock "github.com/stretchr/testify/mock" + + pubsub "github.com/libp2p/go-libp2p-pubsub" +) + +// ScoreOption is an autogenerated mock type for the ScoreOption type +type ScoreOption struct { + mock.Mock +} + +// BuildFlowPubSubScoreOption provides a mock function with given fields: +func (_m *ScoreOption) BuildFlowPubSubScoreOption() pubsub.Option { + ret := _m.Called() + + var r0 pubsub.Option + if rf, ok := ret.Get(0).(func() pubsub.Option); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(pubsub.Option) + } + } + + return r0 +} + +type mockConstructorTestingTNewScoreOption interface { + mock.TestingT + Cleanup(func()) +} + +// NewScoreOption creates a new instance of ScoreOption. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewScoreOption(t mockConstructorTestingTNewScoreOption) *ScoreOption { + mock := &ScoreOption{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/subscription.go b/network/p2p/mock/subscription.go new file mode 100644 index 00000000000..e92949e22f2 --- /dev/null +++ b/network/p2p/mock/subscription.go @@ -0,0 +1,59 @@ +// Code generated by mockery v2.13.1. DO NOT EDIT. + +package mockp2p + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + + pubsub "github.com/libp2p/go-libp2p-pubsub" +) + +// Subscription is an autogenerated mock type for the Subscription type +type Subscription struct { + mock.Mock +} + +// Cancel provides a mock function with given fields: +func (_m *Subscription) Cancel() { + _m.Called() +} + +// Next provides a mock function with given fields: _a0 +func (_m *Subscription) Next(_a0 context.Context) (*pubsub.Message, error) { + ret := _m.Called(_a0) + + var r0 *pubsub.Message + if rf, ok := ret.Get(0).(func(context.Context) *pubsub.Message); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pubsub.Message) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewSubscription interface { + mock.TestingT + Cleanup(func()) +} + +// NewSubscription creates a new instance of Subscription. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewSubscription(t mockConstructorTestingTNewSubscription) *Subscription { + mock := &Subscription{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/subscription_filter.go b/network/p2p/mock/subscription_filter.go new file mode 100644 index 00000000000..1ed71921fd1 --- /dev/null +++ b/network/p2p/mock/subscription_filter.go @@ -0,0 +1,68 @@ +// Code generated by mockery v2.13.1. DO NOT EDIT. + +package mockp2p + +import ( + mock "github.com/stretchr/testify/mock" + + peer "github.com/libp2p/go-libp2p/core/peer" + + pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb" +) + +// SubscriptionFilter is an autogenerated mock type for the SubscriptionFilter type +type SubscriptionFilter struct { + mock.Mock +} + +// CanSubscribe provides a mock function with given fields: _a0 +func (_m *SubscriptionFilter) CanSubscribe(_a0 string) bool { + ret := _m.Called(_a0) + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// FilterIncomingSubscriptions provides a mock function with given fields: from, opts +func (_m *SubscriptionFilter) FilterIncomingSubscriptions(from peer.ID, opts []*pubsub_pb.RPC_SubOpts) ([]*pubsub_pb.RPC_SubOpts, error) { + ret := _m.Called(from, opts) + + var r0 []*pubsub_pb.RPC_SubOpts + if rf, ok := ret.Get(0).(func(peer.ID, []*pubsub_pb.RPC_SubOpts) []*pubsub_pb.RPC_SubOpts); ok { + r0 = rf(from, opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pubsub_pb.RPC_SubOpts) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(peer.ID, []*pubsub_pb.RPC_SubOpts) error); ok { + r1 = rf(from, opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewSubscriptionFilter interface { + mock.TestingT + Cleanup(func()) +} + +// NewSubscriptionFilter creates a new instance of SubscriptionFilter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewSubscriptionFilter(t mockConstructorTestingTNewSubscriptionFilter) *SubscriptionFilter { + mock := &SubscriptionFilter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/topic.go b/network/p2p/mock/topic.go new file mode 100644 index 00000000000..14f806b7fcd --- /dev/null +++ b/network/p2p/mock/topic.go @@ -0,0 +1,95 @@ +// Code generated by mockery v2.13.1. DO NOT EDIT. + +package mockp2p + +import ( + context "context" + + p2p "github.com/onflow/flow-go/network/p2p" + mock "github.com/stretchr/testify/mock" +) + +// Topic is an autogenerated mock type for the Topic type +type Topic struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *Topic) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Publish provides a mock function with given fields: _a0, _a1 +func (_m *Topic) Publish(_a0 context.Context, _a1 []byte) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, []byte) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// String provides a mock function with given fields: +func (_m *Topic) String() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Subscribe provides a mock function with given fields: +func (_m *Topic) Subscribe() (p2p.Subscription, error) { + ret := _m.Called() + + var r0 p2p.Subscription + if rf, ok := ret.Get(0).(func() p2p.Subscription); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(p2p.Subscription) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewTopic interface { + mock.TestingT + Cleanup(func()) +} + +// NewTopic creates a new instance of Topic. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewTopic(t mockConstructorTestingTNewTopic) *Topic { + mock := &Topic{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 0147f3eabd1f91b79d22bebf81d29969c7bfee61 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Nov 2022 16:55:15 -0800 Subject: [PATCH 047/138] make tidy --- integration/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/integration/go.sum b/integration/go.sum index fd0673c96b9..8efa27d1c4f 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -1684,7 +1684,6 @@ github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6Ut github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 h1:c6Ns5fDv1quWqDgIVw+hRRsLfCVTz7tqhSwRP5wjRRo= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 99157829d88f42c50a4f3195964da60aa0725097 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Nov 2022 10:09:33 -0800 Subject: [PATCH 048/138] fixes build issues --- .../internal/mockGossipSubRouter.go | 48 +++++++++---------- insecure/corruptlibp2p/pubsubAdapter.go | 17 +++++-- insecure/corruptnet/libp2p_node_factory.go | 8 ++-- 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go b/insecure/corruptlibp2p/internal/mockGossipSubRouter.go index 342545d1ae3..fb0554987af 100644 --- a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go +++ b/insecure/corruptlibp2p/internal/mockGossipSubRouter.go @@ -6,90 +6,90 @@ import ( pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" ) -type GossipSubRouterFixture struct { +type CorruptGossipSubRouter struct { router *pubsub.GossipSubRouter } -var _ pubsub.GossipPubSubRouter = (*GossipSubRouterFixture)(nil) +var _ pubsub.GossipPubSubRouter = (*CorruptGossipSubRouter)(nil) -func NewGossipSubRouterFixture() *GossipSubRouterFixture { - return &GossipSubRouterFixture{ - router: pubsub.DefaultGossipSubRouter(), +func NewCorruptGossipSubRouter(router *pubsub.GossipSubRouter) *CorruptGossipSubRouter { + return &CorruptGossipSubRouter{ + router: router, } } -func (m *GossipSubRouterFixture) Protocols() []protocol.ID { +func (m *CorruptGossipSubRouter) Protocols() []protocol.ID { return m.router.Protocols() } -func (m *GossipSubRouterFixture) Attach(sub *pubsub.PubSub) { +func (m *CorruptGossipSubRouter) Attach(sub *pubsub.PubSub) { m.router.Attach(sub) } -func (m *GossipSubRouterFixture) AddPeer(pid peer.ID, protocolId protocol.ID) { +func (m *CorruptGossipSubRouter) AddPeer(pid peer.ID, protocolId protocol.ID) { m.AddPeer(pid, protocolId) } -func (m *GossipSubRouterFixture) RemovePeer(pid peer.ID) { +func (m *CorruptGossipSubRouter) RemovePeer(pid peer.ID) { m.RemovePeer(pid) } -func (m *GossipSubRouterFixture) EnoughPeers(topic string, suggested int) bool { +func (m *CorruptGossipSubRouter) EnoughPeers(topic string, suggested int) bool { return m.router.EnoughPeers(topic, suggested) } -func (m *GossipSubRouterFixture) AcceptFrom(pid peer.ID) pubsub.AcceptStatus { +func (m *CorruptGossipSubRouter) AcceptFrom(pid peer.ID) pubsub.AcceptStatus { return m.router.AcceptFrom(pid) } -func (m *GossipSubRouterFixture) HandleRPC(rpc *pubsub.RPC) { +func (m *CorruptGossipSubRouter) HandleRPC(rpc *pubsub.RPC) { m.router.HandleRPC(rpc) } -func (m *GossipSubRouterFixture) Publish(message *pubsub.Message) { +func (m *CorruptGossipSubRouter) Publish(message *pubsub.Message) { m.router.Publish(message) } -func (m *GossipSubRouterFixture) Join(topic string) { +func (m *CorruptGossipSubRouter) Join(topic string) { m.router.Join(topic) } -func (m *GossipSubRouterFixture) Leave(topic string) { +func (m *CorruptGossipSubRouter) Leave(topic string) { m.router.Leave(topic) } -func (m *GossipSubRouterFixture) SetPeerScore(score *pubsub.PeerScore) { +func (m *CorruptGossipSubRouter) SetPeerScore(score *pubsub.PeerScore) { m.router.SetPeerScore(score) } -func (m *GossipSubRouterFixture) GetPeerScore() *pubsub.PeerScore { +func (m *CorruptGossipSubRouter) GetPeerScore() *pubsub.PeerScore { return m.router.GetPeerScore() } -func (m *GossipSubRouterFixture) SetPeerScoreThresholds(thresholds *pubsub.PeerScoreThresholds) { +func (m *CorruptGossipSubRouter) SetPeerScoreThresholds(thresholds *pubsub.PeerScoreThresholds) { m.router.SetPeerScoreThresholds(thresholds) } -func (m *GossipSubRouterFixture) SetGossipTracer(tracer *pubsub.GossipTracer) { +func (m *CorruptGossipSubRouter) SetGossipTracer(tracer *pubsub.GossipTracer) { m.router.SetGossipTracer(tracer) } -func (m *GossipSubRouterFixture) GetGossipTracer() *pubsub.GossipTracer { +func (m *CorruptGossipSubRouter) GetGossipTracer() *pubsub.GossipTracer { return m.router.GetGossipTracer() } -func (m *GossipSubRouterFixture) GetTagTracer() *pubsub.TagTracer { +func (m *CorruptGossipSubRouter) GetTagTracer() *pubsub.TagTracer { return m.router.GetTagTracer() } -func (m *GossipSubRouterFixture) SetDirectPeers(direct map[peer.ID]struct{}) { +func (m *CorruptGossipSubRouter) SetDirectPeers(direct map[peer.ID]struct{}) { m.router.SetDirectPeers(direct) } -func (m *GossipSubRouterFixture) SetPeerGater(gater *pubsub.PeerGater) { +func (m *CorruptGossipSubRouter) SetPeerGater(gater *pubsub.PeerGater) { m.router.SetPeerGater(gater) } -func (m *GossipSubRouterFixture) GetPeerGater() *pubsub.PeerGater { +func (m *CorruptGossipSubRouter) GetPeerGater() *pubsub.PeerGater { return m.router.GetPeerGater() } diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index 77a07e38f15..10451c1e67d 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -14,6 +14,7 @@ import ( type CorruptGossipSubAdapter struct { gossipSub *corrupt.PubSub + router *internal.CorruptGossipSubRouter } var _ p2p.PubSubAdapter = (*CorruptGossipSubAdapter)(nil) @@ -42,20 +43,28 @@ func (c *CorruptGossipSubAdapter) ListPeers(topic string) []peer.ID { return c.ListPeers(topic) } -func NewCorruptGossipSubAdapter(ctx context.Context, router *corrupt.GossipSubRouter, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { +func (c *CorruptGossipSubAdapter) GetRouter() *internal.CorruptGossipSubRouter { + return c.router +} + +func NewCorruptGossipSubAdapter(ctx context.Context, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { gossipSubConfig, ok := cfg.(*internal.CorruptPubSubAdapterConfig) if !ok { return nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) } - gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, router, gossipSubConfig.Build()...) + router, err := corrupt.DefaultGossipSubRouter(h) + if err != nil { + return nil, fmt.Errorf("failed to create gossipsub router: %w", err) + } + corruptRouter := internal.NewCorruptGossipSubRouter(router) + gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, corruptRouter, gossipSubConfig.Build()...) if err != nil { return nil, err } return &CorruptGossipSubAdapter{ gossipSub: gossipSub, + router: corruptRouter, }, nil } - -var _ p2p.PubSubAdapter = (*CorruptGossipSubAdapter)(nil) diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptnet/libp2p_node_factory.go index 26e0d6bdb4e..37422b80e19 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptnet/libp2p_node_factory.go @@ -5,7 +5,6 @@ import ( "time" "github.com/libp2p/go-libp2p/core/host" - corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" "github.com/onflow/flow-go/insecure/corruptlibp2p" "github.com/onflow/flow-go/network/p2p" @@ -29,7 +28,6 @@ func NewCorruptLibP2PNodeFactory( idProvider module.IdentityProvider, metrics module.NetworkMetrics, resolver madns.BasicResolver, - router *corrupt.GossipSubRouter, peerScoringEnabled bool, role string, onInterceptPeerDialFilters, @@ -57,13 +55,13 @@ func NewCorruptLibP2PNodeFactory( connectionPruning, updateInterval) builder.SetCreateNode(NewCorruptLibP2PNode) - builder.SetGossipSubFactory(corruptibleGossipSubFactory(router)) + builder.SetGossipSubFactory(corruptibleGossipSubFactory()) return builder.Build() } } -func corruptibleGossipSubFactory(router *corrupt.GossipSubRouter) p2pbuilder.GossipSubFactoryFuc { +func corruptibleGossipSubFactory() p2pbuilder.GossipSubFactoryFuc { return func(ctx context.Context, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { - return corruptlibp2p.NewCorruptGossipSubAdapter(ctx, router, host, cfg) + return corruptlibp2p.NewCorruptGossipSubAdapter(ctx, host, cfg) } } From 1e746871d7a70a57ff97851a944a9e7c395643e1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Nov 2022 10:12:05 -0800 Subject: [PATCH 049/138] fixes build issues --- network/p2p/p2pnode/libp2pNode_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/network/p2p/p2pnode/libp2pNode_test.go b/network/p2p/p2pnode/libp2pNode_test.go index 4e1efb19435..3418610c384 100644 --- a/network/p2p/p2pnode/libp2pNode_test.go +++ b/network/p2p/p2pnode/libp2pNode_test.go @@ -214,10 +214,10 @@ func TestNode_HasSubscription(t *testing.T) { signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) sporkID := unittest.IdentifierFixture() - node, _ := p2pfixtures.NodeFixture(t, sporkID, "test_has_subscription") + node, _ := p2ptest.NodeFixture(t, sporkID, "test_has_subscription") - p2pfixtures.StartNode(t, signalerCtx, node, 100*time.Millisecond) - defer p2pfixtures.StopNode(t, node, cancel, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx, node, 100*time.Millisecond) + defer p2ptest.StopNode(t, node, cancel, 100*time.Millisecond) logger := unittest.Logger() met := mock.NewNetworkMetrics(t) From 60a14c241ee57a6791a0cde472e7bd80126839ca Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Nov 2022 10:26:10 -0800 Subject: [PATCH 050/138] short-circuit corrupted router --- insecure/corruptlibp2p/pubsubAdapter.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index 10451c1e67d..1695fbef9dc 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -57,14 +57,14 @@ func NewCorruptGossipSubAdapter(ctx context.Context, h host.Host, cfg p2p.PubSub if err != nil { return nil, fmt.Errorf("failed to create gossipsub router: %w", err) } - corruptRouter := internal.NewCorruptGossipSubRouter(router) - gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, corruptRouter, gossipSubConfig.Build()...) + // corruptRouter := internal.NewCorruptGossipSubRouter(router) + gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, router, gossipSubConfig.Build()...) if err != nil { return nil, err } return &CorruptGossipSubAdapter{ gossipSub: gossipSub, - router: corruptRouter, + router: nil, }, nil } From 425eba195ee94a2884c6e4bf3b40c31376dea554 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Nov 2022 10:42:52 -0800 Subject: [PATCH 051/138] lint fix --- .../internal/pubsubAdapterConfig.go | 10 ++++++++-- insecure/corruptnet/libp2p_node_factory.go | 9 ++++++++- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 7 ++++--- 3 files changed, 20 insertions(+), 6 deletions(-) rename insecure/{corruptlibp2p => corruptnet}/internal/pubsubAdapterConfig.go (88%) diff --git a/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go b/insecure/corruptnet/internal/pubsubAdapterConfig.go similarity index 88% rename from insecure/corruptlibp2p/internal/pubsubAdapterConfig.go rename to insecure/corruptnet/internal/pubsubAdapterConfig.go index 8c93f11d0f9..0a82491cb17 100644 --- a/insecure/corruptlibp2p/internal/pubsubAdapterConfig.go +++ b/insecure/corruptnet/internal/pubsubAdapterConfig.go @@ -13,6 +13,14 @@ type CorruptPubSubAdapterConfig struct { options []corrupt.Option } +var _ p2p.PubSubAdapterConfig = (*CorruptPubSubAdapterConfig)(nil) + +func NewCorruptPubSubAdapterConfig(base *p2p.BasePubSubAdapterConfig) *CorruptPubSubAdapterConfig { + return &CorruptPubSubAdapterConfig{ + options: defaultCorruptPubsubOptions(base), + } +} + func (c *CorruptPubSubAdapterConfig) WithRoutingDiscovery(routing routing.ContentRouting) { c.options = append(c.options, corrupt.WithDiscovery(discoveryRouting.NewRoutingDiscovery(routing))) } @@ -35,8 +43,6 @@ func (c *CorruptPubSubAdapterConfig) Build() []corrupt.Option { return c.options } -var _ p2p.PubSubAdapterConfig = (*CorruptPubSubAdapterConfig)(nil) - func defaultCorruptPubsubOptions(base *p2p.BasePubSubAdapterConfig) []corrupt.Option { return []corrupt.Option{ corrupt.WithMessageSigning(true), diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptnet/libp2p_node_factory.go index 37422b80e19..d25a63f7c03 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptnet/libp2p_node_factory.go @@ -7,6 +7,7 @@ import ( "github.com/libp2p/go-libp2p/core/host" "github.com/onflow/flow-go/insecure/corruptlibp2p" + "github.com/onflow/flow-go/insecure/corruptnet/internal" "github.com/onflow/flow-go/network/p2p" madns "github.com/multiformats/go-multiaddr-dns" @@ -55,7 +56,7 @@ func NewCorruptLibP2PNodeFactory( connectionPruning, updateInterval) builder.SetCreateNode(NewCorruptLibP2PNode) - builder.SetGossipSubFactory(corruptibleGossipSubFactory()) + builder.SetGossipSubFactory(corruptibleGossipSubFactory(), corruptibleGossipSubConfigFactory()) return builder.Build() } } @@ -65,3 +66,9 @@ func corruptibleGossipSubFactory() p2pbuilder.GossipSubFactoryFuc { return corruptlibp2p.NewCorruptGossipSubAdapter(ctx, host, cfg) } } + +func corruptibleGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { + return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { + return internal.NewCorruptPubSubAdapterConfig(base) + } +} diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 2c3830c4e1b..6f3daf5fc15 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -84,7 +84,7 @@ type NodeBuilder interface { SetPeerManagerOptions(connectionPruning bool, updateInterval time.Duration) NodeBuilder EnableGossipSubPeerScoring(provider module.IdentityProvider, ops ...scoring.PeerScoreParamsOption) NodeBuilder SetCreateNode(CreateNodeFunc) NodeBuilder - SetGossipSubFactory(f GossipSubFactoryFuc) NodeBuilder + SetGossipSubFactory(GossipSubFactoryFuc, GossipSubAdapterConfigFunc) NodeBuilder Build() (p2p.LibP2PNode, error) } @@ -195,8 +195,9 @@ func (builder *LibP2PNodeBuilder) SetCreateNode(f CreateNodeFunc) NodeBuilder { return builder } -func (builder *LibP2PNodeBuilder) SetGossipSubFactory(f GossipSubFactoryFuc) NodeBuilder { - builder.gossipSubFactory = f +func (builder *LibP2PNodeBuilder) SetGossipSubFactory(gf GossipSubFactoryFuc, cf GossipSubAdapterConfigFunc) NodeBuilder { + builder.gossipSubFactory = gf + builder.gossipSubConfigFunc = cf return builder } From b3370e4d7f7fb96f7c4aee542e52d4d832145c64 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Nov 2022 10:51:21 -0800 Subject: [PATCH 052/138] lint fix --- insecure/corruptlibp2p/internal/mockGossipSubRouter.go | 4 ++-- insecure/corruptlibp2p/pubsubAdapter.go | 2 +- .../internal => corruptlibp2p}/pubsubAdapterConfig.go | 2 +- insecure/corruptnet/libp2p_node_factory.go | 3 +-- 4 files changed, 5 insertions(+), 6 deletions(-) rename insecure/{corruptnet/internal => corruptlibp2p}/pubsubAdapterConfig.go (98%) diff --git a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go b/insecure/corruptlibp2p/internal/mockGossipSubRouter.go index fb0554987af..bb46282dcd6 100644 --- a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go +++ b/insecure/corruptlibp2p/internal/mockGossipSubRouter.go @@ -27,11 +27,11 @@ func (m *CorruptGossipSubRouter) Attach(sub *pubsub.PubSub) { } func (m *CorruptGossipSubRouter) AddPeer(pid peer.ID, protocolId protocol.ID) { - m.AddPeer(pid, protocolId) + m.router.AddPeer(pid, protocolId) } func (m *CorruptGossipSubRouter) RemovePeer(pid peer.ID) { - m.RemovePeer(pid) + m.router.RemovePeer(pid) } func (m *CorruptGossipSubRouter) EnoughPeers(topic string, suggested int) bool { diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index 10451c1e67d..a9c33846a3f 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -48,7 +48,7 @@ func (c *CorruptGossipSubAdapter) GetRouter() *internal.CorruptGossipSubRouter { } func NewCorruptGossipSubAdapter(ctx context.Context, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { - gossipSubConfig, ok := cfg.(*internal.CorruptPubSubAdapterConfig) + gossipSubConfig, ok := cfg.(*CorruptPubSubAdapterConfig) if !ok { return nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) } diff --git a/insecure/corruptnet/internal/pubsubAdapterConfig.go b/insecure/corruptlibp2p/pubsubAdapterConfig.go similarity index 98% rename from insecure/corruptnet/internal/pubsubAdapterConfig.go rename to insecure/corruptlibp2p/pubsubAdapterConfig.go index 0a82491cb17..8cd3d6237eb 100644 --- a/insecure/corruptnet/internal/pubsubAdapterConfig.go +++ b/insecure/corruptlibp2p/pubsubAdapterConfig.go @@ -1,4 +1,4 @@ -package internal +package corruptlibp2p import ( pb "github.com/libp2p/go-libp2p-pubsub/pb" diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptnet/libp2p_node_factory.go index d25a63f7c03..3f8617562cc 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptnet/libp2p_node_factory.go @@ -7,7 +7,6 @@ import ( "github.com/libp2p/go-libp2p/core/host" "github.com/onflow/flow-go/insecure/corruptlibp2p" - "github.com/onflow/flow-go/insecure/corruptnet/internal" "github.com/onflow/flow-go/network/p2p" madns "github.com/multiformats/go-multiaddr-dns" @@ -69,6 +68,6 @@ func corruptibleGossipSubFactory() p2pbuilder.GossipSubFactoryFuc { func corruptibleGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { - return internal.NewCorruptPubSubAdapterConfig(base) + return corruptlibp2p.NewCorruptPubSubAdapterConfig(base) } } From 9d143d8e19437139537d2146865085cda6dd7c9a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Nov 2022 10:53:09 -0800 Subject: [PATCH 053/138] lint fix --- insecure/corruptlibp2p/pubsubAdapter.go | 2 +- insecure/corruptlibp2p/spammerGossipSub_test.go | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index a9c33846a3f..5c9c0858bd2 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -40,7 +40,7 @@ func (c *CorruptGossipSubAdapter) GetTopics() []string { } func (c *CorruptGossipSubAdapter) ListPeers(topic string) []peer.ID { - return c.ListPeers(topic) + return c.gossipSub.ListPeers(topic) } func (c *CorruptGossipSubAdapter) GetRouter() *internal.CorruptGossipSubRouter { diff --git a/insecure/corruptlibp2p/spammerGossipSub_test.go b/insecure/corruptlibp2p/spammerGossipSub_test.go index f29e3d5cebd..3215c3491e4 100644 --- a/insecure/corruptlibp2p/spammerGossipSub_test.go +++ b/insecure/corruptlibp2p/spammerGossipSub_test.go @@ -21,10 +21,9 @@ func TestSpammerGossipSub(t *testing.T) { count := 5 nodes := make([]p2p.LibP2PNode, 0, 5) ids := flow.IdentityList{} - inbounds := make([]chan string, 0, 5) for i := 0; i < count; i++ { - handler, inbound := p2ptest.StreamHandlerFixture(t) + handler, _ := p2ptest.StreamHandlerFixture(t) node, id := p2ptest.NodeFixture( t, sporkId, @@ -35,7 +34,6 @@ func TestSpammerGossipSub(t *testing.T) { nodes = append(nodes, node) ids = append(ids, &id) - inbounds = append(inbounds, inbound) } p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) From 4f37ca19447f0204fd796b3778601051053941ef Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Nov 2022 10:59:34 -0800 Subject: [PATCH 054/138] refactors test helpers to accept interface --- network/internal/p2pfixtures/fixtures.go | 2 +- network/p2p/test/topic_validator_test.go | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 0a3fcc98dbe..46dce208caf 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -157,7 +157,7 @@ func MustEncodeEvent(t *testing.T, v interface{}, channel channels.Channel) []by } // SubMustReceiveMessage checks that the subscription have received the given message within the given timeout by the context. -func SubMustReceiveMessage(t *testing.T, ctx context.Context, expectedMessage []byte, sub *pubsub.Subscription) { +func SubMustReceiveMessage(t *testing.T, ctx context.Context, expectedMessage []byte, sub p2p.Subscription) { received := make(chan struct{}) go func() { msg, err := sub.Next(ctx) diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index 9ac07d124b7..0fe75c79a5e 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -158,10 +158,10 @@ func TestTopicValidator_PublicChannel(t *testing.T) { defer cancel1s() // sn1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub1)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub1) // sn2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub2)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub2) unittest.RequireReturnsBefore(t, wg.Wait, 5*time.Second, "could not receive message on time") } @@ -349,13 +349,13 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { require.NoError(t, err) // sn1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub1)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub1) // sn2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub2)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub2) // an1 also gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub3)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub3) timedCtx, cancel2s := context.WithTimeout(ctx, 2*time.Second) defer cancel2s() @@ -368,7 +368,7 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { require.NoError(t, err) // an1 receives its own message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data2, p2pfixtures.MustBePubSubSubscription(t, sub3)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data2, sub3) var wg sync.WaitGroup @@ -533,13 +533,13 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { require.NoError(t, err) // sn1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub1)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub1) // sn2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub2)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub2) // an1 also gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, p2pfixtures.MustBePubSubSubscription(t, sub3)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub3) // "eject" sn2 to ensure messages published by ejected nodes get rejected identity2.Ejected = true @@ -625,11 +625,11 @@ func TestAuthorizedSenderValidator_ClusterChannel(t *testing.T) { require.NoError(t, err) // ln1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, p2pfixtures.MustBePubSubSubscription(t, sub1)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, sub1) // ln2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, p2pfixtures.MustBePubSubSubscription(t, sub2)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, sub2) // ln3 also gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, p2pfixtures.MustBePubSubSubscription(t, sub3)) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, sub3) } From 5077c2fd455eab127ed97757d5116793571c7407 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Nov 2022 11:03:44 -0800 Subject: [PATCH 055/138] refactors test helpers to accept interface --- network/internal/p2pfixtures/fixtures.go | 4 ++-- network/p2p/scoring/app_score_test.go | 16 +++++++--------- .../p2p/scoring/subscription_validator_test.go | 5 +---- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 46dce208caf..e7c69e1a542 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -175,7 +175,7 @@ func SubMustReceiveMessage(t *testing.T, ctx context.Context, expectedMessage [] } // SubsMustReceiveMessage checks that all subscriptions receive the given message within the given timeout by the context. -func SubsMustReceiveMessage(t *testing.T, ctx context.Context, expectedMessage []byte, subs []*pubsub.Subscription) { +func SubsMustReceiveMessage(t *testing.T, ctx context.Context, expectedMessage []byte, subs []p2p.Subscription) { for _, sub := range subs { SubMustReceiveMessage(t, ctx, expectedMessage, sub) } @@ -280,7 +280,7 @@ func EnsureNotConnectedBetweenGroups(t *testing.T, ctx context.Context, groupA [ func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode, messageFactory func() (interface{}, channels.Topic)) { _, topic := messageFactory() - subs := make([]*pubsub.Subscription, len(nodes)) + subs := make([]p2p.Subscription, len(nodes)) slashingViolationsConsumer := unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector()) for i, node := range nodes { ps, err := node.Subscribe( diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index a681c021409..ae1ae5014ad 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -65,24 +65,22 @@ func TestFullGossipSubConnectivity(t *testing.T) { // all nodes subscribe to block topic (common topic among all roles) // group one - groupOneSubs := make([]*pubsub.Subscription, len(groupOneNodes)) + groupOneSubs := make([]p2p.Subscription, len(groupOneNodes)) + var err error for i, node := range groupOneNodes { - sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) - groupOneSubs[i] = p2pfixtures.MustBePubSubSubscription(t, sub) + groupOneSubs[i], err = node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) require.NoError(t, err) } // group two - groupTwoSubs := make([]*pubsub.Subscription, len(groupTwoNodes)) + groupTwoSubs := make([]p2p.Subscription, len(groupTwoNodes)) for i, node := range groupTwoNodes { - sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) - groupTwoSubs[i] = p2pfixtures.MustBePubSubSubscription(t, sub) + groupTwoSubs[i], err = node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) require.NoError(t, err) } // access node group - accessNodeSubs := make([]*pubsub.Subscription, len(accessNodeGroup)) + accessNodeSubs := make([]p2p.Subscription, len(accessNodeGroup)) for i, node := range accessNodeGroup { - sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) - accessNodeSubs[i] = p2pfixtures.MustBePubSubSubscription(t, sub) + accessNodeSubs[i], err = node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) require.NoError(t, err) } diff --git a/network/p2p/scoring/subscription_validator_test.go b/network/p2p/scoring/subscription_validator_test.go index 2e1fa3f957b..305cd4bdbfc 100644 --- a/network/p2p/scoring/subscription_validator_test.go +++ b/network/p2p/scoring/subscription_validator_test.go @@ -244,10 +244,7 @@ func TestSubscriptionValidator_Integration(t *testing.T) { // checks that the message is received by all nodes. ctx1s, cancel1s := context.WithTimeout(ctx, 1*time.Second) defer cancel1s() - p2pfixtures.SubsMustReceiveMessage(t, ctx1s, proposalMsg, []*pubsub.Subscription{ - p2pfixtures.MustBePubSubSubscription(t, conSub), - p2pfixtures.MustBePubSubSubscription(t, ver1SubBlocks), - p2pfixtures.MustBePubSubSubscription(t, ver2SubBlocks)}) + p2pfixtures.SubsMustReceiveMessage(t, ctx1s, proposalMsg, []p2p.Subscription{conSub, ver1SubBlocks, ver2SubBlocks}) // now consensus node is doing something very bad! // it is subscribing to a channel that it is not supposed to subscribe to. From 3dd6cfb34d1cc374586d6f7d4db669347d03c540 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 23 Nov 2022 08:08:14 -0500 Subject: [PATCH 056/138] [Networking][BFT Testing] Fix CI build for #3585 (abstracts GossipSub-related primitives) (#3592) Fixing CI build (building corrupt Docker images for integration tests) for #3585 updates CI to make all tests run in verbose mode (so test failure messages are clearer to find in CI logs) * dynamically inject `github.com/yhassanzadeh13/go-libp2p-pubsub` into main `go.mod` * reset `insecure`, main module back to original after building corrupt Docker images * updated Makefile to dynamically build corrupt Docker images * go mod tidy - update `go.sum` * updated `go.sum` for injected `go-libp2p-pubsub` * `Makefile` - updates all references to `corrupt` from `corrupted` * fixes unit test * lint * switches log level * put back corrupt image names to `corrupted` for EN, VN * rename image suffix var in `integration/testnet/container.go` * updated all tests to run in verbose mode * fixes topic validator type assertion * fixes build issues * generates mock * fixes unittests * switches logs to error * adds more logging for validator data * overrides validator data Co-authored-by: Yahya Hassanzadeh --- Makefile | 32 ++++---- engine/collection/compliance/core.go | 11 ++- insecure/Makefile | 2 +- insecure/cmd/build_helper1.sh | 12 +++ insecure/cmd/build_helper2.sh | 6 ++ .../corruptlibp2p/internal/subscription.go | 4 + insecure/corruptlibp2p/pubsubAdapter.go | 37 ++++++++- insecure/corruptlibp2p/pubsubAdapterConfig.go | 2 +- insecure/corruptnet/libp2p_node_factory.go | 4 +- insecure/corruptnet/p2p_node.go | 15 ++-- integration/Makefile | 22 +++--- integration/benchmark/cmd/ci/main.go | 52 +++++++------ integration/benchmark/contLoadGenerator.go | 12 --- integration/benchmark/worker_stats_tracker.go | 76 +++++++------------ .../benchmark/worker_stats_tracker_test.go | 14 ++-- integration/testnet/container.go | 8 +- integration/tests/bft/passthrough/suite.go | 6 +- network/p2p/libp2pNode.go | 3 +- network/p2p/middleware/middleware.go | 8 +- network/p2p/middleware/readSubscription.go | 14 +++- network/p2p/mock/lib_p2_p_node.go | 8 +- network/p2p/mock/pub_sub_adapter.go | 10 +-- network/p2p/mock/subscription.go | 14 ++++ network/p2p/mock/topic_validator_func.go | 48 ++++++++++++ network/p2p/p2pbuilder/libp2pNodeBuilder.go | 14 ++-- network/p2p/p2pnode/gossipSubAdapter.go | 26 ++++++- network/p2p/p2pnode/libp2pNode.go | 3 +- network/p2p/pubsub.go | 13 +++- .../scoring/subscription_validator_test.go | 2 +- .../validator/authorized_sender_validator.go | 8 +- .../authorized_sender_validator_test.go | 24 +++--- network/validator/pubsub/topic_validator.go | 33 ++++---- network/validator/validator.go | 7 +- state/cluster/badger/mutator.go | 4 +- state/cluster/badger/mutator_test.go | 13 +++- state/errors.go | 67 +++++++++------- state/protocol/badger/mutator.go | 16 ++-- state/protocol/errors.go | 18 ++--- 38 files changed, 403 insertions(+), 265 deletions(-) create mode 100755 insecure/cmd/build_helper1.sh create mode 100755 insecure/cmd/build_helper2.sh create mode 100644 network/p2p/mock/topic_validator_func.go diff --git a/Makefile b/Makefile index 39e7f5e54ed..92c609d5261 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ cmd/util/util: .PHONY: unittest-main unittest-main: # test all packages with Relic library enabled - go test -coverprofile=$(COVER_PROFILE) -covermode=atomic $(if $(RACE_DETECTOR),-race,) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) --tags relic $(GO_TEST_PACKAGES) + go test -v -coverprofile=$(COVER_PROFILE) -covermode=atomic $(if $(RACE_DETECTOR),-race,) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) --tags relic $(GO_TEST_PACKAGES) .PHONY: install-mock-generators install-mock-generators: @@ -267,15 +267,15 @@ docker-build-execution-debug: docker build -f cmd/Dockerfile --build-arg TARGET=./cmd/execution --build-arg COMMIT=$(COMMIT) --build-arg VERSION=$(IMAGE_TAG) --build-arg GOARCH=$(GOARCH) --target debug \ -t "$(CONTAINER_REGISTRY)/execution-debug:latest" -t "$(CONTAINER_REGISTRY)/execution-debug:$(SHORT_COMMIT)" -t "$(CONTAINER_REGISTRY)/execution-debug:$(IMAGE_TAG)" . -# build corrupted execution node for BFT testing -.PHONY: docker-build-execution-corrupted -docker-build-execution-corrupted: - #temporarily make insecure/ a non-module to allow Docker to use corrupt builders there - mv insecure/go.mod insecure/go2.mod +# build corrupt execution node for BFT testing +.PHONY: docker-build-execution-corrupt +docker-build-execution-corrupt: + # temporarily make insecure/ a non-module to allow Docker to use corrupt builders there + ./insecure/cmd/build_helper1.sh docker build -f cmd/Dockerfile --build-arg TARGET=./insecure/cmd/execution --build-arg COMMIT=$(COMMIT) --build-arg VERSION=$(IMAGE_TAG) --build-arg GOARCH=$(GOARCH) --target production \ --label "git_commit=${COMMIT}" --label "git_tag=${IMAGE_TAG}" \ -t "$(CONTAINER_REGISTRY)/execution-corrupted:latest" -t "$(CONTAINER_REGISTRY)/execution-corrupted:$(SHORT_COMMIT)" -t "$(CONTAINER_REGISTRY)/execution-corrupted:$(IMAGE_TAG)" . - mv insecure/go2.mod insecure/go.mod + ./insecure/cmd/build_helper2.sh .PHONY: docker-build-verification docker-build-verification: @@ -288,15 +288,15 @@ docker-build-verification-debug: docker build -f cmd/Dockerfile --build-arg TARGET=./cmd/verification --build-arg COMMIT=$(COMMIT) --build-arg VERSION=$(IMAGE_TAG) --build-arg GOARCH=$(GOARCH) --target debug \ -t "$(CONTAINER_REGISTRY)/verification-debug:latest" -t "$(CONTAINER_REGISTRY)/verification-debug:$(SHORT_COMMIT)" -t "$(CONTAINER_REGISTRY)/verification-debug:$(IMAGE_TAG)" . -# build corrupted verification node for BFT testing -.PHONY: docker-build-verification-corrupted -docker-build-verification-corrupted: - #temporarily make insecure/ a non-module to allow Docker to use corrupt builders there - mv insecure/go.mod insecure/go2.mod +# build corrupt verification node for BFT testing +.PHONY: docker-build-verification-corrupt +docker-build-verification-corrupt: + # temporarily make insecure/ a non-module to allow Docker to use corrupt builders there + ./insecure/cmd/build_helper1.sh docker build -f cmd/Dockerfile --build-arg TARGET=./insecure/cmd/verification --build-arg COMMIT=$(COMMIT) --build-arg VERSION=$(IMAGE_TAG) --build-arg GOARCH=$(GOARCH) --target production \ --label "git_commit=${COMMIT}" --label "git_tag=${IMAGE_TAG}" \ -t "$(CONTAINER_REGISTRY)/verification-corrupted:latest" -t "$(CONTAINER_REGISTRY)/verification-corrupted:$(SHORT_COMMIT)" -t "$(CONTAINER_REGISTRY)/verification-corrupted:$(IMAGE_TAG)" . - mv insecure/go2.mod insecure/go.mod + ./insecure/cmd/build_helper2.sh .PHONY: docker-build-access docker-build-access: @@ -354,10 +354,10 @@ docker-build-loader: -t "$(CONTAINER_REGISTRY)/loader:latest" -t "$(CONTAINER_REGISTRY)/loader:$(SHORT_COMMIT)" -t "$(CONTAINER_REGISTRY)/loader:$(IMAGE_TAG)" . .PHONY: docker-build-flow -docker-build-flow: docker-build-flow-corrupted docker-build-collection docker-build-consensus docker-build-execution docker-build-verification docker-build-access docker-build-observer docker-build-ghost +docker-build-flow: docker-build-flow-corrupt docker-build-collection docker-build-consensus docker-build-execution docker-build-verification docker-build-access docker-build-observer docker-build-ghost -.PHONY: docker-build-flow-corrupted -docker-build-flow-corrupted: docker-build-execution-corrupted docker-build-verification-corrupted +.PHONY: docker-build-flow-corrupt +docker-build-flow-corrupt: docker-build-execution-corrupt docker-build-verification-corrupt .PHONY: docker-build-benchnet docker-build-benchnet: docker-build-flow docker-build-loader diff --git a/engine/collection/compliance/core.go b/engine/collection/compliance/core.go index 18eb3a9af25..aa2b2c1e70c 100644 --- a/engine/collection/compliance/core.go +++ b/engine/collection/compliance/core.go @@ -269,15 +269,20 @@ func (c *Core) processBlockProposal(proposal *messages.ClusterBlockProposal) err err := c.state.Extend(block) // if the block proposes an invalid extension of the protocol state, then the block is invalid if state.IsInvalidExtensionError(err) { - return engine.NewInvalidInputErrorf("invalid extension of protocol state (block: %x, height: %d): %w", + return engine.NewInvalidInputErrorf("invalid extension of cluster state (block_id: %x, height: %d): %w", header.ID(), header.Height, err) } // protocol state aborted processing of block as it is on an abandoned fork: block is outdated if state.IsOutdatedExtensionError(err) { - return engine.NewOutdatedInputErrorf("outdated extension of protocol state: %w", err) + return engine.NewOutdatedInputErrorf("outdated extension of cluster state (block_id: %x, height: %d): %w", + header.ID(), header.Height, err) + } + if state.IsUnverifiableExtensionError(err) { + return engine.NewUnverifiableInputError("unverifiable extension of cluster state (block_id: %x, height: %d): %w", + header.ID(), header.Height, err) } if err != nil { - return fmt.Errorf("could not extend protocol state (block: %x, height: %d): %w", header.ID(), header.Height, err) + return fmt.Errorf("unexpected error while updating cluster state (block_id: %x, height: %d): %w", header.ID(), header.Height, err) } // retrieve the parent diff --git a/insecure/Makefile b/insecure/Makefile index b2b90808250..4708c71d590 100644 --- a/insecure/Makefile +++ b/insecure/Makefile @@ -11,4 +11,4 @@ endif # runs all unit tests of the insecure module .PHONY: test test: - go test -coverprofile=$(COVER_PROFILE) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) --tags relic $(if $(VERBOSE),-v,) ./... + go test -v -coverprofile=$(COVER_PROFILE) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) --tags relic ./... diff --git a/insecure/cmd/build_helper1.sh b/insecure/cmd/build_helper1.sh new file mode 100755 index 00000000000..6713441ad4f --- /dev/null +++ b/insecure/cmd/build_helper1.sh @@ -0,0 +1,12 @@ +# temporarily make insecure/ a non-module to allow Docker to use corrupt builders there +mv insecure/go.mod insecure/go2.mod + +# make a backup of main go.mod, go.sum so it can be reset back to original after corrupt images are built +cp ./go.mod ./go2.mod +cp ./go.sum ./go2.sum + +# inject forked libp2p-pubsub into main module to allow building corrupt Docker images +echo "require github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5" >> ./go.mod + +# update go.sum since added new dependency +go mod tidy diff --git a/insecure/cmd/build_helper2.sh b/insecure/cmd/build_helper2.sh new file mode 100755 index 00000000000..f35d51e6a33 --- /dev/null +++ b/insecure/cmd/build_helper2.sh @@ -0,0 +1,6 @@ +# reset insecure module to original after building corrupt Docker images +mv insecure/go2.mod insecure/go.mod + +# reset main module to original after building corrupt Docker images +mv ./go2.mod ./go.mod +mv ./go2.sum ./go.sum diff --git a/insecure/corruptlibp2p/internal/subscription.go b/insecure/corruptlibp2p/internal/subscription.go index 888dab4c17b..74d22315665 100644 --- a/insecure/corruptlibp2p/internal/subscription.go +++ b/insecure/corruptlibp2p/internal/subscription.go @@ -38,3 +38,7 @@ func (c *CorruptSubscription) Next(ctx context.Context) (*pubsub.Message, error) Local: m.Local, }, nil } + +func (c *CorruptSubscription) Topic() string { + return c.s.Topic() +} diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index 5c9c0858bd2..d117c11ea82 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -4,8 +4,10 @@ import ( "context" "fmt" + pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" + "github.com/rs/zerolog" corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" "github.com/onflow/flow-go/insecure/corruptlibp2p/internal" @@ -15,12 +17,40 @@ import ( type CorruptGossipSubAdapter struct { gossipSub *corrupt.PubSub router *internal.CorruptGossipSubRouter + logger zerolog.Logger } var _ p2p.PubSubAdapter = (*CorruptGossipSubAdapter)(nil) -func (c *CorruptGossipSubAdapter) RegisterTopicValidator(topic string, val interface{}) error { - return c.gossipSub.RegisterTopicValidator(topic, val, corrupt.WithValidatorInline(true)) +func (c *CorruptGossipSubAdapter) RegisterTopicValidator(topic string, topicValidator p2p.TopicValidatorFunc) error { + var v corrupt.ValidatorEx = func(ctx context.Context, from peer.ID, message *corrupt.Message) corrupt.ValidationResult { + pubsubMsg := &pubsub.Message{ + Message: message.Message, // converting corrupt.Message to pubsub.Message + ID: message.ID, + ReceivedFrom: message.ReceivedFrom, + ValidatorData: message.ValidatorData, + Local: message.Local, + } + result := topicValidator(ctx, from, pubsubMsg) + + // overriding the corrupt.ValidationResult with the result from pubsub.TopicValidatorFunc + message.ValidatorData = pubsubMsg.ValidatorData + switch result { + case p2p.ValidationAccept: + return corrupt.ValidationAccept + case p2p.ValidationIgnore: + return corrupt.ValidationIgnore + case p2p.ValidationReject: + return corrupt.ValidationReject + default: + // should never happen, indicates a bug in the topic validator + c.logger.Fatal().Msgf("invalid validation result: %v", result) + } + // should never happen, indicates a bug in the topic validator, but we need to return something + c.logger.Warn().Msg("invalid validation result, returning reject") + return corrupt.ValidationReject + } + return c.gossipSub.RegisterTopicValidator(topic, v, corrupt.WithValidatorInline(true)) } func (c *CorruptGossipSubAdapter) UnregisterTopicValidator(topic string) error { @@ -47,7 +77,7 @@ func (c *CorruptGossipSubAdapter) GetRouter() *internal.CorruptGossipSubRouter { return c.router } -func NewCorruptGossipSubAdapter(ctx context.Context, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { +func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { gossipSubConfig, ok := cfg.(*CorruptPubSubAdapterConfig) if !ok { return nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) @@ -66,5 +96,6 @@ func NewCorruptGossipSubAdapter(ctx context.Context, h host.Host, cfg p2p.PubSub return &CorruptGossipSubAdapter{ gossipSub: gossipSub, router: corruptRouter, + logger: logger, }, nil } diff --git a/insecure/corruptlibp2p/pubsubAdapterConfig.go b/insecure/corruptlibp2p/pubsubAdapterConfig.go index 8cd3d6237eb..14a0810a76b 100644 --- a/insecure/corruptlibp2p/pubsubAdapterConfig.go +++ b/insecure/corruptlibp2p/pubsubAdapterConfig.go @@ -30,7 +30,7 @@ func (c *CorruptPubSubAdapterConfig) WithSubscriptionFilter(filter p2p.Subscript } func (c *CorruptPubSubAdapterConfig) WithScoreOption(_ p2p.ScoreOption) { - panic("courrpted gossipsub does not support score option") + // Corrupt does not support score options. This is a no-op. } func (c *CorruptPubSubAdapterConfig) WithMessageIdFunction(f func([]byte) string) { diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptnet/libp2p_node_factory.go index 3f8617562cc..bc04e348170 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptnet/libp2p_node_factory.go @@ -61,8 +61,8 @@ func NewCorruptLibP2PNodeFactory( } func corruptibleGossipSubFactory() p2pbuilder.GossipSubFactoryFuc { - return func(ctx context.Context, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { - return corruptlibp2p.NewCorruptGossipSubAdapter(ctx, host, cfg) + return func(ctx context.Context, logger zerolog.Logger, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { + return corruptlibp2p.NewCorruptGossipSubAdapter(ctx, logger, host, cfg) } } diff --git a/insecure/corruptnet/p2p_node.go b/insecure/corruptnet/p2p_node.go index f4398cc39d1..95ab602bd4e 100644 --- a/insecure/corruptnet/p2p_node.go +++ b/insecure/corruptnet/p2p_node.go @@ -22,23 +22,23 @@ import ( // AcceptAllTopicValidator pubsub validator func that does not perform any validation, it will only attempt to decode the message and update the // rawMsg.ValidatorData needed for further processing by the middleware receive loop. Malformed messages that fail to unmarshal or decode will result // in a pubsub.ValidationReject result returned. -func AcceptAllTopicValidator(lg zerolog.Logger, c network.Codec) pubsub.ValidatorEx { +func AcceptAllTopicValidator(lg zerolog.Logger, c network.Codec) p2p.TopicValidatorFunc { lg = lg.With(). Str("component", "corrupted_libp2pnode_topic_validator"). Logger() - return func(ctx context.Context, from peer.ID, rawMsg *pubsub.Message) pubsub.ValidationResult { + return func(ctx context.Context, from peer.ID, rawMsg *pubsub.Message) p2p.ValidationResult { var msg message.Message err := msg.Unmarshal(rawMsg.Data) if err != nil { lg.Err(err).Msg("could not unmarshal raw message data") - return pubsub.ValidationReject + return p2p.ValidationReject } decodedMsgPayload, err := c.Decode(msg.Payload) if err != nil { lg.Err(err).Msg("could not decode message payload") - return pubsub.ValidationReject + return p2p.ValidationReject } rawMsg.ValidatorData = validator.TopicValidatorData{ @@ -47,7 +47,7 @@ func AcceptAllTopicValidator(lg zerolog.Logger, c network.Codec) pubsub.Validato From: from, } - return pubsub.ValidationAccept + return p2p.ValidationAccept } } @@ -60,8 +60,9 @@ type CorruptP2PNode struct { // Subscribe subscribes the node to the given topic with a noop topic validator. // All errors returned from this function can be considered benign. -func (n *CorruptP2PNode) Subscribe(topic channels.Topic, _ pubsub.ValidatorEx) (p2p.Subscription, error) { - return n.Node.Subscribe(topic, AcceptAllTopicValidator(n.logger, n.codec)) +func (n *CorruptP2PNode) Subscribe(topic channels.Topic, _ p2p.TopicValidatorFunc) (p2p.Subscription, error) { + topicValidator := AcceptAllTopicValidator(n.logger, n.codec) + return n.Node.Subscribe(topic, topicValidator) } // NewCorruptLibP2PNode returns corrupted libP2PNode that will subscribe to topics using the AcceptAllTopicValidator. diff --git a/integration/Makefile b/integration/Makefile index 2e0ffa8e8b1..7ad61a4a95d 100644 --- a/integration/Makefile +++ b/integration/Makefile @@ -22,48 +22,48 @@ ci-integration-test: access-tests ghost-tests mvp-tests epochs-tests consensus-t # Run unit tests for test utilities in this module .PHONY: test test: - go test $(if $(VERBOSE),-v,) -tags relic -coverprofile=$(COVER_PROFILE) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) `go list ./... | grep -v -e integration/tests` + go test -v -tags relic -coverprofile=$(COVER_PROFILE) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) `go list ./... | grep -v -e integration/tests` .PHONY: access-tests access-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/access/... + go test -v $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/access/... .PHONY: collection-tests collection-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/collection/... + go test -v $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/collection/... .PHONY: consensus-tests consensus-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/consensus/... + go test -v $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/consensus/... .PHONY: epochs-tests epochs-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/epochs/... + go test -v $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/epochs/... .PHONY: ghost-tests ghost-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/ghost/... + go test -v $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/ghost/... .PHONY: mvp-tests mvp-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/mvp/... + go test -v $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/mvp/... .PHONY: execution-tests execution-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/execution/... + go test -v $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/execution/... .PHONY: verification-tests verification-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/verification/... + go test -v $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/verification/... .PHONY: network-tests network-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/network/... + go test -v $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/network/... # BFT tests need to be run sequentially (-p 1) due to interference between different Docker networks when tests are run in parallel .PHONY: bft-tests bft-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/bft/... -p 1 + go test -v $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/bft/... -p 1 ############################################################################################ diff --git a/integration/benchmark/cmd/ci/main.go b/integration/benchmark/cmd/ci/main.go index 1a2b1e1f464..22b8faa62f4 100644 --- a/integration/benchmark/cmd/ci/main.go +++ b/integration/benchmark/cmd/ci/main.go @@ -212,6 +212,7 @@ func main() { defer wg.Done() dataSlices = recordTransactionData( lg, + workerStatsTracker, *sliceSize, runStartTime, gitSha, @@ -224,6 +225,7 @@ func main() { defer wg.Done() err := adjustTPS( lg, + workerStatsTracker, log, adjustInterval, loadCase.tps, @@ -260,6 +262,7 @@ func main() { func recordTransactionData( lg *benchmark.ContLoadGenerator, + workerStatsTracker *benchmark.WorkerStatsTracker, sliceDuration time.Duration, runStartTime time.Time, gitSha, goVersion, osVersion string, @@ -271,17 +274,15 @@ func recordTransactionData( for { startTime := time.Now() - startExecutedTransactions := lg.GetTxExecuted() - startSentTransactions := lg.GetTxSent() + startStats := workerStatsTracker.GetStats() select { case endTime := <-t.C: - endExecutedTransaction := lg.GetTxExecuted() - endSentTransactions := lg.GetTxSent() + endStats := workerStatsTracker.GetStats() // calculate this slice - outputTps := float64(endExecutedTransaction-startExecutedTransactions) / sliceDuration.Seconds() - inputTps := float64(endSentTransactions-startSentTransactions) / sliceDuration.Seconds() + outputTps := float64(endStats.TxsExecuted-startStats.TxsExecuted) / sliceDuration.Seconds() + inputTps := float64(endStats.TxsSent-startStats.TxsSent) / sliceDuration.Seconds() dataSlices = append(dataSlices, dataSlice{ GitSha: gitSha, @@ -291,8 +292,8 @@ func recordTransactionData( EndTime: endTime, InputTps: inputTps, OutputTps: outputTps, - StartExecutionCount: startExecutedTransactions, - EndExecutionCount: endExecutedTransaction, + StartExecutionCount: startStats.TxsExecuted, + EndExecutionCount: endStats.TxsExecuted, RunStartTime: runStartTime, }) @@ -333,6 +334,7 @@ func sendDataToBigQuery( // Target TPS is always bounded by [minTPS, maxTPS]. func adjustTPS( lg *benchmark.ContLoadGenerator, + workerStatsTracker *benchmark.WorkerStatsTracker, log zerolog.Logger, interval time.Duration, minTPS uint, @@ -340,34 +342,36 @@ func adjustTPS( maxInflight uint, ) error { targetTPS := minTPS + + // Stats for the last round lastTs := time.Now() lastTPS := float64(minTPS) - lastTxs := uint(lg.GetTxExecuted()) - lastTimedoutTxs := lg.GetTxTimedout() + lastStats := workerStatsTracker.GetStats() + lastTxsExecuted := uint(lastStats.TxsExecuted) + lastTxsTimedout := lastStats.TxsTimedout for { select { // NOTE: not using a ticker here since adjusting worker count in SetTPS // can take a while and lead to uneven feedback intervals. case nowTs := <-time.After(interval): - currentSentTxs := lg.GetTxSent() - currentTxs := uint(lg.GetTxExecuted()) - currentTimedoutTxs := lg.GetTxTimedout() + currentStats := workerStatsTracker.GetStats() + // number of timed out transactions in the last interval - timedoutTxs := currentTimedoutTxs - lastTimedoutTxs + txsTimedout := currentStats.TxsTimedout - lastTxsTimedout - inflight := currentSentTxs - int(currentTxs) + inflight := currentStats.TxsSent - currentStats.TxsExecuted inflightPerWorker := inflight / int(targetTPS) skip, currentTPS, unboundedTPS := computeTPS( - lastTxs, - currentTxs, + lastTxsExecuted, + uint(currentStats.TxsExecuted), lastTs, nowTs, lastTPS, targetTPS, inflight, maxInflight, - timedoutTxs > 0, + txsTimedout > 0, ) if skip { @@ -378,7 +382,7 @@ func adjustTPS( Int("inflightPerWorker", inflightPerWorker). Msg("skipped adjusting TPS") - lastTxs = currentTxs + lastTxsExecuted = uint(currentStats.TxsExecuted) lastTPS = currentTPS lastTs = nowTs @@ -394,7 +398,7 @@ func adjustTPS( Uint("targetTPS", boundedTPS). Int("inflight", inflight). Int("inflightPerWorker", inflightPerWorker). - Int("timedoutTxs", timedoutTxs). + Int("txsTimedout", txsTimedout). Msg("adjusting TPS") err := lg.SetTPS(boundedTPS) @@ -403,11 +407,11 @@ func adjustTPS( } targetTPS = boundedTPS - lastTimedoutTxs = currentTimedoutTxs - // + lastTxsTimedout = currentStats.TxsTimedout + // SetTPS is a blocking call, so we need to re-fetch the TxExecuted and time. - // - lastTxs = uint(lg.GetTxExecuted()) + currentStats = workerStatsTracker.GetStats() + lastTxsExecuted = uint(currentStats.TxsExecuted) lastTPS = currentTPS lastTs = time.Now() case <-lg.Done(): diff --git a/integration/benchmark/contLoadGenerator.go b/integration/benchmark/contLoadGenerator.go index 2ad0ae42523..29fa208f387 100644 --- a/integration/benchmark/contLoadGenerator.go +++ b/integration/benchmark/contLoadGenerator.go @@ -359,18 +359,6 @@ func (lg *ContLoadGenerator) Done() <-chan struct{} { return lg.stoppedChannel } -func (lg *ContLoadGenerator) GetTxSent() int { - return lg.workerStatsTracker.GetTxSent() -} - -func (lg *ContLoadGenerator) GetTxExecuted() int { - return lg.workerStatsTracker.GetTxExecuted() -} - -func (lg *ContLoadGenerator) GetTxTimedout() int { - return lg.workerStatsTracker.GetTxTimedout() -} - func (lg *ContLoadGenerator) createAccounts(num int) error { privKey := randomPrivateKey() accountKey := flowsdk.NewAccountKey(). diff --git a/integration/benchmark/worker_stats_tracker.go b/integration/benchmark/worker_stats_tracker.go index 60e9bbc13b2..89086647af9 100644 --- a/integration/benchmark/worker_stats_tracker.go +++ b/integration/benchmark/worker_stats_tracker.go @@ -9,13 +9,13 @@ import ( "github.com/rs/zerolog" ) -type workerStats struct { - workers int - txsSent int - txsTimedout int - txsExecuted int - txsSentMovingAverage float64 - txsExecutedMovingAverage float64 +type WorkerStats struct { + Workers int + TxsSent int + TxsTimedout int + TxsExecuted int + TxsSentMovingAverage float64 + TxsExecutedMovingAverage float64 } // WorkerStatsTracker keeps track of worker stats @@ -25,7 +25,7 @@ type WorkerStatsTracker struct { wg sync.WaitGroup mux sync.Mutex - stats workerStats + stats WorkerStats txsSentEWMA ewma.MovingAverage txsExecutedEWMA ewma.MovingAverage } @@ -53,11 +53,11 @@ func (st *WorkerStatsTracker) updateEWMAforever() { t := time.NewTicker(time.Second) defer t.Stop() - lastStats := st.getStats() + lastStats := st.GetStats() for { select { case <-t.C: - stats := st.getStats() + stats := st.GetStats() st.updateEWMAonce(lastStats, stats) lastStats = stats case <-st.ctx.Done(): @@ -67,12 +67,14 @@ func (st *WorkerStatsTracker) updateEWMAforever() { } // updateEWMAonce updates all Exponentially Weighted Moving Averages with the given stats. -func (st *WorkerStatsTracker) updateEWMAonce(lastStats, stats workerStats) { +func (st *WorkerStatsTracker) updateEWMAonce(lastStats, stats WorkerStats) { st.mux.Lock() defer st.mux.Unlock() - st.txsSentEWMA.Add(float64(stats.txsSent - lastStats.txsSent)) - st.txsExecutedEWMA.Add(float64(stats.txsExecuted - lastStats.txsExecuted)) + st.txsSentEWMA.Add(float64(stats.TxsSent - lastStats.TxsSent)) + st.stats.TxsSentMovingAverage = st.txsSentEWMA.Value() + st.txsExecutedEWMA.Add(float64(stats.TxsExecuted - lastStats.TxsExecuted)) + st.stats.TxsExecutedMovingAverage = st.txsExecutedEWMA.Value() } func (st *WorkerStatsTracker) Stop() { @@ -84,71 +86,47 @@ func (st *WorkerStatsTracker) IncTxTimedout() { st.mux.Lock() defer st.mux.Unlock() - st.stats.txsTimedout++ -} - -func (st *WorkerStatsTracker) GetTxTimedout() int { - st.mux.Lock() - defer st.mux.Unlock() - - return st.stats.txsTimedout + st.stats.TxsTimedout++ } func (st *WorkerStatsTracker) IncTxExecuted() { st.mux.Lock() defer st.mux.Unlock() - st.stats.txsExecuted++ -} - -func (st *WorkerStatsTracker) GetTxExecuted() int { - st.mux.Lock() - defer st.mux.Unlock() - - return st.stats.txsExecuted + st.stats.TxsExecuted++ } func (st *WorkerStatsTracker) AddWorkers(i int) { st.mux.Lock() defer st.mux.Unlock() - st.stats.workers += i + st.stats.Workers += i } func (st *WorkerStatsTracker) IncTxSent() { st.mux.Lock() defer st.mux.Unlock() - st.stats.txsSent++ -} - -func (st *WorkerStatsTracker) GetTxSent() int { - st.mux.Lock() - defer st.mux.Unlock() - - return st.stats.txsSent + st.stats.TxsSent++ } -// TODO(rbtz): make this a method public so that we can remove all getters from the ContLoadGenerator. -func (st *WorkerStatsTracker) getStats() workerStats { +func (st *WorkerStatsTracker) GetStats() WorkerStats { st.mux.Lock() defer st.mux.Unlock() - st.stats.txsSentMovingAverage = st.txsSentEWMA.Value() - st.stats.txsExecutedMovingAverage = st.txsExecutedEWMA.Value() return st.stats } func NewPeriodicStatsLogger(st *WorkerStatsTracker, log zerolog.Logger) *Worker { w := NewWorker(0, 1*time.Second, func(workerID int) { - stats := st.getStats() + stats := st.GetStats() log.Info(). - Int("workers", stats.workers). - Int("txsSent", stats.txsSent). - Int("txsTimedout", stats.txsTimedout). - Int("txsExecuted", stats.txsExecuted). - Float64("txsSentEWMA", stats.txsSentMovingAverage). - Float64("txsExecutedEWMA", stats.txsExecutedMovingAverage). + Int("Workers", stats.Workers). + Int("TxsSent", stats.TxsSent). + Int("TxsTimedout", stats.TxsTimedout). + Int("TxsExecuted", stats.TxsExecuted). + Float64("TxsSentMovingAverage", stats.TxsSentMovingAverage). + Float64("TxsExecutedMovingAverage", stats.TxsExecutedMovingAverage). Msg("worker stats") }) diff --git a/integration/benchmark/worker_stats_tracker_test.go b/integration/benchmark/worker_stats_tracker_test.go index afbf005e299..348f6ce497c 100644 --- a/integration/benchmark/worker_stats_tracker_test.go +++ b/integration/benchmark/worker_stats_tracker_test.go @@ -13,18 +13,18 @@ func TestWorkerStatsTracker(t *testing.T) { st := NewWorkerStatsTracker(context.Background()) st.AddWorkers(1) - stats := st.getStats() - assert.Equal(t, 0, stats.txsSent) - assert.Equal(t, 1, stats.workers) + stats := st.GetStats() + assert.Equal(t, 0, stats.TxsSent) + assert.Equal(t, 1, stats.Workers) st.IncTxSent() st.IncTxSent() st.IncTxExecuted() - stats = st.getStats() - assert.Equal(t, 1, stats.txsExecuted) - assert.Equal(t, 2, stats.txsSent) - assert.Equal(t, 0., stats.txsSentMovingAverage) + stats = st.GetStats() + assert.Equal(t, 1, stats.TxsExecuted) + assert.Equal(t, 2, stats.TxsSent) + assert.Equal(t, 0., stats.TxsSentMovingAverage) st.Stop() } diff --git a/integration/testnet/container.go b/integration/testnet/container.go index 55dc909eceb..20e78ac15db 100644 --- a/integration/testnet/container.go +++ b/integration/testnet/container.go @@ -121,14 +121,14 @@ func (c *ContainerConfig) ImageName() string { if c.Ghost { return defaultRegistry + "/ghost:latest" } - debugSuffix := "" + imageSuffix := "" if c.Debug { - debugSuffix = "-debug" + imageSuffix = "-debug" } else if c.Corrupted { - debugSuffix = "-corrupted" + imageSuffix = "-corrupted" } - return fmt.Sprintf("%s/%s%s:latest", defaultRegistry, c.Role.String(), debugSuffix) + return fmt.Sprintf("%s/%s%s:latest", defaultRegistry, c.Role.String(), imageSuffix) } // Container represents a test Docker container for a generic Flow node. diff --git a/integration/tests/bft/passthrough/suite.go b/integration/tests/bft/passthrough/suite.go index 98b0033c60e..86a063d310f 100644 --- a/integration/tests/bft/passthrough/suite.go +++ b/integration/tests/bft/passthrough/suite.go @@ -83,7 +83,7 @@ func (s *Suite) SetupSuite() { s.verID = unittest.IdentifierFixture() verConfig := testnet.NewNodeConfig(flow.RoleVerification, testnet.WithID(s.verID), - testnet.WithLogLevel(zerolog.FatalLevel), + testnet.WithLogLevel(zerolog.ErrorLevel), testnet.AsCorrupted()) s.nodeConfigs = append(s.nodeConfigs, verConfig) @@ -91,14 +91,14 @@ func (s *Suite) SetupSuite() { s.exe1ID = unittest.IdentifierFixture() exe1Config := testnet.NewNodeConfig(flow.RoleExecution, testnet.WithID(s.exe1ID), - testnet.WithLogLevel(zerolog.FatalLevel), + testnet.WithLogLevel(zerolog.ErrorLevel), testnet.AsCorrupted()) s.nodeConfigs = append(s.nodeConfigs, exe1Config) s.exe2ID = unittest.IdentifierFixture() exe2Config := testnet.NewNodeConfig(flow.RoleExecution, testnet.WithID(s.exe2ID), - testnet.WithLogLevel(zerolog.FatalLevel), + testnet.WithLogLevel(zerolog.ErrorLevel), testnet.AsCorrupted()) s.nodeConfigs = append(s.nodeConfigs, exe2Config) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 6c6df5e65af..dc8bb026d55 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -4,7 +4,6 @@ import ( "context" kbucket "github.com/libp2p/go-libp2p-kbucket" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/host" libp2pnet "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" @@ -43,7 +42,7 @@ type LibP2PNode interface { // ListPeers returns list of peer IDs for peers subscribed to the topic. ListPeers(topic string) []peer.ID // Subscribe subscribes the node to the given topic and returns the subscription - Subscribe(topic channels.Topic, topicValidator pubsub.ValidatorEx) (Subscription, error) + Subscribe(topic channels.Topic, topicValidator TopicValidatorFunc) (Subscription, error) // UnSubscribe cancels the subscriber and closes the topic. UnSubscribe(topic channels.Topic) error // Publish publishes the given payload on the topic. diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index ae9434f0ab8..b6b87dc2f09 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -13,7 +13,6 @@ import ( ggio "github.com/gogo/protobuf/io" "github.com/ipfs/go-datastore" - pubsub "github.com/libp2p/go-libp2p-pubsub" libp2pnetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" @@ -596,14 +595,9 @@ func (m *Middleware) Subscribe(channel channels.Channel) error { if err != nil { return fmt.Errorf("could not subscribe to topic (%s): %w", topic, err) } - sub, ok := s.(*pubsub.Subscription) - if !ok { - // from this point on, we assume that the subscription is a pubsub.Subscription - m.log.Fatal().Str("topic", topic.String()).Msg("could not cast subscription to pubsub.Subscription") - } // create a new readSubscription with the context of the middleware - rs := newReadSubscription(m.ctx, sub, m.processAuthenticatedMessage, m.log, m.metrics) + rs := newReadSubscription(m.ctx, s, m.processAuthenticatedMessage, m.log, m.metrics) m.wg.Add(1) // kick off the receive loop to continuously receive messages diff --git a/network/p2p/middleware/readSubscription.go b/network/p2p/middleware/readSubscription.go index b79f25f97c1..6199415e7f5 100644 --- a/network/p2p/middleware/readSubscription.go +++ b/network/p2p/middleware/readSubscription.go @@ -2,17 +2,19 @@ package middleware import ( "context" + "fmt" "strings" "sync" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" "github.com/rs/zerolog" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/network/message" + "github.com/onflow/flow-go/network/p2p" validator "github.com/onflow/flow-go/network/validator/pubsub" + "github.com/onflow/flow-go/utils/logging" ) // readSubscriptionCB the callback called when a new message is received on the read subscription @@ -23,13 +25,13 @@ type readSubscriptionCB func(msg *message.Message, decodedMsgPayload interface{} type readSubscription struct { ctx context.Context log zerolog.Logger - sub *pubsub.Subscription + sub p2p.Subscription metrics module.NetworkMetrics callback readSubscriptionCB } // newReadSubscription reads the messages coming in on the subscription -func newReadSubscription(ctx context.Context, sub *pubsub.Subscription, callback readSubscriptionCB, log zerolog.Logger, metrics module.NetworkMetrics) *readSubscription { +func newReadSubscription(ctx context.Context, sub p2p.Subscription, callback readSubscriptionCB, log zerolog.Logger, metrics module.NetworkMetrics) *readSubscription { log = log.With(). Str("channel", sub.Topic()). @@ -80,7 +82,11 @@ func (r *readSubscription) receiveLoop(wg *sync.WaitGroup) { validatorData, ok := rawMsg.ValidatorData.(validator.TopicValidatorData) if !ok { - r.log.Error().Str("raw_msg", rawMsg.String()).Msg("[BUG] validator data missing!") + r.log.Error(). + Str("raw_msg", rawMsg.String()). + Bool(logging.KeySuspicious, true). + Str("received_validator_data_type", fmt.Sprintf("%T", rawMsg.ValidatorData)). + Msg("[BUG] validator data missing!") return } diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index 327286d0749..ec77f1a1a5b 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -24,8 +24,6 @@ import ( protocol "github.com/libp2p/go-libp2p/core/protocol" - pubsub "github.com/libp2p/go-libp2p-pubsub" - routing "github.com/libp2p/go-libp2p/core/routing" unicast "github.com/onflow/flow-go/network/p2p/unicast" @@ -332,11 +330,11 @@ func (_m *LibP2PNode) Stop() error { } // Subscribe provides a mock function with given fields: topic, topicValidator -func (_m *LibP2PNode) Subscribe(topic channels.Topic, topicValidator pubsub.ValidatorEx) (p2p.Subscription, error) { +func (_m *LibP2PNode) Subscribe(topic channels.Topic, topicValidator p2p.TopicValidatorFunc) (p2p.Subscription, error) { ret := _m.Called(topic, topicValidator) var r0 p2p.Subscription - if rf, ok := ret.Get(0).(func(channels.Topic, pubsub.ValidatorEx) p2p.Subscription); ok { + if rf, ok := ret.Get(0).(func(channels.Topic, p2p.TopicValidatorFunc) p2p.Subscription); ok { r0 = rf(topic, topicValidator) } else { if ret.Get(0) != nil { @@ -345,7 +343,7 @@ func (_m *LibP2PNode) Subscribe(topic channels.Topic, topicValidator pubsub.Vali } var r1 error - if rf, ok := ret.Get(1).(func(channels.Topic, pubsub.ValidatorEx) error); ok { + if rf, ok := ret.Get(1).(func(channels.Topic, p2p.TopicValidatorFunc) error); ok { r1 = rf(topic, topicValidator) } else { r1 = ret.Error(1) diff --git a/network/p2p/mock/pub_sub_adapter.go b/network/p2p/mock/pub_sub_adapter.go index 95ad2409dd7..f2881ff5f09 100644 --- a/network/p2p/mock/pub_sub_adapter.go +++ b/network/p2p/mock/pub_sub_adapter.go @@ -69,13 +69,13 @@ func (_m *PubSubAdapter) ListPeers(topic string) []peer.ID { return r0 } -// RegisterTopicValidator provides a mock function with given fields: topic, val -func (_m *PubSubAdapter) RegisterTopicValidator(topic string, val interface{}) error { - ret := _m.Called(topic, val) +// RegisterTopicValidator provides a mock function with given fields: topic, topicValidator +func (_m *PubSubAdapter) RegisterTopicValidator(topic string, topicValidator p2p.TopicValidatorFunc) error { + ret := _m.Called(topic, topicValidator) var r0 error - if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { - r0 = rf(topic, val) + if rf, ok := ret.Get(0).(func(string, p2p.TopicValidatorFunc) error); ok { + r0 = rf(topic, topicValidator) } else { r0 = ret.Error(0) } diff --git a/network/p2p/mock/subscription.go b/network/p2p/mock/subscription.go index e92949e22f2..149cbf9c52f 100644 --- a/network/p2p/mock/subscription.go +++ b/network/p2p/mock/subscription.go @@ -43,6 +43,20 @@ func (_m *Subscription) Next(_a0 context.Context) (*pubsub.Message, error) { return r0, r1 } +// Topic provides a mock function with given fields: +func (_m *Subscription) Topic() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + type mockConstructorTestingTNewSubscription interface { mock.TestingT Cleanup(func()) diff --git a/network/p2p/mock/topic_validator_func.go b/network/p2p/mock/topic_validator_func.go new file mode 100644 index 00000000000..51616236577 --- /dev/null +++ b/network/p2p/mock/topic_validator_func.go @@ -0,0 +1,48 @@ +// Code generated by mockery v2.13.1. DO NOT EDIT. + +package mockp2p + +import ( + context "context" + + p2p "github.com/onflow/flow-go/network/p2p" + mock "github.com/stretchr/testify/mock" + + peer "github.com/libp2p/go-libp2p/core/peer" + + pubsub "github.com/libp2p/go-libp2p-pubsub" +) + +// TopicValidatorFunc is an autogenerated mock type for the TopicValidatorFunc type +type TopicValidatorFunc struct { + mock.Mock +} + +// Execute provides a mock function with given fields: _a0, _a1, _a2 +func (_m *TopicValidatorFunc) Execute(_a0 context.Context, _a1 peer.ID, _a2 *pubsub.Message) p2p.ValidationResult { + ret := _m.Called(_a0, _a1, _a2) + + var r0 p2p.ValidationResult + if rf, ok := ret.Get(0).(func(context.Context, peer.ID, *pubsub.Message) p2p.ValidationResult); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Get(0).(p2p.ValidationResult) + } + + return r0 +} + +type mockConstructorTestingTNewTopicValidatorFunc interface { + mock.TestingT + Cleanup(func()) +} + +// NewTopicValidatorFunc creates a new instance of TopicValidatorFunc. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewTopicValidatorFunc(t mockConstructorTestingTNewTopicValidatorFunc) *TopicValidatorFunc { + mock := &TopicValidatorFunc{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 6f3daf5fc15..cf8764663e2 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -42,7 +42,7 @@ import ( // LibP2PFactoryFunc is a factory function type for generating libp2p Node instances. type LibP2PFactoryFunc func() (p2p.LibP2PNode, error) -type GossipSubFactoryFuc func(context.Context, host.Host, p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) +type GossipSubFactoryFuc func(context.Context, zerolog.Logger, host.Host, p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) type CreateNodeFunc func(logger zerolog.Logger, host host.Host, pCache *p2pnode.ProtocolPeerCache, uniMgr *unicast.Manager, peerManager *connection.PeerManager) p2p.LibP2PNode type GossipSubAdapterConfigFunc func(*p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig @@ -127,8 +127,8 @@ func NewNodeBuilder( } func defaultGossipSubFactory() GossipSubFactoryFuc { - return func(ctx context.Context, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { - return p2pnode.NewGossipSubAdapter(ctx, h, cfg) + return func(ctx context.Context, logger zerolog.Logger, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { + return p2pnode.NewGossipSubAdapter(ctx, logger, h, cfg) } } @@ -285,7 +285,7 @@ func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { } // builds GossipSub with the given factory - gossipSub, err := builder.gossipSubFactory(ctx, h, gossipSubConfigs) + gossipSub, err := builder.gossipSubFactory(ctx, builder.logger, h, gossipSubConfigs) if err != nil { ctx.Throw(fmt.Errorf("could not create gossipsub: %w", err)) } @@ -350,12 +350,12 @@ func defaultLibP2POptions(address string, key fcrypto.PrivateKey) ([]config.Opti return nil, fmt.Errorf("failed to translate Flow address to Libp2p multiaddress: %w", err) } - // create a transport which disables port reuse and web socket. + // create a t which disables port reuse and web socket. // Port reuse enables listening and dialing from the same TCP port (https://github.com/libp2p/go-reuseport) // While this sounds great, it intermittently causes a 'broken pipe' error // as the 1-k discovery process and the 1-1 messaging both sometimes attempt to open connection to the same target // As of now there is no requirement of client sockets to be a well-known port, so disabling port reuse all together. - transport := libp2p.Transport(func(u transport.Upgrader) (*tcp.TcpTransport, error) { + t := libp2p.Transport(func(u transport.Upgrader) (*tcp.TcpTransport, error) { return tcp.NewTCPTransport(u, nil, tcp.DisableReuseport()) }) @@ -363,7 +363,7 @@ func defaultLibP2POptions(address string, key fcrypto.PrivateKey) ([]config.Opti options := []config.Option{ libp2p.ListenAddrs(sourceMultiAddr), // set the listen address libp2p.Identity(libp2pKey), // pass in the networking key - transport, // set the protocol + t, // set the transport } return options, nil diff --git a/network/p2p/p2pnode/gossipSubAdapter.go b/network/p2p/p2pnode/gossipSubAdapter.go index 0ff67a3b7ca..be6dbc3b00e 100644 --- a/network/p2p/p2pnode/gossipSubAdapter.go +++ b/network/p2p/p2pnode/gossipSubAdapter.go @@ -7,17 +7,19 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" + "github.com/rs/zerolog" "github.com/onflow/flow-go/network/p2p" ) type GossipSubAdapter struct { gossipSub *pubsub.PubSub + logger zerolog.Logger } var _ p2p.PubSubAdapter = (*GossipSubAdapter)(nil) -func NewGossipSubAdapter(ctx context.Context, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { +func NewGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { gossipSubConfig, ok := cfg.(*GossipSubAdapterConfig) if !ok { return nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) @@ -29,11 +31,29 @@ func NewGossipSubAdapter(ctx context.Context, h host.Host, cfg p2p.PubSubAdapter } return &GossipSubAdapter{ gossipSub: gossipSub, + logger: logger, }, nil } -func (g *GossipSubAdapter) RegisterTopicValidator(topic string, val interface{}) error { - return g.gossipSub.RegisterTopicValidator(topic, val, pubsub.WithValidatorInline(true)) +func (g *GossipSubAdapter) RegisterTopicValidator(topic string, topicValidator p2p.TopicValidatorFunc) error { + var v pubsub.ValidatorEx = func(ctx context.Context, from peer.ID, message *pubsub.Message) pubsub.ValidationResult { + switch result := topicValidator(ctx, from, message); result { + case p2p.ValidationAccept: + return pubsub.ValidationAccept + case p2p.ValidationIgnore: + return pubsub.ValidationIgnore + case p2p.ValidationReject: + return pubsub.ValidationReject + default: + // should never happen, indicates a bug in the topic validator + g.logger.Fatal().Msgf("invalid validation result: %v", result) + } + // should never happen, indicates a bug in the topic validator, but we need to return something + g.logger.Warn().Msg("invalid validation result, returning reject") + return pubsub.ValidationReject + } + + return g.gossipSub.RegisterTopicValidator(topic, v, pubsub.WithValidatorInline(true)) } func (g *GossipSubAdapter) UnregisterTopicValidator(topic string) error { diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index bcf27300695..e0d0a4ad8c8 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -13,7 +13,6 @@ import ( "github.com/hashicorp/go-multierror" dht "github.com/libp2p/go-libp2p-kad-dht" kbucket "github.com/libp2p/go-libp2p-kbucket" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/host" libp2pnet "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" @@ -220,7 +219,7 @@ func (n *Node) ListPeers(topic string) []peer.ID { // Subscribe subscribes the node to the given topic and returns the subscription // All errors returned from this function can be considered benign. -func (n *Node) Subscribe(topic channels.Topic, topicValidator pubsub.ValidatorEx) (p2p.Subscription, error) { +func (n *Node) Subscribe(topic channels.Topic, topicValidator p2p.TopicValidatorFunc) (p2p.Subscription, error) { n.Lock() defer n.Unlock() diff --git a/network/p2p/pubsub.go b/network/p2p/pubsub.go index 654d7a29014..f651d778371 100644 --- a/network/p2p/pubsub.go +++ b/network/p2p/pubsub.go @@ -9,8 +9,18 @@ import ( "github.com/libp2p/go-libp2p/core/routing" ) +type ValidationResult int + +const ( + ValidationAccept ValidationResult = iota + ValidationIgnore + ValidationReject +) + +type TopicValidatorFunc func(context.Context, peer.ID, *pubsub.Message) ValidationResult + type PubSubAdapter interface { - RegisterTopicValidator(topic string, val interface{}) error + RegisterTopicValidator(topic string, topicValidator TopicValidatorFunc) error UnregisterTopicValidator(topic string) error Join(topic string) (Topic, error) GetTopics() []string @@ -37,6 +47,7 @@ type ScoreOption interface { type Subscription interface { Cancel() + Topic() string Next(context.Context) (*pubsub.Message, error) } diff --git a/network/p2p/scoring/subscription_validator_test.go b/network/p2p/scoring/subscription_validator_test.go index 305cd4bdbfc..fe01db76160 100644 --- a/network/p2p/scoring/subscription_validator_test.go +++ b/network/p2p/scoring/subscription_validator_test.go @@ -277,7 +277,7 @@ func TestSubscriptionValidator_Integration(t *testing.T) { ctx1s, cancel1s = context.WithTimeout(ctx, 1*time.Second) defer cancel1s() - p2pfixtures.SubsMustReceiveMessage(t, ctx1s, chunkDataPackRequestMsg, []*pubsub.Subscription{ + p2pfixtures.SubsMustReceiveMessage(t, ctx1s, chunkDataPackRequestMsg, []p2p.Subscription{ p2pfixtures.MustBePubSubSubscription(t, ver1SubChunks), p2pfixtures.MustBePubSubSubscription(t, ver2SubChunks)}) diff --git a/network/validator/authorized_sender_validator.go b/network/validator/authorized_sender_validator.go index 44b885fbf34..ba6371b1744 100644 --- a/network/validator/authorized_sender_validator.go +++ b/network/validator/authorized_sender_validator.go @@ -4,13 +4,13 @@ import ( "errors" "fmt" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" "github.com/rs/zerolog" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/message" + "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/slashing" ) @@ -39,13 +39,13 @@ func NewAuthorizedSenderValidator(log zerolog.Logger, slashingViolationsConsumer // PubSubMessageValidator wraps Validate and returns PubSubMessageValidator callback that returns pubsub.ValidationReject if validation fails and pubsub.ValidationAccept if validation passes. func (av *AuthorizedSenderValidator) PubSubMessageValidator(channel channels.Channel) PubSubMessageValidator { - return func(from peer.ID, msg interface{}) pubsub.ValidationResult { + return func(from peer.ID, msg interface{}) p2p.ValidationResult { _, err := av.Validate(from, msg, channel, message.ProtocolPublish) if err != nil { - return pubsub.ValidationReject + return p2p.ValidationReject } - return pubsub.ValidationAccept + return p2p.ValidationAccept } } diff --git a/network/validator/authorized_sender_validator_test.go b/network/validator/authorized_sender_validator_test.go index 81c21de2cb8..ac764d7ffe8 100644 --- a/network/validator/authorized_sender_validator_test.go +++ b/network/validator/authorized_sender_validator_test.go @@ -4,7 +4,6 @@ import ( "fmt" "testing" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" "github.com/rs/zerolog" "github.com/stretchr/testify/require" @@ -15,6 +14,7 @@ import ( "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/message" + "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/slashing" "github.com/onflow/flow-go/utils/unittest" ) @@ -78,15 +78,15 @@ func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_AuthorizedSen validatePubsub := authorizedSenderValidator.PubSubMessageValidator(c.Channel) pubsubResult := validatePubsub(pid, c.Message) if !c.Protocols.Contains(message.ProtocolPublish) { - require.Equal(s.T(), pubsub.ValidationReject, pubsubResult) + require.Equal(s.T(), p2p.ValidationReject, pubsubResult) } else { - require.Equal(s.T(), pubsub.ValidationAccept, pubsubResult) + require.Equal(s.T(), p2p.ValidationAccept, pubsubResult) } }) } } -// TestValidatorCallback_UnAuthorizedSender checks that AuthorizedSenderValidator.Validate return's pubsub.ValidationReject +// TestValidatorCallback_UnAuthorizedSender checks that AuthorizedSenderValidator.Validate return's p2p.ValidationReject // validation error for all possible invalid combinations (unauthorized sender role, message type). func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnAuthorizedSender() { for _, c := range s.unauthorizedSenderTestCases { @@ -99,7 +99,7 @@ func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnAuthorizedS validatePubsub := authorizedSenderValidator.PubSubMessageValidator(c.Channel) pubsubResult := validatePubsub(pid, c.Message) - require.Equal(s.T(), pubsub.ValidationReject, pubsubResult) + require.Equal(s.T(), p2p.ValidationReject, pubsubResult) }) } } @@ -157,7 +157,7 @@ func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnAuthorizedM validatePubsub := authorizedSenderValidator.PubSubMessageValidator(c.Channel) pubsubResult := validatePubsub(pid, c.Message) - require.Equal(s.T(), pubsub.ValidationReject, pubsubResult) + require.Equal(s.T(), p2p.ValidationReject, pubsubResult) }) } } @@ -182,7 +182,7 @@ func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_ClusterPrefix // ensure ClusterBlockProposal is allowed to be sent via pubsub by authorized sender validateCollConsensusPubsub := authorizedSenderValidator.PubSubMessageValidator(channels.ConsensusCluster(clusterID)) pubsubResult := validateCollConsensusPubsub(pid, &messages.ClusterBlockProposal{}) - require.Equal(s.T(), pubsub.ValidationAccept, pubsubResult) + require.Equal(s.T(), p2p.ValidationAccept, pubsubResult) // validate collection sync cluster SyncRequest is not allowed to be sent on channel via unicast msgType, err = authorizedSenderValidator.Validate(pid, &messages.SyncRequest{}, channels.SyncCluster(clusterID), message.ProtocolUnicast) @@ -192,7 +192,7 @@ func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_ClusterPrefix // ensure SyncRequest is allowed to be sent via pubsub by authorized sender validateSyncClusterPubsub := authorizedSenderValidator.PubSubMessageValidator(channels.SyncCluster(clusterID)) pubsubResult = validateSyncClusterPubsub(pid, &messages.SyncRequest{}) - require.Equal(s.T(), pubsub.ValidationAccept, pubsubResult) + require.Equal(s.T(), p2p.ValidationAccept, pubsubResult) } // TestValidatorCallback_ValidationFailure checks that AuthorizedSenderValidator.Validate returns the expected validation error. @@ -212,7 +212,7 @@ func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_ValidationFai validatePubsub := authorizedSenderValidator.PubSubMessageValidator(channels.SyncCommittee) pubsubResult := validatePubsub(pid, &messages.SyncRequest{}) - require.Equal(s.T(), pubsub.ValidationReject, pubsubResult) + require.Equal(s.T(), p2p.ValidationReject, pubsubResult) }) s.Run("unknown message type", func() { @@ -239,14 +239,14 @@ func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_ValidationFai require.True(s.T(), message.IsUnknownMsgTypeErr(err)) require.Equal(s.T(), "", msgType) pubsubResult := validatePubsub(pid, m) - require.Equal(s.T(), pubsub.ValidationReject, pubsubResult) + require.Equal(s.T(), p2p.ValidationReject, pubsubResult) // nil messages are rejected msgType, err = authorizedSenderValidator.Validate(pid, nil, channels.ConsensusCommittee, message.ProtocolUnicast) require.True(s.T(), message.IsUnknownMsgTypeErr(err)) require.Equal(s.T(), "", msgType) pubsubResult = validatePubsub(pid, nil) - require.Equal(s.T(), pubsub.ValidationReject, pubsubResult) + require.Equal(s.T(), p2p.ValidationReject, pubsubResult) }) s.Run("sender is not staked getIdentityFunc does not return identity ", func() { @@ -266,7 +266,7 @@ func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_ValidationFai validatePubsub := authorizedSenderValidator.PubSubMessageValidator(channels.SyncCommittee) pubsubResult := validatePubsub(pid, &messages.SyncRequest{}) - require.Equal(s.T(), pubsub.ValidationReject, pubsubResult) + require.Equal(s.T(), p2p.ValidationReject, pubsubResult) }) } diff --git a/network/validator/pubsub/topic_validator.go b/network/validator/pubsub/topic_validator.go index c13d0365e3d..629328a8f36 100644 --- a/network/validator/pubsub/topic_validator.go +++ b/network/validator/pubsub/topic_validator.go @@ -11,6 +11,7 @@ import ( "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/codec" + "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/slashing" "github.com/onflow/flow-go/network" @@ -68,24 +69,24 @@ type TopicValidatorData struct { // TopicValidator is the topic validator that is registered with libP2P whenever a flow libP2P node subscribes to a topic. // The TopicValidator will decode and perform validation on the raw pubsub message. -func TopicValidator(log zerolog.Logger, c network.Codec, slashingViolationsConsumer slashing.ViolationsConsumer, peerFilter func(peer.ID) error, validators ...validator.PubSubMessageValidator) pubsub.ValidatorEx { +func TopicValidator(log zerolog.Logger, c network.Codec, slashingViolationsConsumer slashing.ViolationsConsumer, peerFilter func(peer.ID) error, validators ...validator.PubSubMessageValidator) p2p.TopicValidatorFunc { log = log.With(). - Str("component", "libp2p_node_topic_validator"). + Str("component", "libp2p-node-topic-validator"). Logger() - return func(ctx context.Context, receivedFrom peer.ID, rawMsg *pubsub.Message) pubsub.ValidationResult { + return func(ctx context.Context, receivedFrom peer.ID, rawMsg *pubsub.Message) p2p.ValidationResult { var msg message.Message // convert the incoming raw message payload to Message type //bs := binstat.EnterTimeVal(binstat.BinNet+":wire>1protobuf2message", int64(len(rawMsg.Data))) err := msg.Unmarshal(rawMsg.Data) //binstat.Leave(bs) if err != nil { - return pubsub.ValidationReject + return p2p.ValidationReject } from, err := messageSigningID(rawMsg) if err != nil { - return pubsub.ValidationReject + return p2p.ValidationReject } lg := log.With(). @@ -99,7 +100,7 @@ func TopicValidator(log zerolog.Logger, c network.Codec, slashingViolationsConsu Err(err). Bool(logging.KeySuspicious, true). Msg("filtering message from un-allowed peer") - return pubsub.ValidationReject + return p2p.ValidationReject } // verify ChannelID in message matches the topic over which the message was received @@ -109,7 +110,7 @@ func TopicValidator(log zerolog.Logger, c network.Codec, slashingViolationsConsu lg.Warn(). Bool(logging.KeySuspicious, true). Msg("could not convert topic to channel") - return pubsub.ValidationReject + return p2p.ValidationReject } lg = lg.With().Str("channel", msg.ChannelID).Logger() @@ -120,7 +121,7 @@ func TopicValidator(log zerolog.Logger, c network.Codec, slashingViolationsConsu Str("actual_channel", actualChannel.String()). Bool(logging.KeySuspicious, true). Msg("channel id in message does not match pubsub topic") - return pubsub.ValidationReject + return p2p.ValidationReject } // Convert message payload to a known message type @@ -131,11 +132,11 @@ func TopicValidator(log zerolog.Logger, c network.Codec, slashingViolationsConsu case codec.IsErrUnknownMsgCode(err): // slash peer if message contains unknown message code byte slashingViolationsConsumer.OnUnknownMsgTypeError(violation(from, channel, err)) - return pubsub.ValidationReject + return p2p.ValidationReject case codec.IsErrMsgUnmarshal(err) || codec.IsErrInvalidEncoding(err): // slash if peer sent a message that could not be marshalled into the message type denoted by the message code byte slashingViolationsConsumer.OnInvalidMsgError(violation(from, channel, err)) - return pubsub.ValidationReject + return p2p.ValidationReject default: // unexpected error condition. this indicates there's a bug // don't crash as a result of external inputs since that creates a DoS vector. @@ -143,7 +144,7 @@ func TopicValidator(log zerolog.Logger, c network.Codec, slashingViolationsConsu Err(fmt.Errorf("unexpected error while decoding message: %w", err)). Bool(logging.KeySuspicious, true). Msg("rejecting message") - return pubsub.ValidationReject + return p2p.ValidationReject } rawMsg.ValidatorData = TopicValidatorData{ @@ -152,12 +153,12 @@ func TopicValidator(log zerolog.Logger, c network.Codec, slashingViolationsConsu From: from, } - result := pubsub.ValidationAccept - for _, validator := range validators { - switch res := validator(from, decodedMsgPayload); res { - case pubsub.ValidationReject: + result := p2p.ValidationAccept + for _, v := range validators { + switch res := v(from, decodedMsgPayload); res { + case p2p.ValidationReject: return res - case pubsub.ValidationIgnore: + case p2p.ValidationIgnore: result = res } } diff --git a/network/validator/validator.go b/network/validator/validator.go index fb5a6f7ac1a..c85cacf95cb 100644 --- a/network/validator/validator.go +++ b/network/validator/validator.go @@ -1,8 +1,9 @@ package validator import ( - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" + + "github.com/onflow/flow-go/network/p2p" ) // MessageValidator validates the given message with original sender `from` and returns an error if validation fails @@ -10,6 +11,6 @@ import ( // Note: contrarily to pubsub.ValidatorEx, the peerID parameter does not represent the bearer of the message, but its source. type MessageValidator func(from peer.ID, msg interface{}) (string, error) -// PubSubMessageValidator validates the given message with original sender `from` and returns pubsub.ValidationResult. +// PubSubMessageValidator validates the given message with original sender `from` and returns p2p.ValidationResult. // Note: contrarily to pubsub.ValidatorEx, the peerID parameter does not represent the bearer of the message, but its source. -type PubSubMessageValidator func(from peer.ID, msg interface{}) pubsub.ValidationResult +type PubSubMessageValidator func(from peer.ID, msg interface{}) p2p.ValidationResult diff --git a/state/cluster/badger/mutator.go b/state/cluster/badger/mutator.go index 92f53b6332c..89135265e1e 100644 --- a/state/cluster/badger/mutator.go +++ b/state/cluster/badger/mutator.go @@ -125,7 +125,7 @@ func (m *MutableState) Extend(block *cluster.Block) error { // otherwise we would compromise liveness of the cluster. refBlock, err := m.headers.ByBlockID(payload.ReferenceBlockID) if errors.Is(err, storage.ErrNotFound) { - return state.NewInvalidExtensionErrorf("unknown reference block (id=%x)", payload.ReferenceBlockID) + return state.NewUnverifiableExtensionError("cluster block references unknown reference block (id=%x)", payload.ReferenceBlockID) } if err != nil { return fmt.Errorf("could not check reference block: %w", err) @@ -146,7 +146,7 @@ func (m *MutableState) Extend(block *cluster.Block) error { refBlock, err := m.headers.ByBlockID(flowTx.ReferenceBlockID) if errors.Is(err, storage.ErrNotFound) { // unknown reference blocks are invalid - return state.NewInvalidExtensionErrorf("unknown reference block (id=%x): %v", flowTx.ReferenceBlockID, err) + return state.NewUnverifiableExtensionError("collection contains tx (tx_id=%x) with unknown reference block (block_id=%x): %w", flowTx.ID(), flowTx.ReferenceBlockID, err) } if err != nil { return fmt.Errorf("could not check reference block (id=%x): %w", flowTx.ReferenceBlockID, err) diff --git a/state/cluster/badger/mutator_test.go b/state/cluster/badger/mutator_test.go index c985b7b73f5..83f52d2a288 100644 --- a/state/cluster/badger/mutator_test.go +++ b/state/cluster/badger/mutator_test.go @@ -18,6 +18,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/module/trace" + "github.com/onflow/flow-go/state" "github.com/onflow/flow-go/state/cluster" "github.com/onflow/flow-go/state/protocol" pbadger "github.com/onflow/flow-go/state/protocol/badger" @@ -244,6 +245,7 @@ func (suite *MutatorSuite) TestExtend_InvalidChainID() { err := suite.state.Extend(&block) suite.Assert().Error(err) + suite.Assert().True(state.IsInvalidExtensionError(err)) } func (suite *MutatorSuite) TestExtend_InvalidBlockNumber() { @@ -253,6 +255,7 @@ func (suite *MutatorSuite) TestExtend_InvalidBlockNumber() { err := suite.state.Extend(&block) suite.Assert().Error(err) + suite.Assert().True(state.IsInvalidExtensionError(err)) } func (suite *MutatorSuite) TestExtend_DuplicateTxInPayload() { @@ -265,6 +268,7 @@ func (suite *MutatorSuite) TestExtend_DuplicateTxInPayload() { // should fail to extend block with invalid payload err := suite.state.Extend(&block) suite.Assert().Error(err) + suite.Assert().True(state.IsInvalidExtensionError(err)) } func (suite *MutatorSuite) TestExtend_OnParentOfFinalized() { @@ -283,6 +287,7 @@ func (suite *MutatorSuite) TestExtend_OnParentOfFinalized() { // try to extend with the invalid block err = suite.state.Extend(&block2) suite.Assert().Error(err) + suite.Assert().True(state.IsOutdatedExtensionError(err)) } func (suite *MutatorSuite) TestExtend_Success() { @@ -312,7 +317,7 @@ func (suite *MutatorSuite) TestExtend_WithEmptyCollection() { suite.Assert().Nil(err) } -// an unknown reference block is invalid +// an unknown reference block is unverifiable func (suite *MutatorSuite) TestExtend_WithNonExistentReferenceBlock() { block := suite.Block() tx := suite.Tx() @@ -322,9 +327,10 @@ func (suite *MutatorSuite) TestExtend_WithNonExistentReferenceBlock() { block.SetPayload(payload) err := suite.state.Extend(&block) suite.Assert().Error(err) + suite.Assert().True(state.IsUnverifiableExtensionError(err)) } -// a collection with an expired reference block is a VALID extensino of chain state +// a collection with an expired reference block is a VALID extension of chain state func (suite *MutatorSuite) TestExtend_WithExpiredReferenceBlock() { // build enough blocks so that using genesis as a reference block causes // the collection to be expired @@ -378,6 +384,7 @@ func (suite *MutatorSuite) TestExtend_UnfinalizedBlockWithDupeTx() { // should be unable to extend block 2, as it contains a dupe transaction err = suite.state.Extend(&block2) suite.Assert().Error(err) + suite.Assert().True(state.IsInvalidExtensionError(err)) } func (suite *MutatorSuite) TestExtend_FinalizedBlockWithDupeTx() { @@ -404,6 +411,7 @@ func (suite *MutatorSuite) TestExtend_FinalizedBlockWithDupeTx() { // should be unable to extend block 2, as it contains a dupe transaction err = suite.state.Extend(&block2) suite.Assert().Error(err) + suite.Assert().True(state.IsInvalidExtensionError(err)) } func (suite *MutatorSuite) TestExtend_ConflictingForkWithDupeTx() { @@ -510,5 +518,6 @@ func (suite *MutatorSuite) TestExtend_LargeHistory() { block.SetPayload(payload) err = suite.state.Extend(&block) assert.Error(t, err) + suite.Assert().True(state.IsInvalidExtensionError(err)) }) } diff --git a/state/errors.go b/state/errors.go index 3de564abb60..92c144e613d 100644 --- a/state/errors.go +++ b/state/errors.go @@ -14,9 +14,11 @@ var ( ErrUnknownSnapshotReference = errors.New("reference block of the snapshot is not resolvable") ) -// InvalidExtensionError is an error for invalid extension of the state +// InvalidExtensionError is an error for invalid extension of the state. An invalid +// extension is distinct from outdated or unverifiable extensions, in that it indicates +// a malicious input. type InvalidExtensionError struct { - err error + error } func NewInvalidExtensionError(msg string) error { @@ -25,16 +27,12 @@ func NewInvalidExtensionError(msg string) error { func NewInvalidExtensionErrorf(msg string, args ...interface{}) error { return InvalidExtensionError{ - err: fmt.Errorf(msg, args...), + error: fmt.Errorf(msg, args...), } } func (e InvalidExtensionError) Unwrap() error { - return e.err -} - -func (e InvalidExtensionError) Error() string { - return e.err.Error() + return e.error } // IsInvalidExtensionError returns whether the given error is an InvalidExtensionError error @@ -47,7 +45,7 @@ func IsInvalidExtensionError(err error) bool { // Knowing whether an outdated extension is an invalid extension or not would // take more state queries. type OutdatedExtensionError struct { - err error + error } func NewOutdatedExtensionError(msg string) error { @@ -56,31 +54,51 @@ func NewOutdatedExtensionError(msg string) error { func NewOutdatedExtensionErrorf(msg string, args ...interface{}) error { return OutdatedExtensionError{ - err: fmt.Errorf(msg, args...), + error: fmt.Errorf(msg, args...), } } func (e OutdatedExtensionError) Unwrap() error { - return e.err -} - -func (e OutdatedExtensionError) Error() string { - return e.err.Error() + return e.error } func IsOutdatedExtensionError(err error) bool { return errors.As(err, &OutdatedExtensionError{}) } +// UnverifiableExtensionError represents a state extension (block) which cannot be +// verified at the moment. For example, it does not connect to the finalized state, +// or an entity referenced within the payload is unknown. +// Unlike InvalidExtensionError, this error is only used when the failure CANNOT be +// attributed to a malicious input, therefore this error can be treated as a benign failure. +type UnverifiableExtensionError struct { + error +} + +func NewUnverifiableExtensionError(msg string, args ...interface{}) error { + return UnverifiableExtensionError{ + error: fmt.Errorf(msg, args...), + } +} + +func (e UnverifiableExtensionError) Unwrap() error { + return e.error +} + +func IsUnverifiableExtensionError(err error) bool { + var errUnverifiableExtensionError UnverifiableExtensionError + return errors.As(err, &errUnverifiableExtensionError) +} + // NoValidChildBlockError is a sentinel error when the case where a certain block has // no valid child. type NoValidChildBlockError struct { - err error + error } func NewNoValidChildBlockError(msg string) error { return NoValidChildBlockError{ - err: fmt.Errorf(msg), + error: fmt.Errorf(msg), } } @@ -89,11 +107,7 @@ func NewNoValidChildBlockErrorf(msg string, args ...interface{}) error { } func (e NoValidChildBlockError) Unwrap() error { - return e.err -} - -func (e NoValidChildBlockError) Error() string { - return e.err.Error() + return e.error } func IsNoValidChildBlockError(err error) bool { @@ -104,26 +118,25 @@ func IsNoValidChildBlockError(err error) bool { // has not been ingested yet. type UnknownBlockError struct { blockID flow.Identifier - err error + error } // WrapAsUnknownBlockError wraps a given error as UnknownBlockError func WrapAsUnknownBlockError(blockID flow.Identifier, err error) error { return UnknownBlockError{ blockID: blockID, - err: fmt.Errorf("block %v has not been processed yet: %w", blockID, err), + error: fmt.Errorf("block %v has not been processed yet: %w", blockID, err), } } func NewUnknownBlockError(blockID flow.Identifier) error { return UnknownBlockError{ blockID: blockID, - err: fmt.Errorf("block %v has not been processed yet", blockID), + error: fmt.Errorf("block %v has not been processed yet", blockID), } } -func (e UnknownBlockError) Unwrap() error { return e.err } -func (e UnknownBlockError) Error() string { return e.err.Error() } +func (e UnknownBlockError) Unwrap() error { return e.error } func IsUnknownBlockError(err error) bool { var e UnknownBlockError diff --git a/state/protocol/badger/mutator.go b/state/protocol/badger/mutator.go index 8b7ca5eae81..d05f2f2b0cb 100644 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@ -116,13 +116,13 @@ func (m *FollowerState) Extend(ctx context.Context, candidate *flow.Block) error // check if the block header is a valid extension of the finalized state err := m.headerExtend(candidate) if err != nil { - return fmt.Errorf("header does not compliance the chain state: %w", err) + return fmt.Errorf("header not compliant with chain state: %w", err) } // find the last seal at the parent block last, err := m.lastSealed(candidate) if err != nil { - return fmt.Errorf("seal in parent block does not compliance the chain state: %w", err) + return fmt.Errorf("payload seal(s) not compliant with chain state: %w", err) } // insert the block and index the last seal for the block @@ -144,26 +144,26 @@ func (m *MutableState) Extend(ctx context.Context, candidate *flow.Block) error // check if the block header is a valid extension of the finalized state err := m.headerExtend(candidate) if err != nil { - return fmt.Errorf("header does not compliance the chain state: %w", err) + return fmt.Errorf("header not compliant with chain state: %w", err) } // check if the guarantees in the payload is a valid extension of the finalized state err = m.guaranteeExtend(ctx, candidate) if err != nil { - return fmt.Errorf("guarantee does not compliance the chain state: %w", err) + return fmt.Errorf("payload guarantee(s) not compliant with chain state: %w", err) } // check if the receipts in the payload are valid err = m.receiptExtend(ctx, candidate) if err != nil { - return fmt.Errorf("payload receipts not compliant with chain state: %w", err) + return fmt.Errorf("payload receipt(s) not compliant with chain state: %w", err) } // check if the seals in the payload is a valid extension of the finalized // state lastSeal, err := m.sealExtend(ctx, candidate) if err != nil { - return fmt.Errorf("seal in parent block does not compliance the chain state: %w", err) + return fmt.Errorf("payload seal(s) not compliant with chain state: %w", err) } // insert the block and index the last seal for the block @@ -240,8 +240,8 @@ func (m *FollowerState) headerExtend(candidate *flow.Block) error { // for instance: // A (Finalized) <- B (Finalized) <- C (Finalized) <- D <- E <- F // ^- G ^- H ^- I - // block G is not a valid block, because it does not include C which has been finalized. - // block H and I are a valid, because its their includes C. + // block G is not a valid block, because it does not have C (which has been finalized) as an ancestor + // block H and I are valid, because they do have C as an ancestor return state.NewOutdatedExtensionErrorf( "candidate block (height: %d) conflicts with finalized state (ancestor: %d final: %d)", header.Height, ancestor.Height, finalizedHeight) diff --git a/state/protocol/errors.go b/state/protocol/errors.go index e40404b4794..4fabb93f2d0 100644 --- a/state/protocol/errors.go +++ b/state/protocol/errors.go @@ -44,15 +44,15 @@ func IsIdentityNotFound(err error) bool { } type InvalidBlockTimestampError struct { - err error + error } func (e InvalidBlockTimestampError) Unwrap() error { - return e.err + return e.error } func (e InvalidBlockTimestampError) Error() string { - return e.err.Error() + return e.error.Error() } func IsInvalidBlockTimestampError(err error) bool { @@ -62,21 +62,17 @@ func IsInvalidBlockTimestampError(err error) bool { func NewInvalidBlockTimestamp(msg string, args ...interface{}) error { return InvalidBlockTimestampError{ - err: fmt.Errorf(msg, args...), + error: fmt.Errorf(msg, args...), } } // InvalidServiceEventError indicates an invalid service event was processed. type InvalidServiceEventError struct { - err error + error } func (e InvalidServiceEventError) Unwrap() error { - return e.err -} - -func (e InvalidServiceEventError) Error() string { - return e.err.Error() + return e.error } func IsInvalidServiceEventError(err error) bool { @@ -91,7 +87,7 @@ func NewInvalidServiceEventError(msg string, args ...interface{}) error { return state.NewInvalidExtensionErrorf( "cannot extend state with invalid service event: %w", InvalidServiceEventError{ - err: fmt.Errorf(msg, args...), + error: fmt.Errorf(msg, args...), }, ) } From e06d75ccd593726e274c6db0259cab209fcac3d2 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 23 Nov 2022 09:06:19 -0500 Subject: [PATCH 057/138] fix test build --- insecure/corruptlibp2p/libp2p_test.go | 13 ++++++------- insecure/corruptlibp2p/pubsubAdapter.go | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index 279f1b35f5c..5eac8a62cdd 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -3,7 +3,6 @@ package corruptlibp2p import ( "context" "github.com/libp2p/go-libp2p/core/peer" - "github.com/onflow/flow-go/insecure/corruptlibp2p/internal" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network/p2p" @@ -54,10 +53,10 @@ func TestSpam(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) - // create new spammer - gsr := internal.NewGossipSubRouterFixture() - spammer := NewSpammerGossipSub(gsr.Router) - - // start spamming the first peer - spammer.SpamIHave(peerIds[0], 10, 1) + //// create new spammer + //gsr := internal.NewGossipSubRouterFixture() + //spammer := NewSpammerGossipSub(gsr.Router) + // + //// start spamming the first peer + //spammer.SpamIHave(peerIds[0], 10, 1) } diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index cb68efcdf25..204aacd3758 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -87,7 +87,7 @@ func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h ho if err != nil { return nil, fmt.Errorf("failed to create gossipsub router: %w", err) } - // corruptRouter := internal.NewCorruptGossipSubRouter(router) + corruptRouter := internal.NewCorruptGossipSubRouter(router) gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, router, gossipSubConfig.Build()...) if err != nil { return nil, err From 4c1cdf759c71be42b97c79564afeb3d38b4bc6f8 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 10:02:49 -0800 Subject: [PATCH 058/138] burnishes insecure fixtures --- insecure/corruptlibp2p/fixtures.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/insecure/corruptlibp2p/fixtures.go b/insecure/corruptlibp2p/fixtures.go index feccab00fd1..17d623dbf77 100644 --- a/insecure/corruptlibp2p/fixtures.go +++ b/insecure/corruptlibp2p/fixtures.go @@ -8,6 +8,7 @@ import ( type GossipSubCtrlOption func(*pubsubpb.ControlMessage) +// GossipSubCtrlFixture returns a ControlMessage with the given options. func GossipSubCtrlFixture(opts ...GossipSubCtrlOption) *pubsubpb.ControlMessage { msg := &pubsubpb.ControlMessage{} for _, opt := range opts { @@ -16,34 +17,38 @@ func GossipSubCtrlFixture(opts ...GossipSubCtrlOption) *pubsubpb.ControlMessage return msg } +// WithIHave adds iHave control messages of the given size and number to the control message. func WithIHave(msgCount int, msgSize int) GossipSubCtrlOption { return func(msg *pubsubpb.ControlMessage) { iHaves := make([]*pubsubpb.ControlIHave, msgCount) for i := 0; i < msgCount; i++ { - topicId := topicIdFixture() + topicId := gossipSubTopicIdFixture() iHaves[i] = &pubsubpb.ControlIHave{ TopicID: &topicId, - MessageIDs: messageIdsFixture(msgSize), + MessageIDs: gossipSubMessageIdsFixture(msgSize), } } msg.Ihave = iHaves } } -func messageIdFixture() string { +// gossipSubMessageIdFixture returns a random gossipSub message ID. +func gossipSubMessageIdFixture() string { // TODO: messageID length should be a parameter. return unittest.GenerateRandomStringWithLen(10) } -func topicIdFixture() string { +// gossipSubTopicIdFixture returns a random gossipSub topic ID. +func gossipSubTopicIdFixture() string { // TODO: topicID length should be a parameter. return unittest.GenerateRandomStringWithLen(10) } -func messageIdsFixture(count int) []string { +// gossipSubMessageIdsFixture returns a slice of random gossipSub message IDs of the given size. +func gossipSubMessageIdsFixture(count int) []string { msgIds := make([]string, count) for i := 0; i < count; i++ { - msgIds[i] = messageIdFixture() + msgIds[i] = gossipSubMessageIdFixture() } return msgIds } From b296a65bc7b095d26bb55708495899e7ffde5f37 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 10:04:26 -0800 Subject: [PATCH 059/138] renames corrupt gossipsub router --- .../{mockGossipSubRouter.go => corruptGossipSubRouter.go} | 2 ++ 1 file changed, 2 insertions(+) rename insecure/corruptlibp2p/internal/{mockGossipSubRouter.go => corruptGossipSubRouter.go} (93%) diff --git a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go b/insecure/corruptlibp2p/internal/corruptGossipSubRouter.go similarity index 93% rename from insecure/corruptlibp2p/internal/mockGossipSubRouter.go rename to insecure/corruptlibp2p/internal/corruptGossipSubRouter.go index bb46282dcd6..9a268ff3300 100644 --- a/insecure/corruptlibp2p/internal/mockGossipSubRouter.go +++ b/insecure/corruptlibp2p/internal/corruptGossipSubRouter.go @@ -6,6 +6,8 @@ import ( pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" ) +// CorruptGossipSubRouter is a wrapper around GossipSubRouter that allows us to access the internal +// fields of the router for BFT testing and attack implementations. type CorruptGossipSubRouter struct { router *pubsub.GossipSubRouter } From 79236e337085d6586fc5d85461cde7cd35f30d81 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 10:09:47 -0800 Subject: [PATCH 060/138] adds documentation to corrupt topic --- .../corruptlibp2p/internal/corruptGossipSubRouter.go | 2 +- insecure/corruptlibp2p/internal/subscription.go | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/insecure/corruptlibp2p/internal/corruptGossipSubRouter.go b/insecure/corruptlibp2p/internal/corruptGossipSubRouter.go index 9a268ff3300..a203f71deff 100644 --- a/insecure/corruptlibp2p/internal/corruptGossipSubRouter.go +++ b/insecure/corruptlibp2p/internal/corruptGossipSubRouter.go @@ -7,7 +7,7 @@ import ( ) // CorruptGossipSubRouter is a wrapper around GossipSubRouter that allows us to access the internal -// fields of the router for BFT testing and attack implementations. +// fields of the router for BFT testing and attack implementations. type CorruptGossipSubRouter struct { router *pubsub.GossipSubRouter } diff --git a/insecure/corruptlibp2p/internal/subscription.go b/insecure/corruptlibp2p/internal/subscription.go index 74d22315665..8f40731d73c 100644 --- a/insecure/corruptlibp2p/internal/subscription.go +++ b/insecure/corruptlibp2p/internal/subscription.go @@ -9,6 +9,15 @@ import ( "github.com/onflow/flow-go/network/p2p" ) +// CorruptSubscription is a wrapper around the forked pubsub subscription from +// github.com/yhassanzadeh13/go-libp2p-pubsub that implements the p2p.Subscription. +// This is needed because in order to use the forked pubsub module, we need to +// use the entire dependency tree of the forked module which is resolved to +// github.com/yhassanzadeh13/go-libp2p-pubsub. This means that we cannot use +// the original libp2p pubsub module in the same package. +// Note: we use the forked pubsub module for sake of BFT testing and attack vector +// implementation, it is designed to be completely isolated in the "insecure" package, and +// totally separated from the rest of the codebase. type CorruptSubscription struct { s *corrupt.Subscription } From 10280157ba4d712099c38942af5ee014cb9f1198 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 10:13:25 -0800 Subject: [PATCH 061/138] adds documentation to corrupt topic --- insecure/corruptlibp2p/internal/topic.go | 27 ++++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/insecure/corruptlibp2p/internal/topic.go b/insecure/corruptlibp2p/internal/topic.go index 4b6103942f3..0b0c9c77156 100644 --- a/insecure/corruptlibp2p/internal/topic.go +++ b/insecure/corruptlibp2p/internal/topic.go @@ -8,17 +8,20 @@ import ( "github.com/onflow/flow-go/network/p2p" ) +// CorruptTopic is a wrapper around the forked pubsub topic from +// github.com/yhassanzadeh13/go-libp2p-pubsub that implements the p2p.Topic. +// This is needed because in order to use the forked pubsub module, we need to +// use the entire dependency tree of the forked module which is resolved to +// github.com/yhassanzadeh13/go-libp2p-pubsub. This means that we cannot use +// the original libp2p pubsub module in the same package. +// Note: we use the forked pubsub module for sake of BFT testing and attack vector +// implementation, it is designed to be completely isolated in the "insecure" package, and +// totally separated from the rest of the codebase. type CorruptTopic struct { t *corrupt.Topic } -func (c *CorruptTopic) Subscribe() (p2p.Subscription, error) { - sub, err := c.t.Subscribe() - if err != nil { - return nil, err - } - return NewCorruptSubscription(sub), nil -} +var _ p2p.Topic = (*CorruptTopic)(nil) func NewCorruptTopic(t *corrupt.Topic) p2p.Topic { return &CorruptTopic{ @@ -26,8 +29,6 @@ func NewCorruptTopic(t *corrupt.Topic) p2p.Topic { } } -var _ p2p.Topic = (*CorruptTopic)(nil) - func (c *CorruptTopic) String() string { return c.t.String() } @@ -39,3 +40,11 @@ func (c *CorruptTopic) Close() error { func (c *CorruptTopic) Publish(ctx context.Context, bytes []byte) error { return c.t.Publish(ctx, bytes) } + +func (c *CorruptTopic) Subscribe() (p2p.Subscription, error) { + sub, err := c.t.Subscribe() + if err != nil { + return nil, err + } + return NewCorruptSubscription(sub), nil +} From 2b17bde79bd80406ce947a60ac77c4048f65f72c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 13:21:19 -0800 Subject: [PATCH 062/138] refactors corrupt adapter --- insecure/corruptlibp2p/pubsubAdapter.go | 31 ++++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index d117c11ea82..aadca41f68a 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -12,8 +12,18 @@ import ( "github.com/onflow/flow-go/insecure/corruptlibp2p/internal" "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/utils/logging" ) +// CorruptGossipSubAdapter is a wrapper around the forked pubsub topic from +// github.com/yhassanzadeh13/go-libp2p-pubsub that implements the p2p.PubSubAdapter. +// This is needed because in order to use the forked pubsub module, we need to +// use the entire dependency tree of the forked module which is resolved to +// github.com/yhassanzadeh13/go-libp2p-pubsub. This means that we cannot use +// the original libp2p pubsub module in the same package. +// Note: we use the forked pubsub module for sake of BFT testing and attack vector +// implementation, it is designed to be completely isolated in the "insecure" package, and +// totally separated from the rest of the codebase. type CorruptGossipSubAdapter struct { gossipSub *corrupt.PubSub router *internal.CorruptGossipSubRouter @@ -23,7 +33,8 @@ type CorruptGossipSubAdapter struct { var _ p2p.PubSubAdapter = (*CorruptGossipSubAdapter)(nil) func (c *CorruptGossipSubAdapter) RegisterTopicValidator(topic string, topicValidator p2p.TopicValidatorFunc) error { - var v corrupt.ValidatorEx = func(ctx context.Context, from peer.ID, message *corrupt.Message) corrupt.ValidationResult { + // instantiates a corrupt.ValidatorEx that wraps the topicValidatorFunc + var corruptValidator corrupt.ValidatorEx = func(ctx context.Context, from peer.ID, message *corrupt.Message) corrupt.ValidationResult { pubsubMsg := &pubsub.Message{ Message: message.Message, // converting corrupt.Message to pubsub.Message ID: message.ID, @@ -35,6 +46,7 @@ func (c *CorruptGossipSubAdapter) RegisterTopicValidator(topic string, topicVali // overriding the corrupt.ValidationResult with the result from pubsub.TopicValidatorFunc message.ValidatorData = pubsubMsg.ValidatorData + switch result { case p2p.ValidationAccept: return corrupt.ValidationAccept @@ -47,10 +59,12 @@ func (c *CorruptGossipSubAdapter) RegisterTopicValidator(topic string, topicVali c.logger.Fatal().Msgf("invalid validation result: %v", result) } // should never happen, indicates a bug in the topic validator, but we need to return something - c.logger.Warn().Msg("invalid validation result, returning reject") + c.logger.Warn(). + Bool(logging.KeySuspicious, true). + Msg("invalid validation result, returning reject") return corrupt.ValidationReject } - return c.gossipSub.RegisterTopicValidator(topic, v, corrupt.WithValidatorInline(true)) + return c.gossipSub.RegisterTopicValidator(topic, corruptValidator, corrupt.WithValidatorInline(true)) } func (c *CorruptGossipSubAdapter) UnregisterTopicValidator(topic string) error { @@ -83,19 +97,24 @@ func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h ho return nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) } + // initializes a default gossipsub router and wraps it with the corrupt router. router, err := corrupt.DefaultGossipSubRouter(h) if err != nil { return nil, fmt.Errorf("failed to create gossipsub router: %w", err) } corruptRouter := internal.NewCorruptGossipSubRouter(router) + + // injects the corrupt router into the gossipsub constructor gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, corruptRouter, gossipSubConfig.Build()...) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create corrupt gossipsub: %w", err) } - return &CorruptGossipSubAdapter{ + adapter := &CorruptGossipSubAdapter{ gossipSub: gossipSub, router: corruptRouter, logger: logger, - }, nil + } + + return adapter, nil } From 7e93b77a3da050340e5f90b1af58dca38d435697 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 13:29:55 -0800 Subject: [PATCH 063/138] removes redundant test helpers --- network/internal/p2pfixtures/fixtures.go | 18 ++++++------------ network/p2p/dht/dht_test.go | 8 +++----- network/p2p/scoring/app_score_test.go | 7 +++---- .../p2p/scoring/subscription_validator_test.go | 11 +++-------- .../subscription/subscription_filter_test.go | 6 +++--- network/p2p/test/sporking_test.go | 9 ++++----- network/p2p/test/topic_validator_test.go | 10 +++++----- 7 files changed, 27 insertions(+), 42 deletions(-) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index e7c69e1a542..b52bab9764d 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -182,7 +182,7 @@ func SubsMustReceiveMessage(t *testing.T, ctx context.Context, expectedMessage [ } // SubMustNeverReceiveAnyMessage checks that the subscription never receives any message within the given timeout by the context. -func SubMustNeverReceiveAnyMessage(t *testing.T, ctx context.Context, sub *pubsub.Subscription) { +func SubMustNeverReceiveAnyMessage(t *testing.T, ctx context.Context, sub p2p.Subscription) { timeouted := make(chan struct{}) go func() { _, err := sub.Next(ctx) @@ -199,7 +199,7 @@ func SubMustNeverReceiveAnyMessage(t *testing.T, ctx context.Context, sub *pubsu // HasSubReceivedMessage checks that the subscription have received the given message within the given timeout by the context. // It returns true if the subscription has received the message, false otherwise. -func HasSubReceivedMessage(t *testing.T, ctx context.Context, expectedMessage []byte, sub *pubsub.Subscription) bool { +func HasSubReceivedMessage(t *testing.T, ctx context.Context, expectedMessage []byte, sub p2p.Subscription) bool { received := make(chan struct{}) go func() { msg, err := sub.Next(ctx) @@ -222,7 +222,7 @@ func HasSubReceivedMessage(t *testing.T, ctx context.Context, expectedMessage [] } // SubsMustNeverReceiveAnyMessage checks that all subscriptions never receive any message within the given timeout by the context. -func SubsMustNeverReceiveAnyMessage(t *testing.T, ctx context.Context, subs []*pubsub.Subscription) { +func SubsMustNeverReceiveAnyMessage(t *testing.T, ctx context.Context, subs []p2p.Subscription) { for _, sub := range subs { SubMustNeverReceiveAnyMessage(t, ctx, sub) } @@ -291,7 +291,7 @@ func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p. slashingViolationsConsumer, unittest.AllowAllPeerFilter())) require.NoError(t, err) - subs[i] = MustBePubSubSubscription(t, ps) + subs[i] = ps } // let subscriptions propagate @@ -317,7 +317,7 @@ func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p. func EnsureNoPubsubMessageExchange(t *testing.T, ctx context.Context, from []p2p.LibP2PNode, to []p2p.LibP2PNode, messageFactory func() (interface{}, channels.Topic)) { _, topic := messageFactory() - subs := make([]*pubsub.Subscription, len(to)) + subs := make([]p2p.Subscription, len(to)) svc := unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector()) tv := validator.TopicValidator( unittest.Logger(), @@ -333,7 +333,7 @@ func EnsureNoPubsubMessageExchange(t *testing.T, ctx context.Context, from []p2p for i, node := range to { s, err := node.Subscribe(topic, tv) require.NoError(t, err) - subs[i] = MustBePubSubSubscription(t, s) + subs[i] = s } // let subscriptions propagate @@ -469,9 +469,3 @@ func LongStringMessageFactoryFixture(t *testing.T) func() string { return fmt.Sprintf("%s %d \n", msg, time.Now().UnixNano()) // add timestamp to make sure we don't send the same message twice } } - -func MustBePubSubSubscription(t *testing.T, subscription p2p.Subscription) *pubsub.Subscription { - ps, ok := subscription.(*pubsub.Subscription) - require.True(t, ok) - return ps -} diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index bc3320f69b5..5501b087c61 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -6,7 +6,6 @@ import ( "time" golog "github.com/ipfs/go-log/v2" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/assert" @@ -16,8 +15,8 @@ import ( "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/message" + "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/dht" p2ptest "github.com/onflow/flow-go/network/p2p/test" flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" @@ -161,11 +160,10 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { topicValidator := flowpubsub.TopicValidator(logger, codec, unittest.NetworkSlashingViolationsConsumer(logger, metrics.NewNoopCollector()), unittest.AllowAllPeerFilter()) for _, n := range nodes { - ps, err := n.Subscribe(topic, topicValidator) + s, err := n.Subscribe(topic, topicValidator) require.NoError(t, err) - s := p2pfixtures.MustBePubSubSubscription(t, ps) - go func(s *pubsub.Subscription, nodeID peer.ID) { + go func(s p2p.Subscription, nodeID peer.ID) { msg, err := s.Next(ctx) require.NoError(t, err) require.NotNil(t, msg) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index ae1ae5014ad..11fba04213f 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -5,7 +5,6 @@ import ( "testing" "time" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" mocktestify "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -192,11 +191,11 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS require.NoError(t, err) // access node group - accessNodeSubs := make([]*pubsub.Subscription, len(accessNodeGroup)) + accessNodeSubs := make([]p2p.Subscription, len(accessNodeGroup)) for i, node := range accessNodeGroup { sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.NetworkCodec(), slashingViolationsConsumer, unittest.AllowAllPeerFilter())) - accessNodeSubs[i] = p2pfixtures.MustBePubSubSubscription(t, sub) require.NoError(t, err) + accessNodeSubs[i] = sub } // let nodes reside on a full topology, hence no partition is caused by the topology. @@ -214,7 +213,7 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS // If honest peer scoring is enabled, then con1Node and con2Node are certainly in the same mesh, and hence the message is delivered. ctx1s, cancel1s := context.WithTimeout(ctx, 1*time.Second) defer cancel1s() - return p2pfixtures.HasSubReceivedMessage(t, ctx1s, proposalMsg, p2pfixtures.MustBePubSubSubscription(t, con2Sub)) + return p2pfixtures.HasSubReceivedMessage(t, ctx1s, proposalMsg, con2Sub) } // maliciousAppSpecificScore returns a malicious app specific score function that rewards the malicious node and diff --git a/network/p2p/scoring/subscription_validator_test.go b/network/p2p/scoring/subscription_validator_test.go index fe01db76160..f2b8fbaf39c 100644 --- a/network/p2p/scoring/subscription_validator_test.go +++ b/network/p2p/scoring/subscription_validator_test.go @@ -11,7 +11,6 @@ import ( p2ptest "github.com/onflow/flow-go/network/p2p/test" flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" mocktestify "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -263,9 +262,7 @@ func TestSubscriptionValidator_Integration(t *testing.T) { ctx5s, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - p2pfixtures.SubsMustNeverReceiveAnyMessage(t, ctx5s, []*pubsub.Subscription{ - p2pfixtures.MustBePubSubSubscription(t, ver1SubBlocks), - p2pfixtures.MustBePubSubSubscription(t, ver2SubBlocks)}) + p2pfixtures.SubsMustNeverReceiveAnyMessage(t, ctx5s, []p2p.Subscription{ver1SubBlocks, ver2SubBlocks}) // moreover, a verification node publishing a message to the request chunk topic should not reach consensus node. // however, both verification nodes should receive the message. @@ -277,11 +274,9 @@ func TestSubscriptionValidator_Integration(t *testing.T) { ctx1s, cancel1s = context.WithTimeout(ctx, 1*time.Second) defer cancel1s() - p2pfixtures.SubsMustReceiveMessage(t, ctx1s, chunkDataPackRequestMsg, []p2p.Subscription{ - p2pfixtures.MustBePubSubSubscription(t, ver1SubChunks), - p2pfixtures.MustBePubSubSubscription(t, ver2SubChunks)}) + p2pfixtures.SubsMustReceiveMessage(t, ctx1s, chunkDataPackRequestMsg, []p2p.Subscription{ver1SubChunks, ver2SubChunks}) ctx5s, cancel5s = context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - p2pfixtures.SubsMustNeverReceiveAnyMessage(t, ctx5s, []*pubsub.Subscription{p2pfixtures.MustBePubSubSubscription(t, conSubChunks)}) + p2pfixtures.SubsMustNeverReceiveAnyMessage(t, ctx5s, []p2p.Subscription{conSubChunks}) } diff --git a/network/p2p/subscription/subscription_filter_test.go b/network/p2p/subscription/subscription_filter_test.go index d419b60ba18..bf2e73ff65a 100644 --- a/network/p2p/subscription/subscription_filter_test.go +++ b/network/p2p/subscription/subscription_filter_test.go @@ -78,7 +78,7 @@ func TestFilterSubscribe(t *testing.T) { var wg sync.WaitGroup wg.Add(2) - testPublish := func(wg *sync.WaitGroup, from p2p.LibP2PNode, sub *pubsub.Subscription) { + testPublish := func(wg *sync.WaitGroup, from p2p.LibP2PNode, sub p2p.Subscription) { data := []byte("hello") err := from.Publish(context.TODO(), badTopic, data) @@ -99,10 +99,10 @@ func TestFilterSubscribe(t *testing.T) { } // publish a message from node 1 and check that only node2 receives - testPublish(&wg, node1, p2pfixtures.MustBePubSubSubscription(t, sub2)) + testPublish(&wg, node1, sub2) // publish a message from node 2 and check that only node1 receives - testPublish(&wg, node2, p2pfixtures.MustBePubSubSubscription(t, sub1)) + testPublish(&wg, node2, sub1) unittest.RequireReturnsBefore(t, wg.Wait, 1*time.Second, "timeout performing publish test") } diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index 763f7914248..184cca6bc8a 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -5,7 +5,6 @@ import ( "testing" "time" - pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" "github.com/stretchr/testify/assert" @@ -221,7 +220,7 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { time.Sleep(time.Second) // assert that node 1 can successfully send a message to node 2 via PubSub - testOneToKMessagingSucceeds(ctx, t, node1, p2pfixtures.MustBePubSubSubscription(t, sub2), topicBeforeSpork) + testOneToKMessagingSucceeds(ctx, t, node1, sub2, topicBeforeSpork) // new root id after spork rootIDAfterSpork := unittest.IdentifierFixture() @@ -238,7 +237,7 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { require.NoError(t, err) // assert that node 1 can no longer send a message to node 2 via PubSub - testOneToKMessagingFails(ctx, t, node1, p2pfixtures.MustBePubSubSubscription(t, sub2), topicAfterSpork) + testOneToKMessagingFails(ctx, t, node1, sub2, topicAfterSpork) } func testOneToOneMessagingSucceeds(t *testing.T, sourceNode p2p.LibP2PNode, peerInfo peer.AddrInfo) { @@ -263,7 +262,7 @@ func testOneToOneMessagingFails(t *testing.T, sourceNode p2p.LibP2PNode, peerInf func testOneToKMessagingSucceeds(ctx context.Context, t *testing.T, sourceNode p2p.LibP2PNode, - dstnSub *pubsub.Subscription, + dstnSub p2p.Subscription, topic channels.Topic) { payload := createTestMessage(t) @@ -285,7 +284,7 @@ func testOneToKMessagingSucceeds(ctx context.Context, func testOneToKMessagingFails(ctx context.Context, t *testing.T, sourceNode p2p.LibP2PNode, - dstnSub *pubsub.Subscription, + dstnSub p2p.Subscription, topic channels.Topic) { payload := createTestMessage(t) diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index 0fe75c79a5e..0bf96f1f286 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -101,7 +101,7 @@ func TestTopicValidator_Unstaked(t *testing.T) { // sn1 should not receive message from sn2 because sn2 is unstaked timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) defer cancel1s() - p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, p2pfixtures.MustBePubSubSubscription(t, sub1)) + p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) // ensure the correct error is contained in the logged error require.Contains(t, hook.Logs(), "filtering message from un-allowed peer") @@ -375,12 +375,12 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { // sn1 does NOT receive the message due to the topic validator timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) defer cancel1s() - p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, p2pfixtures.MustBePubSubSubscription(t, sub1)) + p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) // sn2 also does not receive the message via gossip from the sn1 (event after the 1 second hearbeat) timedCtx, cancel2s = context.WithTimeout(ctx, 2*time.Second) defer cancel2s() - p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, p2pfixtures.MustBePubSubSubscription(t, sub2)) + p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub2) unittest.RequireReturnsBefore(t, wg.Wait, 5*time.Second, "could not receive message on time") @@ -455,7 +455,7 @@ func TestAuthorizedSenderValidator_InvalidMsg(t *testing.T) { // sn1 should not receive message from sn2 timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) defer cancel1s() - p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, p2pfixtures.MustBePubSubSubscription(t, sub1)) + p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) // ensure the correct error is contained in the logged error require.Contains(t, hook.Logs(), message.ErrUnauthorizedMessageOnChannel.Error()) @@ -553,7 +553,7 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { // sn1 should not receive rejected message from ejected sn2 timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) defer cancel1s() - p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, p2pfixtures.MustBePubSubSubscription(t, sub1)) + p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) // ensure the correct error is contained in the logged error require.Contains(t, hook.Logs(), validator.ErrSenderEjected.Error()) From 7ea4970919e99167c804db8681f422eabdb22016 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 13:31:34 -0800 Subject: [PATCH 064/138] burnishes corrupt configs --- insecure/corruptlibp2p/pubsubAdapterConfig.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/insecure/corruptlibp2p/pubsubAdapterConfig.go b/insecure/corruptlibp2p/pubsubAdapterConfig.go index 14a0810a76b..ded935a20a5 100644 --- a/insecure/corruptlibp2p/pubsubAdapterConfig.go +++ b/insecure/corruptlibp2p/pubsubAdapterConfig.go @@ -9,6 +9,15 @@ import ( "github.com/onflow/flow-go/network/p2p" ) +// CorruptPubSubAdapterConfig is a wrapper around the forked pubsub topic from +// github.com/yhassanzadeh13/go-libp2p-pubsub that implements the p2p.PubSubAdapterConfig. +// This is needed because in order to use the forked pubsub module, we need to +// use the entire dependency tree of the forked module which is resolved to +// github.com/yhassanzadeh13/go-libp2p-pubsub. This means that we cannot use +// the original libp2p pubsub module in the same package. +// Note: we use the forked pubsub module for sake of BFT testing and attack vector +// implementation, it is designed to be completely isolated in the "insecure" package, and +// totally separated from the rest of the codebase. type CorruptPubSubAdapterConfig struct { options []corrupt.Option } From 677e23c00860cf9bc9dd2d07d0debed434ea8c54 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 13:33:03 -0800 Subject: [PATCH 065/138] burnishes spammer gossipsub --- insecure/corruptlibp2p/spammerGossipSub.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/insecure/corruptlibp2p/spammerGossipSub.go b/insecure/corruptlibp2p/spammerGossipSub.go index 5aa7a22dbaf..023771e0d30 100644 --- a/insecure/corruptlibp2p/spammerGossipSub.go +++ b/insecure/corruptlibp2p/spammerGossipSub.go @@ -7,6 +7,8 @@ import ( type ControlMessage int +// SpammerGossipSub is a wrapper around the GossipSubRouter that allows us to +// spam the victim with junk control messages. type SpammerGossipSub struct { router *pubsub.GossipSubRouter } @@ -26,3 +28,5 @@ func (s *SpammerGossipSub) SpamIHave(victim peer.ID, msgCount int, msgSize int) s.router.SendControl(victim, ctlIHave) } } + +// TODO: SpamIWant, SpamGraft, SpamPrune. From 4469543020ab6325cb0e7bccf9e4452a7172dbdd Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 13:41:41 -0800 Subject: [PATCH 066/138] removes spammer gossipsub test --- .../corruptlibp2p/spammerGossipSub_test.go | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 insecure/corruptlibp2p/spammerGossipSub_test.go diff --git a/insecure/corruptlibp2p/spammerGossipSub_test.go b/insecure/corruptlibp2p/spammerGossipSub_test.go deleted file mode 100644 index 3215c3491e4..00000000000 --- a/insecure/corruptlibp2p/spammerGossipSub_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package corruptlibp2p - -import ( - "context" - "testing" - "time" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/network/p2p" - p2ptest "github.com/onflow/flow-go/network/p2p/test" - "github.com/onflow/flow-go/utils/unittest" -) - -func TestSpammerGossipSub(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - sporkId := unittest.IdentifierFixture() - defer cancel() - - count := 5 - nodes := make([]p2p.LibP2PNode, 0, 5) - ids := flow.IdentityList{} - - for i := 0; i < count; i++ { - handler, _ := p2ptest.StreamHandlerFixture(t) - node, id := p2ptest.NodeFixture( - t, - sporkId, - t.Name(), - p2ptest.WithRole(flow.RoleConsensus), - p2ptest.WithDefaultStreamHandler(handler), - ) - - nodes = append(nodes, node) - ids = append(ids, &id) - } - - p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) - defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) - - p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) -} From 77d87205f729508cc5032f3d9694ec2bacafe825 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 13:44:19 -0800 Subject: [PATCH 067/138] burnishes corrupt libp2p --- insecure/corruptnet/libp2p_node_factory.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptnet/libp2p_node_factory.go index bc04e348170..c71ae01b493 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptnet/libp2p_node_factory.go @@ -60,12 +60,16 @@ func NewCorruptLibP2PNodeFactory( } } +// corruptibleGossipSubFactory returns a factory function that creates a new instance of the forked gossipsub module from +// github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. func corruptibleGossipSubFactory() p2pbuilder.GossipSubFactoryFuc { return func(ctx context.Context, logger zerolog.Logger, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { return corruptlibp2p.NewCorruptGossipSubAdapter(ctx, logger, host, cfg) } } +// corruptibleGossipSubConfigFactory returns a factory function that creates a new instance of the forked gossipsub config +// from github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. func corruptibleGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { return corruptlibp2p.NewCorruptPubSubAdapterConfig(base) From d4a024f8b01d0a5d5c0615da965ea80a79af18e0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 16:13:01 -0800 Subject: [PATCH 068/138] wires up gossipsub to tests --- insecure/corruptlibp2p/pubsubAdapter.go | 12 ++--- insecure/corruptnet/libp2p_node_factory.go | 23 +--------- .../internal/corruptGossipSubRouter.go | 0 insecure/internal/factory.go | 45 +++++++++++++++++++ .../internal/subscription.go | 0 .../{corruptlibp2p => }/internal/topic.go | 0 network/p2p/p2pbuilder/libp2pNodeBuilder.go | 6 --- network/p2p/test/fixtures.go | 6 +++ 8 files changed, 59 insertions(+), 33 deletions(-) rename insecure/{corruptlibp2p => }/internal/corruptGossipSubRouter.go (100%) create mode 100644 insecure/internal/factory.go rename insecure/{corruptlibp2p => }/internal/subscription.go (100%) rename insecure/{corruptlibp2p => }/internal/topic.go (100%) diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index aadca41f68a..78057ae16da 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -10,7 +10,7 @@ import ( "github.com/rs/zerolog" corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" - "github.com/onflow/flow-go/insecure/corruptlibp2p/internal" + "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/utils/logging" ) @@ -91,23 +91,23 @@ func (c *CorruptGossipSubAdapter) GetRouter() *internal.CorruptGossipSubRouter { return c.router } -func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { +func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, *internal.CorruptGossipSubRouter, error) { gossipSubConfig, ok := cfg.(*CorruptPubSubAdapterConfig) if !ok { - return nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) + return nil, nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) } // initializes a default gossipsub router and wraps it with the corrupt router. router, err := corrupt.DefaultGossipSubRouter(h) if err != nil { - return nil, fmt.Errorf("failed to create gossipsub router: %w", err) + return nil, nil, fmt.Errorf("failed to create gossipsub router: %w", err) } corruptRouter := internal.NewCorruptGossipSubRouter(router) // injects the corrupt router into the gossipsub constructor gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, corruptRouter, gossipSubConfig.Build()...) if err != nil { - return nil, fmt.Errorf("failed to create corrupt gossipsub: %w", err) + return nil, nil, fmt.Errorf("failed to create corrupt gossipsub: %w", err) } adapter := &CorruptGossipSubAdapter{ @@ -116,5 +116,5 @@ func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h ho logger: logger, } - return adapter, nil + return adapter, corruptRouter, nil } diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptnet/libp2p_node_factory.go index c71ae01b493..c8acd77c51e 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptnet/libp2p_node_factory.go @@ -1,12 +1,9 @@ package corruptnet import ( - "context" "time" - "github.com/libp2p/go-libp2p/core/host" - - "github.com/onflow/flow-go/insecure/corruptlibp2p" + "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/network/p2p" madns "github.com/multiformats/go-multiaddr-dns" @@ -55,23 +52,7 @@ func NewCorruptLibP2PNodeFactory( connectionPruning, updateInterval) builder.SetCreateNode(NewCorruptLibP2PNode) - builder.SetGossipSubFactory(corruptibleGossipSubFactory(), corruptibleGossipSubConfigFactory()) + internal.OverrideWithCorruptGossipSub(builder) return builder.Build() } } - -// corruptibleGossipSubFactory returns a factory function that creates a new instance of the forked gossipsub module from -// github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. -func corruptibleGossipSubFactory() p2pbuilder.GossipSubFactoryFuc { - return func(ctx context.Context, logger zerolog.Logger, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { - return corruptlibp2p.NewCorruptGossipSubAdapter(ctx, logger, host, cfg) - } -} - -// corruptibleGossipSubConfigFactory returns a factory function that creates a new instance of the forked gossipsub config -// from github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. -func corruptibleGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { - return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { - return corruptlibp2p.NewCorruptPubSubAdapterConfig(base) - } -} diff --git a/insecure/corruptlibp2p/internal/corruptGossipSubRouter.go b/insecure/internal/corruptGossipSubRouter.go similarity index 100% rename from insecure/corruptlibp2p/internal/corruptGossipSubRouter.go rename to insecure/internal/corruptGossipSubRouter.go diff --git a/insecure/internal/factory.go b/insecure/internal/factory.go new file mode 100644 index 00000000000..3a357564692 --- /dev/null +++ b/insecure/internal/factory.go @@ -0,0 +1,45 @@ +package internal + +import ( + "context" + + "github.com/libp2p/go-libp2p/core/host" + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/insecure/corruptlibp2p" + "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network/p2p/p2pbuilder" + p2ptest "github.com/onflow/flow-go/network/p2p/test" +) + +func WithCorruptGossipSub(factory p2pbuilder.GossipSubFactoryFuc, config p2pbuilder.GossipSubAdapterConfigFunc) p2ptest.NodeFixtureParameterOption { + return func(p *p2ptest.NodeFixtureParameters) { + p.GossipSubFactory = factory + p.GossipSubConfig = config + } +} + +// CorruptibleGossipSubFactory returns a factory function that creates a new instance of the forked gossipsub module from +// github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. +func CorruptibleGossipSubFactory() (p2pbuilder.GossipSubFactoryFuc, *CorruptGossipSubRouter) { + var rt *CorruptGossipSubRouter + factory := func(ctx context.Context, logger zerolog.Logger, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { + adapter, router, err := corruptlibp2p.NewCorruptGossipSubAdapter(ctx, logger, host, cfg) + rt = router + return adapter, err + } + return factory, rt +} + +// CorruptibleGossipSubConfigFactory returns a factory function that creates a new instance of the forked gossipsub config +// from github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. +func CorruptibleGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { + return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { + return corruptlibp2p.NewCorruptPubSubAdapterConfig(base) + } +} + +func OverrideWithCorruptGossipSub(builder p2pbuilder.NodeBuilder) { + factory, _ := CorruptibleGossipSubFactory() + builder.SetGossipSubFactory(factory, CorruptibleGossipSubConfigFactory()) +} diff --git a/insecure/corruptlibp2p/internal/subscription.go b/insecure/internal/subscription.go similarity index 100% rename from insecure/corruptlibp2p/internal/subscription.go rename to insecure/internal/subscription.go diff --git a/insecure/corruptlibp2p/internal/topic.go b/insecure/internal/topic.go similarity index 100% rename from insecure/corruptlibp2p/internal/topic.go rename to insecure/internal/topic.go diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index cf8764663e2..91240c47eba 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -9,7 +9,6 @@ import ( "github.com/libp2p/go-libp2p" pubsub "github.com/libp2p/go-libp2p-pubsub" - pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/config" "github.com/libp2p/go-libp2p/core/connmgr" "github.com/libp2p/go-libp2p/core/host" @@ -69,11 +68,6 @@ func DefaultLibP2PNodeFactory( } } -// DefaultMessageIDFunction returns a default message ID function based on the message's data -func DefaultMessageIDFunction(msg *pb.Message) string { - return utils.MessageID(msg.Data) -} - type NodeBuilder interface { SetBasicResolver(madns.BasicResolver) NodeBuilder SetSubscriptionFilter(pubsub.SubscriptionFilter) NodeBuilder diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 9f1be2f7428..e8c7061be0a 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -109,6 +109,10 @@ func NodeFixture( builder.SetPeerManagerOptions(parameters.ConnectionPruning, parameters.UpdateInterval) } + if parameters.GossipSubFactory != nil && parameters.GossipSubConfig != nil { + builder.SetGossipSubFactory(parameters.GossipSubFactory, parameters.GossipSubConfig) + } + n, err := builder.Build() require.NoError(t, err) @@ -143,6 +147,8 @@ type NodeFixtureParameters struct { UpdateInterval time.Duration // peer manager parameter PeerProvider p2p.PeersProvider // peer manager parameter ConnGater connmgr.ConnectionGater + GossipSubFactory p2pbuilder.GossipSubFactoryFuc + GossipSubConfig p2pbuilder.GossipSubAdapterConfigFunc } func WithPeerScoringEnabled(idProvider module.IdentityProvider) NodeFixtureParameterOption { From 9607800d81979d31042395be3d96c60ecd1539d2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 17:16:08 -0800 Subject: [PATCH 069/138] burnishes gossipsub adapter --- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 2 +- network/p2p/p2pnode/gossipSubAdapter.go | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 91240c47eba..b6b139261ba 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -344,7 +344,7 @@ func defaultLibP2POptions(address string, key fcrypto.PrivateKey) ([]config.Opti return nil, fmt.Errorf("failed to translate Flow address to Libp2p multiaddress: %w", err) } - // create a t which disables port reuse and web socket. + // create a transport which disables port reuse and web socket. // Port reuse enables listening and dialing from the same TCP port (https://github.com/libp2p/go-reuseport) // While this sounds great, it intermittently causes a 'broken pipe' error // as the 1-k discovery process and the 1-1 messaging both sometimes attempt to open connection to the same target diff --git a/network/p2p/p2pnode/gossipSubAdapter.go b/network/p2p/p2pnode/gossipSubAdapter.go index be6dbc3b00e..d13406fd58a 100644 --- a/network/p2p/p2pnode/gossipSubAdapter.go +++ b/network/p2p/p2pnode/gossipSubAdapter.go @@ -10,8 +10,11 @@ import ( "github.com/rs/zerolog" "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/utils/logging" ) +// GossipSubAdapter is a wrapper around the libp2p GossipSub implementation +// that implements the PubSubAdapter interface for the Flow network. type GossipSubAdapter struct { gossipSub *pubsub.PubSub logger zerolog.Logger @@ -36,6 +39,7 @@ func NewGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h host.Host } func (g *GossipSubAdapter) RegisterTopicValidator(topic string, topicValidator p2p.TopicValidatorFunc) error { + // wrap the topic validator function into a libp2p topic validator function. var v pubsub.ValidatorEx = func(ctx context.Context, from peer.ID, message *pubsub.Message) pubsub.ValidationResult { switch result := topicValidator(ctx, from, message); result { case p2p.ValidationAccept: @@ -49,7 +53,9 @@ func (g *GossipSubAdapter) RegisterTopicValidator(topic string, topicValidator p g.logger.Fatal().Msgf("invalid validation result: %v", result) } // should never happen, indicates a bug in the topic validator, but we need to return something - g.logger.Warn().Msg("invalid validation result, returning reject") + g.logger.Warn(). + Bool(logging.KeySuspicious, true). + Msg("invalid validation result, returning reject") return pubsub.ValidationReject } @@ -63,7 +69,7 @@ func (g *GossipSubAdapter) UnregisterTopicValidator(topic string) error { func (g *GossipSubAdapter) Join(topic string) (p2p.Topic, error) { t, err := g.gossipSub.Join(topic) if err != nil { - return nil, err + return nil, fmt.Errorf("could not join topic %s: %w", topic, err) } return NewGossipSubTopic(t), nil } From 09e59c140030a5cb38061500eb9d79431eb324cf Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 17:18:39 -0800 Subject: [PATCH 070/138] adds godoc --- network/p2p/p2pnode/gossipSubAdapterConfig.go | 4 ++-- network/p2p/p2pnode/gossipSubTopic.go | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/gossipSubAdapterConfig.go b/network/p2p/p2pnode/gossipSubAdapterConfig.go index aa57c04919f..9c6237081b8 100644 --- a/network/p2p/p2pnode/gossipSubAdapterConfig.go +++ b/network/p2p/p2pnode/gossipSubAdapterConfig.go @@ -9,6 +9,8 @@ import ( "github.com/onflow/flow-go/network/p2p" ) +// GossipSubAdapterConfig is a wrapper around libp2p pubsub options that +// implements the PubSubAdapterConfig interface for the Flow network. type GossipSubAdapterConfig struct { options []pubsub.Option } @@ -50,5 +52,3 @@ func defaultPubsubOptions(base *p2p.BasePubSubAdapterConfig) []pubsub.Option { pubsub.WithMaxMessageSize(base.MaxMessageSize), } } - -var _ p2p.PubSubAdapterConfig = (*GossipSubAdapterConfig)(nil) diff --git a/network/p2p/p2pnode/gossipSubTopic.go b/network/p2p/p2pnode/gossipSubTopic.go index 3f2f031bee8..52625e7a814 100644 --- a/network/p2p/p2pnode/gossipSubTopic.go +++ b/network/p2p/p2pnode/gossipSubTopic.go @@ -8,6 +8,8 @@ import ( "github.com/onflow/flow-go/network/p2p" ) +// GossipSubTopic is a wrapper around libp2p pubsub topics that implements the +// PubSubTopic interface for the Flow network. type GossipSubTopic struct { t *pubsub.Topic } From c9ee87c2335ffd27cbee0df43af1719386a044e9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 17:20:05 -0800 Subject: [PATCH 071/138] adds comment --- insecure/internal/subscription.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/insecure/internal/subscription.go b/insecure/internal/subscription.go index 8f40731d73c..3c6125853ee 100644 --- a/insecure/internal/subscription.go +++ b/insecure/internal/subscription.go @@ -39,6 +39,11 @@ func (c *CorruptSubscription) Next(ctx context.Context) (*pubsub.Message, error) if err != nil { return nil, err } + + // we read a corrupt.Message from the corrupt.Subscription, however, we need to return + // a pubsub.Message to the caller of this function, so we need to convert the corrupt.Message. + // Flow codebase uses the original libp2p pubsub module, and the pubsub.Message is defined + // in the original libp2p pubsub module, so we cannot use the corrupt.Message in the Flow codebase. return &pubsub.Message{ Message: m.Message, ID: m.ID, From f41885295ebb5930a23996633ec8b1ebfe6aaaf7 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 17:43:05 -0800 Subject: [PATCH 072/138] adds godoc --- insecure/corruptlibp2p/pubsubAdapterConfig.go | 2 +- insecure/internal/subscription.go | 2 +- network/p2p/mock/pub_sub_adapter_config.go | 2 +- network/p2p/p2pnode/gossipSubAdapterConfig.go | 2 +- network/p2p/pubsub.go | 47 +++++++++++++++++-- 5 files changed, 48 insertions(+), 7 deletions(-) diff --git a/insecure/corruptlibp2p/pubsubAdapterConfig.go b/insecure/corruptlibp2p/pubsubAdapterConfig.go index ded935a20a5..19762c2ceee 100644 --- a/insecure/corruptlibp2p/pubsubAdapterConfig.go +++ b/insecure/corruptlibp2p/pubsubAdapterConfig.go @@ -38,7 +38,7 @@ func (c *CorruptPubSubAdapterConfig) WithSubscriptionFilter(filter p2p.Subscript c.options = append(c.options, corrupt.WithSubscriptionFilter(filter)) } -func (c *CorruptPubSubAdapterConfig) WithScoreOption(_ p2p.ScoreOption) { +func (c *CorruptPubSubAdapterConfig) WithScoreOption(_ p2p.ScoreOptionBuilder) { // Corrupt does not support score options. This is a no-op. } diff --git a/insecure/internal/subscription.go b/insecure/internal/subscription.go index 3c6125853ee..60df67de909 100644 --- a/insecure/internal/subscription.go +++ b/insecure/internal/subscription.go @@ -43,7 +43,7 @@ func (c *CorruptSubscription) Next(ctx context.Context) (*pubsub.Message, error) // we read a corrupt.Message from the corrupt.Subscription, however, we need to return // a pubsub.Message to the caller of this function, so we need to convert the corrupt.Message. // Flow codebase uses the original libp2p pubsub module, and the pubsub.Message is defined - // in the original libp2p pubsub module, so we cannot use the corrupt.Message in the Flow codebase. + // in the original libp2p pubsub module, so we cannot use the corrupt.Message in the Flow codebase. return &pubsub.Message{ Message: m.Message, ID: m.ID, diff --git a/network/p2p/mock/pub_sub_adapter_config.go b/network/p2p/mock/pub_sub_adapter_config.go index 452c3bf1c0d..739236d7ffb 100644 --- a/network/p2p/mock/pub_sub_adapter_config.go +++ b/network/p2p/mock/pub_sub_adapter_config.go @@ -25,7 +25,7 @@ func (_m *PubSubAdapterConfig) WithRoutingDiscovery(_a0 routing.ContentRouting) } // WithScoreOption provides a mock function with given fields: _a0 -func (_m *PubSubAdapterConfig) WithScoreOption(_a0 p2p.ScoreOption) { +func (_m *PubSubAdapterConfig) WithScoreOption(_a0 p2p.ScoreOptionBuilder) { _m.Called(_a0) } diff --git a/network/p2p/p2pnode/gossipSubAdapterConfig.go b/network/p2p/p2pnode/gossipSubAdapterConfig.go index 9c6237081b8..f0c92876211 100644 --- a/network/p2p/p2pnode/gossipSubAdapterConfig.go +++ b/network/p2p/p2pnode/gossipSubAdapterConfig.go @@ -31,7 +31,7 @@ func (g *GossipSubAdapterConfig) WithSubscriptionFilter(filter p2p.SubscriptionF g.options = append(g.options, pubsub.WithSubscriptionFilter(filter)) } -func (g *GossipSubAdapterConfig) WithScoreOption(option p2p.ScoreOption) { +func (g *GossipSubAdapterConfig) WithScoreOption(option p2p.ScoreOptionBuilder) { g.options = append(g.options, option.BuildFlowPubSubScoreOption()) } diff --git a/network/p2p/pubsub.go b/network/p2p/pubsub.go index f651d778371..5f168b4418a 100644 --- a/network/p2p/pubsub.go +++ b/network/p2p/pubsub.go @@ -19,43 +19,84 @@ const ( type TopicValidatorFunc func(context.Context, peer.ID, *pubsub.Message) ValidationResult +// PubSubAdapter is the abstraction of the underlying pubsub logic that is used by the Flow network. type PubSubAdapter interface { + // RegisterTopicValidator registers a validator for topic. RegisterTopicValidator(topic string, topicValidator TopicValidatorFunc) error + + // UnregisterTopicValidator removes a validator from a topic. + // Returns an error if there was no validator registered with the topic. UnregisterTopicValidator(topic string) error + + // Join joins the topic and returns a Topic handle. + // Only one Topic handle should exist per topic, and Join will error if the Topic handle already exists. Join(topic string) (Topic, error) + + // GetTopics returns all the topics within the pubsub network that the current peer has subscribed to. GetTopics() []string + + // ListPeers returns all the peers subscribed to a topic. + // Note that the current peer must be subscribed to the topic for it to query for other peers. + // If the current peer is not subscribed to the topic, an empty list is returned. + // For example, if current peer has subscribed to topics A and B, then ListPeers only return + // subscribed peers for topics A and B, and querying for topic C will return an empty list. ListPeers(topic string) []peer.ID } +// PubSubAdapterConfig abstracts the configuration for the underlying pubsub implementation. type PubSubAdapterConfig interface { WithRoutingDiscovery(routing.ContentRouting) WithSubscriptionFilter(SubscriptionFilter) - WithScoreOption(ScoreOption) + WithScoreOption(ScoreOptionBuilder) WithMessageIdFunction(f func([]byte) string) } +// Topic is the abstraction of the underlying pubsub topic that is used by the Flow network. type Topic interface { + // String returns the topic name as a string. String() string + + // Close closes the topic. Close() error + + // Publish publishes a message to the topic. Publish(context.Context, []byte) error + + // Subscribe returns a subscription to the topic so that the caller can receive messages from the topic. Subscribe() (Subscription, error) } -type ScoreOption interface { +// ScoreOptionBuilder abstracts the configuration for the underlying pubsub score implementation. +type ScoreOptionBuilder interface { + // BuildFlowPubSubScoreOption builds the pubsub score options as pubsub.Option for the Flow network. BuildFlowPubSubScoreOption() pubsub.Option } +// Subscription is the abstraction of the underlying pubsub subscription that is used by the Flow network. type Subscription interface { + // Cancel cancels the subscription so that the caller will no longer receive messages from the topic. Cancel() + + // Topic returns the topic that the subscription is subscribed to. Topic() string + + // Next returns the next message from the subscription. Next(context.Context) (*pubsub.Message, error) } +// BasePubSubAdapterConfig is the base configuration for the underlying pubsub implementation. +// These configurations are common to all pubsub implementations and must be observed by all implementations. type BasePubSubAdapterConfig struct { + // MaxMessageSize is the maximum size of a message that can be sent on the pubsub network. MaxMessageSize int } +// SubscriptionFilter is the abstraction of the underlying pubsub subscription filter that is used by the Flow network. type SubscriptionFilter interface { + // CanSubscribe returns true if the current peer can subscribe to the topic. CanSubscribe(string) bool - FilterIncomingSubscriptions(from peer.ID, opts []*pb.RPC_SubOpts) ([]*pb.RPC_SubOpts, error) + + // FilterIncomingSubscriptions is invoked for all RPCs containing subscription notifications. + // It filters and returns the subscriptions of interest to the current node. + FilterIncomingSubscriptions(peer.ID, []*pb.RPC_SubOpts) ([]*pb.RPC_SubOpts, error) } From da82337fcb566659d14a9174661ac1b16837c269 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 17:50:52 -0800 Subject: [PATCH 073/138] fixes build errors --- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 2 +- network/p2p/test/fixtures.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 4c1f42ed9c9..6ef4d566fd2 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -119,7 +119,7 @@ func NewNodeBuilder( createNode: DefaultCreateNodeFunc, gossipSubFactory: defaultGossipSubFactory(), gossipSubConfigFunc: defaultGossipSubAdapterConfig(), - metrics: metrics, + metrics: metrics, } } diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index e8c7061be0a..4bce1d65816 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -79,7 +79,12 @@ func NodeFixture( connManager := connection.NewConnManager(logger, noopMetrics) resourceManager := testutils.NewResourceManager(t) - builder := p2pbuilder.NewNodeBuilder(logger, parameters.Address, parameters.Key, sporkID). + builder := p2pbuilder.NewNodeBuilder( + logger, + metrics.NewNoopCollector(), + parameters.Address, + parameters.Key, + sporkID). SetConnectionManager(connManager). SetRoutingSystem(func(c context.Context, h host.Host) (routing.Routing, error) { return p2pdht.NewDHT(c, h, From 4740ac0844818ef55b8e3a2c67263a5244813029 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 18:05:50 -0800 Subject: [PATCH 074/138] moves two files --- insecure/{corruptnet => corruptlibp2p}/libp2p_node_factory.go | 2 +- insecure/{corruptnet => corruptlibp2p}/p2p_node.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename insecure/{corruptnet => corruptlibp2p}/libp2p_node_factory.go (98%) rename insecure/{corruptnet => corruptlibp2p}/p2p_node.go (99%) diff --git a/insecure/corruptnet/libp2p_node_factory.go b/insecure/corruptlibp2p/libp2p_node_factory.go similarity index 98% rename from insecure/corruptnet/libp2p_node_factory.go rename to insecure/corruptlibp2p/libp2p_node_factory.go index c8acd77c51e..b240060d9c7 100644 --- a/insecure/corruptnet/libp2p_node_factory.go +++ b/insecure/corruptlibp2p/libp2p_node_factory.go @@ -1,4 +1,4 @@ -package corruptnet +package corruptlibp2p import ( "time" diff --git a/insecure/corruptnet/p2p_node.go b/insecure/corruptlibp2p/p2p_node.go similarity index 99% rename from insecure/corruptnet/p2p_node.go rename to insecure/corruptlibp2p/p2p_node.go index 95ab602bd4e..baf8834afc0 100644 --- a/insecure/corruptnet/p2p_node.go +++ b/insecure/corruptlibp2p/p2p_node.go @@ -1,4 +1,4 @@ -package corruptnet +package corruptlibp2p import ( "context" From 244a0554287c882ac9aca915ae23590afbf6888a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 18:07:44 -0800 Subject: [PATCH 075/138] tidy --- integration/go.mod | 1 - integration/go.sum | 2 -- 2 files changed, 3 deletions(-) diff --git a/integration/go.mod b/integration/go.mod index 53b92e0b51e..4d6b315be4a 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -254,7 +254,6 @@ require ( github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect - github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/zeebo/blake3 v0.2.3 // indirect go.opencensus.io v0.23.0 // indirect diff --git a/integration/go.sum b/integration/go.sum index 0f8d169acc1..edc19f24c5a 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -1682,8 +1682,6 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 h1:c6Ns5fDv1quWqDgIVw+hRRsLfCVTz7tqhSwRP5wjRRo= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 304cd5d11c969a243da3eb4ef113918e283f24b5 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 18:13:36 -0800 Subject: [PATCH 076/138] removes import cycle --- insecure/corruptlibp2p/libp2p_node_factory.go | 30 ++++++++++++++++- insecure/internal/factory.go | 32 ------------------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_node_factory.go b/insecure/corruptlibp2p/libp2p_node_factory.go index b240060d9c7..1dc0a0b11d3 100644 --- a/insecure/corruptlibp2p/libp2p_node_factory.go +++ b/insecure/corruptlibp2p/libp2p_node_factory.go @@ -1,8 +1,11 @@ package corruptlibp2p import ( + "context" "time" + "github.com/libp2p/go-libp2p/core/host" + "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/network/p2p" @@ -52,7 +55,32 @@ func NewCorruptLibP2PNodeFactory( connectionPruning, updateInterval) builder.SetCreateNode(NewCorruptLibP2PNode) - internal.OverrideWithCorruptGossipSub(builder) + overrideWithCorruptGossipSub(builder) return builder.Build() } } + +// CorruptibleGossipSubFactory returns a factory function that creates a new instance of the forked gossipsub module from +// github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. +func CorruptibleGossipSubFactory() (p2pbuilder.GossipSubFactoryFuc, *internal.CorruptGossipSubRouter) { + var rt *internal.CorruptGossipSubRouter + factory := func(ctx context.Context, logger zerolog.Logger, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { + adapter, router, err := NewCorruptGossipSubAdapter(ctx, logger, host, cfg) + rt = router + return adapter, err + } + return factory, rt +} + +// CorruptibleGossipSubConfigFactory returns a factory function that creates a new instance of the forked gossipsub config +// from github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. +func CorruptibleGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { + return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { + return NewCorruptPubSubAdapterConfig(base) + } +} + +func overrideWithCorruptGossipSub(builder p2pbuilder.NodeBuilder) { + factory, _ := CorruptibleGossipSubFactory() + builder.SetGossipSubFactory(factory, CorruptibleGossipSubConfigFactory()) +} diff --git a/insecure/internal/factory.go b/insecure/internal/factory.go index 3a357564692..d7f881cccd5 100644 --- a/insecure/internal/factory.go +++ b/insecure/internal/factory.go @@ -1,13 +1,6 @@ package internal import ( - "context" - - "github.com/libp2p/go-libp2p/core/host" - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/insecure/corruptlibp2p" - "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2ptest "github.com/onflow/flow-go/network/p2p/test" ) @@ -18,28 +11,3 @@ func WithCorruptGossipSub(factory p2pbuilder.GossipSubFactoryFuc, config p2pbuil p.GossipSubConfig = config } } - -// CorruptibleGossipSubFactory returns a factory function that creates a new instance of the forked gossipsub module from -// github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. -func CorruptibleGossipSubFactory() (p2pbuilder.GossipSubFactoryFuc, *CorruptGossipSubRouter) { - var rt *CorruptGossipSubRouter - factory := func(ctx context.Context, logger zerolog.Logger, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { - adapter, router, err := corruptlibp2p.NewCorruptGossipSubAdapter(ctx, logger, host, cfg) - rt = router - return adapter, err - } - return factory, rt -} - -// CorruptibleGossipSubConfigFactory returns a factory function that creates a new instance of the forked gossipsub config -// from github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. -func CorruptibleGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { - return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { - return corruptlibp2p.NewCorruptPubSubAdapterConfig(base) - } -} - -func OverrideWithCorruptGossipSub(builder p2pbuilder.NodeBuilder) { - factory, _ := CorruptibleGossipSubFactory() - builder.SetGossipSubFactory(factory, CorruptibleGossipSubConfigFactory()) -} From 5dfd7db4df2ff52c5fab439b6d84f031d45cdef0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 18:20:12 -0800 Subject: [PATCH 077/138] lint fix --- insecure/cmd/corrupted_builder.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/insecure/cmd/corrupted_builder.go b/insecure/cmd/corrupted_builder.go index ea9b99a0738..62585db02cd 100644 --- a/insecure/cmd/corrupted_builder.go +++ b/insecure/cmd/corrupted_builder.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/onflow/flow-go/cmd" + "github.com/onflow/flow-go/insecure/corruptlibp2p" "github.com/onflow/flow-go/insecure/corruptnet" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/network/p2p" @@ -44,7 +45,7 @@ func (cnb *CorruptedNodeBuilder) enqueueNetworkingLayer() { myAddr = cnb.FlowNodeBuilder.BaseConfig.BindAddr } - libP2PNodeFactory := corruptnet.NewCorruptLibP2PNodeFactory( + libP2PNodeFactory := corruptlibp2p.NewCorruptLibP2PNodeFactory( cnb.Logger, cnb.RootChainID, myAddr, From e021a198e2face813524569eb5ae81f9a6b37f9c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 18:24:12 -0800 Subject: [PATCH 078/138] tidy --- integration/go.mod | 1 + integration/go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/integration/go.mod b/integration/go.mod index 4d6b315be4a..53b92e0b51e 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -254,6 +254,7 @@ require ( github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect + github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/zeebo/blake3 v0.2.3 // indirect go.opencensus.io v0.23.0 // indirect diff --git a/integration/go.sum b/integration/go.sum index edc19f24c5a..0f8d169acc1 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -1682,6 +1682,8 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 h1:c6Ns5fDv1quWqDgIVw+hRRsLfCVTz7tqhSwRP5wjRRo= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 517ba463921f877ab08028a26e2cf13dbd1f8814 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Nov 2022 18:33:07 -0800 Subject: [PATCH 079/138] generates mocks --- network/p2p/mock/score_option_builder.go | 45 ++++++++++++++++++++++++ network/p2p/mock/subscription_filter.go | 10 +++--- 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 network/p2p/mock/score_option_builder.go diff --git a/network/p2p/mock/score_option_builder.go b/network/p2p/mock/score_option_builder.go new file mode 100644 index 00000000000..d2ff9ea7a13 --- /dev/null +++ b/network/p2p/mock/score_option_builder.go @@ -0,0 +1,45 @@ +// Code generated by mockery v2.13.1. DO NOT EDIT. + +package mockp2p + +import ( + mock "github.com/stretchr/testify/mock" + + pubsub "github.com/libp2p/go-libp2p-pubsub" +) + +// ScoreOptionBuilder is an autogenerated mock type for the ScoreOptionBuilder type +type ScoreOptionBuilder struct { + mock.Mock +} + +// BuildFlowPubSubScoreOption provides a mock function with given fields: +func (_m *ScoreOptionBuilder) BuildFlowPubSubScoreOption() pubsub.Option { + ret := _m.Called() + + var r0 pubsub.Option + if rf, ok := ret.Get(0).(func() pubsub.Option); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(pubsub.Option) + } + } + + return r0 +} + +type mockConstructorTestingTNewScoreOptionBuilder interface { + mock.TestingT + Cleanup(func()) +} + +// NewScoreOptionBuilder creates a new instance of ScoreOptionBuilder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewScoreOptionBuilder(t mockConstructorTestingTNewScoreOptionBuilder) *ScoreOptionBuilder { + mock := &ScoreOptionBuilder{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/subscription_filter.go b/network/p2p/mock/subscription_filter.go index 1ed71921fd1..ce365736abf 100644 --- a/network/p2p/mock/subscription_filter.go +++ b/network/p2p/mock/subscription_filter.go @@ -29,13 +29,13 @@ func (_m *SubscriptionFilter) CanSubscribe(_a0 string) bool { return r0 } -// FilterIncomingSubscriptions provides a mock function with given fields: from, opts -func (_m *SubscriptionFilter) FilterIncomingSubscriptions(from peer.ID, opts []*pubsub_pb.RPC_SubOpts) ([]*pubsub_pb.RPC_SubOpts, error) { - ret := _m.Called(from, opts) +// FilterIncomingSubscriptions provides a mock function with given fields: _a0, _a1 +func (_m *SubscriptionFilter) FilterIncomingSubscriptions(_a0 peer.ID, _a1 []*pubsub_pb.RPC_SubOpts) ([]*pubsub_pb.RPC_SubOpts, error) { + ret := _m.Called(_a0, _a1) var r0 []*pubsub_pb.RPC_SubOpts if rf, ok := ret.Get(0).(func(peer.ID, []*pubsub_pb.RPC_SubOpts) []*pubsub_pb.RPC_SubOpts); ok { - r0 = rf(from, opts) + r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*pubsub_pb.RPC_SubOpts) @@ -44,7 +44,7 @@ func (_m *SubscriptionFilter) FilterIncomingSubscriptions(from peer.ID, opts []* var r1 error if rf, ok := ret.Get(1).(func(peer.ID, []*pubsub_pb.RPC_SubOpts) error); ok { - r1 = rf(from, opts) + r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) } From dac49ef90ac4f411150c5919b3e3f362503a13af Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 25 Nov 2022 17:47:15 -0800 Subject: [PATCH 080/138] adds spamming test skeleton --- insecure/corruptlibp2p/libp2p_test.go | 86 ++++++++++----------- insecure/corruptlibp2p/spammerGossipSub.go | 15 ++-- insecure/internal/corruptGossipSubRouter.go | 37 +++++---- insecure/internal/factory.go | 13 ---- network/p2p/test/fixtures.go | 7 ++ 5 files changed, 77 insertions(+), 81 deletions(-) delete mode 100644 insecure/internal/factory.go diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index 279f1b35f5c..e28bd1ac62d 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -1,63 +1,59 @@ -package corruptlibp2p +package corruptlibp2p_test import ( "context" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/onflow/flow-go/insecure/corruptlibp2p/internal" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/network/p2p" - p2ptest "github.com/onflow/flow-go/network/p2p/test" - "github.com/onflow/flow-go/utils/unittest" "testing" "time" "github.com/stretchr/testify/require" -) -func TestGetGossipSubParams(t *testing.T) { - gossipSubParams := getGossipSubParams() - - require.Equal(t, gossipSubParams, gossipSubParams) -} + "github.com/onflow/flow-go/insecure/corruptlibp2p" + internalinsecure "github.com/onflow/flow-go/insecure/internal" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" + "github.com/onflow/flow-go/utils/unittest" +) func TestSpam(t *testing.T) { + sporkId := unittest.IdentifierFixture() + + spammerGossipSubOpt, spammerRouter := WithCorruptGossipSub() + spammerNode, _ := p2ptest.NodeFixture( + t, + sporkId, + t.Name(), + spammerGossipSubOpt, + ) + // require.NotNil(t, spammerRouter) + + victimGossipSubOpt, _ := WithCorruptGossipSub() + victimNode, victimId := p2ptest.NodeFixture( + t, + sporkId, + t.Name(), + victimGossipSubOpt, + ) + victimPeerId, err := unittest.PeerIDFromFlowID(&victimId) + require.NoError(t, err) + + // starts nodes ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - sporkId := unittest.IdentifierFixture() defer cancel() - - count := 5 - nodes := make([]p2p.LibP2PNode, 0, 5) - ids := flow.IdentityList{} - inbounds := make([]chan string, 0, 5) - peerIds := make([]peer.ID, 5) - - for i := 0; i < count; i++ { - handler, inbound := p2ptest.StreamHandlerFixture(t) - node, id := p2ptest.NodeFixture( - t, - sporkId, - t.Name(), - p2ptest.WithRole(flow.RoleConsensus), - p2ptest.WithDefaultStreamHandler(handler), - ) - peerId, err := unittest.PeerIDFromFlowID(&id) - require.NoError(t, err) - - nodes = append(nodes, node) - ids = append(ids, &id) - inbounds = append(inbounds, inbound) - peerIds = append(peerIds, peerId) - } - - p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) - defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + p2ptest.StartNodes(t, signalerCtx, []p2p.LibP2PNode{spammerNode, victimNode}, 100*time.Second) + defer p2ptest.StopNodes(t, []p2p.LibP2PNode{spammerNode, victimNode}, cancel, 100*time.Second) // create new spammer - gsr := internal.NewGossipSubRouterFixture() - spammer := NewSpammerGossipSub(gsr.Router) + require.NotNil(t, spammerRouter) + spammer := corruptlibp2p.NewGossipSubSpammer(spammerRouter) // start spamming the first peer - spammer.SpamIHave(peerIds[0], 10, 1) + spammer.SpamIHave(victimPeerId, 10, 1) +} + +func WithCorruptGossipSub() (p2ptest.NodeFixtureParameterOption, *internalinsecure.CorruptGossipSubRouter) { + factory, router := corruptlibp2p.CorruptibleGossipSubFactory() + config := corruptlibp2p.CorruptibleGossipSubConfigFactory() + return p2ptest.WithGossipSub(factory, config), router } diff --git a/insecure/corruptlibp2p/spammerGossipSub.go b/insecure/corruptlibp2p/spammerGossipSub.go index 023771e0d30..70a611b5ae1 100644 --- a/insecure/corruptlibp2p/spammerGossipSub.go +++ b/insecure/corruptlibp2p/spammerGossipSub.go @@ -2,19 +2,20 @@ package corruptlibp2p import ( "github.com/libp2p/go-libp2p/core/peer" - pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" + + "github.com/onflow/flow-go/insecure/internal" ) type ControlMessage int -// SpammerGossipSub is a wrapper around the GossipSubRouter that allows us to +// GossipSubSpammer is a wrapper around the GossipSubRouter that allows us to // spam the victim with junk control messages. -type SpammerGossipSub struct { - router *pubsub.GossipSubRouter +type GossipSubSpammer struct { + router *internal.CorruptGossipSubRouter } -func NewSpammerGossipSubRouter(router *pubsub.GossipSubRouter) *SpammerGossipSub { - return &SpammerGossipSub{ +func NewGossipSubSpammer(router *internal.CorruptGossipSubRouter) *GossipSubSpammer { + return &GossipSubSpammer{ router: router, } } @@ -22,7 +23,7 @@ func NewSpammerGossipSubRouter(router *pubsub.GossipSubRouter) *SpammerGossipSub // SpamIHave spams the victim with junk iHave messages. // msgCount is the number of iHave messages to send. // msgSize is the number of messageIDs to include in each iHave message. -func (s *SpammerGossipSub) SpamIHave(victim peer.ID, msgCount int, msgSize int) { +func (s *GossipSubSpammer) SpamIHave(victim peer.ID, msgCount int, msgSize int) { for i := 0; i < msgCount; i++ { ctlIHave := GossipSubCtrlFixture(WithIHave(msgCount, msgSize)) s.router.SendControl(victim, ctlIHave) diff --git a/insecure/internal/corruptGossipSubRouter.go b/insecure/internal/corruptGossipSubRouter.go index a203f71deff..41293f94281 100644 --- a/insecure/internal/corruptGossipSubRouter.go +++ b/insecure/internal/corruptGossipSubRouter.go @@ -1,20 +1,21 @@ package internal import ( + pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" - pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" + corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" ) // CorruptGossipSubRouter is a wrapper around GossipSubRouter that allows us to access the internal // fields of the router for BFT testing and attack implementations. type CorruptGossipSubRouter struct { - router *pubsub.GossipSubRouter + router *corrupt.GossipSubRouter } -var _ pubsub.GossipPubSubRouter = (*CorruptGossipSubRouter)(nil) +var _ corrupt.GossipPubSubRouter = (*CorruptGossipSubRouter)(nil) -func NewCorruptGossipSubRouter(router *pubsub.GossipSubRouter) *CorruptGossipSubRouter { +func NewCorruptGossipSubRouter(router *corrupt.GossipSubRouter) *CorruptGossipSubRouter { return &CorruptGossipSubRouter{ router: router, } @@ -24,7 +25,7 @@ func (m *CorruptGossipSubRouter) Protocols() []protocol.ID { return m.router.Protocols() } -func (m *CorruptGossipSubRouter) Attach(sub *pubsub.PubSub) { +func (m *CorruptGossipSubRouter) Attach(sub *corrupt.PubSub) { m.router.Attach(sub) } @@ -40,15 +41,15 @@ func (m *CorruptGossipSubRouter) EnoughPeers(topic string, suggested int) bool { return m.router.EnoughPeers(topic, suggested) } -func (m *CorruptGossipSubRouter) AcceptFrom(pid peer.ID) pubsub.AcceptStatus { +func (m *CorruptGossipSubRouter) AcceptFrom(pid peer.ID) corrupt.AcceptStatus { return m.router.AcceptFrom(pid) } -func (m *CorruptGossipSubRouter) HandleRPC(rpc *pubsub.RPC) { +func (m *CorruptGossipSubRouter) HandleRPC(rpc *corrupt.RPC) { m.router.HandleRPC(rpc) } -func (m *CorruptGossipSubRouter) Publish(message *pubsub.Message) { +func (m *CorruptGossipSubRouter) Publish(message *corrupt.Message) { m.router.Publish(message) } @@ -60,27 +61,27 @@ func (m *CorruptGossipSubRouter) Leave(topic string) { m.router.Leave(topic) } -func (m *CorruptGossipSubRouter) SetPeerScore(score *pubsub.PeerScore) { +func (m *CorruptGossipSubRouter) SetPeerScore(score *corrupt.PeerScore) { m.router.SetPeerScore(score) } -func (m *CorruptGossipSubRouter) GetPeerScore() *pubsub.PeerScore { +func (m *CorruptGossipSubRouter) GetPeerScore() *corrupt.PeerScore { return m.router.GetPeerScore() } -func (m *CorruptGossipSubRouter) SetPeerScoreThresholds(thresholds *pubsub.PeerScoreThresholds) { +func (m *CorruptGossipSubRouter) SetPeerScoreThresholds(thresholds *corrupt.PeerScoreThresholds) { m.router.SetPeerScoreThresholds(thresholds) } -func (m *CorruptGossipSubRouter) SetGossipTracer(tracer *pubsub.GossipTracer) { +func (m *CorruptGossipSubRouter) SetGossipTracer(tracer *corrupt.GossipTracer) { m.router.SetGossipTracer(tracer) } -func (m *CorruptGossipSubRouter) GetGossipTracer() *pubsub.GossipTracer { +func (m *CorruptGossipSubRouter) GetGossipTracer() *corrupt.GossipTracer { return m.router.GetGossipTracer() } -func (m *CorruptGossipSubRouter) GetTagTracer() *pubsub.TagTracer { +func (m *CorruptGossipSubRouter) GetTagTracer() *corrupt.TagTracer { return m.router.GetTagTracer() } @@ -88,10 +89,14 @@ func (m *CorruptGossipSubRouter) SetDirectPeers(direct map[peer.ID]struct{}) { m.router.SetDirectPeers(direct) } -func (m *CorruptGossipSubRouter) SetPeerGater(gater *pubsub.PeerGater) { +func (m *CorruptGossipSubRouter) SetPeerGater(gater *corrupt.PeerGater) { m.router.SetPeerGater(gater) } -func (m *CorruptGossipSubRouter) GetPeerGater() *pubsub.PeerGater { +func (m *CorruptGossipSubRouter) GetPeerGater() *corrupt.PeerGater { return m.router.GetPeerGater() } + +func (m *CorruptGossipSubRouter) SendControl(p peer.ID, ctl *pb.ControlMessage) { + m.router.SendControl(p, ctl) +} diff --git a/insecure/internal/factory.go b/insecure/internal/factory.go deleted file mode 100644 index d7f881cccd5..00000000000 --- a/insecure/internal/factory.go +++ /dev/null @@ -1,13 +0,0 @@ -package internal - -import ( - "github.com/onflow/flow-go/network/p2p/p2pbuilder" - p2ptest "github.com/onflow/flow-go/network/p2p/test" -) - -func WithCorruptGossipSub(factory p2pbuilder.GossipSubFactoryFuc, config p2pbuilder.GossipSubAdapterConfigFunc) p2ptest.NodeFixtureParameterOption { - return func(p *p2ptest.NodeFixtureParameters) { - p.GossipSubFactory = factory - p.GossipSubConfig = config - } -} diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 4bce1d65816..d334ef9b4fe 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -156,6 +156,13 @@ type NodeFixtureParameters struct { GossipSubConfig p2pbuilder.GossipSubAdapterConfigFunc } +func WithGossipSub(factory p2pbuilder.GossipSubFactoryFuc, config p2pbuilder.GossipSubAdapterConfigFunc) NodeFixtureParameterOption { + return func(p *NodeFixtureParameters) { + p.GossipSubFactory = factory + p.GossipSubConfig = config + } +} + func WithPeerScoringEnabled(idProvider module.IdentityProvider) NodeFixtureParameterOption { return func(p *NodeFixtureParameters) { p.PeerScoringEnabled = true From 44b8a2ee10d56c8aea84c27891b3c7828a2490e4 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 25 Nov 2022 18:09:35 -0800 Subject: [PATCH 081/138] fixes spam test --- insecure/corruptlibp2p/libp2p_node_factory.go | 12 +++++----- insecure/corruptlibp2p/libp2p_test.go | 22 +++++++------------ insecure/corruptlibp2p/pubsubAdapter.go | 10 ++++----- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_node_factory.go b/insecure/corruptlibp2p/libp2p_node_factory.go index 1dc0a0b11d3..36ef1ed858b 100644 --- a/insecure/corruptlibp2p/libp2p_node_factory.go +++ b/insecure/corruptlibp2p/libp2p_node_factory.go @@ -6,7 +6,6 @@ import ( "github.com/libp2p/go-libp2p/core/host" - "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/network/p2p" madns "github.com/multiformats/go-multiaddr-dns" @@ -62,14 +61,13 @@ func NewCorruptLibP2PNodeFactory( // CorruptibleGossipSubFactory returns a factory function that creates a new instance of the forked gossipsub module from // github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. -func CorruptibleGossipSubFactory() (p2pbuilder.GossipSubFactoryFuc, *internal.CorruptGossipSubRouter) { - var rt *internal.CorruptGossipSubRouter +func CorruptibleGossipSubFactory(onAdapterCreate func(adapter p2p.PubSubAdapter)) p2pbuilder.GossipSubFactoryFuc { factory := func(ctx context.Context, logger zerolog.Logger, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { - adapter, router, err := NewCorruptGossipSubAdapter(ctx, logger, host, cfg) - rt = router + adapter, err := NewCorruptGossipSubAdapter(ctx, logger, host, cfg) + onAdapterCreate(adapter) return adapter, err } - return factory, rt + return factory } // CorruptibleGossipSubConfigFactory returns a factory function that creates a new instance of the forked gossipsub config @@ -81,6 +79,6 @@ func CorruptibleGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { } func overrideWithCorruptGossipSub(builder p2pbuilder.NodeBuilder) { - factory, _ := CorruptibleGossipSubFactory() + factory := CorruptibleGossipSubFactory(func(adapter p2p.PubSubAdapter) {}) builder.SetGossipSubFactory(factory, CorruptibleGossipSubConfigFactory()) } diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index e28bd1ac62d..cb2ccf3d502 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/flow-go/insecure/corruptlibp2p" - internalinsecure "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network/p2p" p2ptest "github.com/onflow/flow-go/network/p2p/test" @@ -18,21 +17,19 @@ import ( func TestSpam(t *testing.T) { sporkId := unittest.IdentifierFixture() - spammerGossipSubOpt, spammerRouter := WithCorruptGossipSub() + var spammerAdapter p2p.PubSubAdapter spammerNode, _ := p2ptest.NodeFixture( t, sporkId, t.Name(), - spammerGossipSubOpt, + p2ptest.WithGossipSub(corruptlibp2p.CorruptibleGossipSubFactory(func(adapter p2p.PubSubAdapter) { spammerAdapter = adapter }), corruptlibp2p.CorruptibleGossipSubConfigFactory()), ) - // require.NotNil(t, spammerRouter) - victimGossipSubOpt, _ := WithCorruptGossipSub() victimNode, victimId := p2ptest.NodeFixture( t, sporkId, t.Name(), - victimGossipSubOpt, + p2ptest.WithGossipSub(corruptlibp2p.CorruptibleGossipSubFactory(func(adapter p2p.PubSubAdapter) {}), corruptlibp2p.CorruptibleGossipSubConfigFactory()), ) victimPeerId, err := unittest.PeerIDFromFlowID(&victimId) require.NoError(t, err) @@ -45,15 +42,12 @@ func TestSpam(t *testing.T) { defer p2ptest.StopNodes(t, []p2p.LibP2PNode{spammerNode, victimNode}, cancel, 100*time.Second) // create new spammer - require.NotNil(t, spammerRouter) - spammer := corruptlibp2p.NewGossipSubSpammer(spammerRouter) + require.NotNil(t, spammerAdapter) + corruptAdapter, ok := spammerAdapter.(*corruptlibp2p.CorruptGossipSubAdapter) + require.True(t, ok) + + spammer := corruptlibp2p.NewGossipSubSpammer(corruptAdapter.GetRouter()) // start spamming the first peer spammer.SpamIHave(victimPeerId, 10, 1) } - -func WithCorruptGossipSub() (p2ptest.NodeFixtureParameterOption, *internalinsecure.CorruptGossipSubRouter) { - factory, router := corruptlibp2p.CorruptibleGossipSubFactory() - config := corruptlibp2p.CorruptibleGossipSubConfigFactory() - return p2ptest.WithGossipSub(factory, config), router -} diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index 78057ae16da..aadfa0196c8 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -91,23 +91,23 @@ func (c *CorruptGossipSubAdapter) GetRouter() *internal.CorruptGossipSubRouter { return c.router } -func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, *internal.CorruptGossipSubRouter, error) { +func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { gossipSubConfig, ok := cfg.(*CorruptPubSubAdapterConfig) if !ok { - return nil, nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) + return nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) } // initializes a default gossipsub router and wraps it with the corrupt router. router, err := corrupt.DefaultGossipSubRouter(h) if err != nil { - return nil, nil, fmt.Errorf("failed to create gossipsub router: %w", err) + return nil, fmt.Errorf("failed to create gossipsub router: %w", err) } corruptRouter := internal.NewCorruptGossipSubRouter(router) // injects the corrupt router into the gossipsub constructor gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, corruptRouter, gossipSubConfig.Build()...) if err != nil { - return nil, nil, fmt.Errorf("failed to create corrupt gossipsub: %w", err) + return nil, fmt.Errorf("failed to create corrupt gossipsub: %w", err) } adapter := &CorruptGossipSubAdapter{ @@ -116,5 +116,5 @@ func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h ho logger: logger, } - return adapter, corruptRouter, nil + return adapter, nil } From dc80931f84d4cfa3fb7cf770d0fb2a481f008df8 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Nov 2022 17:37:48 -0800 Subject: [PATCH 082/138] encapsulates topic and message id as constants --- insecure/corruptlibp2p/fixtures.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/fixtures.go b/insecure/corruptlibp2p/fixtures.go index 17d623dbf77..61a4d3f111d 100644 --- a/insecure/corruptlibp2p/fixtures.go +++ b/insecure/corruptlibp2p/fixtures.go @@ -6,6 +6,13 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) +const ( + // topicIDFixtureLen is the length of the topic ID fixture for testing. + topicIDFixtureLen = 10 + // messageIDFixtureLen is the length of the message ID fixture for testing. + messageIDFixtureLen = 10 +) + type GossipSubCtrlOption func(*pubsubpb.ControlMessage) // GossipSubCtrlFixture returns a ControlMessage with the given options. @@ -35,13 +42,13 @@ func WithIHave(msgCount int, msgSize int) GossipSubCtrlOption { // gossipSubMessageIdFixture returns a random gossipSub message ID. func gossipSubMessageIdFixture() string { // TODO: messageID length should be a parameter. - return unittest.GenerateRandomStringWithLen(10) + return unittest.GenerateRandomStringWithLen(messageIDFixtureLen) } // gossipSubTopicIdFixture returns a random gossipSub topic ID. func gossipSubTopicIdFixture() string { // TODO: topicID length should be a parameter. - return unittest.GenerateRandomStringWithLen(10) + return unittest.GenerateRandomStringWithLen(topicIDFixtureLen) } // gossipSubMessageIdsFixture returns a slice of random gossipSub message IDs of the given size. From 2cbea2a4f0df1643b72ea1b2c1c8ed4d9ec030e7 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Nov 2022 17:46:36 -0800 Subject: [PATCH 083/138] adding more context to the log message --- insecure/corruptlibp2p/pubsubAdapter.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index 78057ae16da..6f2ab1f883b 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -56,11 +56,21 @@ func (c *CorruptGossipSubAdapter) RegisterTopicValidator(topic string, topicVali return corrupt.ValidationReject default: // should never happen, indicates a bug in the topic validator - c.logger.Fatal().Msgf("invalid validation result: %v", result) + c.logger.Fatal(). + Bool(logging.KeySuspicious, true). + Str("topic", topic). + Str("origin_peer", from.String()). + Str("result", fmt.Sprintf("%v", result)). + Str("message_type", fmt.Sprintf("%T", message.Data)). + Msgf("invalid validation result, should be a bug in the topic validator") } // should never happen, indicates a bug in the topic validator, but we need to return something c.logger.Warn(). Bool(logging.KeySuspicious, true). + Str("topic", topic). + Str("origin_peer", from.String()). + Str("result", fmt.Sprintf("%v", result)). + Str("message_type", fmt.Sprintf("%T", message.Data)). Msg("invalid validation result, returning reject") return corrupt.ValidationReject } From fa5175aa4a0ffdeb0150636457edefa1c857e2f8 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 1 Dec 2022 13:23:25 -0500 Subject: [PATCH 084/138] using local version of libp2p for debugging --- insecure/corruptlibp2p/libp2p_test.go | 4 +++- insecure/go.mod | 2 ++ insecure/internal/corruptGossipSubRouter.go | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index cb2ccf3d502..7238a9f6244 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -49,5 +49,7 @@ func TestSpam(t *testing.T) { spammer := corruptlibp2p.NewGossipSubSpammer(corruptAdapter.GetRouter()) // start spamming the first peer - spammer.SpamIHave(victimPeerId, 10, 1) + spammer.SpamIHave(victimPeerId, 1, 1) + + time.Sleep(10 * time.Second) } diff --git a/insecure/go.mod b/insecure/go.mod index c4b756de979..2ca27bb6ff5 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -254,3 +254,5 @@ require ( ) replace github.com/onflow/flow-go => ../ + +//replace github.com/yhassanzadeh13/go-libp2p-pubsub => ../../yhassanzadeh13/go-libp2p-pubsub diff --git a/insecure/internal/corruptGossipSubRouter.go b/insecure/internal/corruptGossipSubRouter.go index 41293f94281..d8d4e62e4fc 100644 --- a/insecure/internal/corruptGossipSubRouter.go +++ b/insecure/internal/corruptGossipSubRouter.go @@ -1,6 +1,7 @@ package internal import ( + "fmt" pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" @@ -46,6 +47,7 @@ func (m *CorruptGossipSubRouter) AcceptFrom(pid peer.ID) corrupt.AcceptStatus { } func (m *CorruptGossipSubRouter) HandleRPC(rpc *corrupt.RPC) { + fmt.Println("HandleRPC called: ", rpc.String()) m.router.HandleRPC(rpc) } @@ -98,5 +100,7 @@ func (m *CorruptGossipSubRouter) GetPeerGater() *corrupt.PeerGater { } func (m *CorruptGossipSubRouter) SendControl(p peer.ID, ctl *pb.ControlMessage) { + fmt.Println("SendControl called: ", ctl.String()) + m.router.SendControl(p, ctl) } From d4937ff24a66108377e78b1512ad8a8eb26e2771 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 1 Dec 2022 13:36:10 -0500 Subject: [PATCH 085/138] updated script file names for overriding, restoring mods --- Makefile | 8 ++++---- insecure/cmd/{build_helper1.sh => mods_override.sh} | 0 insecure/cmd/{build_helper2.sh => mods_restore.sh} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename insecure/cmd/{build_helper1.sh => mods_override.sh} (100%) rename insecure/cmd/{build_helper2.sh => mods_restore.sh} (100%) diff --git a/Makefile b/Makefile index 92c609d5261..d70db5ee376 100644 --- a/Makefile +++ b/Makefile @@ -271,11 +271,11 @@ docker-build-execution-debug: .PHONY: docker-build-execution-corrupt docker-build-execution-corrupt: # temporarily make insecure/ a non-module to allow Docker to use corrupt builders there - ./insecure/cmd/build_helper1.sh + ./insecure/cmd/mods_override.sh docker build -f cmd/Dockerfile --build-arg TARGET=./insecure/cmd/execution --build-arg COMMIT=$(COMMIT) --build-arg VERSION=$(IMAGE_TAG) --build-arg GOARCH=$(GOARCH) --target production \ --label "git_commit=${COMMIT}" --label "git_tag=${IMAGE_TAG}" \ -t "$(CONTAINER_REGISTRY)/execution-corrupted:latest" -t "$(CONTAINER_REGISTRY)/execution-corrupted:$(SHORT_COMMIT)" -t "$(CONTAINER_REGISTRY)/execution-corrupted:$(IMAGE_TAG)" . - ./insecure/cmd/build_helper2.sh + ./insecure/cmd/mods_restore.sh .PHONY: docker-build-verification docker-build-verification: @@ -292,11 +292,11 @@ docker-build-verification-debug: .PHONY: docker-build-verification-corrupt docker-build-verification-corrupt: # temporarily make insecure/ a non-module to allow Docker to use corrupt builders there - ./insecure/cmd/build_helper1.sh + ./insecure/cmd/mods_override.sh docker build -f cmd/Dockerfile --build-arg TARGET=./insecure/cmd/verification --build-arg COMMIT=$(COMMIT) --build-arg VERSION=$(IMAGE_TAG) --build-arg GOARCH=$(GOARCH) --target production \ --label "git_commit=${COMMIT}" --label "git_tag=${IMAGE_TAG}" \ -t "$(CONTAINER_REGISTRY)/verification-corrupted:latest" -t "$(CONTAINER_REGISTRY)/verification-corrupted:$(SHORT_COMMIT)" -t "$(CONTAINER_REGISTRY)/verification-corrupted:$(IMAGE_TAG)" . - ./insecure/cmd/build_helper2.sh + ./insecure/cmd/mods_restore.sh .PHONY: docker-build-access docker-build-access: diff --git a/insecure/cmd/build_helper1.sh b/insecure/cmd/mods_override.sh similarity index 100% rename from insecure/cmd/build_helper1.sh rename to insecure/cmd/mods_override.sh diff --git a/insecure/cmd/build_helper2.sh b/insecure/cmd/mods_restore.sh similarity index 100% rename from insecure/cmd/build_helper2.sh rename to insecure/cmd/mods_restore.sh From 86498c7dc636f1081dc601bfcee07ec134a00884 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 1 Dec 2022 16:35:33 -0500 Subject: [PATCH 086/138] using local `yhassanzadeh13/go-libp2p-pubsub` --- insecure/go.mod | 2 +- insecure/go.sum | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/insecure/go.mod b/insecure/go.mod index 44f875041b3..0c49f7c094b 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -255,4 +255,4 @@ require ( replace github.com/onflow/flow-go => ../ -//replace github.com/yhassanzadeh13/go-libp2p-pubsub => ../../yhassanzadeh13/go-libp2p-pubsub +replace github.com/yhassanzadeh13/go-libp2p-pubsub => ../../yhassanzadeh13/go-libp2p-pubsub diff --git a/insecure/go.sum b/insecure/go.sum index 0e431795981..a734e7e2a5e 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -1489,8 +1489,6 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 h1:c6Ns5fDv1quWqDgIVw+hRRsLfCVTz7tqhSwRP5wjRRo= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 12022a653c3c0f04252399384fb084fb74a733ee Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 5 Dec 2022 11:27:19 -0500 Subject: [PATCH 087/138] update `TestSpam` to work with new GossipSub (WIP) --- insecure/corruptlibp2p/libp2p_test.go | 17 ++++++++--------- insecure/go.sum | 2 -- insecure/internal/corruptGossipSubRouter.go | 4 ++++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index 7238a9f6244..0f79606f4b3 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/flow-go/insecure/corruptlibp2p" + "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network/p2p" p2ptest "github.com/onflow/flow-go/network/p2p/test" @@ -17,19 +18,21 @@ import ( func TestSpam(t *testing.T) { sporkId := unittest.IdentifierFixture() - var spammerAdapter p2p.PubSubAdapter + gossipSubFactoryFunc, corruptGossipSubRouter := corruptlibp2p.CorruptibleGossipSubFactory() + require.NotNil(t, corruptGossipSubRouter) + spammerNode, _ := p2ptest.NodeFixture( t, sporkId, t.Name(), - p2ptest.WithGossipSub(corruptlibp2p.CorruptibleGossipSubFactory(func(adapter p2p.PubSubAdapter) { spammerAdapter = adapter }), corruptlibp2p.CorruptibleGossipSubConfigFactory()), + internal.WithCorruptGossipSub(gossipSubFactoryFunc, corruptlibp2p.CorruptibleGossipSubConfigFactory()), ) victimNode, victimId := p2ptest.NodeFixture( t, sporkId, t.Name(), - p2ptest.WithGossipSub(corruptlibp2p.CorruptibleGossipSubFactory(func(adapter p2p.PubSubAdapter) {}), corruptlibp2p.CorruptibleGossipSubConfigFactory()), + internal.WithCorruptGossipSub(gossipSubFactoryFunc, corruptlibp2p.CorruptibleGossipSubConfigFactory()), ) victimPeerId, err := unittest.PeerIDFromFlowID(&victimId) require.NoError(t, err) @@ -42,14 +45,10 @@ func TestSpam(t *testing.T) { defer p2ptest.StopNodes(t, []p2p.LibP2PNode{spammerNode, victimNode}, cancel, 100*time.Second) // create new spammer - require.NotNil(t, spammerAdapter) - corruptAdapter, ok := spammerAdapter.(*corruptlibp2p.CorruptGossipSubAdapter) - require.True(t, ok) - - spammer := corruptlibp2p.NewGossipSubSpammer(corruptAdapter.GetRouter()) + spammer := corruptlibp2p.NewSpammerGossipSubRouter(corruptGossipSubRouter.GetRouter()) // start spamming the first peer spammer.SpamIHave(victimPeerId, 1, 1) - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) } diff --git a/insecure/go.sum b/insecure/go.sum index cb0c44d232a..bac993a1353 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -1510,8 +1510,6 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 h1:c6Ns5fDv1quWqDgIVw+hRRsLfCVTz7tqhSwRP5wjRRo= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/insecure/internal/corruptGossipSubRouter.go b/insecure/internal/corruptGossipSubRouter.go index a203f71deff..189baad2a46 100644 --- a/insecure/internal/corruptGossipSubRouter.go +++ b/insecure/internal/corruptGossipSubRouter.go @@ -20,6 +20,10 @@ func NewCorruptGossipSubRouter(router *pubsub.GossipSubRouter) *CorruptGossipSub } } +func (m *CorruptGossipSubRouter) GetRouter() *pubsub.GossipSubRouter { + return m.router +} + func (m *CorruptGossipSubRouter) Protocols() []protocol.ID { return m.router.Protocols() } From fea4155e5086ce1ed1b75a8099c6808ffa8f1f09 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Dec 2022 11:31:32 -0800 Subject: [PATCH 088/138] [BFT Testing][Networking] Fixes nil issue with router during tests (#3665) * tidy * upgrades corrupt gossipsub version * loads router through options * wires up inspector --- insecure/corruptlibp2p/libp2p_node_factory.go | 22 ++-- insecure/corruptlibp2p/libp2p_test.go | 23 +++- insecure/corruptlibp2p/pubsubAdapter.go | 20 ++-- insecure/corruptlibp2p/pubsubAdapterConfig.go | 18 +++- insecure/go.mod | 6 +- insecure/go.sum | 6 ++ insecure/internal/corruptGossipSubRouter.go | 101 ------------------ 7 files changed, 64 insertions(+), 132 deletions(-) delete mode 100644 insecure/internal/corruptGossipSubRouter.go diff --git a/insecure/corruptlibp2p/libp2p_node_factory.go b/insecure/corruptlibp2p/libp2p_node_factory.go index 53658cb42d7..420f8969649 100644 --- a/insecure/corruptlibp2p/libp2p_node_factory.go +++ b/insecure/corruptlibp2p/libp2p_node_factory.go @@ -5,8 +5,9 @@ import ( "time" "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" + corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" - "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/network/p2p" madns "github.com/multiformats/go-multiaddr-dns" @@ -65,14 +66,15 @@ func NewCorruptLibP2PNodeFactory( // CorruptibleGossipSubFactory returns a factory function that creates a new instance of the forked gossipsub module from // github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. -func CorruptibleGossipSubFactory() (p2pbuilder.GossipSubFactoryFunc, *internal.CorruptGossipSubRouter) { - var rt *internal.CorruptGossipSubRouter +func CorruptibleGossipSubFactory(routerOpts ...func(*corrupt.GossipSubRouter)) p2pbuilder.GossipSubFactoryFunc { factory := func(ctx context.Context, logger zerolog.Logger, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { adapter, router, err := NewCorruptGossipSubAdapter(ctx, logger, host, cfg) - rt = router + for _, opt := range routerOpts { + opt(router) + } return adapter, err } - return factory, rt + return factory } // CorruptibleGossipSubConfigFactory returns a factory function that creates a new instance of the forked gossipsub config @@ -83,7 +85,15 @@ func CorruptibleGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { } } +// CorruptibleGossipSubConfigFactoryWithInspector returns a factory function that creates a new instance of the forked gossipsub config +// from github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. +func CorruptibleGossipSubConfigFactoryWithInspector(inspector func(peer.ID, *corrupt.RPC) error) p2pbuilder.GossipSubAdapterConfigFunc { + return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { + return NewCorruptPubSubAdapterConfigWithInspector(base, inspector) + } +} + func overrideWithCorruptGossipSub(builder p2pbuilder.NodeBuilder) { - factory, _ := CorruptibleGossipSubFactory() + factory := CorruptibleGossipSubFactory() builder.SetGossipSubFactory(factory, CorruptibleGossipSubConfigFactory()) } diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index 0f79606f4b3..c20964fdd9c 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -5,7 +5,9 @@ import ( "testing" "time" + "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" + corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" "github.com/onflow/flow-go/insecure/corruptlibp2p" "github.com/onflow/flow-go/insecure/internal" @@ -18,21 +20,32 @@ import ( func TestSpam(t *testing.T) { sporkId := unittest.IdentifierFixture() - gossipSubFactoryFunc, corruptGossipSubRouter := corruptlibp2p.CorruptibleGossipSubFactory() - require.NotNil(t, corruptGossipSubRouter) + var router *corrupt.GossipSubRouter + factory := corruptlibp2p.CorruptibleGossipSubFactory(func(r *corrupt.GossipSubRouter) { + require.NotNil(t, r) + router = r // save the router at the initialization time of the factory + }) spammerNode, _ := p2ptest.NodeFixture( t, sporkId, t.Name(), - internal.WithCorruptGossipSub(gossipSubFactoryFunc, corruptlibp2p.CorruptibleGossipSubConfigFactory()), + internal.WithCorruptGossipSub(factory, + corruptlibp2p.CorruptibleGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { + // here we can inspect the incoming RPC message to the spammer node + return nil + })), ) victimNode, victimId := p2ptest.NodeFixture( t, sporkId, t.Name(), - internal.WithCorruptGossipSub(gossipSubFactoryFunc, corruptlibp2p.CorruptibleGossipSubConfigFactory()), + internal.WithCorruptGossipSub(factory, + corruptlibp2p.CorruptibleGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { + // here we can inspect the incoming RPC message to the victim node + return nil + })), ) victimPeerId, err := unittest.PeerIDFromFlowID(&victimId) require.NoError(t, err) @@ -45,7 +58,7 @@ func TestSpam(t *testing.T) { defer p2ptest.StopNodes(t, []p2p.LibP2PNode{spammerNode, victimNode}, cancel, 100*time.Second) // create new spammer - spammer := corruptlibp2p.NewSpammerGossipSubRouter(corruptGossipSubRouter.GetRouter()) + spammer := corruptlibp2p.NewSpammerGossipSubRouter(router) // start spamming the first peer spammer.SpamIHave(victimPeerId, 1, 1) diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsubAdapter.go index 36606f60a8c..eb321f3492f 100644 --- a/insecure/corruptlibp2p/pubsubAdapter.go +++ b/insecure/corruptlibp2p/pubsubAdapter.go @@ -26,7 +26,7 @@ import ( // totally separated from the rest of the codebase. type CorruptGossipSubAdapter struct { gossipSub *corrupt.PubSub - router *internal.CorruptGossipSubRouter + router *corrupt.GossipSubRouter logger zerolog.Logger } @@ -101,34 +101,26 @@ func (c *CorruptGossipSubAdapter) ListPeers(topic string) []peer.ID { return c.gossipSub.ListPeers(topic) } -func (c *CorruptGossipSubAdapter) GetRouter() *internal.CorruptGossipSubRouter { - return c.router -} - -func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, *internal.CorruptGossipSubRouter, error) { +func NewCorruptGossipSubAdapter(ctx context.Context, logger zerolog.Logger, h host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, *corrupt.GossipSubRouter, error) { gossipSubConfig, ok := cfg.(*CorruptPubSubAdapterConfig) if !ok { return nil, nil, fmt.Errorf("invalid gossipsub config type: %T", cfg) } // initializes a default gossipsub router and wraps it with the corrupt router. - router, err := corrupt.DefaultGossipSubRouter(h) - if err != nil { - return nil, nil, fmt.Errorf("failed to create gossipsub router: %w", err) - } - corruptRouter := internal.NewCorruptGossipSubRouter(router) + router := corrupt.DefaultGossipSubRouter(h) // injects the corrupt router into the gossipsub constructor - gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, corruptRouter, gossipSubConfig.Build()...) + gossipSub, err := corrupt.NewGossipSubWithRouter(ctx, h, router, gossipSubConfig.Build()...) if err != nil { return nil, nil, fmt.Errorf("failed to create corrupt gossipsub: %w", err) } adapter := &CorruptGossipSubAdapter{ gossipSub: gossipSub, - router: corruptRouter, + router: router, logger: logger, } - return adapter, corruptRouter, nil + return adapter, router, nil } diff --git a/insecure/corruptlibp2p/pubsubAdapterConfig.go b/insecure/corruptlibp2p/pubsubAdapterConfig.go index 309033d6c1b..14efc373be2 100644 --- a/insecure/corruptlibp2p/pubsubAdapterConfig.go +++ b/insecure/corruptlibp2p/pubsubAdapterConfig.go @@ -21,7 +21,8 @@ import ( // implementation, it is designed to be completely isolated in the "insecure" package, and // totally separated from the rest of the codebase. type CorruptPubSubAdapterConfig struct { - options []corrupt.Option + options []corrupt.Option + inspector func(peer.ID, *corrupt.RPC) error } var _ p2p.PubSubAdapterConfig = (*CorruptPubSubAdapterConfig)(nil) @@ -32,6 +33,13 @@ func NewCorruptPubSubAdapterConfig(base *p2p.BasePubSubAdapterConfig) *CorruptPu } } +func NewCorruptPubSubAdapterConfigWithInspector(base *p2p.BasePubSubAdapterConfig, inspector func(peer.ID, *corrupt.RPC) error) *CorruptPubSubAdapterConfig { + return &CorruptPubSubAdapterConfig{ + options: defaultCorruptPubsubOptions(base), + inspector: inspector, + } +} + func (c *CorruptPubSubAdapterConfig) WithRoutingDiscovery(routing routing.ContentRouting) { c.options = append(c.options, corrupt.WithDiscovery(discoveryRouting.NewRoutingDiscovery(routing))) } @@ -45,10 +53,16 @@ func (c *CorruptPubSubAdapterConfig) WithScoreOption(_ p2p.ScoreOptionBuilder) { } func (c *CorruptPubSubAdapterConfig) WithAppSpecificRpcInspector(_ func(peer.ID, *pubsub.RPC) error) { - // Corrupt does not support app-specific inspector for now. This is a no-op. + // Corrupt does not support app specific rpc inspectors. This is a no-op. } func (c *CorruptPubSubAdapterConfig) WithMessageIdFunction(f func([]byte) string) { + if c.inspector != nil { + c.options = append(c.options, corrupt.WithAppSpecificRpcInspector(func(id peer.ID, rpc *corrupt.RPC) error { + return c.inspector(id, rpc) + })) + } + c.options = append(c.options, corrupt.WithMessageIdFn(func(pmsg *pb.Message) string { return f(pmsg.Data) })) diff --git a/insecure/go.mod b/insecure/go.mod index f3d323663a6..6c14ee39588 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -7,14 +7,14 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/ipfs/go-datastore v0.6.0 github.com/libp2p/go-libp2p v0.23.3 - github.com/libp2p/go-libp2p-pubsub v0.8.2-0.20221201175637-3d2eab35722e + github.com/libp2p/go-libp2p-pubsub v0.8.2 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/onflow/flow-go v0.0.0-00010101000000-000000000000 github.com/onflow/flow-go/crypto v0.24.4 github.com/rs/zerolog v1.28.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.0 - github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 + github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b google.golang.org/grpc v1.50.1 google.golang.org/protobuf v1.28.1 ) @@ -260,5 +260,3 @@ require ( ) replace github.com/onflow/flow-go => ../ - -replace github.com/yhassanzadeh13/go-libp2p-pubsub => ../../yhassanzadeh13/go-libp2p-pubsub diff --git a/insecure/go.sum b/insecure/go.sum index bac993a1353..debbbeccac6 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -887,6 +887,8 @@ github.com/libp2p/go-libp2p-peerstore v0.8.0 h1:bzTG693TA1Ju/zKmUCQzDLSqiJnyRFVw github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= github.com/libp2p/go-libp2p-pubsub v0.8.2-0.20221201175637-3d2eab35722e h1:phmi6mEoO5y2AQP68+4vZhNpHtZ4dum2ieFtWdmjXak= github.com/libp2p/go-libp2p-pubsub v0.8.2-0.20221201175637-3d2eab35722e/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= +github.com/libp2p/go-libp2p-pubsub v0.8.2 h1:QLGUmkgKmwEVxVDYGsqc5t9CykOMY2Y21cXQHjR462I= +github.com/libp2p/go-libp2p-pubsub v0.8.2/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= @@ -1510,6 +1512,10 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b h1:9qSLOxaiPv3HR5THaAcUuXRi4OFIexDseSp8ZBKOMsk= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11 h1:WYWfp6ydU0v6ev+amaIRIfxJEoEnf9HP0o523qlg/1o= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11/go.mod h1:GwZ5VBU63KlM8wZsnVvSGDIMeGMOO9Cb3uLXDP8WFYM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/insecure/internal/corruptGossipSubRouter.go b/insecure/internal/corruptGossipSubRouter.go deleted file mode 100644 index 189baad2a46..00000000000 --- a/insecure/internal/corruptGossipSubRouter.go +++ /dev/null @@ -1,101 +0,0 @@ -package internal - -import ( - "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/protocol" - pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" -) - -// CorruptGossipSubRouter is a wrapper around GossipSubRouter that allows us to access the internal -// fields of the router for BFT testing and attack implementations. -type CorruptGossipSubRouter struct { - router *pubsub.GossipSubRouter -} - -var _ pubsub.GossipPubSubRouter = (*CorruptGossipSubRouter)(nil) - -func NewCorruptGossipSubRouter(router *pubsub.GossipSubRouter) *CorruptGossipSubRouter { - return &CorruptGossipSubRouter{ - router: router, - } -} - -func (m *CorruptGossipSubRouter) GetRouter() *pubsub.GossipSubRouter { - return m.router -} - -func (m *CorruptGossipSubRouter) Protocols() []protocol.ID { - return m.router.Protocols() -} - -func (m *CorruptGossipSubRouter) Attach(sub *pubsub.PubSub) { - m.router.Attach(sub) -} - -func (m *CorruptGossipSubRouter) AddPeer(pid peer.ID, protocolId protocol.ID) { - m.router.AddPeer(pid, protocolId) -} - -func (m *CorruptGossipSubRouter) RemovePeer(pid peer.ID) { - m.router.RemovePeer(pid) -} - -func (m *CorruptGossipSubRouter) EnoughPeers(topic string, suggested int) bool { - return m.router.EnoughPeers(topic, suggested) -} - -func (m *CorruptGossipSubRouter) AcceptFrom(pid peer.ID) pubsub.AcceptStatus { - return m.router.AcceptFrom(pid) -} - -func (m *CorruptGossipSubRouter) HandleRPC(rpc *pubsub.RPC) { - m.router.HandleRPC(rpc) -} - -func (m *CorruptGossipSubRouter) Publish(message *pubsub.Message) { - m.router.Publish(message) -} - -func (m *CorruptGossipSubRouter) Join(topic string) { - m.router.Join(topic) -} - -func (m *CorruptGossipSubRouter) Leave(topic string) { - m.router.Leave(topic) -} - -func (m *CorruptGossipSubRouter) SetPeerScore(score *pubsub.PeerScore) { - m.router.SetPeerScore(score) -} - -func (m *CorruptGossipSubRouter) GetPeerScore() *pubsub.PeerScore { - return m.router.GetPeerScore() -} - -func (m *CorruptGossipSubRouter) SetPeerScoreThresholds(thresholds *pubsub.PeerScoreThresholds) { - m.router.SetPeerScoreThresholds(thresholds) -} - -func (m *CorruptGossipSubRouter) SetGossipTracer(tracer *pubsub.GossipTracer) { - m.router.SetGossipTracer(tracer) -} - -func (m *CorruptGossipSubRouter) GetGossipTracer() *pubsub.GossipTracer { - return m.router.GetGossipTracer() -} - -func (m *CorruptGossipSubRouter) GetTagTracer() *pubsub.TagTracer { - return m.router.GetTagTracer() -} - -func (m *CorruptGossipSubRouter) SetDirectPeers(direct map[peer.ID]struct{}) { - m.router.SetDirectPeers(direct) -} - -func (m *CorruptGossipSubRouter) SetPeerGater(gater *pubsub.PeerGater) { - m.router.SetPeerGater(gater) -} - -func (m *CorruptGossipSubRouter) GetPeerGater() *pubsub.PeerGater { - return m.router.GetPeerGater() -} From d4aa93413866a4df833a5f60bd2fe675f4985668 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 6 Dec 2022 11:54:37 -0500 Subject: [PATCH 089/138] joined spammer and victim as peers --- insecure/corruptlibp2p/libp2p_test.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index c20964fdd9c..dbab649c956 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -2,6 +2,7 @@ package corruptlibp2p_test import ( "context" + "github.com/onflow/flow-go/network/p2p/utils" "testing" "time" @@ -50,6 +51,9 @@ func TestSpam(t *testing.T) { victimPeerId, err := unittest.PeerIDFromFlowID(&victimId) require.NoError(t, err) + victimPeerInfo, err := utils.PeerAddressInfo(victimId) + require.NoError(t, err) + // starts nodes ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) @@ -57,11 +61,25 @@ func TestSpam(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, []p2p.LibP2PNode{spammerNode, victimNode}, 100*time.Second) defer p2ptest.StopNodes(t, []p2p.LibP2PNode{spammerNode, victimNode}, cancel, 100*time.Second) + // connect spammer and victim + err = spammerNode.AddPeer(ctx, victimPeerInfo) + require.NoError(t, err) + // create new spammer spammer := corruptlibp2p.NewSpammerGossipSubRouter(router) + require.NotNil(t, router) // start spamming the first peer - spammer.SpamIHave(victimPeerId, 1, 1) + go func() { + spammer.SpamIHave(victimPeerId, 1, 1) + }() + + inbounds := make([]chan string, 1) - time.Sleep(5 * time.Second) + select { + case rcv := <-inbounds[0]: + require.Equal(t, "foo", rcv) + case <-time.After(3 * time.Second): + require.Fail(t, "did not receive spam message") + } } From 242c3bb0dce36193940eb611b78a2873dddd4d56 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 6 Dec 2022 14:58:45 -0500 Subject: [PATCH 090/138] closing channel after receiving spammer's message --- insecure/corruptlibp2p/libp2p_test.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index dbab649c956..564f68fc4f0 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -2,6 +2,7 @@ package corruptlibp2p_test import ( "context" + "fmt" "github.com/onflow/flow-go/network/p2p/utils" "testing" "time" @@ -33,11 +34,13 @@ func TestSpam(t *testing.T) { t.Name(), internal.WithCorruptGossipSub(factory, corruptlibp2p.CorruptibleGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { + fmt.Println("spammerNode>RPC inspector") // here we can inspect the incoming RPC message to the spammer node return nil })), ) + received := make(chan struct{}) victimNode, victimId := p2ptest.NodeFixture( t, sporkId, @@ -45,6 +48,8 @@ func TestSpam(t *testing.T) { internal.WithCorruptGossipSub(factory, corruptlibp2p.CorruptibleGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { // here we can inspect the incoming RPC message to the victim node + fmt.Println("victimNode>RPC inspector") + close(received) // acknowledge victim received spammer's message return nil })), ) @@ -64,22 +69,22 @@ func TestSpam(t *testing.T) { // connect spammer and victim err = spammerNode.AddPeer(ctx, victimPeerInfo) require.NoError(t, err) + connected, err := spammerNode.IsConnected(victimPeerInfo.ID) + require.NoError(t, err) + require.True(t, connected) // create new spammer spammer := corruptlibp2p.NewSpammerGossipSubRouter(router) require.NotNil(t, router) // start spamming the first peer - go func() { - spammer.SpamIHave(victimPeerId, 1, 1) - }() - - inbounds := make([]chan string, 1) + spammer.SpamIHave(victimPeerId, 1, 1) + // check that victim received spammer's message select { - case rcv := <-inbounds[0]: - require.Equal(t, "foo", rcv) - case <-time.After(3 * time.Second): + case <-received: + return + case <-time.After(1 * time.Second): require.Fail(t, "did not receive spam message") } } From 3d8dc945e699e371a06b30b58a13e836793647af Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 6 Dec 2022 15:41:24 -0500 Subject: [PATCH 091/138] Spammer - GenerateCtlMessages() --- insecure/corruptlibp2p/libp2p_test.go | 7 +++++-- insecure/corruptlibp2p/spammerGossipSub.go | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index 564f68fc4f0..8f768490baa 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -48,7 +48,7 @@ func TestSpam(t *testing.T) { internal.WithCorruptGossipSub(factory, corruptlibp2p.CorruptibleGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { // here we can inspect the incoming RPC message to the victim node - fmt.Println("victimNode>RPC inspector") + fmt.Println("victimNode>RPC inspector: ", rpc.Control.String()) close(received) // acknowledge victim received spammer's message return nil })), @@ -77,8 +77,11 @@ func TestSpam(t *testing.T) { spammer := corruptlibp2p.NewSpammerGossipSubRouter(router) require.NotNil(t, router) + // prepare to spam - generate control messages + controlMessages := spammer.GenerateCtlMessages(1, 5) + // start spamming the first peer - spammer.SpamIHave(victimPeerId, 1, 1) + spammer.SpamIHave(victimPeerId, controlMessages) // check that victim received spammer's message select { diff --git a/insecure/corruptlibp2p/spammerGossipSub.go b/insecure/corruptlibp2p/spammerGossipSub.go index 023771e0d30..91346556bcc 100644 --- a/insecure/corruptlibp2p/spammerGossipSub.go +++ b/insecure/corruptlibp2p/spammerGossipSub.go @@ -1,6 +1,7 @@ package corruptlibp2p import ( + pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" ) @@ -22,11 +23,27 @@ func NewSpammerGossipSubRouter(router *pubsub.GossipSubRouter) *SpammerGossipSub // SpamIHave spams the victim with junk iHave messages. // msgCount is the number of iHave messages to send. // msgSize is the number of messageIDs to include in each iHave message. +// func (s *SpammerGossipSub) SpamIHave(victim peer.ID, ctlMessages []pb.ControlMessage) { func (s *SpammerGossipSub) SpamIHave(victim peer.ID, msgCount int, msgSize int) { + //s.router.SendControl(victim, ctlMessages[0]) + //for _, ctlMessage := range ctlMessages { + // s.router.SendControl(victim, ctlMessage) + //} for i := 0; i < msgCount; i++ { ctlIHave := GossipSubCtrlFixture(WithIHave(msgCount, msgSize)) s.router.SendControl(victim, ctlIHave) } } +// GenerateCtlMessages generates control messages before they are sent so the test can prepare +// to receive them before they are sent by the spammer. +func (s *SpammerGossipSub) GenerateCtlMessages(msgCount, msgSize int) []pb.ControlMessage { + var ctlMessages []pb.ControlMessage + for i := 0; i < msgCount; i++ { + ctlIHave := GossipSubCtrlFixture(WithIHave(msgCount, msgSize)) + ctlMessages = append(ctlMessages, *ctlIHave) + } + return ctlMessages +} + // TODO: SpamIWant, SpamGraft, SpamPrune. From deb4ad8af18aeefb349ec0a7f45293020d3b832b Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 6 Dec 2022 16:02:52 -0500 Subject: [PATCH 092/138] SpamIHave() uses slice of control messages --- insecure/corruptlibp2p/spammerGossipSub.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/insecure/corruptlibp2p/spammerGossipSub.go b/insecure/corruptlibp2p/spammerGossipSub.go index 91346556bcc..1d2942e4c7f 100644 --- a/insecure/corruptlibp2p/spammerGossipSub.go +++ b/insecure/corruptlibp2p/spammerGossipSub.go @@ -23,15 +23,9 @@ func NewSpammerGossipSubRouter(router *pubsub.GossipSubRouter) *SpammerGossipSub // SpamIHave spams the victim with junk iHave messages. // msgCount is the number of iHave messages to send. // msgSize is the number of messageIDs to include in each iHave message. -// func (s *SpammerGossipSub) SpamIHave(victim peer.ID, ctlMessages []pb.ControlMessage) { -func (s *SpammerGossipSub) SpamIHave(victim peer.ID, msgCount int, msgSize int) { - //s.router.SendControl(victim, ctlMessages[0]) - //for _, ctlMessage := range ctlMessages { - // s.router.SendControl(victim, ctlMessage) - //} - for i := 0; i < msgCount; i++ { - ctlIHave := GossipSubCtrlFixture(WithIHave(msgCount, msgSize)) - s.router.SendControl(victim, ctlIHave) +func (s *SpammerGossipSub) SpamIHave(victim peer.ID, ctlMessages []pb.ControlMessage) { + for _, ctlMessage := range ctlMessages { + s.router.SendControl(victim, &ctlMessage) } } From b242972013a43e6d54372add446b33d53bf612a7 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 6 Dec 2022 22:19:43 -0500 Subject: [PATCH 093/138] waiting until all messages received by victim --- insecure/corruptlibp2p/libp2p_test.go | 25 +++++++++++++++++++--- insecure/corruptlibp2p/spammerGossipSub.go | 18 ++++++++++------ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index 8f768490baa..087d9e94d3b 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -20,6 +20,7 @@ import ( ) func TestSpam(t *testing.T) { + const messagesToSpam = 3 sporkId := unittest.IdentifierFixture() var router *corrupt.GossipSubRouter @@ -41,15 +42,31 @@ func TestSpam(t *testing.T) { ) received := make(chan struct{}) + receivedCounter := 0 victimNode, victimId := p2ptest.NodeFixture( t, sporkId, t.Name(), internal.WithCorruptGossipSub(factory, corruptlibp2p.CorruptibleGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { + iHaves := rpc.GetControl().GetIhave() + if len(iHaves) == 0 { + // don't inspect control messages with no IHAVE messages + return nil + } + receivedCounter++ + + if receivedCounter == messagesToSpam { + close(received) // acknowledge victim received all of spammer's messages + return nil + } + // here we can inspect the incoming RPC message to the victim node fmt.Println("victimNode>RPC inspector: ", rpc.Control.String()) - close(received) // acknowledge victim received spammer's message + + for i := 0; i < len(iHaves); i++ { + fmt.Println("Ihave loop: ", iHaves[i].String()) + } return nil })), ) @@ -78,7 +95,9 @@ func TestSpam(t *testing.T) { require.NotNil(t, router) // prepare to spam - generate control messages - controlMessages := spammer.GenerateCtlMessages(1, 5) + controlMessages := spammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) + + // prepare to receive spam messages // start spamming the first peer spammer.SpamIHave(victimPeerId, controlMessages) @@ -87,7 +106,7 @@ func TestSpam(t *testing.T) { select { case <-received: return - case <-time.After(1 * time.Second): + case <-time.After(2 * time.Second): require.Fail(t, "did not receive spam message") } } diff --git a/insecure/corruptlibp2p/spammerGossipSub.go b/insecure/corruptlibp2p/spammerGossipSub.go index 1d2942e4c7f..16c5c89de1a 100644 --- a/insecure/corruptlibp2p/spammerGossipSub.go +++ b/insecure/corruptlibp2p/spammerGossipSub.go @@ -3,7 +3,9 @@ package corruptlibp2p import ( pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" + "github.com/stretchr/testify/require" pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" + "testing" ) type ControlMessage int @@ -29,15 +31,19 @@ func (s *SpammerGossipSub) SpamIHave(victim peer.ID, ctlMessages []pb.ControlMes } } -// GenerateCtlMessages generates control messages before they are sent so the test can prepare +// GenerateIHaveCtlMessages generates IHAVE control messages before they are sent so the test can prepare // to receive them before they are sent by the spammer. -func (s *SpammerGossipSub) GenerateCtlMessages(msgCount, msgSize int) []pb.ControlMessage { - var ctlMessages []pb.ControlMessage +func (s *SpammerGossipSub) GenerateIHaveCtlMessages(t *testing.T, msgCount, msgSize int) []pb.ControlMessage { + //var ctlMessageMap = make(map[string]pb.ControlMessage) + var iHaveCtlMsgs []pb.ControlMessage for i := 0; i < msgCount; i++ { - ctlIHave := GossipSubCtrlFixture(WithIHave(msgCount, msgSize)) - ctlMessages = append(ctlMessages, *ctlIHave) + iHaveCtlMsg := GossipSubCtrlFixture(WithIHave(msgCount, msgSize)) + + iHaves := iHaveCtlMsg.GetIhave() + require.Equal(t, msgCount, len(iHaves)) + iHaveCtlMsgs = append(iHaveCtlMsgs, *iHaveCtlMsg) } - return ctlMessages + return iHaveCtlMsgs } // TODO: SpamIWant, SpamGraft, SpamPrune. From e9aa47b7087ba622436853c643293d58ab146b04 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 06:00:30 -0500 Subject: [PATCH 094/138] checks that received messages == spam messages --- insecure/corruptlibp2p/libp2p_test.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index 087d9e94d3b..fc75e18b3d5 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -3,6 +3,7 @@ package corruptlibp2p_test import ( "context" "fmt" + pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/onflow/flow-go/network/p2p/utils" "testing" "time" @@ -42,7 +43,10 @@ func TestSpam(t *testing.T) { ) received := make(chan struct{}) + + // keeps track of how many messages victim received from spammer - to know when to stop listening for more messages receivedCounter := 0 + var iHaveReceivedCtlMsgs []pb.ControlMessage victimNode, victimId := p2ptest.NodeFixture( t, sporkId, @@ -55,6 +59,7 @@ func TestSpam(t *testing.T) { return nil } receivedCounter++ + iHaveReceivedCtlMsgs = append(iHaveReceivedCtlMsgs, *rpc.GetControl()) if receivedCounter == messagesToSpam { close(received) // acknowledge victim received all of spammer's messages @@ -94,13 +99,11 @@ func TestSpam(t *testing.T) { spammer := corruptlibp2p.NewSpammerGossipSubRouter(router) require.NotNil(t, router) - // prepare to spam - generate control messages - controlMessages := spammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) - - // prepare to receive spam messages + // prepare to spam - generate IHAVE control messages + iHaveSentMessages := spammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) - // start spamming the first peer - spammer.SpamIHave(victimPeerId, controlMessages) + // start spamming the victime peer + spammer.SpamIHave(victimPeerId, iHaveSentMessages) // check that victim received spammer's message select { @@ -109,4 +112,8 @@ func TestSpam(t *testing.T) { case <-time.After(2 * time.Second): require.Fail(t, "did not receive spam message") } + + // check contents of received messages - should match what spammer sent + require.Equal(t, len(iHaveSentMessages), len(iHaveReceivedCtlMsgs)) + require.ElementsMatch(t, iHaveReceivedCtlMsgs, iHaveSentMessages) } From 52bb30eef9e1e762beac99b04cc458ee216b8d4c Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 06:25:44 -0500 Subject: [PATCH 095/138] clean up --- insecure/corruptlibp2p/libp2p_test.go | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index fc75e18b3d5..5516a16898d 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -2,7 +2,6 @@ package corruptlibp2p_test import ( "context" - "fmt" pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/onflow/flow-go/network/p2p/utils" "testing" @@ -36,7 +35,6 @@ func TestSpam(t *testing.T) { t.Name(), internal.WithCorruptGossipSub(factory, corruptlibp2p.CorruptibleGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { - fmt.Println("spammerNode>RPC inspector") // here we can inspect the incoming RPC message to the spammer node return nil })), @@ -63,14 +61,6 @@ func TestSpam(t *testing.T) { if receivedCounter == messagesToSpam { close(received) // acknowledge victim received all of spammer's messages - return nil - } - - // here we can inspect the incoming RPC message to the victim node - fmt.Println("victimNode>RPC inspector: ", rpc.Control.String()) - - for i := 0; i < len(iHaves); i++ { - fmt.Println("Ihave loop: ", iHaves[i].String()) } return nil })), @@ -85,6 +75,7 @@ func TestSpam(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) defer cancel() + p2ptest.StartNodes(t, signalerCtx, []p2p.LibP2PNode{spammerNode, victimNode}, 100*time.Second) defer p2ptest.StopNodes(t, []p2p.LibP2PNode{spammerNode, victimNode}, cancel, 100*time.Second) @@ -102,7 +93,7 @@ func TestSpam(t *testing.T) { // prepare to spam - generate IHAVE control messages iHaveSentMessages := spammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) - // start spamming the victime peer + // start spamming the victim peer spammer.SpamIHave(victimPeerId, iHaveSentMessages) // check that victim received spammer's message @@ -113,7 +104,7 @@ func TestSpam(t *testing.T) { require.Fail(t, "did not receive spam message") } - // check contents of received messages - should match what spammer sent + // check contents of received messages should match what spammer sent require.Equal(t, len(iHaveSentMessages), len(iHaveReceivedCtlMsgs)) require.ElementsMatch(t, iHaveReceivedCtlMsgs, iHaveSentMessages) } From b2fc0cba542c3efc4209667876b56814c63a2016 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 06:27:58 -0500 Subject: [PATCH 096/138] renaming var --- insecure/corruptlibp2p/libp2p_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index 5516a16898d..918e21cb421 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -91,10 +91,10 @@ func TestSpam(t *testing.T) { require.NotNil(t, router) // prepare to spam - generate IHAVE control messages - iHaveSentMessages := spammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) + iHaveSentCtlMsgs := spammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) // start spamming the victim peer - spammer.SpamIHave(victimPeerId, iHaveSentMessages) + spammer.SpamIHave(victimPeerId, iHaveSentCtlMsgs) // check that victim received spammer's message select { @@ -105,6 +105,6 @@ func TestSpam(t *testing.T) { } // check contents of received messages should match what spammer sent - require.Equal(t, len(iHaveSentMessages), len(iHaveReceivedCtlMsgs)) - require.ElementsMatch(t, iHaveReceivedCtlMsgs, iHaveSentMessages) + require.Equal(t, len(iHaveSentCtlMsgs), len(iHaveReceivedCtlMsgs)) + require.ElementsMatch(t, iHaveReceivedCtlMsgs, iHaveSentCtlMsgs) } From 890dd69212eaf18e5fb113f5e3fd6fd180a14be3 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 07:17:00 -0500 Subject: [PATCH 097/138] bug fix - continue test after received all messages --- insecure/corruptlibp2p/libp2p_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/libp2p_test.go index 918e21cb421..66eb9a689d2 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/libp2p_test.go @@ -99,8 +99,8 @@ func TestSpam(t *testing.T) { // check that victim received spammer's message select { case <-received: - return - case <-time.After(2 * time.Second): + break + case <-time.After(1 * time.Second): require.Fail(t, "did not receive spam message") } From e9b1e7bae42382d8578882535d73a5c6b3e8a5eb Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 07:35:25 -0500 Subject: [PATCH 098/138] adding docs, renaming files --- .../{spammerGossipSub.go => gossipsub_spammer.go} | 12 ++++++------ .../{pubsubAdapter.go => pubsub_adapter.go} | 0 ...bsubAdapterConfig.go => pubsub_adapter_config.go} | 0 .../corruptlibp2p/{libp2p_test.go => spam_test.go} | 7 +++++-- 4 files changed, 11 insertions(+), 8 deletions(-) rename insecure/corruptlibp2p/{spammerGossipSub.go => gossipsub_spammer.go} (72%) rename insecure/corruptlibp2p/{pubsubAdapter.go => pubsub_adapter.go} (100%) rename insecure/corruptlibp2p/{pubsubAdapterConfig.go => pubsub_adapter_config.go} (100%) rename insecure/corruptlibp2p/{libp2p_test.go => spam_test.go} (89%) diff --git a/insecure/corruptlibp2p/spammerGossipSub.go b/insecure/corruptlibp2p/gossipsub_spammer.go similarity index 72% rename from insecure/corruptlibp2p/spammerGossipSub.go rename to insecure/corruptlibp2p/gossipsub_spammer.go index 16c5c89de1a..8c459e1f0b8 100644 --- a/insecure/corruptlibp2p/spammerGossipSub.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -10,14 +10,14 @@ import ( type ControlMessage int -// SpammerGossipSub is a wrapper around the GossipSubRouter that allows us to +// GossipSubRouterSpammer is a wrapper around the GossipSubRouter that allows us to // spam the victim with junk control messages. -type SpammerGossipSub struct { +type GossipSubRouterSpammer struct { router *pubsub.GossipSubRouter } -func NewSpammerGossipSubRouter(router *pubsub.GossipSubRouter) *SpammerGossipSub { - return &SpammerGossipSub{ +func NewGossipSubRouterSpammer(router *pubsub.GossipSubRouter) *GossipSubRouterSpammer { + return &GossipSubRouterSpammer{ router: router, } } @@ -25,7 +25,7 @@ func NewSpammerGossipSubRouter(router *pubsub.GossipSubRouter) *SpammerGossipSub // SpamIHave spams the victim with junk iHave messages. // msgCount is the number of iHave messages to send. // msgSize is the number of messageIDs to include in each iHave message. -func (s *SpammerGossipSub) SpamIHave(victim peer.ID, ctlMessages []pb.ControlMessage) { +func (s *GossipSubRouterSpammer) SpamIHave(victim peer.ID, ctlMessages []pb.ControlMessage) { for _, ctlMessage := range ctlMessages { s.router.SendControl(victim, &ctlMessage) } @@ -33,7 +33,7 @@ func (s *SpammerGossipSub) SpamIHave(victim peer.ID, ctlMessages []pb.ControlMes // GenerateIHaveCtlMessages generates IHAVE control messages before they are sent so the test can prepare // to receive them before they are sent by the spammer. -func (s *SpammerGossipSub) GenerateIHaveCtlMessages(t *testing.T, msgCount, msgSize int) []pb.ControlMessage { +func (s *GossipSubRouterSpammer) GenerateIHaveCtlMessages(t *testing.T, msgCount, msgSize int) []pb.ControlMessage { //var ctlMessageMap = make(map[string]pb.ControlMessage) var iHaveCtlMsgs []pb.ControlMessage for i := 0; i < msgCount; i++ { diff --git a/insecure/corruptlibp2p/pubsubAdapter.go b/insecure/corruptlibp2p/pubsub_adapter.go similarity index 100% rename from insecure/corruptlibp2p/pubsubAdapter.go rename to insecure/corruptlibp2p/pubsub_adapter.go diff --git a/insecure/corruptlibp2p/pubsubAdapterConfig.go b/insecure/corruptlibp2p/pubsub_adapter_config.go similarity index 100% rename from insecure/corruptlibp2p/pubsubAdapterConfig.go rename to insecure/corruptlibp2p/pubsub_adapter_config.go diff --git a/insecure/corruptlibp2p/libp2p_test.go b/insecure/corruptlibp2p/spam_test.go similarity index 89% rename from insecure/corruptlibp2p/libp2p_test.go rename to insecure/corruptlibp2p/spam_test.go index 66eb9a689d2..648f0c51126 100644 --- a/insecure/corruptlibp2p/libp2p_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -19,7 +19,10 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -func TestSpam(t *testing.T) { +// TestSpam_IHave sets up a 2 node test between a victim node and a spammer. The spammer sends a few IHAVE control messages +// to the victim node without being subscribed to any of the same topics. +// The test then checks that the victim node received all the messages from the spammer. +func TestSpam_IHave(t *testing.T) { const messagesToSpam = 3 sporkId := unittest.IdentifierFixture() @@ -87,7 +90,7 @@ func TestSpam(t *testing.T) { require.True(t, connected) // create new spammer - spammer := corruptlibp2p.NewSpammerGossipSubRouter(router) + spammer := corruptlibp2p.NewGossipSubRouterSpammer(router) require.NotNil(t, router) // prepare to spam - generate IHAVE control messages From 68ab3ac951e2ce7028f322220aaec599242fd77d Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 07:42:43 -0500 Subject: [PATCH 099/138] `go mod tidy` in integration/ --- integration/go.mod | 2 +- integration/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration/go.mod b/integration/go.mod index 6a3c063ee1e..590d98e8299 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -167,7 +167,7 @@ require ( github.com/libp2p/go-libp2p-core v0.20.1 // indirect github.com/libp2p/go-libp2p-kad-dht v0.18.0 // indirect github.com/libp2p/go-libp2p-kbucket v0.4.7 // indirect - github.com/libp2p/go-libp2p-pubsub v0.8.2-0.20221201175637-3d2eab35722e // indirect + github.com/libp2p/go-libp2p-pubsub v0.8.2 // indirect github.com/libp2p/go-libp2p-record v0.2.0 // indirect github.com/libp2p/go-libp2p-tls v0.4.1 // indirect github.com/libp2p/go-msgio v0.2.0 // indirect diff --git a/integration/go.sum b/integration/go.sum index 57b94429d0c..d3248e6574f 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -1002,8 +1002,8 @@ github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuD github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= github.com/libp2p/go-libp2p-peerstore v0.8.0 h1:bzTG693TA1Ju/zKmUCQzDLSqiJnyRFVwPpuloZ/OZtI= github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-pubsub v0.8.2-0.20221201175637-3d2eab35722e h1:phmi6mEoO5y2AQP68+4vZhNpHtZ4dum2ieFtWdmjXak= -github.com/libp2p/go-libp2p-pubsub v0.8.2-0.20221201175637-3d2eab35722e/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= +github.com/libp2p/go-libp2p-pubsub v0.8.2 h1:QLGUmkgKmwEVxVDYGsqc5t9CykOMY2Y21cXQHjR462I= +github.com/libp2p/go-libp2p-pubsub v0.8.2/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= From 4de0f83e3a3ec42ca3006aa5b46859d80b935ca0 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 08:23:28 -0500 Subject: [PATCH 100/138] CI fix - building corrupt images --- Makefile | 2 +- insecure/cmd/mods_override.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a1b24b8e476..e8d2728f2b8 100644 --- a/Makefile +++ b/Makefile @@ -310,7 +310,7 @@ docker-build-access-debug: docker build -f cmd/Dockerfile --build-arg TARGET=./cmd/access --build-arg COMMIT=$(COMMIT) --build-arg VERSION=$(IMAGE_TAG) --build-arg GOARCH=$(GOARCH) --target debug \ -t "$(CONTAINER_REGISTRY)/access-debug:latest" -t "$(CONTAINER_REGISTRY)/access-debug:$(SHORT_COMMIT)" -t "$(CONTAINER_REGISTRY)/access-debug:$(IMAGE_TAG)" . -# build corrupted access node for BFT testing +# build corrupt access node for BFT testing .PHONY: docker-build-access-corrupt docker-build-access-corrupt: #temporarily make insecure/ a non-module to allow Docker to use corrupt builders there diff --git a/insecure/cmd/mods_override.sh b/insecure/cmd/mods_override.sh index 6713441ad4f..efb71d52069 100755 --- a/insecure/cmd/mods_override.sh +++ b/insecure/cmd/mods_override.sh @@ -6,7 +6,7 @@ cp ./go.mod ./go2.mod cp ./go.sum ./go2.sum # inject forked libp2p-pubsub into main module to allow building corrupt Docker images -echo "require github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5" >> ./go.mod +echo "require github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b" >> ./go.mod # update go.sum since added new dependency go mod tidy From 48faf116cb7677eaba3746e9646fd110339e38d0 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 08:26:35 -0500 Subject: [PATCH 101/138] `go mod tidy` in insecure --- insecure/go.sum | 4 ---- 1 file changed, 4 deletions(-) diff --git a/insecure/go.sum b/insecure/go.sum index debbbeccac6..1c04a268b87 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -885,8 +885,6 @@ github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuD github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= github.com/libp2p/go-libp2p-peerstore v0.8.0 h1:bzTG693TA1Ju/zKmUCQzDLSqiJnyRFVwPpuloZ/OZtI= github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-pubsub v0.8.2-0.20221201175637-3d2eab35722e h1:phmi6mEoO5y2AQP68+4vZhNpHtZ4dum2ieFtWdmjXak= -github.com/libp2p/go-libp2p-pubsub v0.8.2-0.20221201175637-3d2eab35722e/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= github.com/libp2p/go-libp2p-pubsub v0.8.2 h1:QLGUmkgKmwEVxVDYGsqc5t9CykOMY2Y21cXQHjR462I= github.com/libp2p/go-libp2p-pubsub v0.8.2/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= @@ -1514,8 +1512,6 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b h1:9qSLOxaiPv3HR5THaAcUuXRi4OFIexDseSp8ZBKOMsk= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11 h1:WYWfp6ydU0v6ev+amaIRIfxJEoEnf9HP0o523qlg/1o= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11/go.mod h1:GwZ5VBU63KlM8wZsnVvSGDIMeGMOO9Cb3uLXDP8WFYM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From db0f2f716b83c8d60712497c22a7be602bad3c0b Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 08:45:49 -0500 Subject: [PATCH 102/138] CI fix - `integration/go.mod` forked pubsub version updated --- integration/go.mod | 2 +- integration/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration/go.mod b/integration/go.mod index 590d98e8299..06af24e8005 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -257,7 +257,7 @@ require ( github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect - github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 // indirect + github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/zeebo/blake3 v0.2.3 // indirect go.opencensus.io v0.23.0 // indirect diff --git a/integration/go.sum b/integration/go.sum index d3248e6574f..2363f16a511 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -1686,8 +1686,8 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5 h1:c6Ns5fDv1quWqDgIVw+hRRsLfCVTz7tqhSwRP5wjRRo= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.12-0.20221110181155-60457b3ef6d5/go.mod h1:lqsC4XUC1754dyyPIJjQ4rhVhmv2SMAgq+1MJUoJyOU= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b h1:9qSLOxaiPv3HR5THaAcUuXRi4OFIexDseSp8ZBKOMsk= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 4ffc43b877581a9518a839cf31a1f910fd833cc0 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 08:49:58 -0500 Subject: [PATCH 103/138] lint fix in `insecure` --- insecure/corruptlibp2p/gossipsub_spammer.go | 3 ++- insecure/corruptlibp2p/libp2p.go | 11 ----------- insecure/corruptlibp2p/spam_test.go | 6 ++++-- 3 files changed, 6 insertions(+), 14 deletions(-) delete mode 100644 insecure/corruptlibp2p/libp2p.go diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index 8c459e1f0b8..871cdbd9038 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -1,11 +1,12 @@ package corruptlibp2p import ( + "testing" + pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" - "testing" ) type ControlMessage int diff --git a/insecure/corruptlibp2p/libp2p.go b/insecure/corruptlibp2p/libp2p.go deleted file mode 100644 index 5d06a2a899c..00000000000 --- a/insecure/corruptlibp2p/libp2p.go +++ /dev/null @@ -1,11 +0,0 @@ -package corruptlibp2p - -import ( - corruptpubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" -) - -// getGossipSubParams is a small test function to test that can access forked pubsub module -func getGossipSubParams() corruptpubsub.GossipSubParams { - defaultParams := corruptpubsub.DefaultGossipSubParams() - return defaultParams -} diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index 648f0c51126..69343efdfb5 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -2,11 +2,13 @@ package corruptlibp2p_test import ( "context" - pb "github.com/libp2p/go-libp2p-pubsub/pb" - "github.com/onflow/flow-go/network/p2p/utils" "testing" "time" + pb "github.com/libp2p/go-libp2p-pubsub/pb" + + "github.com/onflow/flow-go/network/p2p/utils" + "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" From 51e1efe3bee109efeb871f61e7025dc4057f3998 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 09:09:10 -0500 Subject: [PATCH 104/138] `corruptible` to `corrupt` renaming funcs --- insecure/corruptlibp2p/libp2p_node_factory.go | 18 +++++++++--------- insecure/corruptlibp2p/spam_test.go | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_node_factory.go b/insecure/corruptlibp2p/libp2p_node_factory.go index 420f8969649..460c42376d9 100644 --- a/insecure/corruptlibp2p/libp2p_node_factory.go +++ b/insecure/corruptlibp2p/libp2p_node_factory.go @@ -39,7 +39,7 @@ func NewCorruptLibP2PNodeFactory( ) p2pbuilder.LibP2PFactoryFunc { return func() (p2p.LibP2PNode, error) { if chainID != flow.BftTestnet { - panic("illegal chain id for using corruptible conduit factory") + panic("illegal chain id for using corrupt libp2p node") } builder := p2pbuilder.DefaultNodeBuilder( @@ -64,9 +64,9 @@ func NewCorruptLibP2PNodeFactory( } } -// CorruptibleGossipSubFactory returns a factory function that creates a new instance of the forked gossipsub module from +// CorruptGossipSubFactory returns a factory function that creates a new instance of the forked gossipsub module from // github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. -func CorruptibleGossipSubFactory(routerOpts ...func(*corrupt.GossipSubRouter)) p2pbuilder.GossipSubFactoryFunc { +func CorruptGossipSubFactory(routerOpts ...func(*corrupt.GossipSubRouter)) p2pbuilder.GossipSubFactoryFunc { factory := func(ctx context.Context, logger zerolog.Logger, host host.Host, cfg p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) { adapter, router, err := NewCorruptGossipSubAdapter(ctx, logger, host, cfg) for _, opt := range routerOpts { @@ -77,23 +77,23 @@ func CorruptibleGossipSubFactory(routerOpts ...func(*corrupt.GossipSubRouter)) p return factory } -// CorruptibleGossipSubConfigFactory returns a factory function that creates a new instance of the forked gossipsub config +// CorruptGossipSubConfigFactory returns a factory function that creates a new instance of the forked gossipsub config // from github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. -func CorruptibleGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { +func CorruptGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { return NewCorruptPubSubAdapterConfig(base) } } -// CorruptibleGossipSubConfigFactoryWithInspector returns a factory function that creates a new instance of the forked gossipsub config +// CorruptGossipSubConfigFactoryWithInspector returns a factory function that creates a new instance of the forked gossipsub config // from github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. -func CorruptibleGossipSubConfigFactoryWithInspector(inspector func(peer.ID, *corrupt.RPC) error) p2pbuilder.GossipSubAdapterConfigFunc { +func CorruptGossipSubConfigFactoryWithInspector(inspector func(peer.ID, *corrupt.RPC) error) p2pbuilder.GossipSubAdapterConfigFunc { return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { return NewCorruptPubSubAdapterConfigWithInspector(base, inspector) } } func overrideWithCorruptGossipSub(builder p2pbuilder.NodeBuilder) { - factory := CorruptibleGossipSubFactory() - builder.SetGossipSubFactory(factory, CorruptibleGossipSubConfigFactory()) + factory := CorruptGossipSubFactory() + builder.SetGossipSubFactory(factory, CorruptGossipSubConfigFactory()) } diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index 69343efdfb5..9abc9de9a81 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -29,7 +29,7 @@ func TestSpam_IHave(t *testing.T) { sporkId := unittest.IdentifierFixture() var router *corrupt.GossipSubRouter - factory := corruptlibp2p.CorruptibleGossipSubFactory(func(r *corrupt.GossipSubRouter) { + factory := corruptlibp2p.CorruptGossipSubFactory(func(r *corrupt.GossipSubRouter) { require.NotNil(t, r) router = r // save the router at the initialization time of the factory }) @@ -39,7 +39,7 @@ func TestSpam_IHave(t *testing.T) { sporkId, t.Name(), internal.WithCorruptGossipSub(factory, - corruptlibp2p.CorruptibleGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { + corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { // here we can inspect the incoming RPC message to the spammer node return nil })), @@ -55,7 +55,7 @@ func TestSpam_IHave(t *testing.T) { sporkId, t.Name(), internal.WithCorruptGossipSub(factory, - corruptlibp2p.CorruptibleGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { + corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { iHaves := rpc.GetControl().GetIhave() if len(iHaves) == 0 { // don't inspect control messages with no IHAVE messages From 2ad324e431a6e72dab4508b0c6864c1d1a556d45 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 7 Dec 2022 09:44:31 -0500 Subject: [PATCH 105/138] channel var renaming --- insecure/corruptlibp2p/spam_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index 9abc9de9a81..dd168e899b3 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -45,7 +45,7 @@ func TestSpam_IHave(t *testing.T) { })), ) - received := make(chan struct{}) + receivedAllMsgs := make(chan struct{}) // keeps track of how many messages victim received from spammer - to know when to stop listening for more messages receivedCounter := 0 @@ -65,7 +65,7 @@ func TestSpam_IHave(t *testing.T) { iHaveReceivedCtlMsgs = append(iHaveReceivedCtlMsgs, *rpc.GetControl()) if receivedCounter == messagesToSpam { - close(received) // acknowledge victim received all of spammer's messages + close(receivedAllMsgs) // acknowledge victim received all of spammer's messages } return nil })), @@ -101,12 +101,12 @@ func TestSpam_IHave(t *testing.T) { // start spamming the victim peer spammer.SpamIHave(victimPeerId, iHaveSentCtlMsgs) - // check that victim received spammer's message + // check that victim received all spam messages select { - case <-received: + case <-receivedAllMsgs: break case <-time.After(1 * time.Second): - require.Fail(t, "did not receive spam message") + require.Fail(t, "did not receive spam messages") } // check contents of received messages should match what spammer sent From ef90710a36f9019ee390d3d0c0b7aaff02c6e1ca Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 19 Dec 2022 14:52:20 -0800 Subject: [PATCH 106/138] [Spam Testing] Fixes race conditions (#3687) * [Execution] Break blobservice out to own component in en builder * readd tracer * add explicit check if blobservice is nil * [Access] Add admin command to get execution data (#3649) * [Access] Add admin command to get execution data * only add admin command when exec sync is enabled * fix unittest * fix readme howto comment * also set max receive size in admin server * add max receive size to admin servers grpc client * fix conflict with master * [Logging] Add log throttle for debug/trace level events * fix lint issue * increase default limit to 2000 * add sampler helper and specific sampler for score options * Add payer can pay check to transaction execution * Replace ledger migration tool's view with fvm/utils' SimpleView This is needed to unblock #3644 since the original migration view does not fully implement the view api. gh: #3102 * implements atomic router * Improve result collector to return error instead of panic gh: #3547 * Invalidate derived data when meter param overrides change. gh: #3102 * [Testing] Use localhost unittest to reduce firewall alerts * update some integration tests * use net.JoinHostPort * wip * fixes test * wip * upgrades go mod * sets separate factories * applies the victim node id directly from the host * reverts ensure pubsub message exchange to internal * cleans up atomic router * doc cleanup * cleans up * Revert "Merge remote-tracking branch 'origin/master' into yahya/6424-gossipsub-spam-test-fix-race" This reverts commit 7e30235c5089095b6964691d9f31eb6266568f89. * fixes retry race condition --- insecure/corruptlibp2p/gossipsub_spammer.go | 7 +- insecure/corruptlibp2p/spam_test.go | 114 ++++++----- insecure/go.mod | 2 +- insecure/go.sum | 8 + network/internal/p2pfixtures/fixtures.go | 184 ++++++++---------- .../p2p/connection/connection_gater_test.go | 4 +- network/p2p/test/fixtures.go | 29 +++ 7 files changed, 189 insertions(+), 159 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index 871cdbd9038..c47aadecaa2 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -1,12 +1,11 @@ package corruptlibp2p import ( - "testing" - pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" + "testing" ) type ControlMessage int @@ -26,9 +25,9 @@ func NewGossipSubRouterSpammer(router *pubsub.GossipSubRouter) *GossipSubRouterS // SpamIHave spams the victim with junk iHave messages. // msgCount is the number of iHave messages to send. // msgSize is the number of messageIDs to include in each iHave message. -func (s *GossipSubRouterSpammer) SpamIHave(victim peer.ID, ctlMessages []pb.ControlMessage) { +func (s *GossipSubRouterSpammer) SpamIHave(t *testing.T, victim peer.ID, ctlMessages []pb.ControlMessage) { for _, ctlMessage := range ctlMessages { - s.router.SendControl(victim, &ctlMessage) + require.True(t, s.router.SendControl(victim, &ctlMessage)) } } diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index dd168e899b3..ed85e2f61cd 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -2,13 +2,13 @@ package corruptlibp2p_test import ( "context" + "github.com/onflow/flow-go/model/flow" + "sync" "testing" "time" pb "github.com/libp2p/go-libp2p-pubsub/pb" - "github.com/onflow/flow-go/network/p2p/utils" - "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" @@ -21,95 +21,117 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// TestSpam_IHave sets up a 2 node test between a victim node and a spammer. The spammer sends a few IHAVE control messages +// TestSpam_IHave sets up a 2 node test between a victim node and a spammer. The spammer sends a few iHAVE control messages // to the victim node without being subscribed to any of the same topics. // The test then checks that the victim node received all the messages from the spammer. func TestSpam_IHave(t *testing.T) { const messagesToSpam = 3 sporkId := unittest.IdentifierFixture() - var router *corrupt.GossipSubRouter - factory := corruptlibp2p.CorruptGossipSubFactory(func(r *corrupt.GossipSubRouter) { - require.NotNil(t, r) - router = r // save the router at the initialization time of the factory - }) - - spammerNode, _ := p2ptest.NodeFixture( + router := newAtomicRouter() + spammerNode, spammerId := p2ptest.NodeFixture( t, sporkId, t.Name(), - internal.WithCorruptGossipSub(factory, + p2ptest.WithRole(flow.RoleConsensus), + internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(func(r *corrupt.GossipSubRouter) { + require.NotNil(t, r) + router.set(r) + }), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { // here we can inspect the incoming RPC message to the spammer node return nil })), ) - receivedAllMsgs := make(chan struct{}) + allSpamIHavesReceived := sync.WaitGroup{} + allSpamIHavesReceived.Add(messagesToSpam) - // keeps track of how many messages victim received from spammer - to know when to stop listening for more messages - receivedCounter := 0 var iHaveReceivedCtlMsgs []pb.ControlMessage victimNode, victimId := p2ptest.NodeFixture( t, sporkId, t.Name(), - internal.WithCorruptGossipSub(factory, + p2ptest.WithRole(flow.RoleConsensus), + internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { iHaves := rpc.GetControl().GetIhave() if len(iHaves) == 0 { - // don't inspect control messages with no IHAVE messages + // don't inspect control messages with no iHAVE messages return nil } - receivedCounter++ - iHaveReceivedCtlMsgs = append(iHaveReceivedCtlMsgs, *rpc.GetControl()) - if receivedCounter == messagesToSpam { - close(receivedAllMsgs) // acknowledge victim received all of spammer's messages - } + iHaveReceivedCtlMsgs = append(iHaveReceivedCtlMsgs, *rpc.GetControl()) + allSpamIHavesReceived.Done() // acknowledge that victim received a message. return nil })), ) - victimPeerId, err := unittest.PeerIDFromFlowID(&victimId) - require.NoError(t, err) - - victimPeerInfo, err := utils.PeerAddressInfo(victimId) - require.NoError(t, err) // starts nodes ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) defer cancel() - - p2ptest.StartNodes(t, signalerCtx, []p2p.LibP2PNode{spammerNode, victimNode}, 100*time.Second) - defer p2ptest.StopNodes(t, []p2p.LibP2PNode{spammerNode, victimNode}, cancel, 100*time.Second) - - // connect spammer and victim - err = spammerNode.AddPeer(ctx, victimPeerInfo) - require.NoError(t, err) - connected, err := spammerNode.IsConnected(victimPeerInfo.ID) - require.NoError(t, err) - require.True(t, connected) + nodes := []p2p.LibP2PNode{spammerNode, victimNode} + p2ptest.StartNodes(t, signalerCtx, nodes, 5*time.Second) + defer p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) + + require.Eventuallyf(t, func() bool { + // ensuring the spammer router has been initialized. + // this is needed because the router is initialized asynchronously. + return router.get() != nil + }, 1*time.Second, 100*time.Millisecond, "spammer router not set") + + // prior to the test we should ensure that spammer and victim connect and discover each other. + // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. + // without a priory connection established, directly spamming pubsub messages may cause a race condition in the pubsub implementation. + p2ptest.EnsureConnected(t, ctx, nodes) + p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, flow.IdentityList{&spammerId, &victimId}) + p2ptest.EnsureStreamCreationInBothDirections(t, ctx, nodes) // create new spammer - spammer := corruptlibp2p.NewGossipSubRouterSpammer(router) + spammer := corruptlibp2p.NewGossipSubRouterSpammer(router.get()) require.NotNil(t, router) - // prepare to spam - generate IHAVE control messages + // prepare to spam - generate iHAVE control messages iHaveSentCtlMsgs := spammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) // start spamming the victim peer - spammer.SpamIHave(victimPeerId, iHaveSentCtlMsgs) + spammer.SpamIHave(t, victimNode.Host().ID(), iHaveSentCtlMsgs) // check that victim received all spam messages - select { - case <-receivedAllMsgs: - break - case <-time.After(1 * time.Second): - require.Fail(t, "did not receive spam messages") - } + unittest.RequireReturnsBefore(t, allSpamIHavesReceived.Wait, 1*time.Second, "victim did not receive all spam messages") // check contents of received messages should match what spammer sent - require.Equal(t, len(iHaveSentCtlMsgs), len(iHaveReceivedCtlMsgs)) require.ElementsMatch(t, iHaveReceivedCtlMsgs, iHaveSentCtlMsgs) } + +// atomicRouter is a wrapper around the corrupt.GossipSubRouter that allows atomic access to the router. +// This is done to avoid race conditions when accessing the router from multiple goroutines. +type atomicRouter struct { + mu sync.Mutex + router *corrupt.GossipSubRouter +} + +func newAtomicRouter() *atomicRouter { + return &atomicRouter{ + mu: sync.Mutex{}, + } +} + +// SetRouter sets the router if it has never been set. +func (a *atomicRouter) set(router *corrupt.GossipSubRouter) bool { + a.mu.Lock() + defer a.mu.Unlock() + if a.router == nil { + a.router = router + return true + } + return false +} + +// GetRouter returns the router. +func (a *atomicRouter) get() *corrupt.GossipSubRouter { + a.mu.Lock() + defer a.mu.Unlock() + return a.router +} diff --git a/insecure/go.mod b/insecure/go.mod index bdd7553d1a4..0b4fcb6c2d8 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -14,7 +14,7 @@ require ( github.com/rs/zerolog v1.28.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.0 - github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b + github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208234712-b44d9133e4ee google.golang.org/grpc v1.50.1 google.golang.org/protobuf v1.28.1 ) diff --git a/insecure/go.sum b/insecure/go.sum index e989428e208..e9f7589b36f 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -1485,6 +1485,14 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b h1:9qSLOxaiPv3HR5THaAcUuXRi4OFIexDseSp8ZBKOMsk= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208175358-bf05f82c565e h1:+7MOzfuoHgh6Rpy7BJBEPcMgDvC/cTaFv7QlWXjT8+M= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208175358-bf05f82c565e/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208185058-df6c36a76983 h1:AzbDxc7heoGrroxZwRg+LBbneh15o0UvvxfUzmzg6aY= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208185058-df6c36a76983/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208204955-09668489428c h1:WbA09PVlZtZu9QztQDCw4ZYHvptFkRvJtFLal3a3O58= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208204955-09668489428c/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208234712-b44d9133e4ee h1:yFB2xjfswpuRh8FHagdBMKcBMltjr5u/XKzX6fkJO5E= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208234712-b44d9133e4ee/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index c76e7412020..a6d8c3e33a1 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "fmt" + "github.com/onflow/flow-go/network/message" "net" "testing" "time" @@ -27,7 +28,6 @@ import ( "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2putils" "github.com/onflow/flow-go/network/internal/testutils" - "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" p2pdht "github.com/onflow/flow-go/network/p2p/dht" "github.com/onflow/flow-go/network/p2p/keyutils" @@ -147,46 +147,6 @@ func PeerIdsFixture(t *testing.T, n int) []peer.ID { return peerIDs } -// MustEncodeEvent encodes and returns the given event and fails the test if it faces any issue while encoding. -func MustEncodeEvent(t *testing.T, v interface{}, channel channels.Channel) []byte { - bz, err := unittest.NetworkCodec().Encode(v) - require.NoError(t, err) - - msg := message.Message{ - ChannelID: channel.String(), - Payload: bz, - } - data, err := msg.Marshal() - require.NoError(t, err) - - return data -} - -// SubMustReceiveMessage checks that the subscription have received the given message within the given timeout by the context. -func SubMustReceiveMessage(t *testing.T, ctx context.Context, expectedMessage []byte, sub p2p.Subscription) { - received := make(chan struct{}) - go func() { - msg, err := sub.Next(ctx) - require.NoError(t, err) - require.Equal(t, expectedMessage, msg.Data) - close(received) - }() - - select { - case <-received: - return - case <-ctx.Done(): - require.Fail(t, "timeout on receiving expected pubsub message") - } -} - -// SubsMustReceiveMessage checks that all subscriptions receive the given message within the given timeout by the context. -func SubsMustReceiveMessage(t *testing.T, ctx context.Context, expectedMessage []byte, subs []p2p.Subscription) { - for _, sub := range subs { - SubMustReceiveMessage(t, ctx, expectedMessage, sub) - } -} - // SubMustNeverReceiveAnyMessage checks that the subscription never receives any message within the given timeout by the context. func SubMustNeverReceiveAnyMessage(t *testing.T, ctx context.Context, sub p2p.Subscription) { timeouted := make(chan struct{}) @@ -249,19 +209,6 @@ func AddNodesToEachOthersPeerStore(t *testing.T, nodes []p2p.LibP2PNode, ids flo } } -// EnsureConnected ensures that the given nodes are connected to each other. -// It fails the test if any of the nodes is not connected to any other node. -func EnsureConnected(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode) { - for _, node := range nodes { - for _, other := range nodes { - if node == other { - continue - } - require.NoError(t, node.Host().Connect(ctx, other.Host().Peerstore().PeerInfo(other.Host().ID()))) - } - } -} - // EnsureNotConnected ensures that no connection exists from "from" nodes to "to" nodes. func EnsureNotConnected(t *testing.T, ctx context.Context, from []p2p.LibP2PNode, to []p2p.LibP2PNode) { for _, node := range from { @@ -282,43 +229,6 @@ func EnsureNotConnectedBetweenGroups(t *testing.T, ctx context.Context, groupA [ EnsureNotConnected(t, ctx, groupB, groupA) } -// EnsurePubsubMessageExchange ensures that the given nodes exchange the given message on the given channel through pubsub. -func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode, messageFactory func() (interface{}, channels.Topic)) { - _, topic := messageFactory() - - subs := make([]p2p.Subscription, len(nodes)) - slashingViolationsConsumer := unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector()) - for i, node := range nodes { - ps, err := node.Subscribe( - topic, - validator.TopicValidator( - unittest.Logger(), - unittest.NetworkCodec(), - slashingViolationsConsumer, - unittest.AllowAllPeerFilter())) - require.NoError(t, err) - subs[i] = ps - } - - // let subscriptions propagate - time.Sleep(1 * time.Second) - - channel, ok := channels.ChannelFromTopic(topic) - require.True(t, ok) - - for _, node := range nodes { - // creates a unique message to be published by the node - msg, _ := messageFactory() - data := MustEncodeEvent(t, msg, channel) - require.NoError(t, node.Publish(ctx, topic, data)) - - // wait for the message to be received by all nodes - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - SubsMustReceiveMessage(t, ctx, data, subs) - cancel() - } -} - // EnsureNoPubsubMessageExchange ensures that the no pubsub message is exchanged "from" the given nodes "to" the given nodes. func EnsureNoPubsubMessageExchange(t *testing.T, ctx context.Context, from []p2p.LibP2PNode, to []p2p.LibP2PNode, messageFactory func() (interface{}, channels.Topic)) { _, topic := messageFactory() @@ -452,21 +362,6 @@ func EnsureStreamCreation(t *testing.T, ctx context.Context, from []p2p.LibP2PNo } } -// EnsureStreamCreationInBothDirections ensure that between each pair of nodes in the given list, a stream is created in both directions. -func EnsureStreamCreationInBothDirections(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode) { - for _, this := range nodes { - for _, other := range nodes { - if this == other { - continue - } - // stream creation should pass without error - s, err := this.CreateStream(ctx, other.Host().ID()) - require.NoError(t, err) - require.NotNil(t, s) - } - } -} - // LongStringMessageFactoryFixture returns a function that creates a long unique string message. func LongStringMessageFactoryFixture(t *testing.T) func() string { return func() string { @@ -475,3 +370,80 @@ func LongStringMessageFactoryFixture(t *testing.T) func() string { return fmt.Sprintf("%s %d \n", msg, time.Now().UnixNano()) // add timestamp to make sure we don't send the same message twice } } + +// EnsurePubsubMessageExchange ensures that the given nodes exchange the given message on the given channel through pubsub. +func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode, messageFactory func() (interface{}, channels.Topic)) { + _, topic := messageFactory() + + subs := make([]p2p.Subscription, len(nodes)) + slashingViolationsConsumer := unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector()) + for i, node := range nodes { + ps, err := node.Subscribe( + topic, + validator.TopicValidator( + unittest.Logger(), + unittest.NetworkCodec(), + slashingViolationsConsumer, + unittest.AllowAllPeerFilter())) + require.NoError(t, err) + subs[i] = ps + } + + // let subscriptions propagate + time.Sleep(1 * time.Second) + + channel, ok := channels.ChannelFromTopic(topic) + require.True(t, ok) + + for _, node := range nodes { + // creates a unique message to be published by the node + msg, _ := messageFactory() + data := MustEncodeEvent(t, msg, channel) + require.NoError(t, node.Publish(ctx, topic, data)) + + // wait for the message to be received by all nodes + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + SubsMustReceiveMessage(t, ctx, data, subs) + cancel() + } +} + +// MustEncodeEvent encodes and returns the given event and fails the test if it faces any issue while encoding. +func MustEncodeEvent(t *testing.T, v interface{}, channel channels.Channel) []byte { + bz, err := unittest.NetworkCodec().Encode(v) + require.NoError(t, err) + + msg := message.Message{ + ChannelID: channel.String(), + Payload: bz, + } + data, err := msg.Marshal() + require.NoError(t, err) + + return data +} + +// SubMustReceiveMessage checks that the subscription have received the given message within the given timeout by the context. +func SubMustReceiveMessage(t *testing.T, ctx context.Context, expectedMessage []byte, sub p2p.Subscription) { + received := make(chan struct{}) + go func() { + msg, err := sub.Next(ctx) + require.NoError(t, err) + require.Equal(t, expectedMessage, msg.Data) + close(received) + }() + + select { + case <-received: + return + case <-ctx.Done(): + require.Fail(t, "timeout on receiving expected pubsub message") + } +} + +// SubsMustReceiveMessage checks that all subscriptions receive the given message within the given timeout by the context. +func SubsMustReceiveMessage(t *testing.T, ctx context.Context, expectedMessage []byte, subs []p2p.Subscription) { + for _, sub := range subs { + SubMustReceiveMessage(t, ctx, expectedMessage, sub) + } +} diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index 95be3671788..17464778058 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -94,7 +94,7 @@ func TestConnectionGating(t *testing.T) { node2Peers.Add(node1.Host().ID(), struct{}{}) // now both nodes should be able to connect to each other. - p2pfixtures.EnsureStreamCreationInBothDirections(t, ctx, []p2p.LibP2PNode{node1, node2}) + p2ptest.EnsureStreamCreationInBothDirections(t, ctx, []p2p.LibP2PNode{node1, node2}) }) } @@ -273,7 +273,7 @@ func ensureCommunicationSilenceAmongGroups(t *testing.T, ctx context.Context, sp // ensureCommunicationOverAllProtocols ensures that all nodes are connected to each other, and they can exchange messages over the pubsub and unicast. func ensureCommunicationOverAllProtocols(t *testing.T, ctx context.Context, sporkId flow.Identifier, nodes []p2p.LibP2PNode, inbounds []chan string) { - p2pfixtures.EnsureConnected(t, ctx, nodes) + p2ptest.EnsureConnected(t, ctx, nodes) p2pfixtures.EnsurePubsubMessageExchange(t, ctx, nodes, func() (interface{}, channels.Topic) { blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) return unittest.ProposalFixture(), blockTopic diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 088766031d7..3be5295ed91 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -304,3 +304,32 @@ func LetNodesDiscoverEachOther(t *testing.T, ctx context.Context, nodes []p2p.Li } } } + +// EnsureConnected ensures that the given nodes are connected to each other. +// It fails the test if any of the nodes is not connected to any other node. +func EnsureConnected(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode) { + for _, node := range nodes { + for _, other := range nodes { + if node == other { + continue + } + require.NoError(t, node.Host().Connect(ctx, other.Host().Peerstore().PeerInfo(other.Host().ID()))) + require.Equal(t, node.Host().Network().Connectedness(other.Host().ID()), network.Connected) + } + } +} + +// EnsureStreamCreationInBothDirections ensure that between each pair of nodes in the given list, a stream is created in both directions. +func EnsureStreamCreationInBothDirections(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode) { + for _, this := range nodes { + for _, other := range nodes { + if this == other { + continue + } + // stream creation should pass without error + s, err := this.CreateStream(ctx, other.Host().ID()) + require.NoError(t, err) + require.NotNil(t, s) + } + } +} From d08c76d6739cc1b05b25a8260195b9a4b84204d6 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 19 Dec 2022 18:52:52 -0500 Subject: [PATCH 107/138] moved atomicRouter, spammer node creation to `gossipsub_spammer.go` --- insecure/corruptlibp2p/gossipsub_spammer.go | 62 ++++++++++++++++++++- insecure/corruptlibp2p/spam_test.go | 53 ++---------------- 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index c47aadecaa2..27823ea3fef 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -3,8 +3,14 @@ package corruptlibp2p import ( pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" + "github.com/onflow/flow-go/insecure/internal" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/stretchr/testify/require" - pubsub "github.com/yhassanzadeh13/go-libp2p-pubsub" + corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" + + "sync" "testing" ) @@ -13,10 +19,10 @@ type ControlMessage int // GossipSubRouterSpammer is a wrapper around the GossipSubRouter that allows us to // spam the victim with junk control messages. type GossipSubRouterSpammer struct { - router *pubsub.GossipSubRouter + router *corrupt.GossipSubRouter } -func NewGossipSubRouterSpammer(router *pubsub.GossipSubRouter) *GossipSubRouterSpammer { +func NewGossipSubRouterSpammer(router *corrupt.GossipSubRouter) *GossipSubRouterSpammer { return &GossipSubRouterSpammer{ router: router, } @@ -46,4 +52,54 @@ func (s *GossipSubRouterSpammer) GenerateIHaveCtlMessages(t *testing.T, msgCount return iHaveCtlMsgs } +func GetSpammerNode(t *testing.T, sporkId flow.Identifier) (p2p.LibP2PNode, flow.Identity, *atomicRouter) { + router := newAtomicRouter() + spammerNode, spammerId := p2ptest.NodeFixture( + t, + sporkId, + t.Name(), + p2ptest.WithRole(flow.RoleConsensus), + internal.WithCorruptGossipSub(CorruptGossipSubFactory(func(r *corrupt.GossipSubRouter) { + require.NotNil(t, r) + router.set(r) + }), + CorruptGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { + // here we can inspect the incoming RPC message to the spammer node + return nil + })), + ) + return spammerNode, spammerId, router +} + +// atomicRouter is a wrapper around the corrupt.GossipSubRouter that allows atomic access to the router. +// This is done to avoid race conditions when accessing the router from multiple goroutines. +type atomicRouter struct { + mu sync.Mutex + router *corrupt.GossipSubRouter +} + +func newAtomicRouter() *atomicRouter { + return &atomicRouter{ + mu: sync.Mutex{}, + } +} + +// SetRouter sets the router if it has never been set. +func (a *atomicRouter) set(router *corrupt.GossipSubRouter) bool { + a.mu.Lock() + defer a.mu.Unlock() + if a.router == nil { + a.router = router + return true + } + return false +} + +// Get returns the router. +func (a *atomicRouter) Get() *corrupt.GossipSubRouter { + a.mu.Lock() + defer a.mu.Unlock() + return a.router +} + // TODO: SpamIWant, SpamGraft, SpamPrune. diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index ed85e2f61cd..041cdfe7505 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -28,21 +28,7 @@ func TestSpam_IHave(t *testing.T) { const messagesToSpam = 3 sporkId := unittest.IdentifierFixture() - router := newAtomicRouter() - spammerNode, spammerId := p2ptest.NodeFixture( - t, - sporkId, - t.Name(), - p2ptest.WithRole(flow.RoleConsensus), - internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(func(r *corrupt.GossipSubRouter) { - require.NotNil(t, r) - router.set(r) - }), - corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { - // here we can inspect the incoming RPC message to the spammer node - return nil - })), - ) + spammerNode, spammerId, router := corruptlibp2p.GetSpammerNode(t, sporkId) allSpamIHavesReceived := sync.WaitGroup{} allSpamIHavesReceived.Add(messagesToSpam) @@ -78,7 +64,7 @@ func TestSpam_IHave(t *testing.T) { require.Eventuallyf(t, func() bool { // ensuring the spammer router has been initialized. // this is needed because the router is initialized asynchronously. - return router.get() != nil + return router.Get() != nil }, 1*time.Second, 100*time.Millisecond, "spammer router not set") // prior to the test we should ensure that spammer and victim connect and discover each other. @@ -88,8 +74,8 @@ func TestSpam_IHave(t *testing.T) { p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, flow.IdentityList{&spammerId, &victimId}) p2ptest.EnsureStreamCreationInBothDirections(t, ctx, nodes) - // create new spammer - spammer := corruptlibp2p.NewGossipSubRouterSpammer(router.get()) + // create new spammer with fully initialized gossipsub router + spammer := corruptlibp2p.NewGossipSubRouterSpammer(router.Get()) require.NotNil(t, router) // prepare to spam - generate iHAVE control messages @@ -104,34 +90,3 @@ func TestSpam_IHave(t *testing.T) { // check contents of received messages should match what spammer sent require.ElementsMatch(t, iHaveReceivedCtlMsgs, iHaveSentCtlMsgs) } - -// atomicRouter is a wrapper around the corrupt.GossipSubRouter that allows atomic access to the router. -// This is done to avoid race conditions when accessing the router from multiple goroutines. -type atomicRouter struct { - mu sync.Mutex - router *corrupt.GossipSubRouter -} - -func newAtomicRouter() *atomicRouter { - return &atomicRouter{ - mu: sync.Mutex{}, - } -} - -// SetRouter sets the router if it has never been set. -func (a *atomicRouter) set(router *corrupt.GossipSubRouter) bool { - a.mu.Lock() - defer a.mu.Unlock() - if a.router == nil { - a.router = router - return true - } - return false -} - -// GetRouter returns the router. -func (a *atomicRouter) get() *corrupt.GossipSubRouter { - a.mu.Lock() - defer a.mu.Unlock() - return a.router -} From 575f9faede20e442e3094d2bc17b8e8add52b206 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 19 Dec 2022 18:56:41 -0500 Subject: [PATCH 108/138] lint fix --- insecure/corruptlibp2p/gossipsub_spammer.go | 5 +++-- insecure/corruptlibp2p/spam_test.go | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index 27823ea3fef..c9d90148cda 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -3,12 +3,13 @@ package corruptlibp2p import ( pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" + "github.com/stretchr/testify/require" + corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" + "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network/p2p" p2ptest "github.com/onflow/flow-go/network/p2p/test" - "github.com/stretchr/testify/require" - corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" "sync" "testing" diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index 041cdfe7505..b87f4714ac2 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -2,11 +2,12 @@ package corruptlibp2p_test import ( "context" - "github.com/onflow/flow-go/model/flow" "sync" "testing" "time" + "github.com/onflow/flow-go/model/flow" + pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" From dc7ebefe9ea0afa71543e853dbecf8477bff0dea Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 20 Dec 2022 12:35:23 -0500 Subject: [PATCH 109/138] lint fix (go mod tidy) --- insecure/go.sum | 8 -------- integration/go.mod | 2 +- integration/go.sum | 4 ++-- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/insecure/go.sum b/insecure/go.sum index 45aee0f0bb6..5cef7286442 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -1483,14 +1483,6 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b h1:9qSLOxaiPv3HR5THaAcUuXRi4OFIexDseSp8ZBKOMsk= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208175358-bf05f82c565e h1:+7MOzfuoHgh6Rpy7BJBEPcMgDvC/cTaFv7QlWXjT8+M= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208175358-bf05f82c565e/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208185058-df6c36a76983 h1:AzbDxc7heoGrroxZwRg+LBbneh15o0UvvxfUzmzg6aY= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208185058-df6c36a76983/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208204955-09668489428c h1:WbA09PVlZtZu9QztQDCw4ZYHvptFkRvJtFLal3a3O58= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208204955-09668489428c/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208234712-b44d9133e4ee h1:yFB2xjfswpuRh8FHagdBMKcBMltjr5u/XKzX6fkJO5E= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208234712-b44d9133e4ee/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/integration/go.mod b/integration/go.mod index 5d6e1eff3ad..6cfa20ae7a5 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -260,7 +260,7 @@ require ( github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect - github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b // indirect + github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208234712-b44d9133e4ee // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/zeebo/blake3 v0.2.3 // indirect go.opencensus.io v0.23.0 // indirect diff --git a/integration/go.sum b/integration/go.sum index 557af3e1f76..89a7be83b92 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -1680,8 +1680,8 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b h1:9qSLOxaiPv3HR5THaAcUuXRi4OFIexDseSp8ZBKOMsk= -github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208234712-b44d9133e4ee h1:yFB2xjfswpuRh8FHagdBMKcBMltjr5u/XKzX6fkJO5E= +github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208234712-b44d9133e4ee/go.mod h1:Tylw4k1H86gbJx84i3r7qahN/mBaeMpUBvHY0Igshfw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From caf2be20f0db198e7866cdcbf841f6da8714d3a7 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 20 Dec 2022 12:56:20 -0500 Subject: [PATCH 110/138] lint fix --- network/internal/p2pfixtures/fixtures.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index a6d8c3e33a1..0e8574263ab 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -5,11 +5,12 @@ import ( "bytes" "context" "fmt" - "github.com/onflow/flow-go/network/message" "net" "testing" "time" + "github.com/onflow/flow-go/network/message" + addrutil "github.com/libp2p/go-addr-util" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/host" From 5fbe70c79bcf42c93fdb5682f4a46f2ec7da900c Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 20 Dec 2022 15:54:27 -0500 Subject: [PATCH 111/138] fix image build --- insecure/cmd/mods_override.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/insecure/cmd/mods_override.sh b/insecure/cmd/mods_override.sh index efb71d52069..6f6b4d4a6a7 100755 --- a/insecure/cmd/mods_override.sh +++ b/insecure/cmd/mods_override.sh @@ -6,7 +6,7 @@ cp ./go.mod ./go2.mod cp ./go.sum ./go2.sum # inject forked libp2p-pubsub into main module to allow building corrupt Docker images -echo "require github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221205183923-5b979fd7d80b" >> ./go.mod +echo "require github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.2-0.20221208234712-b44d9133e4ee" >> ./go.mod # update go.sum since added new dependency go mod tidy From f30ec0b34c2aa9082dee2a755a2dc697a660beb9 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 20 Dec 2022 17:09:24 -0500 Subject: [PATCH 112/138] moved waiting for spammer to `gossipsub_spammer.go` --- insecure/corruptlibp2p/gossipsub_spammer.go | 9 +++++++++ insecure/corruptlibp2p/spam_test.go | 6 +----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index c9d90148cda..80433c9b33d 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -5,6 +5,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" + "time" "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/model/flow" @@ -72,6 +73,14 @@ func GetSpammerNode(t *testing.T, sporkId flow.Identifier) (p2p.LibP2PNode, flow return spammerNode, spammerId, router } +func WaitUntilInitialized(t *testing.T, router *atomicRouter) { + require.Eventuallyf(t, func() bool { + // ensuring the spammer router has been initialized. + // this is needed because the router is initialized asynchronously. + return router.Get() != nil + }, 1*time.Second, 100*time.Millisecond, "spammer router not set") +} + // atomicRouter is a wrapper around the corrupt.GossipSubRouter that allows atomic access to the router. // This is done to avoid race conditions when accessing the router from multiple goroutines. type atomicRouter struct { diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index b87f4714ac2..a986ae69d2d 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -62,11 +62,7 @@ func TestSpam_IHave(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, nodes, 5*time.Second) defer p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) - require.Eventuallyf(t, func() bool { - // ensuring the spammer router has been initialized. - // this is needed because the router is initialized asynchronously. - return router.Get() != nil - }, 1*time.Second, 100*time.Millisecond, "spammer router not set") + corruptlibp2p.WaitUntilInitialized(t, router) // prior to the test we should ensure that spammer and victim connect and discover each other. // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. From a78c9e8dbd4b2a5f2fd46969a624562d2053ae18 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 20 Dec 2022 19:13:01 -0500 Subject: [PATCH 113/138] moved spammer init out of test - WIP --- insecure/corruptlibp2p/gossipsub_spammer.go | 34 +++++++++++++++------ insecure/corruptlibp2p/spam_test.go | 25 ++++++++++----- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index 80433c9b33d..cb2d960a708 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -21,7 +21,10 @@ type ControlMessage int // GossipSubRouterSpammer is a wrapper around the GossipSubRouter that allows us to // spam the victim with junk control messages. type GossipSubRouterSpammer struct { - router *corrupt.GossipSubRouter + router *corrupt.GossipSubRouter + atomicRouter *atomicRouter + SpammerNode p2p.LibP2PNode + SpammerId flow.Identity } func NewGossipSubRouterSpammer(router *corrupt.GossipSubRouter) *GossipSubRouterSpammer { @@ -30,12 +33,22 @@ func NewGossipSubRouterSpammer(router *corrupt.GossipSubRouter) *GossipSubRouter } } +func NewGossipSubRouterSpammer2(t *testing.T, sporkId flow.Identifier) *GossipSubRouterSpammer { + spammerNode, spammerId, atomicRouter := GetSpammerNode(t, sporkId) + return &GossipSubRouterSpammer{ + //router: atomicRouter.Get(), + atomicRouter: atomicRouter, + SpammerId: spammerId, + SpammerNode: spammerNode, + } +} + // SpamIHave spams the victim with junk iHave messages. // msgCount is the number of iHave messages to send. // msgSize is the number of messageIDs to include in each iHave message. func (s *GossipSubRouterSpammer) SpamIHave(t *testing.T, victim peer.ID, ctlMessages []pb.ControlMessage) { for _, ctlMessage := range ctlMessages { - require.True(t, s.router.SendControl(victim, &ctlMessage)) + require.True(t, s.atomicRouter.Get().SendControl(victim, &ctlMessage)) } } @@ -54,6 +67,15 @@ func (s *GossipSubRouterSpammer) GenerateIHaveCtlMessages(t *testing.T, msgCount return iHaveCtlMsgs } +func (s *GossipSubRouterSpammer) WaitUntilInitialized(t *testing.T) { + require.Eventuallyf(t, func() bool { + // ensuring the spammer router has been initialized. + // this is needed because the router is initialized asynchronously. + return s.atomicRouter.Get() != nil + }, 1*time.Second, 100*time.Millisecond, "spammer router not set") + s.atomicRouter.set(s.atomicRouter.Get()) +} + func GetSpammerNode(t *testing.T, sporkId flow.Identifier) (p2p.LibP2PNode, flow.Identity, *atomicRouter) { router := newAtomicRouter() spammerNode, spammerId := p2ptest.NodeFixture( @@ -73,14 +95,6 @@ func GetSpammerNode(t *testing.T, sporkId flow.Identifier) (p2p.LibP2PNode, flow return spammerNode, spammerId, router } -func WaitUntilInitialized(t *testing.T, router *atomicRouter) { - require.Eventuallyf(t, func() bool { - // ensuring the spammer router has been initialized. - // this is needed because the router is initialized asynchronously. - return router.Get() != nil - }, 1*time.Second, 100*time.Millisecond, "spammer router not set") -} - // atomicRouter is a wrapper around the corrupt.GossipSubRouter that allows atomic access to the router. // This is done to avoid race conditions when accessing the router from multiple goroutines. type atomicRouter struct { diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index a986ae69d2d..84de3f03df9 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -26,10 +26,14 @@ import ( // to the victim node without being subscribed to any of the same topics. // The test then checks that the victim node received all the messages from the spammer. func TestSpam_IHave(t *testing.T) { + + // create new spammer + const messagesToSpam = 3 sporkId := unittest.IdentifierFixture() - spammerNode, spammerId, router := corruptlibp2p.GetSpammerNode(t, sporkId) + //_, _, router := corruptlibp2p.GetSpammerNode(t, sporkId) + gossipsubRouterSpammer := corruptlibp2p.NewGossipSubRouterSpammer2(t, sporkId) allSpamIHavesReceived := sync.WaitGroup{} allSpamIHavesReceived.Add(messagesToSpam) @@ -58,28 +62,33 @@ func TestSpam_IHave(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) defer cancel() - nodes := []p2p.LibP2PNode{spammerNode, victimNode} + //nodes := []p2p.LibP2PNode{spammerNode, victimNode} + nodes := []p2p.LibP2PNode{gossipsubRouterSpammer.SpammerNode, victimNode} p2ptest.StartNodes(t, signalerCtx, nodes, 5*time.Second) defer p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) - corruptlibp2p.WaitUntilInitialized(t, router) + //corruptlibp2p.WaitUntilInitialized(t, router) + gossipsubRouterSpammer.WaitUntilInitialized(t) // prior to the test we should ensure that spammer and victim connect and discover each other. // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. // without a priory connection established, directly spamming pubsub messages may cause a race condition in the pubsub implementation. p2ptest.EnsureConnected(t, ctx, nodes) - p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, flow.IdentityList{&spammerId, &victimId}) + //p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, flow.IdentityList{&spammerId, &victimId}) + p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, flow.IdentityList{&gossipsubRouterSpammer.SpammerId, &victimId}) p2ptest.EnsureStreamCreationInBothDirections(t, ctx, nodes) // create new spammer with fully initialized gossipsub router - spammer := corruptlibp2p.NewGossipSubRouterSpammer(router.Get()) - require.NotNil(t, router) + //spammer := corruptlibp2p.NewGossipSubRouterSpammer(router.Get()) + //require.NotNil(t, router) // prepare to spam - generate iHAVE control messages - iHaveSentCtlMsgs := spammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) + //iHaveSentCtlMsgs := spammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) + iHaveSentCtlMsgs := gossipsubRouterSpammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) // start spamming the victim peer - spammer.SpamIHave(t, victimNode.Host().ID(), iHaveSentCtlMsgs) + //spammer.SpamIHave(t, victimNode.Host().ID(), iHaveSentCtlMsgs) + gossipsubRouterSpammer.SpamIHave(t, victimNode.Host().ID(), iHaveSentCtlMsgs) // check that victim received all spam messages unittest.RequireReturnsBefore(t, allSpamIHavesReceived.Wait, 1*time.Second, "victim did not receive all spam messages") From 9840e883c5a6afa7d21d063fe39a076e17fed682 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Dec 2022 11:58:57 -0500 Subject: [PATCH 114/138] clean up --- insecure/corruptlibp2p/gossipsub_spammer.go | 7 ------- insecure/corruptlibp2p/spam_test.go | 13 ------------- 2 files changed, 20 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index cb2d960a708..7c61b2da0ac 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -27,16 +27,9 @@ type GossipSubRouterSpammer struct { SpammerId flow.Identity } -func NewGossipSubRouterSpammer(router *corrupt.GossipSubRouter) *GossipSubRouterSpammer { - return &GossipSubRouterSpammer{ - router: router, - } -} - func NewGossipSubRouterSpammer2(t *testing.T, sporkId flow.Identifier) *GossipSubRouterSpammer { spammerNode, spammerId, atomicRouter := GetSpammerNode(t, sporkId) return &GossipSubRouterSpammer{ - //router: atomicRouter.Get(), atomicRouter: atomicRouter, SpammerId: spammerId, SpammerNode: spammerNode, diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index 84de3f03df9..e06ba0e8ebd 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -26,13 +26,9 @@ import ( // to the victim node without being subscribed to any of the same topics. // The test then checks that the victim node received all the messages from the spammer. func TestSpam_IHave(t *testing.T) { - - // create new spammer - const messagesToSpam = 3 sporkId := unittest.IdentifierFixture() - //_, _, router := corruptlibp2p.GetSpammerNode(t, sporkId) gossipsubRouterSpammer := corruptlibp2p.NewGossipSubRouterSpammer2(t, sporkId) allSpamIHavesReceived := sync.WaitGroup{} @@ -62,32 +58,23 @@ func TestSpam_IHave(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) defer cancel() - //nodes := []p2p.LibP2PNode{spammerNode, victimNode} nodes := []p2p.LibP2PNode{gossipsubRouterSpammer.SpammerNode, victimNode} p2ptest.StartNodes(t, signalerCtx, nodes, 5*time.Second) defer p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) - //corruptlibp2p.WaitUntilInitialized(t, router) gossipsubRouterSpammer.WaitUntilInitialized(t) // prior to the test we should ensure that spammer and victim connect and discover each other. // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. // without a priory connection established, directly spamming pubsub messages may cause a race condition in the pubsub implementation. p2ptest.EnsureConnected(t, ctx, nodes) - //p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, flow.IdentityList{&spammerId, &victimId}) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, flow.IdentityList{&gossipsubRouterSpammer.SpammerId, &victimId}) p2ptest.EnsureStreamCreationInBothDirections(t, ctx, nodes) - // create new spammer with fully initialized gossipsub router - //spammer := corruptlibp2p.NewGossipSubRouterSpammer(router.Get()) - //require.NotNil(t, router) - // prepare to spam - generate iHAVE control messages - //iHaveSentCtlMsgs := spammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) iHaveSentCtlMsgs := gossipsubRouterSpammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) // start spamming the victim peer - //spammer.SpamIHave(t, victimNode.Host().ID(), iHaveSentCtlMsgs) gossipsubRouterSpammer.SpamIHave(t, victimNode.Host().ID(), iHaveSentCtlMsgs) // check that victim received all spam messages From 64b37d1451bfd87e68e131336c8060ab193006d0 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Dec 2022 13:39:40 -0500 Subject: [PATCH 115/138] lint fix --- insecure/corruptlibp2p/gossipsub_spammer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index 7c61b2da0ac..f59a19fbd23 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -1,11 +1,12 @@ package corruptlibp2p import ( + "time" + pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" corrupt "github.com/yhassanzadeh13/go-libp2p-pubsub" - "time" "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/model/flow" @@ -21,7 +22,6 @@ type ControlMessage int // GossipSubRouterSpammer is a wrapper around the GossipSubRouter that allows us to // spam the victim with junk control messages. type GossipSubRouterSpammer struct { - router *corrupt.GossipSubRouter atomicRouter *atomicRouter SpammerNode p2p.LibP2PNode SpammerId flow.Identity From 48f0a85eb0c93d0ec822564a2faad94dd5856404 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 21 Dec 2022 11:11:56 -0800 Subject: [PATCH 116/138] moves ensure pubsub message exchange to exportable package --- insecure/corruptlibp2p/spam_test.go | 5 +++ network/internal/p2pfixtures/fixtures.go | 37 ----------------- .../p2p/connection/connection_gater_test.go | 2 +- network/p2p/test/fixtures.go | 40 +++++++++++++++++++ 4 files changed, 46 insertions(+), 38 deletions(-) diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index e06ba0e8ebd..a346c20a782 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/network/channels" pb "github.com/libp2p/go-libp2p-pubsub/pb" @@ -70,6 +71,10 @@ func TestSpam_IHave(t *testing.T) { p2ptest.EnsureConnected(t, ctx, nodes) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, flow.IdentityList{&gossipsubRouterSpammer.SpammerId, &victimId}) p2ptest.EnsureStreamCreationInBothDirections(t, ctx, nodes) + p2ptest.EnsurePubsubMessageExchange(t, ctx, nodes, func() (interface{}, channels.Topic) { + blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) + return unittest.ProposalFixture(), blockTopic + }) // prepare to spam - generate iHAVE control messages iHaveSentCtlMsgs := gossipsubRouterSpammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 0e8574263ab..7ed95a51295 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -372,43 +372,6 @@ func LongStringMessageFactoryFixture(t *testing.T) func() string { } } -// EnsurePubsubMessageExchange ensures that the given nodes exchange the given message on the given channel through pubsub. -func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode, messageFactory func() (interface{}, channels.Topic)) { - _, topic := messageFactory() - - subs := make([]p2p.Subscription, len(nodes)) - slashingViolationsConsumer := unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector()) - for i, node := range nodes { - ps, err := node.Subscribe( - topic, - validator.TopicValidator( - unittest.Logger(), - unittest.NetworkCodec(), - slashingViolationsConsumer, - unittest.AllowAllPeerFilter())) - require.NoError(t, err) - subs[i] = ps - } - - // let subscriptions propagate - time.Sleep(1 * time.Second) - - channel, ok := channels.ChannelFromTopic(topic) - require.True(t, ok) - - for _, node := range nodes { - // creates a unique message to be published by the node - msg, _ := messageFactory() - data := MustEncodeEvent(t, msg, channel) - require.NoError(t, node.Publish(ctx, topic, data)) - - // wait for the message to be received by all nodes - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - SubsMustReceiveMessage(t, ctx, data, subs) - cancel() - } -} - // MustEncodeEvent encodes and returns the given event and fails the test if it faces any issue while encoding. func MustEncodeEvent(t *testing.T, v interface{}, channel channels.Channel) []byte { bz, err := unittest.NetworkCodec().Encode(v) diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index 17464778058..7634e4347df 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -274,7 +274,7 @@ func ensureCommunicationSilenceAmongGroups(t *testing.T, ctx context.Context, sp // ensureCommunicationOverAllProtocols ensures that all nodes are connected to each other, and they can exchange messages over the pubsub and unicast. func ensureCommunicationOverAllProtocols(t *testing.T, ctx context.Context, sporkId flow.Identifier, nodes []p2p.LibP2PNode, inbounds []chan string) { p2ptest.EnsureConnected(t, ctx, nodes) - p2pfixtures.EnsurePubsubMessageExchange(t, ctx, nodes, func() (interface{}, channels.Topic) { + p2ptest.EnsurePubsubMessageExchange(t, ctx, nodes, func() (interface{}, channels.Topic) { blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) return unittest.ProposalFixture(), blockTopic }) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 3be5295ed91..bfcafd557fe 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -21,6 +21,8 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/internal/testutils" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/connection" @@ -29,6 +31,7 @@ import ( "github.com/onflow/flow-go/network/p2p/scoring" "github.com/onflow/flow-go/network/p2p/unicast" "github.com/onflow/flow-go/network/p2p/utils" + "github.com/onflow/flow-go/network/validator/pubsub" "github.com/onflow/flow-go/utils/logging" "github.com/onflow/flow-go/utils/unittest" ) @@ -333,3 +336,40 @@ func EnsureStreamCreationInBothDirections(t *testing.T, ctx context.Context, nod } } } + +// EnsurePubsubMessageExchange ensures that the given nodes exchange the given message on the given channel through pubsub. +func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode, messageFactory func() (interface{}, channels.Topic)) { + _, topic := messageFactory() + + subs := make([]p2p.Subscription, len(nodes)) + slashingViolationsConsumer := unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector()) + for i, node := range nodes { + ps, err := node.Subscribe( + topic, + validator.TopicValidator( + unittest.Logger(), + unittest.NetworkCodec(), + slashingViolationsConsumer, + unittest.AllowAllPeerFilter())) + require.NoError(t, err) + subs[i] = ps + } + + // let subscriptions propagate + time.Sleep(1 * time.Second) + + channel, ok := channels.ChannelFromTopic(topic) + require.True(t, ok) + + for _, node := range nodes { + // creates a unique message to be published by the node + msg, _ := messageFactory() + data := p2pfixtures.MustEncodeEvent(t, msg, channel) + require.NoError(t, node.Publish(ctx, topic, data)) + + // wait for the message to be received by all nodes + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + p2pfixtures.SubsMustReceiveMessage(t, ctx, data, subs) + cancel() + } +} From 2b2e34ca21f35a5a455fcf0df953bea241aebd51 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 21 Dec 2022 11:14:13 -0800 Subject: [PATCH 117/138] lint --- insecure/go.mod | 1 + insecure/go.sum | 1 + integration/go.mod | 1 + integration/go.sum | 1 + 4 files changed, 4 insertions(+) diff --git a/insecure/go.mod b/insecure/go.mod index f81cc18f196..03866f9fc33 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -130,6 +130,7 @@ require ( github.com/klauspost/compress v1.15.10 // indirect github.com/klauspost/cpuid/v2 v2.1.1 // indirect github.com/koron/go-ssdp v0.0.3 // indirect + github.com/libp2p/go-addr-util v0.1.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect diff --git a/insecure/go.sum b/insecure/go.sum index 5cef7286442..549f83c6c36 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -777,6 +777,7 @@ github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdA github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= github.com/libp2p/go-addr-util v0.1.0 h1:acKsntI33w2bTU7tC9a0SaPimJGfSI0bFKC18ChxeVI= +github.com/libp2p/go-addr-util v0.1.0/go.mod h1:6I3ZYuFr2O/9D+SoyM0zEw0EF3YkldtTX406BpdQMqw= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= diff --git a/integration/go.mod b/integration/go.mod index 6cfa20ae7a5..631f5ba7a63 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -161,6 +161,7 @@ require ( github.com/klauspost/compress v1.15.10 // indirect github.com/klauspost/cpuid/v2 v2.1.1 // indirect github.com/koron/go-ssdp v0.0.3 // indirect + github.com/libp2p/go-addr-util v0.1.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect diff --git a/integration/go.sum b/integration/go.sum index 89a7be83b92..412bf6d1dec 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -902,6 +902,7 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= github.com/libp2p/go-addr-util v0.1.0 h1:acKsntI33w2bTU7tC9a0SaPimJGfSI0bFKC18ChxeVI= +github.com/libp2p/go-addr-util v0.1.0/go.mod h1:6I3ZYuFr2O/9D+SoyM0zEw0EF3YkldtTX406BpdQMqw= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= From b7e46c35a4de70f59355ef19fb47b53776b02126 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Dec 2022 17:56:33 -0500 Subject: [PATCH 118/138] `EnsurePubsubMessageExchange` replaces other methods --- insecure/corruptlibp2p/spam_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index a346c20a782..02936731c96 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -36,7 +36,7 @@ func TestSpam_IHave(t *testing.T) { allSpamIHavesReceived.Add(messagesToSpam) var iHaveReceivedCtlMsgs []pb.ControlMessage - victimNode, victimId := p2ptest.NodeFixture( + victimNode, _ := p2ptest.NodeFixture( t, sporkId, t.Name(), @@ -68,9 +68,6 @@ func TestSpam_IHave(t *testing.T) { // prior to the test we should ensure that spammer and victim connect and discover each other. // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. // without a priory connection established, directly spamming pubsub messages may cause a race condition in the pubsub implementation. - p2ptest.EnsureConnected(t, ctx, nodes) - p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, flow.IdentityList{&gossipsubRouterSpammer.SpammerId, &victimId}) - p2ptest.EnsureStreamCreationInBothDirections(t, ctx, nodes) p2ptest.EnsurePubsubMessageExchange(t, ctx, nodes, func() (interface{}, channels.Topic) { blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) return unittest.ProposalFixture(), blockTopic From 874b40ddfddb9a96068ebdaa23856f427f8fc317 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Dec 2022 18:08:07 -0500 Subject: [PATCH 119/138] minor renaming, adding docs --- insecure/corruptlibp2p/spam_test.go | 15 ++++++++------- network/p2p/test/fixtures.go | 3 ++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index 02936731c96..cb606224fcb 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -30,7 +30,7 @@ func TestSpam_IHave(t *testing.T) { const messagesToSpam = 3 sporkId := unittest.IdentifierFixture() - gossipsubRouterSpammer := corruptlibp2p.NewGossipSubRouterSpammer2(t, sporkId) + gsrSpammer := corruptlibp2p.NewGossipSubRouterSpammer2(t, sporkId) allSpamIHavesReceived := sync.WaitGroup{} allSpamIHavesReceived.Add(messagesToSpam) @@ -59,25 +59,26 @@ func TestSpam_IHave(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) defer cancel() - nodes := []p2p.LibP2PNode{gossipsubRouterSpammer.SpammerNode, victimNode} + nodes := []p2p.LibP2PNode{gsrSpammer.SpammerNode, victimNode} p2ptest.StartNodes(t, signalerCtx, nodes, 5*time.Second) defer p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) - gossipsubRouterSpammer.WaitUntilInitialized(t) + gsrSpammer.WaitUntilInitialized(t) - // prior to the test we should ensure that spammer and victim connect and discover each other. + // prior to the test we should ensure that spammer and victim connect. // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. - // without a priory connection established, directly spamming pubsub messages may cause a race condition in the pubsub implementation. + // without a prior connection established, directly spamming pubsub messages may cause a race condition in the pubsub implementation. + p2ptest.EnsureConnected(t, ctx, nodes) p2ptest.EnsurePubsubMessageExchange(t, ctx, nodes, func() (interface{}, channels.Topic) { blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) return unittest.ProposalFixture(), blockTopic }) // prepare to spam - generate iHAVE control messages - iHaveSentCtlMsgs := gossipsubRouterSpammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) + iHaveSentCtlMsgs := gsrSpammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) // start spamming the victim peer - gossipsubRouterSpammer.SpamIHave(t, victimNode.Host().ID(), iHaveSentCtlMsgs) + gsrSpammer.SpamIHave(t, victimNode.Host().ID(), iHaveSentCtlMsgs) // check that victim received all spam messages unittest.RequireReturnsBefore(t, allSpamIHavesReceived.Wait, 1*time.Second, "victim did not receive all spam messages") diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index bfcafd557fe..a8c94cad96e 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -337,7 +337,8 @@ func EnsureStreamCreationInBothDirections(t *testing.T, ctx context.Context, nod } } -// EnsurePubsubMessageExchange ensures that the given nodes exchange the given message on the given channel through pubsub. +// EnsurePubsubMessageExchange ensures that the given connected nodes exchange the given message on the given channel through pubsub. +// Note: EnsureConnected() must be called to connect all nodes before calling this function. func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode, messageFactory func() (interface{}, channels.Topic)) { _, topic := messageFactory() From e0ea321bee6824b974370dfb47f67b64f7f6128a Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Dec 2022 18:32:35 -0500 Subject: [PATCH 120/138] more convenient to use `SpamIHave()` --- insecure/corruptlibp2p/gossipsub_spammer.go | 9 ++++----- insecure/corruptlibp2p/spam_test.go | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index f59a19fbd23..72107cbec9b 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -1,6 +1,8 @@ package corruptlibp2p import ( + "sync" + "testing" "time" pb "github.com/libp2p/go-libp2p-pubsub/pb" @@ -12,9 +14,6 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network/p2p" p2ptest "github.com/onflow/flow-go/network/p2p/test" - - "sync" - "testing" ) type ControlMessage int @@ -39,9 +38,9 @@ func NewGossipSubRouterSpammer2(t *testing.T, sporkId flow.Identifier) *GossipSu // SpamIHave spams the victim with junk iHave messages. // msgCount is the number of iHave messages to send. // msgSize is the number of messageIDs to include in each iHave message. -func (s *GossipSubRouterSpammer) SpamIHave(t *testing.T, victim peer.ID, ctlMessages []pb.ControlMessage) { +func (s *GossipSubRouterSpammer) SpamIHave(t *testing.T, victim p2p.LibP2PNode, ctlMessages []pb.ControlMessage) { for _, ctlMessage := range ctlMessages { - require.True(t, s.atomicRouter.Get().SendControl(victim, &ctlMessage)) + require.True(t, s.atomicRouter.Get().SendControl(victim.Host().ID(), &ctlMessage)) } } diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index cb606224fcb..62ad43ff3d6 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -48,7 +48,6 @@ func TestSpam_IHave(t *testing.T) { // don't inspect control messages with no iHAVE messages return nil } - iHaveReceivedCtlMsgs = append(iHaveReceivedCtlMsgs, *rpc.GetControl()) allSpamIHavesReceived.Done() // acknowledge that victim received a message. return nil @@ -78,7 +77,7 @@ func TestSpam_IHave(t *testing.T) { iHaveSentCtlMsgs := gsrSpammer.GenerateIHaveCtlMessages(t, messagesToSpam, 5) // start spamming the victim peer - gsrSpammer.SpamIHave(t, victimNode.Host().ID(), iHaveSentCtlMsgs) + gsrSpammer.SpamIHave(t, victimNode, iHaveSentCtlMsgs) // check that victim received all spam messages unittest.RequireReturnsBefore(t, allSpamIHavesReceived.Wait, 1*time.Second, "victim did not receive all spam messages") From 32983b6ccce6cff62cb9e2a9b9a2f0b8a511b932 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Dec 2022 18:39:42 -0500 Subject: [PATCH 121/138] lint fix --- network/p2p/test/fixtures.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index a8c94cad96e..6cdd751f750 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -31,7 +31,7 @@ import ( "github.com/onflow/flow-go/network/p2p/scoring" "github.com/onflow/flow-go/network/p2p/unicast" "github.com/onflow/flow-go/network/p2p/utils" - "github.com/onflow/flow-go/network/validator/pubsub" + validator "github.com/onflow/flow-go/network/validator/pubsub" "github.com/onflow/flow-go/utils/logging" "github.com/onflow/flow-go/utils/unittest" ) From c9eb01607b06caa73a90e43b489e0330ca4dd0b5 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Dec 2022 18:54:00 -0500 Subject: [PATCH 122/138] simplified `getSpammerNode()` --- insecure/corruptlibp2p/gossipsub_spammer.go | 13 ++++++------- insecure/corruptlibp2p/spam_test.go | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index 72107cbec9b..41f9e01efef 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -23,14 +23,13 @@ type ControlMessage int type GossipSubRouterSpammer struct { atomicRouter *atomicRouter SpammerNode p2p.LibP2PNode - SpammerId flow.Identity } -func NewGossipSubRouterSpammer2(t *testing.T, sporkId flow.Identifier) *GossipSubRouterSpammer { - spammerNode, spammerId, atomicRouter := GetSpammerNode(t, sporkId) +// NewGossipSubRouterSpammer is the main method tests call for spamming attacks. +func NewGossipSubRouterSpammer(t *testing.T, sporkId flow.Identifier) *GossipSubRouterSpammer { + spammerNode, atomicRouter := getSpammerNode(t, sporkId) return &GossipSubRouterSpammer{ atomicRouter: atomicRouter, - SpammerId: spammerId, SpammerNode: spammerNode, } } @@ -68,9 +67,9 @@ func (s *GossipSubRouterSpammer) WaitUntilInitialized(t *testing.T) { s.atomicRouter.set(s.atomicRouter.Get()) } -func GetSpammerNode(t *testing.T, sporkId flow.Identifier) (p2p.LibP2PNode, flow.Identity, *atomicRouter) { +func getSpammerNode(t *testing.T, sporkId flow.Identifier) (p2p.LibP2PNode, *atomicRouter) { router := newAtomicRouter() - spammerNode, spammerId := p2ptest.NodeFixture( + spammerNode, _ := p2ptest.NodeFixture( t, sporkId, t.Name(), @@ -84,7 +83,7 @@ func GetSpammerNode(t *testing.T, sporkId flow.Identifier) (p2p.LibP2PNode, flow return nil })), ) - return spammerNode, spammerId, router + return spammerNode, router } // atomicRouter is a wrapper around the corrupt.GossipSubRouter that allows atomic access to the router. diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index 62ad43ff3d6..a1f233a85e8 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -30,7 +30,7 @@ func TestSpam_IHave(t *testing.T) { const messagesToSpam = 3 sporkId := unittest.IdentifierFixture() - gsrSpammer := corruptlibp2p.NewGossipSubRouterSpammer2(t, sporkId) + gsrSpammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkId) allSpamIHavesReceived := sync.WaitGroup{} allSpamIHavesReceived.Add(messagesToSpam) From 4efcaac2865a9374e1a1a935391dbd9748a96507 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Dec 2022 18:56:43 -0500 Subject: [PATCH 123/138] removed unused type --- insecure/corruptlibp2p/gossipsub_spammer.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index 41f9e01efef..bbf3ee4fcfe 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -16,8 +16,6 @@ import ( p2ptest "github.com/onflow/flow-go/network/p2p/test" ) -type ControlMessage int - // GossipSubRouterSpammer is a wrapper around the GossipSubRouter that allows us to // spam the victim with junk control messages. type GossipSubRouterSpammer struct { From 841260af83953c6744a04b0f574bf81885a549fa Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 4 Jan 2023 17:17:32 -0500 Subject: [PATCH 124/138] sets inspector as option function, removes 2nd constructor for `CorruptPubSubAdapterConfig` --- insecure/corruptlibp2p/libp2p_node_factory.go | 2 +- .../corruptlibp2p/pubsub_adapter_config.go | 20 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/insecure/corruptlibp2p/libp2p_node_factory.go b/insecure/corruptlibp2p/libp2p_node_factory.go index 9939b83bdb1..c333d40f730 100644 --- a/insecure/corruptlibp2p/libp2p_node_factory.go +++ b/insecure/corruptlibp2p/libp2p_node_factory.go @@ -90,7 +90,7 @@ func CorruptGossipSubConfigFactory() p2pbuilder.GossipSubAdapterConfigFunc { // from github.com/yhassanzadeh13/go-libp2p-pubsub for the purpose of BFT testing and attack vector implementation. func CorruptGossipSubConfigFactoryWithInspector(inspector func(peer.ID, *corrupt.RPC) error) p2pbuilder.GossipSubAdapterConfigFunc { return func(base *p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig { - return NewCorruptPubSubAdapterConfigWithInspector(base, inspector) + return NewCorruptPubSubAdapterConfig(base, WithInspector(inspector)) } } diff --git a/insecure/corruptlibp2p/pubsub_adapter_config.go b/insecure/corruptlibp2p/pubsub_adapter_config.go index 14efc373be2..415f87d2ddb 100644 --- a/insecure/corruptlibp2p/pubsub_adapter_config.go +++ b/insecure/corruptlibp2p/pubsub_adapter_config.go @@ -27,17 +27,23 @@ type CorruptPubSubAdapterConfig struct { var _ p2p.PubSubAdapterConfig = (*CorruptPubSubAdapterConfig)(nil) -func NewCorruptPubSubAdapterConfig(base *p2p.BasePubSubAdapterConfig) *CorruptPubSubAdapterConfig { - return &CorruptPubSubAdapterConfig{ - options: defaultCorruptPubsubOptions(base), +func WithInspector(inspector func(peer.ID, *corrupt.RPC) error) func(config *CorruptPubSubAdapterConfig) { + return func(config *CorruptPubSubAdapterConfig) { + config.inspector = inspector + config.options = append(config.options, corrupt.WithAppSpecificRpcInspector(func(id peer.ID, rpc *corrupt.RPC) error { + return config.inspector(id, rpc) + })) } } -func NewCorruptPubSubAdapterConfigWithInspector(base *p2p.BasePubSubAdapterConfig, inspector func(peer.ID, *corrupt.RPC) error) *CorruptPubSubAdapterConfig { - return &CorruptPubSubAdapterConfig{ - options: defaultCorruptPubsubOptions(base), - inspector: inspector, +func NewCorruptPubSubAdapterConfig(base *p2p.BasePubSubAdapterConfig, opts ...func(config *CorruptPubSubAdapterConfig)) *CorruptPubSubAdapterConfig { + cfg := &CorruptPubSubAdapterConfig{ + options: defaultCorruptPubsubOptions(base), + } + for _, opt := range opts { + opt(cfg) } + return cfg } func (c *CorruptPubSubAdapterConfig) WithRoutingDiscovery(routing routing.ContentRouting) { From 3fa4920a00d2e6ab48bfc972a1196d5012796889 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 09:16:51 -0500 Subject: [PATCH 125/138] spammer node supports any flow role --- insecure/corruptlibp2p/gossipsub_spammer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index bbf3ee4fcfe..cd6432b9c37 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -25,7 +25,7 @@ type GossipSubRouterSpammer struct { // NewGossipSubRouterSpammer is the main method tests call for spamming attacks. func NewGossipSubRouterSpammer(t *testing.T, sporkId flow.Identifier) *GossipSubRouterSpammer { - spammerNode, atomicRouter := getSpammerNode(t, sporkId) + spammerNode, atomicRouter := getSpammerNode(t, sporkId, flow.RoleConsensus) return &GossipSubRouterSpammer{ atomicRouter: atomicRouter, SpammerNode: spammerNode, @@ -65,13 +65,13 @@ func (s *GossipSubRouterSpammer) WaitUntilInitialized(t *testing.T) { s.atomicRouter.set(s.atomicRouter.Get()) } -func getSpammerNode(t *testing.T, sporkId flow.Identifier) (p2p.LibP2PNode, *atomicRouter) { +func getSpammerNode(t *testing.T, sporkId flow.Identifier, role flow.Role) (p2p.LibP2PNode, *atomicRouter) { router := newAtomicRouter() spammerNode, _ := p2ptest.NodeFixture( t, sporkId, t.Name(), - p2ptest.WithRole(flow.RoleConsensus), + p2ptest.WithRole(role), internal.WithCorruptGossipSub(CorruptGossipSubFactory(func(r *corrupt.GossipSubRouter) { require.NotNil(t, r) router.set(r) From 162fc9f4d097096e4ec6f764a8dda7cd2b1ee615 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 09:21:00 -0500 Subject: [PATCH 126/138] `getSpammerNode()` renamed to `createSpammerNode()` --- insecure/corruptlibp2p/gossipsub_spammer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index cd6432b9c37..d6ce7ac42fb 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -25,7 +25,7 @@ type GossipSubRouterSpammer struct { // NewGossipSubRouterSpammer is the main method tests call for spamming attacks. func NewGossipSubRouterSpammer(t *testing.T, sporkId flow.Identifier) *GossipSubRouterSpammer { - spammerNode, atomicRouter := getSpammerNode(t, sporkId, flow.RoleConsensus) + spammerNode, atomicRouter := createSpammerNode(t, sporkId, flow.RoleConsensus) return &GossipSubRouterSpammer{ atomicRouter: atomicRouter, SpammerNode: spammerNode, @@ -65,7 +65,7 @@ func (s *GossipSubRouterSpammer) WaitUntilInitialized(t *testing.T) { s.atomicRouter.set(s.atomicRouter.Get()) } -func getSpammerNode(t *testing.T, sporkId flow.Identifier, role flow.Role) (p2p.LibP2PNode, *atomicRouter) { +func createSpammerNode(t *testing.T, sporkId flow.Identifier, role flow.Role) (p2p.LibP2PNode, *atomicRouter) { router := newAtomicRouter() spammerNode, _ := p2ptest.NodeFixture( t, From e3ae0c76ac0e78fc60d9c1b541255b8fe787e988 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 10:05:15 -0500 Subject: [PATCH 127/138] atomicRouter `set()` godoc update --- insecure/corruptlibp2p/gossipsub_spammer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index d6ce7ac42fb..8a610f364e7 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -97,7 +97,7 @@ func newAtomicRouter() *atomicRouter { } } -// SetRouter sets the router if it has never been set. +// SetRouter sets the router if it has never been set. Returns true if the router was set, false otherwise. func (a *atomicRouter) set(router *corrupt.GossipSubRouter) bool { a.mu.Lock() defer a.mu.Unlock() From 81ffd2c6c36e2414f2be31ecf660c05f84e7f487 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 10:12:00 -0500 Subject: [PATCH 128/138] docs update - `WithAppSpecificRpcInspector()` --- insecure/corruptlibp2p/pubsub_adapter_config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/pubsub_adapter_config.go b/insecure/corruptlibp2p/pubsub_adapter_config.go index f8f858722f1..87b08a807cf 100644 --- a/insecure/corruptlibp2p/pubsub_adapter_config.go +++ b/insecure/corruptlibp2p/pubsub_adapter_config.go @@ -75,11 +75,11 @@ func (c *CorruptPubSubAdapterConfig) WithSubscriptionFilter(filter p2p.Subscript } func (c *CorruptPubSubAdapterConfig) WithScoreOption(_ p2p.ScoreOptionBuilder) { - // Corrupt does not support score options. This is a no-op. + // CorruptPubSub does not support score options. This is a no-op. } func (c *CorruptPubSubAdapterConfig) WithAppSpecificRpcInspector(_ func(peer.ID, *pubsub.RPC) error) { - // Corrupt does not support app specific rpc inspectors. This is a no-op. + // CorruptPubSub receives its inspector at a different time than the original pubsub (i.e., at creation time). } func (c *CorruptPubSubAdapterConfig) WithMessageIdFunction(f func([]byte) string) { From 525b48506d37a120530c39d0a28bf5c992aacd5c Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 10:14:00 -0500 Subject: [PATCH 129/138] removing commented code --- insecure/corruptlibp2p/gossipsub_spammer.go | 1 - 1 file changed, 1 deletion(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index 8a610f364e7..251aaef0ce4 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -44,7 +44,6 @@ func (s *GossipSubRouterSpammer) SpamIHave(t *testing.T, victim p2p.LibP2PNode, // GenerateIHaveCtlMessages generates IHAVE control messages before they are sent so the test can prepare // to receive them before they are sent by the spammer. func (s *GossipSubRouterSpammer) GenerateIHaveCtlMessages(t *testing.T, msgCount, msgSize int) []pb.ControlMessage { - //var ctlMessageMap = make(map[string]pb.ControlMessage) var iHaveCtlMsgs []pb.ControlMessage for i := 0; i < msgCount; i++ { iHaveCtlMsg := GossipSubCtrlFixture(WithIHave(msgCount, msgSize)) From 8b48684d7ed7ad742f1050ccf40bb9bc465a3847 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 10:17:16 -0500 Subject: [PATCH 130/138] rename Spammer `WaitUntilInitialized()` to `Start()` --- insecure/corruptlibp2p/gossipsub_spammer.go | 3 ++- insecure/corruptlibp2p/spam_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index 251aaef0ce4..b5f9898cbb1 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -55,7 +55,8 @@ func (s *GossipSubRouterSpammer) GenerateIHaveCtlMessages(t *testing.T, msgCount return iHaveCtlMsgs } -func (s *GossipSubRouterSpammer) WaitUntilInitialized(t *testing.T) { +// Start starts the spammer and waits until it is fully initialized before returning. +func (s *GossipSubRouterSpammer) Start(t *testing.T) { require.Eventuallyf(t, func() bool { // ensuring the spammer router has been initialized. // this is needed because the router is initialized asynchronously. diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index a1f233a85e8..9a40e26665f 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -62,7 +62,7 @@ func TestSpam_IHave(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, nodes, 5*time.Second) defer p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) - gsrSpammer.WaitUntilInitialized(t) + gsrSpammer.Start(t) // prior to the test we should ensure that spammer and victim connect. // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. From c4b2f8e600da067ba1a53266a3bd2e26ce674c53 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 10:20:12 -0500 Subject: [PATCH 131/138] update godoc for `GenerateIHaveCtlMessages()` in insecure/corruptlibp2p/gossipsub_spammer.go Co-authored-by: Yahya Hassanzadeh --- insecure/corruptlibp2p/gossipsub_spammer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index b5f9898cbb1..59aad171cbe 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -42,7 +42,7 @@ func (s *GossipSubRouterSpammer) SpamIHave(t *testing.T, victim p2p.LibP2PNode, } // GenerateIHaveCtlMessages generates IHAVE control messages before they are sent so the test can prepare -// to receive them before they are sent by the spammer. +// to expect receiving them before they are sent by the spammer. func (s *GossipSubRouterSpammer) GenerateIHaveCtlMessages(t *testing.T, msgCount, msgSize int) []pb.ControlMessage { var iHaveCtlMsgs []pb.ControlMessage for i := 0; i < msgCount; i++ { From fbe145535caf39612d50992772e7977e71583d10 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 10:40:47 -0500 Subject: [PATCH 132/138] godoc update - `SpamIHave()` --- insecure/corruptlibp2p/gossipsub_spammer.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index 59aad171cbe..a918e2e2dd0 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -33,8 +33,7 @@ func NewGossipSubRouterSpammer(t *testing.T, sporkId flow.Identifier) *GossipSub } // SpamIHave spams the victim with junk iHave messages. -// msgCount is the number of iHave messages to send. -// msgSize is the number of messageIDs to include in each iHave message. +// ctlMessages is the list of spam messages to send to the victim node. func (s *GossipSubRouterSpammer) SpamIHave(t *testing.T, victim p2p.LibP2PNode, ctlMessages []pb.ControlMessage) { for _, ctlMessage := range ctlMessages { require.True(t, s.atomicRouter.Get().SendControl(victim.Host().ID(), &ctlMessage)) From 8056dbd8ad957fcd230f6fa4fb497dc7fc9337d0 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 10:46:54 -0500 Subject: [PATCH 133/138] renamed atomicRouter var to `router` --- insecure/corruptlibp2p/gossipsub_spammer.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index a918e2e2dd0..f54323f7658 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -19,16 +19,16 @@ import ( // GossipSubRouterSpammer is a wrapper around the GossipSubRouter that allows us to // spam the victim with junk control messages. type GossipSubRouterSpammer struct { - atomicRouter *atomicRouter - SpammerNode p2p.LibP2PNode + router *atomicRouter + SpammerNode p2p.LibP2PNode } // NewGossipSubRouterSpammer is the main method tests call for spamming attacks. func NewGossipSubRouterSpammer(t *testing.T, sporkId flow.Identifier) *GossipSubRouterSpammer { spammerNode, atomicRouter := createSpammerNode(t, sporkId, flow.RoleConsensus) return &GossipSubRouterSpammer{ - atomicRouter: atomicRouter, - SpammerNode: spammerNode, + router: atomicRouter, + SpammerNode: spammerNode, } } @@ -36,7 +36,7 @@ func NewGossipSubRouterSpammer(t *testing.T, sporkId flow.Identifier) *GossipSub // ctlMessages is the list of spam messages to send to the victim node. func (s *GossipSubRouterSpammer) SpamIHave(t *testing.T, victim p2p.LibP2PNode, ctlMessages []pb.ControlMessage) { for _, ctlMessage := range ctlMessages { - require.True(t, s.atomicRouter.Get().SendControl(victim.Host().ID(), &ctlMessage)) + require.True(t, s.router.Get().SendControl(victim.Host().ID(), &ctlMessage)) } } @@ -59,9 +59,9 @@ func (s *GossipSubRouterSpammer) Start(t *testing.T) { require.Eventuallyf(t, func() bool { // ensuring the spammer router has been initialized. // this is needed because the router is initialized asynchronously. - return s.atomicRouter.Get() != nil + return s.router.Get() != nil }, 1*time.Second, 100*time.Millisecond, "spammer router not set") - s.atomicRouter.set(s.atomicRouter.Get()) + s.router.set(s.router.Get()) } func createSpammerNode(t *testing.T, sporkId flow.Identifier, role flow.Role) (p2p.LibP2PNode, *atomicRouter) { From 42d96ee38d4a9ad931855b194b037deeae6f0da4 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 10:49:42 -0500 Subject: [PATCH 134/138] renamed (2nd time) atomicRouter var to `router` --- insecure/corruptlibp2p/gossipsub_spammer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index f54323f7658..b7f7857cb90 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -25,9 +25,9 @@ type GossipSubRouterSpammer struct { // NewGossipSubRouterSpammer is the main method tests call for spamming attacks. func NewGossipSubRouterSpammer(t *testing.T, sporkId flow.Identifier) *GossipSubRouterSpammer { - spammerNode, atomicRouter := createSpammerNode(t, sporkId, flow.RoleConsensus) + spammerNode, router := createSpammerNode(t, sporkId, flow.RoleConsensus) return &GossipSubRouterSpammer{ - router: atomicRouter, + router: router, SpammerNode: spammerNode, } } From 84550e52b62f0f3dcde5b179a78be6d1027b5043 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 11:01:03 -0500 Subject: [PATCH 135/138] spammer, victim roles match at test level --- insecure/corruptlibp2p/gossipsub_spammer.go | 4 ++-- insecure/corruptlibp2p/spam_test.go | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index b7f7857cb90..70a3ef4a780 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -24,8 +24,8 @@ type GossipSubRouterSpammer struct { } // NewGossipSubRouterSpammer is the main method tests call for spamming attacks. -func NewGossipSubRouterSpammer(t *testing.T, sporkId flow.Identifier) *GossipSubRouterSpammer { - spammerNode, router := createSpammerNode(t, sporkId, flow.RoleConsensus) +func NewGossipSubRouterSpammer(t *testing.T, sporkId flow.Identifier, role flow.Role) *GossipSubRouterSpammer { + spammerNode, router := createSpammerNode(t, sporkId, role) return &GossipSubRouterSpammer{ router: router, SpammerNode: spammerNode, diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index 9a40e26665f..b3a619e0006 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -29,8 +29,9 @@ import ( func TestSpam_IHave(t *testing.T) { const messagesToSpam = 3 sporkId := unittest.IdentifierFixture() + role := flow.RoleConsensus - gsrSpammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkId) + gsrSpammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkId, role) allSpamIHavesReceived := sync.WaitGroup{} allSpamIHavesReceived.Add(messagesToSpam) @@ -40,7 +41,7 @@ func TestSpam_IHave(t *testing.T) { t, sporkId, t.Name(), - p2ptest.WithRole(flow.RoleConsensus), + p2ptest.WithRole(role), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(func(id peer.ID, rpc *corrupt.RPC) error { iHaves := rpc.GetControl().GetIhave() From 11bb5a87d83f0bc196fd22af7a8bf9df60120c09 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 5 Jan 2023 14:27:45 -0500 Subject: [PATCH 136/138] removed setting RPC inspector in `WithMessageIdFunction()` --- insecure/corruptlibp2p/pubsub_adapter_config.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/insecure/corruptlibp2p/pubsub_adapter_config.go b/insecure/corruptlibp2p/pubsub_adapter_config.go index 87b08a807cf..94c053bac79 100644 --- a/insecure/corruptlibp2p/pubsub_adapter_config.go +++ b/insecure/corruptlibp2p/pubsub_adapter_config.go @@ -83,12 +83,6 @@ func (c *CorruptPubSubAdapterConfig) WithAppSpecificRpcInspector(_ func(peer.ID, } func (c *CorruptPubSubAdapterConfig) WithMessageIdFunction(f func([]byte) string) { - if c.inspector != nil { - c.options = append(c.options, corrupt.WithAppSpecificRpcInspector(func(id peer.ID, rpc *corrupt.RPC) error { - return c.inspector(id, rpc) - })) - } - c.options = append(c.options, corrupt.WithMessageIdFn(func(pmsg *pb.Message) string { return f(pmsg.Data) })) From 77c9dde79e6503b00f78c0f7ab2d6ffd2c36990b Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 9 Jan 2023 15:59:22 -0500 Subject: [PATCH 137/138] reverted `NewCorruptPubSubAdapterConfig()` and `defaultCorruptPubsubOptions()` match with `master` still failing `TestGossipSubSignatureRequirement/TestGossipSubSignatureRequirement` --- .../corruptlibp2p/pubsub_adapter_config.go | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/insecure/corruptlibp2p/pubsub_adapter_config.go b/insecure/corruptlibp2p/pubsub_adapter_config.go index 94c053bac79..3575f0888ab 100644 --- a/insecure/corruptlibp2p/pubsub_adapter_config.go +++ b/insecure/corruptlibp2p/pubsub_adapter_config.go @@ -55,16 +55,19 @@ func WithInspector(inspector func(peer.ID, *corrupt.RPC) error) func(config *Cor } func NewCorruptPubSubAdapterConfig(base *p2p.BasePubSubAdapterConfig, opts ...CorruptPubSubAdapterConfigOption) *CorruptPubSubAdapterConfig { - cfg := &CorruptPubSubAdapterConfig{ - options: defaultCorruptPubsubOptions(base), + config := &CorruptPubSubAdapterConfig{ + withMessageSigning: true, + withStrictSignatureVerification: true, } + for _, opt := range opts { - opt(cfg) + opt(config) } - return cfg -} -// + config.options = defaultCorruptPubsubOptions(base, config.withMessageSigning, config.withStrictSignatureVerification) + + return config +} func (c *CorruptPubSubAdapterConfig) WithRoutingDiscovery(routing routing.ContentRouting) { c.options = append(c.options, corrupt.WithDiscovery(discoveryRouting.NewRoutingDiscovery(routing))) @@ -92,10 +95,10 @@ func (c *CorruptPubSubAdapterConfig) Build() []corrupt.Option { return c.options } -func defaultCorruptPubsubOptions(base *p2p.BasePubSubAdapterConfig) []corrupt.Option { +func defaultCorruptPubsubOptions(base *p2p.BasePubSubAdapterConfig, withMessageSigning, withStrictSignatureVerification bool) []corrupt.Option { return []corrupt.Option{ - corrupt.WithMessageSigning(true), - corrupt.WithStrictSignatureVerification(true), + corrupt.WithMessageSigning(withMessageSigning), + corrupt.WithStrictSignatureVerification(withStrictSignatureVerification), corrupt.WithMaxMessageSize(base.MaxMessageSize), } } From 1dd0ac7fc37c40914e3cab1692471e71b65a58cc Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 10 Jan 2023 16:54:40 -0800 Subject: [PATCH 138/138] fixes test (#3794) --- insecure/corruptlibp2p/pubsub_adapter_config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/insecure/corruptlibp2p/pubsub_adapter_config.go b/insecure/corruptlibp2p/pubsub_adapter_config.go index 3575f0888ab..f10ef335326 100644 --- a/insecure/corruptlibp2p/pubsub_adapter_config.go +++ b/insecure/corruptlibp2p/pubsub_adapter_config.go @@ -58,13 +58,15 @@ func NewCorruptPubSubAdapterConfig(base *p2p.BasePubSubAdapterConfig, opts ...Co config := &CorruptPubSubAdapterConfig{ withMessageSigning: true, withStrictSignatureVerification: true, + options: make([]corrupt.Option, 0), } for _, opt := range opts { opt(config) } - config.options = defaultCorruptPubsubOptions(base, config.withMessageSigning, config.withStrictSignatureVerification) + // Note: we append the default options at the end to make sure that we are not overriding the options provided by the caller. + config.options = append(config.options, defaultCorruptPubsubOptions(base, config.withMessageSigning, config.withStrictSignatureVerification)...) return config }