From df3cce95a83953184d2da06996d5aae46c2d76b4 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:51:51 +0200 Subject: [PATCH 01/82] wip: testutils --- consensus/e2e_tests/utils_test.go | 25 +- internal/testutil/bus.go | 93 +++++++ internal/testutil/consensus/mocks.go | 24 ++ .../testutil/constructors/constructors.go | 103 ++++++++ internal/testutil/identity.go | 1 + internal/testutil/keys.go | 44 ++++ internal/testutil/mockdns.go | 21 +- internal/testutil/p2p/constants.go | 15 ++ internal/testutil/p2p/mocknet.go | 45 ++++ internal/testutil/p2p/mocks.go | 41 ++++ internal/testutil/p2p/network.go | 111 +++++++++ internal/testutil/persistence/mocks.go | 85 +++++++ internal/testutil/runtime/genesis.go | 44 ++++ internal/testutil/runtime/mocks.go | 51 ++++ .../testutil/telemetry/event_metrics_agent.go | 38 +++ internal/testutil/telemetry/mocks.go | 49 ++++ .../testutil/telemetry/time_series_agent.go | 31 +++ p2p/module_raintree_test.go | 221 +++++++++-------- p2p/module_test.go | 22 +- p2p/raintree/peers_manager_test.go | 3 +- p2p/transport_encryption_test.go | 28 +-- p2p/utils_test.go | 226 +++--------------- 22 files changed, 969 insertions(+), 352 deletions(-) create mode 100644 internal/testutil/bus.go create mode 100644 internal/testutil/consensus/mocks.go create mode 100644 internal/testutil/constructors/constructors.go create mode 100644 internal/testutil/identity.go create mode 100644 internal/testutil/keys.go create mode 100644 internal/testutil/p2p/constants.go create mode 100644 internal/testutil/p2p/mocknet.go create mode 100644 internal/testutil/p2p/mocks.go create mode 100644 internal/testutil/p2p/network.go create mode 100644 internal/testutil/persistence/mocks.go create mode 100644 internal/testutil/runtime/genesis.go create mode 100644 internal/testutil/runtime/mocks.go create mode 100644 internal/testutil/telemetry/event_metrics_agent.go create mode 100644 internal/testutil/telemetry/mocks.go create mode 100644 internal/testutil/telemetry/time_series_agent.go diff --git a/consensus/e2e_tests/utils_test.go b/consensus/e2e_tests/utils_test.go index 41340abc6..2f533b21b 100644 --- a/consensus/e2e_tests/utils_test.go +++ b/consensus/e2e_tests/utils_test.go @@ -3,6 +3,9 @@ package e2e_tests import ( "context" "fmt" + "github.com/pokt-network/pocket/internal/testutil/p2p" + "github.com/pokt-network/pocket/internal/testutil/persistence" + telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" "os" "reflect" "sort" @@ -13,7 +16,6 @@ import ( "github.com/golang/mock/gomock" "github.com/pokt-network/pocket/consensus" typesCons "github.com/pokt-network/pocket/consensus/types" - persistenceMocks "github.com/pokt-network/pocket/persistence/types/mocks" "github.com/pokt-network/pocket/runtime" "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/runtime/defaults" @@ -101,7 +103,7 @@ func CreateTestConsensusPocketNode( bus modules.Bus, eventsChannel modules.EventsChannel, ) *shared.Node { - persistenceMock := basePersistenceMock(t, eventsChannel, bus) + persistenceMock := persistence_testutil.PersistenceMockWithBlockStore(t, eventsChannel, bus) bus.RegisterModule(persistenceMock) consensusMod, err := consensus.Create(bus) @@ -115,9 +117,9 @@ func CreateTestConsensusPocketNode( runtimeMgr := (bus).GetRuntimeMgr() // TODO(olshansky): At the moment we are using the same base mocks for all the tests, // but note that they will need to be customized on a per test basis. - p2pMock := baseP2PMock(t, eventsChannel) + p2pMock := p2p_testutil.BaseP2PMock(t, eventsChannel) utilityMock := baseUtilityMock(t, eventsChannel, runtimeMgr.GetGenesis(), consensusModule) - telemetryMock := baseTelemetryMock(t, eventsChannel) + telemetryMock := telemetry_testutil.MinimalTelemetryMock(t) loggerMock := baseLoggerMock(t, eventsChannel) rpcMock := baseRpcMock(t, eventsChannel) @@ -548,21 +550,6 @@ func baseReplicaUtilityUnitOfWorkMock(t *testing.T, genesisState *genesis.Genesi return utilityReplicaUnitOfWorkMock } -func baseTelemetryMock(t *testing.T, _ modules.EventsChannel) *mockModules.MockTelemetryModule { - ctrl := gomock.NewController(t) - telemetryMock := mockModules.NewMockTelemetryModule(ctrl) - timeSeriesAgentMock := baseTelemetryTimeSeriesAgentMock(t) - eventMetricsAgentMock := baseTelemetryEventMetricsAgentMock(t) - - telemetryMock.EXPECT().Start().Return(nil).AnyTimes() - telemetryMock.EXPECT().SetBus(gomock.Any()).Return().AnyTimes() - telemetryMock.EXPECT().GetTimeSeriesAgent().Return(timeSeriesAgentMock).AnyTimes() - telemetryMock.EXPECT().GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() - telemetryMock.EXPECT().GetModuleName().Return(modules.TelemetryModuleName).AnyTimes() - - return telemetryMock -} - func baseRpcMock(t *testing.T, _ modules.EventsChannel) *mockModules.MockRPCModule { ctrl := gomock.NewController(t) rpcMock := mockModules.NewMockRPCModule(ctrl) diff --git a/internal/testutil/bus.go b/internal/testutil/bus.go new file mode 100644 index 000000000..6a0ea6102 --- /dev/null +++ b/internal/testutil/bus.go @@ -0,0 +1,93 @@ +package testutil + +import ( + "github.com/golang/mock/gomock" + "github.com/pokt-network/pocket/p2p/providers/current_height_provider" + "github.com/pokt-network/pocket/p2p/providers/peerstore_provider" + "github.com/pokt-network/pocket/runtime" + "github.com/pokt-network/pocket/shared/messaging" + "github.com/regen-network/gocuke" + + "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/mocks" +) + +type BusEventHandler func(*messaging.PocketEnvelope) +type BusEventHandlerFactory func(t gocuke.TestingT, bus modules.Bus) BusEventHandler + +// MinimalBusMock returns a bus mock with a module registry and minimal +// expectations registered to maximize re-usability. +func MinimalBusMock( + t gocuke.TestingT, + runtimeMgr modules.RuntimeMgr, +) *mock_modules.MockBus { + t.Helper() + + ctrl := gomock.NewController(t) + busMock := mock_modules.NewMockBus(ctrl) + busMock.EXPECT().GetRuntimeMgr().Return(runtimeMgr).AnyTimes() + busMock.EXPECT().RegisterModule(gomock.Any()).DoAndReturn(func(m modules.Module) { + m.SetBus(busMock) + }).AnyTimes() + + mockModulesRegistry := mock_modules.NewMockModulesRegistry(ctrl) + + // TODO_THIS_COMMIT: refactor - this doesn't belong here + mockModulesRegistry.EXPECT().GetModule(peerstore_provider.ModuleName).Return(nil, runtime.ErrModuleNotRegistered(peerstore_provider.ModuleName)).AnyTimes() + mockModulesRegistry.EXPECT().GetModule(current_height_provider.ModuleName).Return(nil, runtime.ErrModuleNotRegistered(current_height_provider.ModuleName)).AnyTimes() + + busMock.EXPECT().GetModulesRegistry().Return(mockModulesRegistry).AnyTimes() + return busMock +} + +// BaseBusMock returns a base bus mock which will accept any event, +// passing it to the provided handler function, any number of times. +func BaseBusMock( + t gocuke.TestingT, + runtimeMgr modules.RuntimeMgr, +) *mock_modules.MockBus { + t.Helper() + + return WithoutBusEventHandler(t, MinimalBusMock(t, runtimeMgr)) +} + +// BusMockWithEventHandler returns a base bus mock which will accept any event, +// any number of times, calling the `handler` returned from `handlerFactory` +// with the event as an argument. +func BusMockWithEventHandler( + t gocuke.TestingT, + runtimeMgr modules.RuntimeMgr, + handlerFactory BusEventHandlerFactory, +) *mock_modules.MockBus { + t.Helper() + + busMock := MinimalBusMock(t, runtimeMgr) + return WithBusEventHandler(t, busMock, handlerFactory) +} + +// WithBusEventHandler adds an expectation to a bus mock such that it will accept +// any event, any number of times, calling the `handler` returned from `handlerFactory` +// with the event as an argument. +func WithBusEventHandler( + t gocuke.TestingT, + busMock *mock_modules.MockBus, + handlerFactory BusEventHandlerFactory, +) *mock_modules.MockBus { + t.Helper() + + handler := handlerFactory(t, busMock) + busMock.EXPECT().PublishEventToBus(gomock.Any()).Do(handler).AnyTimes() + return busMock +} + +// WithoutBusEventHandler adds an expectation to a bus mock such that it will accept +// any event, any number of times. +func WithoutBusEventHandler( + t gocuke.TestingT, + busMock *mock_modules.MockBus, +) *mock_modules.MockBus { + t.Helper() + + busMock.EXPECT().PublishEventToBus(gomock.Any()).AnyTimes() + return busMock +} diff --git a/internal/testutil/consensus/mocks.go b/internal/testutil/consensus/mocks.go new file mode 100644 index 000000000..64888cfcb --- /dev/null +++ b/internal/testutil/consensus/mocks.go @@ -0,0 +1,24 @@ +package consensus_testutil + +import ( + "testing" + + "github.com/golang/mock/gomock" + + "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/mocks" +) + +// Consensus mock - only needed for validatorMap access +func PrepareConsensusMock(t *testing.T, busMock *mock_modules.MockBus) *mock_modules.MockConsensusModule { + ctrl := gomock.NewController(t) + consensusMock := mock_modules.NewMockConsensusModule(ctrl) + consensusMock.EXPECT().CurrentHeight().Return(uint64(1)).AnyTimes() + + consensusMock.EXPECT().GetBus().Return(busMock).AnyTimes() + consensusMock.EXPECT().SetBus(busMock).AnyTimes() + consensusMock.EXPECT().GetModuleName().Return(modules.ConsensusModuleName).AnyTimes() + busMock.RegisterModule(consensusMock) + + return consensusMock +} diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go new file mode 100644 index 000000000..6de6b7910 --- /dev/null +++ b/internal/testutil/constructors/constructors.go @@ -0,0 +1,103 @@ +package constructors + +import ( + "net" + + "github.com/golang/mock/gomock" + libp2pHost "github.com/libp2p/go-libp2p/core/host" + mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" + "github.com/regen-network/gocuke" + "github.com/stretchr/testify/require" + + "github.com/pokt-network/pocket/internal/testutil" + p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" + "github.com/pokt-network/pocket/p2p" + "github.com/pokt-network/pocket/runtime/configs" + "github.com/pokt-network/pocket/runtime/configs/types" + "github.com/pokt-network/pocket/runtime/genesis" + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/pocket/shared/modules" + mock_modules "github.com/pokt-network/pocket/shared/modules/mocks" +) + +type peerIDString = string + +// NewP2PMocknetModules returns a map of peer IDs to P2PModules using libp2p mocknet hosts. +func NewP2PModulesAndMocknet( + t gocuke.TestingT, + count int, + genesisState *genesis.GenesisState, + busEventHandlerFactory testutil.BusEventHandlerFactory, +) ( + buses map[peerIDString]modules.Bus, + p2pModules map[peerIDString]modules.P2PModule, + libp2pNetworkMock mocknet.Mocknet, +) { + libp2pNetworkMock = mocknet.New() + // destroy mocknet on test cleanup + t.Cleanup(func() { + err := libp2pNetworkMock.Close() + require.NoError(t, err) + }) + + // CONSIDERATION: using an iterator/generator would prevent unintentional + // ID collisions + privKeys := testutil.LoadLocalnetPrivateKeys(t, count) + + // CONSIDERATION: using an iterator/generator would prevent unintentional + // serviceURL collisions + serviceURLs := p2p_testutil.SequentialServiceURLs(t, count) + peerIDs := p2p_testutil.SetupMockNetPeers(t, libp2pNetworkMock, privKeys, serviceURLs) + + for i, peerID := range peerIDs { + // TECHDEBT: refactor + host := libp2pNetworkMock.Hosts()[i] + peerIDStr := peerID.String() + buses[peerIDStr], p2pModules[peerIDStr] = NewP2PModuleWithHost( + t, privKeys[i], + serviceURLs[i], + host, + genesisState, + busEventHandlerFactory, + ) + } + return buses, p2pModules, libp2pNetworkMock +} + +// TODO_THIS_COMMIT: consider following create factory convention (?) +func NewP2PModuleWithHost( + t gocuke.TestingT, + privKey cryptoPocket.PrivateKey, + serviceURL string, + host libp2pHost.Host, + genesisState *genesis.GenesisState, + busEventHandlerFactory testutil.BusEventHandlerFactory, +) (modules.Bus, modules.P2PModule) { + t.Helper() + + hostname, _, err := net.SplitHostPort(serviceURL) + require.NoError(t, err) + + // TODO_THIS_COMMIT: refactor to `BaseNodeMocks` or something + ctrl := gomock.NewController(t) + runtimeMgrMock := mock_modules.NewMockRuntimeMgr(ctrl) + runtimeMgrMock.EXPECT().GetConfig().Return(&configs.Config{ + P2P: &configs.P2PConfig{ + PrivateKey: privKey.String(), + Hostname: hostname, + //Port: 0, + ConnectionType: types.ConnectionType_TCPConnection, + MaxNonces: 100, + //IsClientOnly: false, + //BootstrapNodesCsv: "", + }, + }).AnyTimes() + + busMock := testutil.BusMockWithEventHandler(t, runtimeMgrMock, busEventHandlerFactory) + busMock.EXPECT().GetRuntimeMgr().Return(runtimeMgrMock).AnyTimes() + + mod, err := p2p.Create(busMock, p2p.WithHostOption(host)) + require.NoError(t, err) + + return busMock, mod.(modules.P2PModule) +} diff --git a/internal/testutil/identity.go b/internal/testutil/identity.go new file mode 100644 index 000000000..110b2e6a7 --- /dev/null +++ b/internal/testutil/identity.go @@ -0,0 +1 @@ +package testutil diff --git a/internal/testutil/keys.go b/internal/testutil/keys.go new file mode 100644 index 000000000..c9de34064 --- /dev/null +++ b/internal/testutil/keys.go @@ -0,0 +1,44 @@ +package testutil + +import ( + "bufio" + "os" + "path/filepath" + "regexp" + "runtime" + + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" + "github.com/stretchr/testify/require" +) + +var ( + privKeyManifestKeyRegex = regexp.MustCompile(`\s+"\d+":\s+(\w+)\s+`) +) + +func LoadLocalnetPrivateKeys(t require.TestingT, keyCount int) (privKeys []cryptoPocket.PrivateKey) { + _, filename, _, _ := runtime.Caller(0) + pkgDir := filepath.Dir(filename) + relativePathToKeys := filepath.Join(pkgDir, "..", "..", "build", "localnet", "manifests", "private-keys.yaml") + + privKeyManifest, err := os.Open(relativePathToKeys) + require.NoError(t, err) + + privKeys = make([]cryptoPocket.PrivateKey, 0, keyCount) + + // scann through file & extract private keys + scanner := bufio.NewScanner(privKeyManifest) + scanner.Split(bufio.ScanLines) + + for i, done := 0, false; i < keyCount || !done; i++ { + done = !scanner.Scan() + line := scanner.Text() + matches := privKeyManifestKeyRegex.FindStringSubmatch(line) + if len(matches) > 0 { + privKey, err := cryptoPocket.NewPrivateKey(matches[1]) + require.NoError(t, err) + + privKeys = append(privKeys, privKey) + } + } + return privKeys +} diff --git a/internal/testutil/mockdns.go b/internal/testutil/mockdns.go index 009a0e0fa..019a97860 100644 --- a/internal/testutil/mockdns.go +++ b/internal/testutil/mockdns.go @@ -2,15 +2,16 @@ package testutil import ( "fmt" - "net" - "net/url" - "testing" - "github.com/foxcpp/go-mockdns" + "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" + "net" + "net/url" ) -func PrepareDNSMockFromServiceURLs(t *testing.T, serviceURLs []string) (done func()) { +func PrepareDNSMockFromServiceURLs(t gocuke.TestingT, serviceURLs []string) (srv *mockdns.Server, done func()) { + t.Helper() + zones := make(map[string]mockdns.Zone) for i, u := range serviceURLs { // Perpend `scheme://` as serviceURLs are currently scheme-less. @@ -30,13 +31,15 @@ func PrepareDNSMockFromServiceURLs(t *testing.T, serviceURLs []string) (done fun } } - return PrepareDNSMock(zones) + return PrepareDNSMock(t, zones) } -func PrepareDNSMock(zones map[string]mockdns.Zone) (done func()) { - srv, _ := mockdns.NewServerWithLogger(zones, noopLogger{}, false) +func PrepareDNSMock(t gocuke.TestingT, zones map[string]mockdns.Zone) (srv *mockdns.Server, done func()) { + t.Helper() + + srv, _ = mockdns.NewServerWithLogger(zones, noopLogger{}, false) srv.PatchNet(net.DefaultResolver) - return func() { + return srv, func() { _ = srv.Close() mockdns.UnpatchNet(net.DefaultResolver) } diff --git a/internal/testutil/p2p/constants.go b/internal/testutil/p2p/constants.go new file mode 100644 index 000000000..8cc1f0bc9 --- /dev/null +++ b/internal/testutil/p2p/constants.go @@ -0,0 +1,15 @@ +package p2p_testutil + +import ( + "fmt" + + "github.com/pokt-network/pocket/runtime/defaults" +) + +var ( + // IP4ServiceURL is a string representing a valid IPv4 based ServiceURL using the loopback interface. + IP4ServiceURL = fmt.Sprintf("127.0.0.1:%d", defaults.DefaultP2PPort) + // IP6ServiceURL is a string representing a valid IPv6 based ServiceURL. + // (see: https://www.rfc-editor.org/rfc/rfc3986#section-3.2.2) + IP6ServiceURL = fmt.Sprintf("[2a00:1450:4005:802::2004]:%d", defaults.DefaultP2PPort) +) diff --git a/internal/testutil/p2p/mocknet.go b/internal/testutil/p2p/mocknet.go new file mode 100644 index 000000000..e97234510 --- /dev/null +++ b/internal/testutil/p2p/mocknet.go @@ -0,0 +1,45 @@ +package p2p_testutil + +import ( + "testing" + + libp2pCrypto "github.com/libp2p/go-libp2p/core/crypto" + libp2pHost "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/p2p/net/mock" + "github.com/stretchr/testify/require" + + "github.com/pokt-network/pocket/p2p/types" + "github.com/pokt-network/pocket/p2p/utils" + "github.com/pokt-network/pocket/shared/crypto" +) + +func NewTestPeer(t *testing.T) (*types.NetworkPeer, libp2pHost.Host) { + t.Helper() + + selfPrivKey, err := crypto.GeneratePrivateKey() + require.NoError(t, err) + + selfAddr := selfPrivKey.Address() + selfPeer := &types.NetworkPeer{ + PublicKey: selfPrivKey.PublicKey(), + Address: selfAddr, + ServiceURL: IP4ServiceURL, + } + return selfPeer, NewLibp2pMockNetHost(t, selfPrivKey, selfPeer) +} + +func NewLibp2pMockNetHost(t *testing.T, privKey crypto.PrivateKey, peer *types.NetworkPeer) libp2pHost.Host { + t.Helper() + + libp2pPrivKey, err := libp2pCrypto.UnmarshalEd25519PrivateKey(privKey.Bytes()) + require.NoError(t, err) + + libp2pMultiAddr, err := utils.Libp2pMultiaddrFromServiceURL(peer.ServiceURL) + require.NoError(t, err) + + libp2pMockNet := mocknet.New() + host, err := libp2pMockNet.AddPeer(libp2pPrivKey, libp2pMultiAddr) + require.NoError(t, err) + + return host +} diff --git a/internal/testutil/p2p/mocks.go b/internal/testutil/p2p/mocks.go new file mode 100644 index 000000000..61c437a34 --- /dev/null +++ b/internal/testutil/p2p/mocks.go @@ -0,0 +1,41 @@ +package p2p_testutil + +import ( + "testing" + + "github.com/golang/mock/gomock" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/pocket/shared/messaging" + "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/mocks" +) + +// Creates a p2p module mock with mock implementations of some basic functionality +func BaseP2PMock(t *testing.T, eventsChannel modules.EventsChannel) *mock_modules.MockP2PModule { + ctrl := gomock.NewController(t) + p2pMock := mock_modules.NewMockP2PModule(ctrl) + + p2pMock.EXPECT().Start().Return(nil).AnyTimes() + p2pMock.EXPECT().SetBus(gomock.Any()).Return().AnyTimes() + p2pMock.EXPECT(). + Broadcast(gomock.Any()). + Do(func(msg *anypb.Any) { + e := &messaging.PocketEnvelope{Content: msg} + eventsChannel <- e + }). + AnyTimes() + // CONSIDERATION: Adding a check to not to send message to itself + p2pMock.EXPECT(). + Send(gomock.Any(), gomock.Any()). + Do(func(addr crypto.Address, msg *anypb.Any) { + e := &messaging.PocketEnvelope{Content: msg} + eventsChannel <- e + }). + AnyTimes() + p2pMock.EXPECT().GetModuleName().Return(modules.P2PModuleName).AnyTimes() + p2pMock.EXPECT().HandleEvent(gomock.Any()).Return(nil).AnyTimes() + + return p2pMock +} diff --git a/internal/testutil/p2p/network.go b/internal/testutil/p2p/network.go new file mode 100644 index 000000000..6ae35b1b9 --- /dev/null +++ b/internal/testutil/p2p/network.go @@ -0,0 +1,111 @@ +package p2p_testutil + +import ( + "fmt" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + libp2pPeer "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/p2p/net/mock" + "github.com/regen-network/gocuke" + "github.com/stretchr/testify/require" + + "github.com/pokt-network/pocket/internal/testutil" + "github.com/pokt-network/pocket/p2p/types" + "github.com/pokt-network/pocket/p2p/utils" + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" +) + +func NewMocknetWithNPeers(t gocuke.TestingT, peerCount int) (mocknet.Mocknet, []string) { + t.Helper() + + // load pre-generated validator keypairs + libp2pNetworkMock := mocknet.New() + privKeys := testutil.LoadLocalnetPrivateKeys(t, peerCount) + serviceURLs := SequentialServiceURLs(t, peerCount) + _ = SetupMockNetPeers(t, libp2pNetworkMock, privKeys, serviceURLs) + + return libp2pNetworkMock, serviceURLs +} + +func SetupMockNetPeers( + t gocuke.TestingT, + netMock mocknet.Mocknet, + privKeys []cryptoPocket.PrivateKey, + serviceURLs []string, +) (peerIDs []peer.ID) { + t.Helper() + + // MUST add mockdns before any libp2p host comes online. Otherwise, it will + // error while attempting to resolve its own hostname. + _, dnsSrvDone := testutil.PrepareDNSMockFromServiceURLs(t, serviceURLs) + t.Cleanup(dnsSrvDone) + + // Add a libp2p peers/hosts to the `MockNet` with the keypairs corresponding + // to the genesis validators' keypairs + for i, peerInfo := range PeersFromPrivKeysAndServiceURLs(t, privKeys, serviceURLs) { + libp2pPrivKey, err := crypto.UnmarshalEd25519PrivateKey(privKeys[i].Bytes()) + require.NoError(t, err) + + _, err = netMock.AddPeer(libp2pPrivKey, peerInfo.Addrs[0]) + require.NoError(t, err) + + peerIDs = append(peerIDs, peerInfo.ID) + } + + // Link all peers such that any may dial/connect to any other. + err := netMock.LinkAll() + require.NoError(t, err) + + return peerIDs +} + +// CONSIDERATION: serviceURLs are only unique within their respective slice; +// consider building an iterator/generator instead. +func SequentialServiceURLs(t gocuke.TestingT, count int) (serviceURLs []string) { + t.Helper() + + for i := 0; i < count; i++ { + serviceURLs = append(serviceURLs, NewServiceURL(i+1)) + } + return serviceURLs +} + +func PeersFromPrivKeysAndServiceURLs( + t gocuke.TestingT, + privKeys []cryptoPocket.PrivateKey, + serviceURLs []string, +) (peersInfo []libp2pPeer.AddrInfo) { + t.Helper() + + serviceURLCount, privKeyCount := len(serviceURLs), len(privKeys) + maxCount := serviceURLCount + if privKeyCount < serviceURLCount { + maxCount = privKeyCount + } + + for i, privKey := range privKeys[:maxCount] { + peerInfo := peerFromPrivKeyAndServiceURL(t, privKey, NewServiceURL(i+1)) + peersInfo = append(peersInfo, peerInfo) + } + return peersInfo +} + +func peerFromPrivKeyAndServiceURL(t gocuke.TestingT, privKey cryptoPocket.PrivateKey, serviceURL string) libp2pPeer.AddrInfo { + t.Helper() + + peerInfo, err := utils.Libp2pAddrInfoFromPeer(&types.NetworkPeer{ + PublicKey: privKey.PublicKey(), + Address: privKey.Address(), + ServiceURL: serviceURL, + }) + require.NoError(t, err) + + return peerInfo +} + +const ServiceURLFormat = "node%d.consensus:42069" + +// TECHDEBT: rename `validatorId()` to `serviceURL()` +func NewServiceURL(i int) string { + return fmt.Sprintf(ServiceURLFormat, i) +} diff --git a/internal/testutil/persistence/mocks.go b/internal/testutil/persistence/mocks.go new file mode 100644 index 000000000..07702b867 --- /dev/null +++ b/internal/testutil/persistence/mocks.go @@ -0,0 +1,85 @@ +package persistence_testutil + +import ( + "fmt" + "github.com/pokt-network/pocket/persistence/types/mocks" + "github.com/pokt-network/pocket/shared/codec" + "github.com/pokt-network/pocket/shared/core/types" + "github.com/pokt-network/pocket/shared/utils" + "testing" + + "github.com/golang/mock/gomock" + + "github.com/pokt-network/pocket/runtime/genesis" + "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/mocks" +) + +// Persistence mock - only needed for validatorMap access +func BasePersistenceMock(t *testing.T, busMock *mock_modules.MockBus, genesisState *genesis.GenesisState) *mock_modules.MockPersistenceModule { + ctrl := gomock.NewController(t) + + persistenceModuleMock := mock_modules.NewMockPersistenceModule(ctrl) + readCtxMock := mock_modules.NewMockPersistenceReadContext(ctrl) + + readCtxMock.EXPECT().GetAllValidators(gomock.Any()).Return(genesisState.GetValidators(), nil).AnyTimes() + persistenceModuleMock.EXPECT().NewReadContext(gomock.Any()).Return(readCtxMock, nil).AnyTimes() + readCtxMock.EXPECT().Release().AnyTimes() + + persistenceModuleMock.EXPECT().GetBus().Return(busMock).AnyTimes() + persistenceModuleMock.EXPECT().SetBus(busMock).AnyTimes() + persistenceModuleMock.EXPECT().GetModuleName().Return(modules.PersistenceModuleName).AnyTimes() + busMock.RegisterModule(persistenceModuleMock) + + return persistenceModuleMock +} + +// Creates a persistence module mock with mock implementations of some basic functionality +func PersistenceMockWithBlockStore(t *testing.T, _ modules.EventsChannel, bus modules.Bus) *mock_modules.MockPersistenceModule { + ctrl := gomock.NewController(t) + persistenceMock := mock_modules.NewMockPersistenceModule(ctrl) + persistenceReadContextMock := mock_modules.NewMockPersistenceReadContext(ctrl) + + persistenceMock.EXPECT().GetModuleName().Return(modules.PersistenceModuleName).AnyTimes() + persistenceMock.EXPECT().Start().Return(nil).AnyTimes() + persistenceMock.EXPECT().SetBus(gomock.Any()).Return().AnyTimes() + persistenceMock.EXPECT().NewReadContext(gomock.Any()).Return(persistenceReadContextMock, nil).AnyTimes() + + persistenceMock.EXPECT().ReleaseWriteContext().Return(nil).AnyTimes() + + blockStoreMock := mock_kvstore.NewMockKVStore(ctrl) + + blockStoreMock.EXPECT().Get(gomock.Any()).DoAndReturn(func(height []byte) ([]byte, error) { + heightInt := utils.HeightFromBytes(height) + if bus.GetConsensusModule().CurrentHeight() < heightInt { + return nil, fmt.Errorf("requested height is higher than current height of the node's consensus module") + } + blockWithHeight := &types.Block{ + BlockHeader: &types.BlockHeader{ + Height: utils.HeightFromBytes(height), + }, + } + return codec.GetCodec().Marshal(blockWithHeight) + }).AnyTimes() + + persistenceMock.EXPECT().GetBlockStore().Return(blockStoreMock).AnyTimes() + + persistenceReadContextMock.EXPECT().GetMaximumBlockHeight().DoAndReturn(func() (uint64, error) { + height := bus.GetConsensusModule().CurrentHeight() + return height, nil + }).AnyTimes() + + persistenceReadContextMock.EXPECT().GetMinimumBlockHeight().DoAndReturn(func() (uint64, error) { + // mock minimum block height in persistence module to 1 if current height is equal or more than 1, else return 0 as the minimum height + if bus.GetConsensusModule().CurrentHeight() >= 1 { + return 1, nil + } + return 0, nil + }).AnyTimes() + + persistenceReadContextMock.EXPECT().GetAllValidators(gomock.Any()).Return(bus.GetRuntimeMgr().GetGenesis().Validators, nil).AnyTimes() + persistenceReadContextMock.EXPECT().GetBlockHash(gomock.Any()).Return("", nil).AnyTimes() + persistenceReadContextMock.EXPECT().Release().AnyTimes() + + return persistenceMock +} diff --git a/internal/testutil/runtime/genesis.go b/internal/testutil/runtime/genesis.go new file mode 100644 index 000000000..b1d30deb5 --- /dev/null +++ b/internal/testutil/runtime/genesis.go @@ -0,0 +1,44 @@ +package runtime_testutil + +import ( + "github.com/pokt-network/pocket/internal/testutil/p2p" + "github.com/pokt-network/pocket/runtime/genesis" + "github.com/pokt-network/pocket/runtime/test_artifacts" + "github.com/pokt-network/pocket/shared/core/types" + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" + "github.com/regen-network/gocuke" +) + +func BaseGenesisStateMock(t gocuke.TestingT, valKeys []cryptoPocket.PrivateKey, serviceURLs []string) *genesis.GenesisState { + t.Helper() + + genesisState := new(genesis.GenesisState) + validators := make([]*types.Actor, len(valKeys)) + for i, valKey := range valKeys { + addr := valKey.Address().String() + mockActor := &types.Actor{ + ActorType: types.ActorType_ACTOR_TYPE_VAL, + Address: addr, + PublicKey: valKey.PublicKey().String(), + ServiceUrl: serviceURLs[i], + StakedAmount: test_artifacts.DefaultStakeAmountString, + PausedHeight: int64(0), + UnstakingHeight: int64(0), + Output: addr, + } + validators[i] = mockActor + } + genesisState.Validators = validators + + return genesisState +} + +func GenesisWithSequentialServiceURLs(t gocuke.TestingT, valKeys []cryptoPocket.PrivateKey) *genesis.GenesisState { + t.Helper() + + serviceURLs := make([]string, len(valKeys)) + for i := range valKeys { + serviceURLs[i] = p2p_testutil.NewServiceURL(i) + } + return BaseGenesisStateMock(t, valKeys, serviceURLs) +} diff --git a/internal/testutil/runtime/mocks.go b/internal/testutil/runtime/mocks.go new file mode 100644 index 000000000..cf6549a08 --- /dev/null +++ b/internal/testutil/runtime/mocks.go @@ -0,0 +1,51 @@ +package runtime_testutil + +import ( + "net" + "strconv" + + "github.com/golang/mock/gomock" + "github.com/regen-network/gocuke" + "github.com/stretchr/testify/require" + + "github.com/pokt-network/pocket/runtime/configs" + "github.com/pokt-network/pocket/runtime/configs/types" + "github.com/pokt-network/pocket/runtime/defaults" + "github.com/pokt-network/pocket/runtime/genesis" + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/pocket/shared/modules" + mock_modules "github.com/pokt-network/pocket/shared/modules/mocks" +) + +func BaseRuntimeManagerMock( + t gocuke.TestingT, + privKey cryptoPocket.PrivateKey, + serviceURL string, + genesisState *genesis.GenesisState, +) modules.RuntimeMgr { + ctrl := gomock.NewController(t) + runtimeMgrMock := mock_modules.NewMockRuntimeMgr(ctrl) + + hostname, portStr, err := net.SplitHostPort(serviceURL) + require.NoError(t, err) + + port, err := strconv.Atoi(portStr) + require.NoError(t, err) + + cfg := &configs.Config{ + RootDirectory: "", + // TODO: need this? + //PrivateKey: privKey.String(), + P2P: &configs.P2PConfig{ + Hostname: hostname, + PrivateKey: privKey.String(), + Port: uint32(port), + ConnectionType: types.ConnectionType_EmptyConnection, + MaxNonces: defaults.DefaultP2PMaxNonces, + }, + } + + runtimeMgrMock.EXPECT().GetConfig().Return(cfg).AnyTimes() + runtimeMgrMock.EXPECT().GetGenesis().Return(genesisState).AnyTimes() + return runtimeMgrMock +} diff --git a/internal/testutil/telemetry/event_metrics_agent.go b/internal/testutil/telemetry/event_metrics_agent.go new file mode 100644 index 000000000..f100c994a --- /dev/null +++ b/internal/testutil/telemetry/event_metrics_agent.go @@ -0,0 +1,38 @@ +package telemetry_testutil + +import ( + "log" + "sync" + "testing" + + "github.com/golang/mock/gomock" + "github.com/regen-network/gocuke" + + "github.com/pokt-network/pocket/shared/modules/mocks" + "github.com/pokt-network/pocket/telemetry" +) + +func BaseEventMetricsAgentMock(t gocuke.TestingT) *mock_modules.MockEventMetricsAgent { + t.Helper() + + ctrl := gomock.NewController(t) + eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + return eventMetricsAgentMock +} + +// TODO_THIS_COMMIT: refactor... +// Events metric mock - Needed to help with proper counts for number of expected network writes +func PrepareEventMetricsAgentMock(t *testing.T, valId string, wg *sync.WaitGroup, expectedNumNetworkWrites int) *mock_modules.MockEventMetricsAgent { + ctrl := gomock.NewController(t) + eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) + + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Eq(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).Do(func(n, e any, l ...any) { + log.Printf("[valId: %s] Write\n", valId) + wg.Done() + }).Times(expectedNumNetworkWrites) + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Not(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).AnyTimes() + + return eventMetricsAgentMock +} diff --git a/internal/testutil/telemetry/mocks.go b/internal/testutil/telemetry/mocks.go new file mode 100644 index 000000000..fa68c690b --- /dev/null +++ b/internal/testutil/telemetry/mocks.go @@ -0,0 +1,49 @@ +package telemetry_testutil + +import ( + "github.com/golang/mock/gomock" + "github.com/regen-network/gocuke" + + "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/mocks" +) + +func MinimalTelemetryMock(t gocuke.TestingT) modules.TelemetryModule { + t.Helper() + + ctrl := gomock.NewController(t) + telemetryMock := mock_modules.NewMockTelemetryModule(ctrl) + + telemetryMock.EXPECT().Start().Return(nil).AnyTimes() + telemetryMock.EXPECT().SetBus(gomock.Any()).Return().AnyTimes() + telemetryMock.EXPECT().GetModuleName().Return(modules.TelemetryModuleName).AnyTimes() + + return telemetryMock +} + +func BaseTelemetryMock(t gocuke.TestingT) modules.TelemetryModule { + t.Helper() + return WithTimeSeriesAgent(t, WithEventMetricsAgent(t, MinimalTelemetryMock(t))) +} + +func WithTimeSeriesAgent(t gocuke.TestingT, telemetryMod modules.TelemetryModule) modules.TelemetryModule { + t.Helper() + + telemetryMock := telemetryMod.(*mock_modules.MockTelemetryModule) + timeSeriesAgentMock := BaseTimeSeriesAgentMock(t) + eventMetricsAgentMock := BaseEventMetricsAgentMock(t) + + telemetryMock.EXPECT().GetTimeSeriesAgent().Return(timeSeriesAgentMock).AnyTimes() + telemetryMock.EXPECT().GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() + return telemetryMock +} + +func WithEventMetricsAgent(t gocuke.TestingT, telemetryMod modules.TelemetryModule) modules.TelemetryModule { + t.Helper() + + telemetryMock := telemetryMod.(*mock_modules.MockTelemetryModule) + eventMetricsAgentMock := BaseEventMetricsAgentMock(t) + + telemetryMock.EXPECT().GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() + return telemetryMock +} diff --git a/internal/testutil/telemetry/time_series_agent.go b/internal/testutil/telemetry/time_series_agent.go new file mode 100644 index 000000000..9d63dbd03 --- /dev/null +++ b/internal/testutil/telemetry/time_series_agent.go @@ -0,0 +1,31 @@ +package telemetry_testutil + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/regen-network/gocuke" + + "github.com/pokt-network/pocket/shared/modules/mocks" +) + +func BaseTimeSeriesAgentMock(t gocuke.TestingT) *mock_modules.MockTimeSeriesAgent { + t.Helper() + + ctrl := gomock.NewController(t) + timeSeriesAgentMock := mock_modules.NewMockTimeSeriesAgent(ctrl) + timeSeriesAgentMock.EXPECT().CounterRegister(gomock.Any(), gomock.Any()).AnyTimes() + timeSeriesAgentMock.EXPECT().CounterIncrement(gomock.Any()).AnyTimes() + return timeSeriesAgentMock +} + +// Noop mock - no specific business logic to tend to in the timeseries agent mock +func NoopTelemetryTimeSeriesAgentMock(t *testing.T) *mock_modules.MockTimeSeriesAgent { + ctrl := gomock.NewController(t) + timeseriesAgentMock := mock_modules.NewMockTimeSeriesAgent(ctrl) + + timeseriesAgentMock.EXPECT().CounterRegister(gomock.Any(), gomock.Any()).AnyTimes() + timeseriesAgentMock.EXPECT().CounterIncrement(gomock.Any()).AnyTimes() + + return timeseriesAgentMock +} diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index 9bd873913..085eacb65 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -1,24 +1,28 @@ //go:build test -package p2p +package p2p_test import ( + "github.com/pokt-network/pocket/internal/testutil" + "github.com/pokt-network/pocket/internal/testutil/constructors" + "github.com/pokt-network/pocket/internal/testutil/p2p" + "github.com/pokt-network/pocket/shared/messaging" + "github.com/pokt-network/pocket/shared/modules" + "github.com/regen-network/gocuke" "log" "os" "path/filepath" "regexp" "sort" "strconv" - "strings" "sync" "testing" - libp2pNetwork "github.com/libp2p/go-libp2p/core/network" - mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/anypb" - "github.com/pokt-network/pocket/internal/testutil" + consensus_testutil "github.com/pokt-network/pocket/internal/testutil/consensus" + persistence_testutil "github.com/pokt-network/pocket/internal/testutil/persistence" "github.com/pokt-network/pocket/p2p/protocol" "github.com/pokt-network/pocket/p2p/raintree" ) @@ -40,7 +44,7 @@ func TestMain(m *testing.M) { // ### RainTree Unit Tests ### func TestRainTreeNetworkCompleteOneNodes(t *testing.T) { // val_1 - originatorNode := validatorId(1) + originatorNode := p2p_testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ originatorNode: {0, 0}, // val_1, the originator, does 0 network reads or writes } @@ -51,14 +55,13 @@ func TestRainTreeNetworkCompleteTwoNodes(t *testing.T) { // val_1 // └───────┐ // val_2 - originatorNode := validatorId(1) + originatorNode := p2p_testutil.NewServiceURL(1) // Per the diagram above, in the case of a 2 node network, the originator node (val_1) does a // single write to another node (val_2), also the // originator node and never performs any reads or writes during a RainTree broadcast. expectedCalls := TestNetworkSimulationConfig{ - // Attempt: I think Validator 1 is sending a message in a 2 (including self) node network - originatorNode: {0, 1}, // val_1 does a single network write (to val_2) - validatorId(2): {1, 0}, // val_2 does a single network read (from val_1) + // Attempt: I think Validator 1 is sending a message in a 2 (including self) node network originatorNode: {0, 1}, // val_1 does a single network write (to val_2) + p2p_testutil.NewServiceURL(2): {1, 0}, // val_2 does a single network read (from val_1) } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -67,11 +70,11 @@ func TestRainTreeNetworkCompleteThreeNodes(t *testing.T) { // val_1 // ┌───────┴────┬─────────┐ // val_2 val_1 val_3 - originatorNode := validatorId(1) + originatorNode := p2p_testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {0, 2}, // val_1 does two network writes (to val_2 and val_3) - validatorId(2): {1, 0}, // val_2 does a single network read (from val_1) - validatorId(3): {1, 0}, // val_2 does a single network read (from val_3) + originatorNode: {0, 2}, // val_1 does two network writes (to val_2 and val_3) + p2p_testutil.NewServiceURL(2): {1, 0}, // val_2 does a single network read (from val_1) + p2p_testutil.NewServiceURL(3): {1, 0}, // val_2 does a single network read (from val_3) } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -83,12 +86,12 @@ func TestRainTreeNetworkCompleteFourNodes(t *testing.T) { // val_2 val_1 val_3 // └───────┐ └───────┐ └───────┐ // val_3 val_2 val_4 - originatorNode := validatorId(1) + originatorNode := p2p_testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {0, 3}, // val_1 does 3 network writes (two to val_2 and 1 to val_3) - validatorId(2): {2, 1}, // val_2 does 2 network reads (both from val_1) and 1 network write (to val_3) - validatorId(3): {2, 1}, // val_2 does 2 network reads (from val_1 and val_2) and 1 network write (to val_4) - validatorId(4): {1, 0}, // val_2 does 1 network read (from val_3) + originatorNode: {0, 3}, // val_1 does 3 network writes (two to val_2 and 1 to val_3) + p2p_testutil.NewServiceURL(2): {2, 1}, // val_2 does 2 network reads (both from val_1) and 1 network write (to val_3) + p2p_testutil.NewServiceURL(3): {2, 1}, // val_2 does 2 network reads (from val_1 and val_2) and 1 network write (to val_4) + p2p_testutil.NewServiceURL(4): {1, 0}, // val_2 does 1 network read (from val_3) } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -99,17 +102,17 @@ func TestRainTreeNetworkCompleteNineNodes(t *testing.T) { // val_4 val_1 val_7 // ┌───────┴────┬─────────┐ ┌───────┴────┬─────────┐ ┌───────┴────┬─────────┐ // val_6 val_4 val_8 val_3 val_1 val_5 val_9 val_7 val_2 - originatorNode := validatorId(1) + originatorNode := p2p_testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {0, 4}, - validatorId(2): {1, 0}, - validatorId(3): {1, 0}, - validatorId(4): {1, 2}, - validatorId(5): {1, 0}, - validatorId(6): {1, 0}, - validatorId(7): {1, 2}, - validatorId(8): {1, 0}, - validatorId(9): {1, 0}, + originatorNode: {0, 4}, + p2p_testutil.NewServiceURL(2): {1, 0}, + p2p_testutil.NewServiceURL(3): {1, 0}, + p2p_testutil.NewServiceURL(4): {1, 2}, + p2p_testutil.NewServiceURL(5): {1, 0}, + p2p_testutil.NewServiceURL(6): {1, 0}, + p2p_testutil.NewServiceURL(7): {1, 2}, + p2p_testutil.NewServiceURL(8): {1, 0}, + p2p_testutil.NewServiceURL(9): {1, 0}, } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -123,20 +126,20 @@ func TestRainTreeNetworkCompleteNineNodes(t *testing.T) { // val_8 val_7 val_10 val_6 val_5 val_8 val_11 val_10 val_5 val_4 val_3 val_6 val_2 val_1 val_4 val_7 val_6 val_1 val_12 val_11 val_2 val_10 val_9 val_12 val_3 val_2 val_9 func TestRainTreeCompleteTwelveNodes(t *testing.T) { - originatorNode := validatorId(1) + originatorNode := p2p_testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {1, 6}, - validatorId(2): {3, 2}, - validatorId(3): {2, 2}, - validatorId(4): {2, 0}, - validatorId(5): {2, 4}, - validatorId(6): {3, 2}, - validatorId(7): {2, 2}, - validatorId(8): {2, 0}, - validatorId(9): {2, 4}, - validatorId(10): {3, 2}, - validatorId(11): {2, 2}, - validatorId(12): {2, 0}, + originatorNode: {1, 6}, + p2p_testutil.NewServiceURL(2): {3, 2}, + p2p_testutil.NewServiceURL(3): {2, 2}, + p2p_testutil.NewServiceURL(4): {2, 0}, + p2p_testutil.NewServiceURL(5): {2, 4}, + p2p_testutil.NewServiceURL(6): {3, 2}, + p2p_testutil.NewServiceURL(7): {2, 2}, + p2p_testutil.NewServiceURL(8): {2, 0}, + p2p_testutil.NewServiceURL(9): {2, 4}, + p2p_testutil.NewServiceURL(10): {3, 2}, + p2p_testutil.NewServiceURL(11): {2, 2}, + p2p_testutil.NewServiceURL(12): {2, 0}, } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -149,26 +152,26 @@ func TestRainTreeNetworkCompleteEighteenNodes(t *testing.T) { // val_11 val_7 val_15 val_5 val_1 val_9 val_17 val_13 val_3 // ┌────────┴─────┬───────────┐ ┌───────┴────┬──────────┐ ┌────────┴─────┬──────────┐ ┌───────┴────┬──────────┐ ┌───────┴────┬─────────┐ ┌────────┴────┬─────────┐ ┌───────┴─────┬──────────┐ ┌────────┴─────┬───────────┐ ┌───────┴────┬──────────┐ // val_13 val_11 val_16 val_9 val_7 val_12 val_17 val_15 val_8 val_7 val_5 val_10 val_3 val_1 val_6 val_11 val_9 val_2 val_1 val_17 val_4 val_15 val_13 val_18 val_5 val_3 val_14 - originatorNode := validatorId(1) + originatorNode := p2p_testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {1, 6}, - validatorId(2): {1, 0}, - validatorId(3): {2, 2}, - validatorId(4): {1, 0}, - validatorId(5): {2, 2}, - validatorId(6): {1, 0}, - validatorId(7): {2, 4}, - validatorId(8): {1, 0}, - validatorId(9): {2, 2}, - validatorId(10): {1, 0}, - validatorId(11): {2, 2}, - validatorId(12): {1, 0}, - validatorId(13): {2, 4}, - validatorId(14): {1, 0}, - validatorId(15): {2, 2}, - validatorId(16): {1, 0}, - validatorId(17): {2, 2}, - validatorId(18): {1, 0}, + originatorNode: {1, 6}, + p2p_testutil.NewServiceURL(2): {1, 0}, + p2p_testutil.NewServiceURL(3): {2, 2}, + p2p_testutil.NewServiceURL(4): {1, 0}, + p2p_testutil.NewServiceURL(5): {2, 2}, + p2p_testutil.NewServiceURL(6): {1, 0}, + p2p_testutil.NewServiceURL(7): {2, 4}, + p2p_testutil.NewServiceURL(8): {1, 0}, + p2p_testutil.NewServiceURL(9): {2, 2}, + p2p_testutil.NewServiceURL(10): {1, 0}, + p2p_testutil.NewServiceURL(11): {2, 2}, + p2p_testutil.NewServiceURL(12): {1, 0}, + p2p_testutil.NewServiceURL(13): {2, 4}, + p2p_testutil.NewServiceURL(14): {1, 0}, + p2p_testutil.NewServiceURL(15): {2, 2}, + p2p_testutil.NewServiceURL(16): {1, 0}, + p2p_testutil.NewServiceURL(17): {2, 2}, + p2p_testutil.NewServiceURL(18): {1, 0}, } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -181,35 +184,35 @@ func TestRainTreeNetworkCompleteTwentySevenNodes(t *testing.T) { // val_16 val_10 val_22 val_7 val_1 val_13 val_25 val_19 val_4 // ┌────────┴─────┬───────────┐ ┌────────┴─────┬───────────┐ ┌────────┴─────┬───────────┐ ┌────────┴────┬──────────┐ ┌───────┴────┬─────────┐ ┌────────┴─────┬──────────┐ ┌───────┴─────┬──────────┐ ┌────────┴─────┬───────────┐ ┌───────┴────┬──────────┐ // val_20 val_16 val_24 val_14 val_10 val_18 val_26 val_22 val_12 val_11 val_7 val_15 val_5 val_1 val_9 val_17 val_13 val_3 val_2 val_25 val_6 val_23 val_19 val_27 val_8 val_4 val_21 - originatorNode := validatorId(1) + originatorNode := p2p_testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {0, 6}, - validatorId(2): {1, 0}, - validatorId(3): {1, 0}, - validatorId(4): {1, 2}, - validatorId(5): {1, 0}, - validatorId(6): {1, 0}, - validatorId(7): {1, 2}, - validatorId(8): {1, 0}, - validatorId(9): {1, 0}, - validatorId(10): {1, 4}, - validatorId(11): {1, 0}, - validatorId(12): {1, 0}, - validatorId(13): {1, 2}, - validatorId(14): {1, 0}, - validatorId(15): {1, 0}, - validatorId(16): {1, 2}, - validatorId(17): {1, 0}, - validatorId(18): {1, 0}, - validatorId(19): {1, 4}, - validatorId(20): {1, 0}, - validatorId(21): {1, 0}, - validatorId(22): {1, 2}, - validatorId(23): {1, 0}, - validatorId(24): {1, 0}, - validatorId(25): {1, 2}, - validatorId(26): {1, 0}, - validatorId(27): {1, 0}, + originatorNode: {0, 6}, + p2p_testutil.NewServiceURL(2): {1, 0}, + p2p_testutil.NewServiceURL(3): {1, 0}, + p2p_testutil.NewServiceURL(4): {1, 2}, + p2p_testutil.NewServiceURL(5): {1, 0}, + p2p_testutil.NewServiceURL(6): {1, 0}, + p2p_testutil.NewServiceURL(7): {1, 2}, + p2p_testutil.NewServiceURL(8): {1, 0}, + p2p_testutil.NewServiceURL(9): {1, 0}, + p2p_testutil.NewServiceURL(10): {1, 4}, + p2p_testutil.NewServiceURL(11): {1, 0}, + p2p_testutil.NewServiceURL(12): {1, 0}, + p2p_testutil.NewServiceURL(13): {1, 2}, + p2p_testutil.NewServiceURL(14): {1, 0}, + p2p_testutil.NewServiceURL(15): {1, 0}, + p2p_testutil.NewServiceURL(16): {1, 2}, + p2p_testutil.NewServiceURL(17): {1, 0}, + p2p_testutil.NewServiceURL(18): {1, 0}, + p2p_testutil.NewServiceURL(19): {1, 4}, + p2p_testutil.NewServiceURL(20): {1, 0}, + p2p_testutil.NewServiceURL(21): {1, 0}, + p2p_testutil.NewServiceURL(22): {1, 2}, + p2p_testutil.NewServiceURL(23): {1, 0}, + p2p_testutil.NewServiceURL(24): {1, 0}, + p2p_testutil.NewServiceURL(25): {1, 2}, + p2p_testutil.NewServiceURL(26): {1, 0}, + p2p_testutil.NewServiceURL(27): {1, 0}, } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -224,24 +227,38 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te numValidators := len(networkSimulationConfig) runtimeConfigs := createMockRuntimeMgrs(t, numValidators) genesisMock := runtimeConfigs[0].GetGenesis() - busMocks := createMockBuses(t, runtimeConfigs) + + var wg sync.WaitGroup + //busMocks := createMockBuses(t, runtimeConfigs, &wg) + busEventHandlerFactory := func(t gocuke.TestingT, bus modules.Bus) testutil.BusEventHandler { + return func(data *messaging.PocketEnvelope) { + // `p2pModule#handleNetworkData()` calls `modules.Bus#PublishEventToBus()` + // assumes that P2P module is the only event producer running during the test + wg.Done() + } + } + + busMocks, p2pModules, _ := constructors.NewP2PModulesAndMocknet( + t, numValidators, + genesisMock, + busEventHandlerFactory, + ) valIds := make([]string, 0, numValidators) for valId := range networkSimulationConfig { valIds = append(valIds, valId) } + // TODO_THIS_COMMIT: need this? + // sort `valIds` in ascending order sort.Slice(valIds, func(i, j int) bool { iId := extractNumericId(valIds[i]) jId := extractNumericId(valIds[j]) return iId < jId }) - testutil.PrepareDNSMockFromServiceURLs(t, valIds) - // Create connection and bus mocks along with a shared WaitGroup to track the number of expected // reads and writes throughout the mocked local network - var wg sync.WaitGroup for i, valId := range valIds { expectedCall := networkSimulationConfig[valId] expectedReads := expectedCall.numNetworkReads @@ -252,23 +269,19 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te wg.Add(expectedReads) wg.Add(expectedWrites) - persistenceMock := preparePersistenceMock(t, busMocks[i], genesisMock) - consensusMock := prepareConsensusMock(t, busMocks[i]) + persistenceMock := persistence_testutil.BasePersistenceMock(t, busMocks[i], genesisMock) + consensusMock := consensus_testutil.PrepareConsensusMock(t, busMocks[i]) telemetryMock := prepareTelemetryMock(t, busMocks[i], valId, &wg, expectedWrites) - prepareBusMock(busMocks[i], persistenceMock, consensusMock, telemetryMock) + busMocks[i].EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() + busMocks[i].EXPECT().GetConsensusModule().Return(consensusMock).AnyTimes() + busMocks[i].EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() } - libp2pMockNet := mocknet.New() - defer func() { - err := libp2pMockNet.Close() - require.NoError(t, err) - }() - // Inject the connection and bus mocks into the P2P modules - p2pModules := createP2PModules(t, busMocks, libp2pMockNet) + //p2pModules := createP2PModules(t, busMocks, libp2pNetworkMock, valIds) - for serviceURL, p2pMod := range p2pModules { + for _, p2pMod := range p2pModules { err := p2pMod.Start() require.NoError(t, err) diff --git a/p2p/module_test.go b/p2p/module_test.go index 79cd17066..85e6e858a 100644 --- a/p2p/module_test.go +++ b/p2p/module_test.go @@ -1,7 +1,12 @@ -package p2p +package p2p_test import ( "fmt" + "github.com/pokt-network/pocket/internal/testutil" + p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" + "github.com/pokt-network/pocket/internal/testutil/persistence" + "github.com/pokt-network/pocket/internal/testutil/runtime" + "github.com/pokt-network/pocket/p2p" "strings" "testing" @@ -111,10 +116,11 @@ func Test_Create_configureBootstrapNodes(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockRuntimeMgr := mockModules.NewMockRuntimeMgr(ctrl) - mockBus := createMockBus(t, mockRuntimeMgr) + mockBus := testutil.BaseBusMock(t, mockRuntimeMgr) - genesisStateMock := createMockGenesisState(keys) - persistenceMock := preparePersistenceMock(t, mockBus, genesisStateMock) + serviceURLs := p2p_testutil.SequentialServiceURLs(t, len(keys)) + genesisStateMock := runtime_testutil.BaseGenesisStateMock(t, keys, serviceURLs) + persistenceMock := persistence_testutil.BasePersistenceMock(t, mockBus, genesisStateMock) mockBus.EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() mockConsensusModule := mockModules.NewMockConsensusModule(ctrl) @@ -137,7 +143,7 @@ func Test_Create_configureBootstrapNodes(t *testing.T) { } host := newLibp2pMockNetHost(t, privKey, peer) - p2pMod, err := Create(mockBus, WithHostOption(host)) + p2pMod, err := p2p.Create(mockBus, p2p.WithHostOption(host)) if (err != nil) != tt.wantErr { t.Errorf("p2pModule.Create() error = %v, wantErr %v", err, tt.wantErr) } @@ -155,10 +161,10 @@ func TestP2pModule_WithHostOption_Restart(t *testing.T) { privKey := cryptoPocket.GetPrivKeySeed(1) mockRuntimeMgr := mockModules.NewMockRuntimeMgr(ctrl) - mockBus := createMockBus(t, mockRuntimeMgr) + mockBus := testutil.BaseBusMock(t, mockRuntimeMgr) - genesisStateMock := createMockGenesisState(nil) - persistenceMock := preparePersistenceMock(t, mockBus, genesisStateMock) + genesisStateMock := runtime_testutil.BaseGenesisStateMock(t, nil, nil) + persistenceMock := persistence_testutil.BasePersistenceMock(t, mockBus, genesisStateMock) mockBus.EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() consensusModuleMock := mockModules.NewMockConsensusModule(ctrl) diff --git a/p2p/raintree/peers_manager_test.go b/p2p/raintree/peers_manager_test.go index 7fa01a3d3..742080088 100644 --- a/p2p/raintree/peers_manager_test.go +++ b/p2p/raintree/peers_manager_test.go @@ -356,5 +356,6 @@ func mockAlphabetValidatorServiceURLsDNS(t *testing.T) (done func()) { } } - return testutil.PrepareDNSMock(zones) + _, done = testutil.PrepareDNSMock(t, zones) + return done } diff --git a/p2p/transport_encryption_test.go b/p2p/transport_encryption_test.go index d95cb6496..407f18e09 100644 --- a/p2p/transport_encryption_test.go +++ b/p2p/transport_encryption_test.go @@ -3,6 +3,11 @@ package p2p import ( "context" "fmt" + "github.com/pokt-network/pocket/internal/testutil" + p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" + "github.com/pokt-network/pocket/internal/testutil/persistence" + "github.com/pokt-network/pocket/internal/testutil/runtime" + "github.com/pokt-network/pocket/internal/testutil/telemetry" "testing" "time" @@ -11,7 +16,6 @@ import ( "github.com/multiformats/go-multiaddr" "github.com/stretchr/testify/require" - "github.com/pokt-network/pocket/internal/testutil" "github.com/pokt-network/pocket/p2p/protocol" typesP2P "github.com/pokt-network/pocket/p2p/types" "github.com/pokt-network/pocket/p2p/utils" @@ -19,7 +23,6 @@ import ( "github.com/pokt-network/pocket/runtime/configs/types" "github.com/pokt-network/pocket/runtime/defaults" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" - "github.com/pokt-network/pocket/shared/modules" mockModules "github.com/pokt-network/pocket/shared/modules/mocks" ) @@ -45,27 +48,18 @@ func TestP2pModule_Insecure_Error(t *testing.T) { }, }).AnyTimes() - timeSeriesAgentMock := prepareNoopTimeSeriesAgentMock(t) - eventMetricsAgentMock := mockModules.NewMockEventMetricsAgent(ctrl) - eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + telemetryMock := telemetry_testutil.BaseTelemetryMock(t) - telemetryMock := mockModules.NewMockTelemetryModule(ctrl) - telemetryMock.EXPECT().GetTimeSeriesAgent().Return(timeSeriesAgentMock).AnyTimes() - telemetryMock.EXPECT().GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() - telemetryMock.EXPECT().GetModuleName().Return(modules.TelemetryModuleName).AnyTimes() - - busMock := createMockBus(t, runtimeMgrMock) + busMock := testutil.BaseBusMock(t, runtimeMgrMock) busMock.EXPECT().GetConsensusModule().Return(mockConsensusModule).AnyTimes() - busMock.EXPECT().GetRuntimeMgr().Return(runtimeMgrMock).AnyTimes() busMock.EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() - genesisStateMock := createMockGenesisState(keys[:1]) - persistenceMock := preparePersistenceMock(t, busMock, genesisStateMock) + keys := testutil.LoadLocalnetPrivateKeys(t, 1) + serviceURLs := p2p_testutil.SequentialServiceURLs(t, 1) + genesisStateMock := runtime_testutil.BaseGenesisStateMock(t, keys, serviceURLs) + persistenceMock := persistence_testutil.BasePersistenceMock(t, busMock, genesisStateMock) busMock.EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() - telemetryMock.EXPECT().GetBus().Return(busMock).AnyTimes() - telemetryMock.EXPECT().SetBus(busMock).AnyTimes() - serviceURLs := make([]string, len(genesisStateMock.Validators)) for i, actor := range genesisStateMock.Validators { serviceURLs[i] = actor.ServiceUrl diff --git a/p2p/utils_test.go b/p2p/utils_test.go index 66466acb0..706377dc7 100644 --- a/p2p/utils_test.go +++ b/p2p/utils_test.go @@ -1,41 +1,29 @@ -package p2p +package p2p_test import ( - "fmt" - "log" - "net" + "github.com/pokt-network/pocket/internal/testutil" + "github.com/pokt-network/pocket/internal/testutil/runtime" + "github.com/pokt-network/pocket/internal/testutil/telemetry" + "github.com/pokt-network/pocket/shared/messaging" + "github.com/regen-network/gocuke" "sort" - "strconv" "sync" "testing" "time" "github.com/golang/mock/gomock" - libp2pCrypto "github.com/libp2p/go-libp2p/core/crypto" - libp2pPeer "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" - "github.com/pokt-network/pocket/p2p/providers/current_height_provider" - "github.com/pokt-network/pocket/p2p/providers/peerstore_provider" - typesP2P "github.com/pokt-network/pocket/p2p/types" - "github.com/pokt-network/pocket/p2p/utils" - "github.com/pokt-network/pocket/runtime" - "github.com/pokt-network/pocket/runtime/configs" - "github.com/pokt-network/pocket/runtime/configs/types" - "github.com/pokt-network/pocket/runtime/defaults" - "github.com/pokt-network/pocket/runtime/genesis" - "github.com/pokt-network/pocket/runtime/test_artifacts" - coreTypes "github.com/pokt-network/pocket/shared/core/types" + "github.com/stretchr/testify/require" + + p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/pocket/shared/modules" mockModules "github.com/pokt-network/pocket/shared/modules/mocks" - "github.com/pokt-network/pocket/telemetry" - "github.com/stretchr/testify/require" ) // ~~~~~~ RainTree Unit Test Configurations ~~~~~~ const ( - serviceURLFormat = "node%d.consensus:42069" eventsChannelSize = 10000 // Since we simulate up to a 27 node network, we will pre-generate a n >= 27 number of keys to avoid generation // every time. The genesis config seed start is set for deterministic key generation and 42 was chosen arbitrarily. @@ -75,12 +63,6 @@ type TestNetworkSimulationConfig map[string]struct { // node IDs the specific read or write is coming from or going to. } -// CLEANUP: This could (should?) be a codebase-wide shared test helper -// TECHDEBT: rename `validatorId()` to `serviceURL()` -func validatorId(i int) string { - return fmt.Sprintf(serviceURLFormat, i) -} - func waitForNetworkSimulationCompletion(t *testing.T, wg *sync.WaitGroup) { // Wait for all messages to be transmitted done := make(chan struct{}) @@ -102,179 +84,61 @@ func waitForNetworkSimulationCompletion(t *testing.T, wg *sync.WaitGroup) { // ~~~~~~ RainTree Unit Test Mocks ~~~~~~ // createP2PModules returns a map of configured p2pModules keyed by an incremental naming convention (eg: `val_1`, `val_2`, etc.) -func createP2PModules(t *testing.T, busMocks []*mockModules.MockBus, netMock mocknet.Mocknet) (p2pModules map[string]*p2pModule) { - peerIDs := setupMockNetPeers(t, netMock, len(busMocks)) +func createP2PModules(t *testing.T, busMocks []*mockModules.MockBus, netMock mocknet.Mocknet, serviceURLs []string) (p2pModules map[string]*p2pModule) { + t.Helper() + + require.GreaterOrEqualf(t, len(serviceURLs), len(busMocks), "number of bus mocks must be less than or equal to the number of service URLs") + peerIDs := p2p_testutil.SetupMockNetPeers(t, netMock, keys[:len(busMocks)], serviceURLs) p2pModules = make(map[string]*p2pModule, len(busMocks)) for i := range busMocks { host := netMock.Host(peerIDs[i]) p2pMod, err := Create(busMocks[i], WithHostOption(host)) require.NoError(t, err) - p2pModules[validatorId(i+1)] = p2pMod.(*p2pModule) + p2pModules[serviceURLs[i]] = p2pMod.(*p2pModule) } return } -func setupMockNetPeers(t *testing.T, netMock mocknet.Mocknet, numPeers int) (peerIDs []libp2pPeer.ID) { - // Add a libp2p peers/hosts to the `MockNet` with the keypairs corresponding - // to the genesis validators' keypairs - for i, privKey := range keys[:numPeers] { - peerInfo, err := utils.Libp2pAddrInfoFromPeer(&typesP2P.NetworkPeer{ - PublicKey: privKey.PublicKey(), - Address: privKey.Address(), - ServiceURL: validatorId(i + 1), - }) - require.NoError(t, err) - - libp2pPrivKey, err := libp2pCrypto.UnmarshalEd25519PrivateKey(privKey.Bytes()) - require.NoError(t, err) - - _, err = netMock.AddPeer(libp2pPrivKey, peerInfo.Addrs[0]) - require.NoError(t, err) - - peerIDs = append(peerIDs, peerInfo.ID) - } - - // Link all peers such that any may dial/connect to any other. - err := netMock.LinkAll() - require.NoError(t, err) - - return peerIDs -} - // createMockRuntimeMgrs creates `numValidators` instances of mocked `RuntimeMgr` that are essentially // representing the runtime environments of the validators that we will use in our tests func createMockRuntimeMgrs(t *testing.T, numValidators int) []modules.RuntimeMgr { - ctrl := gomock.NewController(t) mockRuntimeMgrs := make([]modules.RuntimeMgr, numValidators) valKeys := make([]cryptoPocket.PrivateKey, numValidators) copy(valKeys, keys[:numValidators]) - mockGenesisState := createMockGenesisState(valKeys) + mockGenesisState := runtime_testutil.GenesisWithSequentialServiceURLs(t, valKeys) for i := range mockRuntimeMgrs { - serviceURL := validatorId(i + 1) - hostname, portStr, err := net.SplitHostPort(serviceURL) - require.NoError(t, err) - - port, err := strconv.Atoi(portStr) - require.NoError(t, err) - - cfg := &configs.Config{ - RootDirectory: "", - PrivateKey: valKeys[i].String(), - P2P: &configs.P2PConfig{ - Hostname: hostname, - PrivateKey: valKeys[i].String(), - Port: uint32(port), - ConnectionType: types.ConnectionType_EmptyConnection, - MaxNonces: defaults.DefaultP2PMaxNonces, - }, - } - - mockRuntimeMgr := mockModules.NewMockRuntimeMgr(ctrl) - mockRuntimeMgr.EXPECT().GetConfig().Return(cfg).AnyTimes() - mockRuntimeMgr.EXPECT().GetGenesis().Return(mockGenesisState).AnyTimes() - mockRuntimeMgrs[i] = mockRuntimeMgr + mockRuntimeMgrs[i] = runtime_testutil.BaseRuntimeManagerMock( + t, valKeys[i], + p2p_testutil.NewServiceURL(i+1), + mockGenesisState, + ) } return mockRuntimeMgrs } -func createMockBuses(t *testing.T, runtimeMgrs []modules.RuntimeMgr) []*mockModules.MockBus { +func createMockBuses(t *testing.T, runtimeMgrs []modules.RuntimeMgr, wg *sync.WaitGroup) []*mockModules.MockBus { mockBuses := make([]*mockModules.MockBus, len(runtimeMgrs)) for i := range mockBuses { - mockBuses[i] = createMockBus(t, runtimeMgrs[i]) - } - return mockBuses -} - -func createMockBus(t *testing.T, runtimeMgr modules.RuntimeMgr) *mockModules.MockBus { - ctrl := gomock.NewController(t) - mockBus := mockModules.NewMockBus(ctrl) - mockBus.EXPECT().GetRuntimeMgr().Return(runtimeMgr).AnyTimes() - mockBus.EXPECT().RegisterModule(gomock.Any()).DoAndReturn(func(m modules.Module) { - m.SetBus(mockBus) - }).AnyTimes() - mockModulesRegistry := mockModules.NewMockModulesRegistry(ctrl) - mockModulesRegistry.EXPECT().GetModule(peerstore_provider.ModuleName).Return(nil, runtime.ErrModuleNotRegistered(peerstore_provider.ModuleName)).AnyTimes() - mockModulesRegistry.EXPECT().GetModule(current_height_provider.ModuleName).Return(nil, runtime.ErrModuleNotRegistered(current_height_provider.ModuleName)).AnyTimes() - mockBus.EXPECT().GetModulesRegistry().Return(mockModulesRegistry).AnyTimes() - mockBus.EXPECT().PublishEventToBus(gomock.Any()).AnyTimes() - return mockBus -} - -// createMockGenesisState configures and returns a mocked GenesisState -func createMockGenesisState(valKeys []cryptoPocket.PrivateKey) *genesis.GenesisState { - genesisState := new(genesis.GenesisState) - validators := make([]*coreTypes.Actor, len(valKeys)) - for i, valKey := range valKeys { - addr := valKey.Address().String() - mockActor := &coreTypes.Actor{ - ActorType: coreTypes.ActorType_ACTOR_TYPE_VAL, - Address: addr, - PublicKey: valKey.PublicKey().String(), - ServiceUrl: validatorId(i + 1), - StakedAmount: test_artifacts.DefaultStakeAmountString, - PausedHeight: int64(0), - UnstakingHeight: int64(0), - Output: addr, + handlerFactory := func(t gocuke.TestingT, bus modules.Bus) testutil.BusEventHandler { + return func(data *messaging.PocketEnvelope) { + wg.Done() + } } - validators[i] = mockActor + mockBuses[i] = testutil.BusMockWithEventHandler(t, runtimeMgrs[i], handlerFactory) } - genesisState.Validators = validators - - return genesisState -} - -// Bus Mock - needed to return the appropriate modules when accessed -func prepareBusMock(busMock *mockModules.MockBus, - persistenceMock *mockModules.MockPersistenceModule, - consensusMock *mockModules.MockConsensusModule, - telemetryMock *mockModules.MockTelemetryModule, -) { - busMock.EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() - busMock.EXPECT().GetConsensusModule().Return(consensusMock).AnyTimes() - busMock.EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() -} - -// Consensus mock - only needed for validatorMap access -func prepareConsensusMock(t *testing.T, busMock *mockModules.MockBus) *mockModules.MockConsensusModule { - ctrl := gomock.NewController(t) - consensusMock := mockModules.NewMockConsensusModule(ctrl) - consensusMock.EXPECT().CurrentHeight().Return(uint64(1)).AnyTimes() - - consensusMock.EXPECT().GetBus().Return(busMock).AnyTimes() - consensusMock.EXPECT().SetBus(busMock).AnyTimes() - consensusMock.EXPECT().GetModuleName().Return(modules.ConsensusModuleName).AnyTimes() - busMock.RegisterModule(consensusMock) - - return consensusMock -} - -// Persistence mock - only needed for validatorMap access -func preparePersistenceMock(t *testing.T, busMock *mockModules.MockBus, genesisState *genesis.GenesisState) *mockModules.MockPersistenceModule { - ctrl := gomock.NewController(t) - - persistenceModuleMock := mockModules.NewMockPersistenceModule(ctrl) - readCtxMock := mockModules.NewMockPersistenceReadContext(ctrl) - - readCtxMock.EXPECT().GetAllValidators(gomock.Any()).Return(genesisState.GetValidators(), nil).AnyTimes() - persistenceModuleMock.EXPECT().NewReadContext(gomock.Any()).Return(readCtxMock, nil).AnyTimes() - readCtxMock.EXPECT().Release().AnyTimes() - - persistenceModuleMock.EXPECT().GetBus().Return(busMock).AnyTimes() - persistenceModuleMock.EXPECT().SetBus(busMock).AnyTimes() - persistenceModuleMock.EXPECT().GetModuleName().Return(modules.PersistenceModuleName).AnyTimes() - busMock.RegisterModule(persistenceModuleMock) - - return persistenceModuleMock + return mockBuses } +// TODO_THIS_COMMIT: refactor // Telemetry mock - Needed to help with proper counts for number of expected network writes func prepareTelemetryMock(t *testing.T, busMock *mockModules.MockBus, valId string, wg *sync.WaitGroup, expectedNumNetworkWrites int) *mockModules.MockTelemetryModule { ctrl := gomock.NewController(t) telemetryMock := mockModules.NewMockTelemetryModule(ctrl) - timeSeriesAgentMock := prepareNoopTimeSeriesAgentMock(t) - eventMetricsAgentMock := prepareEventMetricsAgentMock(t, valId, wg, expectedNumNetworkWrites) + timeSeriesAgentMock := telemetry_testutil.BaseTimeSeriesAgentMock(t) + // TODO_THIS_COMMIT: refactor + eventMetricsAgentMock := telemetry_testutil.PrepareEventMetricsAgentMock(t, valId, wg, expectedNumNetworkWrites) telemetryMock.EXPECT().GetTimeSeriesAgent().Return(timeSeriesAgentMock).AnyTimes() telemetryMock.EXPECT().GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() @@ -286,29 +150,3 @@ func prepareTelemetryMock(t *testing.T, busMock *mockModules.MockBus, valId stri return telemetryMock } - -// Noop mock - no specific business logic to tend to in the timeseries agent mock -func prepareNoopTimeSeriesAgentMock(t *testing.T) *mockModules.MockTimeSeriesAgent { - ctrl := gomock.NewController(t) - timeseriesAgentMock := mockModules.NewMockTimeSeriesAgent(ctrl) - - timeseriesAgentMock.EXPECT().CounterRegister(gomock.Any(), gomock.Any()).AnyTimes() - timeseriesAgentMock.EXPECT().CounterIncrement(gomock.Any()).AnyTimes() - - return timeseriesAgentMock -} - -// Events metric mock - Needed to help with proper counts for number of expected network writes -func prepareEventMetricsAgentMock(t *testing.T, valId string, wg *sync.WaitGroup, expectedNumNetworkWrites int) *mockModules.MockEventMetricsAgent { - ctrl := gomock.NewController(t) - eventMetricsAgentMock := mockModules.NewMockEventMetricsAgent(ctrl) - - eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Eq(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).Do(func(n, e any, l ...any) { - log.Printf("[valId: %s] Write\n", valId) - wg.Done() - }).Times(expectedNumNetworkWrites) - eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Not(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).AnyTimes() - - return eventMetricsAgentMock -} From 1b4ea5d44f8814de93d1540a24d1ca17c59acb81 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 11 May 2023 09:37:30 +0200 Subject: [PATCH 02/82] wip: checkpoint --- .../testutil/constructors/constructors.go | 40 +++++-------------- p2p/module_raintree_test.go | 14 +++---- p2p/module_test.go | 18 +++++---- p2p/testutil.go | 6 +++ p2p/utils_test.go | 11 +++-- 5 files changed, 39 insertions(+), 50 deletions(-) create mode 100644 p2p/testutil.go diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 6de6b7910..50b049884 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -1,26 +1,22 @@ package constructors import ( - "net" - - "github.com/golang/mock/gomock" libp2pHost "github.com/libp2p/go-libp2p/core/host" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" + runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" "github.com/pokt-network/pocket/internal/testutil" p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" "github.com/pokt-network/pocket/p2p" - "github.com/pokt-network/pocket/runtime/configs" - "github.com/pokt-network/pocket/runtime/configs/types" "github.com/pokt-network/pocket/runtime/genesis" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/pocket/shared/modules" mock_modules "github.com/pokt-network/pocket/shared/modules/mocks" ) -type peerIDString = string +type serviceURLStr = string // NewP2PMocknetModules returns a map of peer IDs to P2PModules using libp2p mocknet hosts. func NewP2PModulesAndMocknet( @@ -29,8 +25,8 @@ func NewP2PModulesAndMocknet( genesisState *genesis.GenesisState, busEventHandlerFactory testutil.BusEventHandlerFactory, ) ( - buses map[peerIDString]modules.Bus, - p2pModules map[peerIDString]modules.P2PModule, + buses map[serviceURLStr]*mock_modules.MockBus, + p2pModules map[serviceURLStr]modules.P2PModule, libp2pNetworkMock mocknet.Mocknet, ) { libp2pNetworkMock = mocknet.New() @@ -47,13 +43,12 @@ func NewP2PModulesAndMocknet( // CONSIDERATION: using an iterator/generator would prevent unintentional // serviceURL collisions serviceURLs := p2p_testutil.SequentialServiceURLs(t, count) - peerIDs := p2p_testutil.SetupMockNetPeers(t, libp2pNetworkMock, privKeys, serviceURLs) + _ = p2p_testutil.SetupMockNetPeers(t, libp2pNetworkMock, privKeys, serviceURLs) - for i, peerID := range peerIDs { + for i, serviceURL := range serviceURLs { // TECHDEBT: refactor host := libp2pNetworkMock.Hosts()[i] - peerIDStr := peerID.String() - buses[peerIDStr], p2pModules[peerIDStr] = NewP2PModuleWithHost( + buses[serviceURL], p2pModules[serviceURL] = NewP2PModuleWithHost( t, privKeys[i], serviceURLs[i], host, @@ -72,27 +67,10 @@ func NewP2PModuleWithHost( host libp2pHost.Host, genesisState *genesis.GenesisState, busEventHandlerFactory testutil.BusEventHandlerFactory, -) (modules.Bus, modules.P2PModule) { +) (*mock_modules.MockBus, modules.P2PModule) { t.Helper() - hostname, _, err := net.SplitHostPort(serviceURL) - require.NoError(t, err) - - // TODO_THIS_COMMIT: refactor to `BaseNodeMocks` or something - ctrl := gomock.NewController(t) - runtimeMgrMock := mock_modules.NewMockRuntimeMgr(ctrl) - runtimeMgrMock.EXPECT().GetConfig().Return(&configs.Config{ - P2P: &configs.P2PConfig{ - PrivateKey: privKey.String(), - Hostname: hostname, - //Port: 0, - ConnectionType: types.ConnectionType_TCPConnection, - MaxNonces: 100, - //IsClientOnly: false, - //BootstrapNodesCsv: "", - }, - }).AnyTimes() - + runtimeMgrMock := runtime_testutil.BaseRuntimeManagerMock(t, privKey, serviceURL, genesisState) busMock := testutil.BusMockWithEventHandler(t, runtimeMgrMock, busEventHandlerFactory) busMock.EXPECT().GetRuntimeMgr().Return(runtimeMgrMock).AnyTimes() diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index 085eacb65..4c53a6f79 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -259,7 +259,7 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te // Create connection and bus mocks along with a shared WaitGroup to track the number of expected // reads and writes throughout the mocked local network - for i, valId := range valIds { + for _, valId := range valIds { expectedCall := networkSimulationConfig[valId] expectedReads := expectedCall.numNetworkReads expectedWrites := expectedCall.numNetworkWrites @@ -269,13 +269,13 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te wg.Add(expectedReads) wg.Add(expectedWrites) - persistenceMock := persistence_testutil.BasePersistenceMock(t, busMocks[i], genesisMock) - consensusMock := consensus_testutil.PrepareConsensusMock(t, busMocks[i]) - telemetryMock := prepareTelemetryMock(t, busMocks[i], valId, &wg, expectedWrites) + persistenceMock := persistence_testutil.BasePersistenceMock(t, busMocks[valId], genesisMock) + consensusMock := consensus_testutil.PrepareConsensusMock(t, busMocks[valId]) + telemetryMock := prepareTelemetryMock(t, busMocks[valId], valId, &wg, expectedWrites) - busMocks[i].EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() - busMocks[i].EXPECT().GetConsensusModule().Return(consensusMock).AnyTimes() - busMocks[i].EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() + busMocks[valId].EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() + busMocks[valId].EXPECT().GetConsensusModule().Return(consensusMock).AnyTimes() + busMocks[valId].EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() } // Inject the connection and bus mocks into the P2P modules diff --git a/p2p/module_test.go b/p2p/module_test.go index 85e6e858a..69bedb1a9 100644 --- a/p2p/module_test.go +++ b/p2p/module_test.go @@ -1,12 +1,7 @@ -package p2p_test +package p2p import ( "fmt" - "github.com/pokt-network/pocket/internal/testutil" - p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" - "github.com/pokt-network/pocket/internal/testutil/persistence" - "github.com/pokt-network/pocket/internal/testutil/runtime" - "github.com/pokt-network/pocket/p2p" "strings" "testing" @@ -14,6 +9,12 @@ import ( libp2pCrypto "github.com/libp2p/go-libp2p/core/crypto" libp2pHost "github.com/libp2p/go-libp2p/core/host" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" + "github.com/stretchr/testify/require" + + "github.com/pokt-network/pocket/internal/testutil" + p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" + "github.com/pokt-network/pocket/internal/testutil/persistence" + "github.com/pokt-network/pocket/internal/testutil/runtime" typesP2P "github.com/pokt-network/pocket/p2p/types" "github.com/pokt-network/pocket/p2p/utils" "github.com/pokt-network/pocket/runtime/configs" @@ -21,7 +22,6 @@ import ( cryptoPocket "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/pocket/shared/modules" mockModules "github.com/pokt-network/pocket/shared/modules/mocks" - "github.com/stretchr/testify/require" ) // TECHDEBT(#609): move & de-dup. @@ -112,6 +112,8 @@ func Test_Create_configureBootstrapNodes(t *testing.T) { }, } + keys := testutil.LoadLocalnetPrivateKeys(t, 10) + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) @@ -143,7 +145,7 @@ func Test_Create_configureBootstrapNodes(t *testing.T) { } host := newLibp2pMockNetHost(t, privKey, peer) - p2pMod, err := p2p.Create(mockBus, p2p.WithHostOption(host)) + p2pMod, err := Create(mockBus, WithHostOption(host)) if (err != nil) != tt.wantErr { t.Errorf("p2pModule.Create() error = %v, wantErr %v", err, tt.wantErr) } diff --git a/p2p/testutil.go b/p2p/testutil.go new file mode 100644 index 000000000..a030d8682 --- /dev/null +++ b/p2p/testutil.go @@ -0,0 +1,6 @@ +//go:build test + +package p2p + +// P2PModule exports the `p2pModule` type for use in tests +type P2PModule = p2pModule diff --git a/p2p/utils_test.go b/p2p/utils_test.go index 706377dc7..7de0f6429 100644 --- a/p2p/utils_test.go +++ b/p2p/utils_test.go @@ -1,9 +1,12 @@ +//go:build test + package p2p_test import ( "github.com/pokt-network/pocket/internal/testutil" "github.com/pokt-network/pocket/internal/testutil/runtime" "github.com/pokt-network/pocket/internal/testutil/telemetry" + "github.com/pokt-network/pocket/p2p" "github.com/pokt-network/pocket/shared/messaging" "github.com/regen-network/gocuke" "sort" @@ -84,18 +87,18 @@ func waitForNetworkSimulationCompletion(t *testing.T, wg *sync.WaitGroup) { // ~~~~~~ RainTree Unit Test Mocks ~~~~~~ // createP2PModules returns a map of configured p2pModules keyed by an incremental naming convention (eg: `val_1`, `val_2`, etc.) -func createP2PModules(t *testing.T, busMocks []*mockModules.MockBus, netMock mocknet.Mocknet, serviceURLs []string) (p2pModules map[string]*p2pModule) { +func createP2PModules(t *testing.T, busMocks []*mockModules.MockBus, netMock mocknet.Mocknet, serviceURLs []string) (p2pModules map[string]*p2p.P2PModule) { t.Helper() require.GreaterOrEqualf(t, len(serviceURLs), len(busMocks), "number of bus mocks must be less than or equal to the number of service URLs") peerIDs := p2p_testutil.SetupMockNetPeers(t, netMock, keys[:len(busMocks)], serviceURLs) - p2pModules = make(map[string]*p2pModule, len(busMocks)) + p2pModules = make(map[string]*p2p.P2PModule, len(busMocks)) for i := range busMocks { host := netMock.Host(peerIDs[i]) - p2pMod, err := Create(busMocks[i], WithHostOption(host)) + p2pMod, err := p2p.Create(busMocks[i], p2p.WithHostOption(host)) require.NoError(t, err) - p2pModules[serviceURLs[i]] = p2pMod.(*p2pModule) + p2pModules[serviceURLs[i]] = p2pMod.(*p2p.P2PModule) } return } From 906452dd13bbe4e20216c8406c43d13f1c6c28b7 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:01:20 +0200 Subject: [PATCH 03/82] wip: testutils --- .../testutil/constructors/constructors.go | 1 + internal/testutil/keys.go | 3 ++- internal/testutil/keys_test.go | 23 +++++++++++++++++++ internal/testutil/p2p/network.go | 2 ++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 internal/testutil/keys_test.go diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 50b049884..699da383d 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -47,6 +47,7 @@ func NewP2PModulesAndMocknet( for i, serviceURL := range serviceURLs { // TECHDEBT: refactor + t.Logf("hosts: %v", libp2pNetworkMock.Hosts()) host := libp2pNetworkMock.Hosts()[i] buses[serviceURL], p2pModules[serviceURL] = NewP2PModuleWithHost( t, privKeys[i], diff --git a/internal/testutil/keys.go b/internal/testutil/keys.go index c9de34064..5c621fe11 100644 --- a/internal/testutil/keys.go +++ b/internal/testutil/keys.go @@ -29,7 +29,7 @@ func LoadLocalnetPrivateKeys(t require.TestingT, keyCount int) (privKeys []crypt scanner := bufio.NewScanner(privKeyManifest) scanner.Split(bufio.ScanLines) - for i, done := 0, false; i < keyCount || !done; i++ { + for i, done := 0, false; i < keyCount && !done; { done = !scanner.Scan() line := scanner.Text() matches := privKeyManifestKeyRegex.FindStringSubmatch(line) @@ -38,6 +38,7 @@ func LoadLocalnetPrivateKeys(t require.TestingT, keyCount int) (privKeys []crypt require.NoError(t, err) privKeys = append(privKeys, privKey) + i++ } } return privKeys diff --git a/internal/testutil/keys_test.go b/internal/testutil/keys_test.go new file mode 100644 index 000000000..fc423fb5e --- /dev/null +++ b/internal/testutil/keys_test.go @@ -0,0 +1,23 @@ +package testutil_test + +import ( + "testing" + + "github.com/pokt-network/pocket/internal/testutil" + "github.com/stretchr/testify/require" +) + +func TestLoadLocalnetPrivateKeys(t *testing.T) { + keyCount := 1000 + privKeys := testutil.LoadLocalnetPrivateKeys(t, keyCount) + + require.Lenf(t, privKeys, keyCount, "expected %d private keys; got %d", keyCount, len(privKeys)) + + // ensure each key is unique + seen := make(map[string]struct{}) + for _, privKey := range privKeys { + seen[privKey.String()] = struct{}{} + } + + require.Lenf(t, seen, keyCount, "expected %d unique private keys; got %d", keyCount, len(seen)) +} diff --git a/internal/testutil/p2p/network.go b/internal/testutil/p2p/network.go index 6ae35b1b9..67cd69d05 100644 --- a/internal/testutil/p2p/network.go +++ b/internal/testutil/p2p/network.go @@ -35,6 +35,8 @@ func SetupMockNetPeers( ) (peerIDs []peer.ID) { t.Helper() + // TODO_THIS_COMMIT: return these + // // MUST add mockdns before any libp2p host comes online. Otherwise, it will // error while attempting to resolve its own hostname. _, dnsSrvDone := testutil.PrepareDNSMockFromServiceURLs(t, serviceURLs) From 06ef34ecf4bab1904b330ab26afa83f07c7fa445 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 11 May 2023 14:39:14 +0200 Subject: [PATCH 04/82] wip: checkpoint --- .../testutil/constructors/constructors.go | 79 +++++++++++++++---- internal/testutil/mockdns.go | 48 ++++++----- internal/testutil/p2p/network.go | 31 +++++++- p2p/module_raintree_test.go | 2 +- p2p/raintree/peers_manager_test.go | 2 +- p2p/transport_encryption_test.go | 5 +- 6 files changed, 126 insertions(+), 41 deletions(-) diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 699da383d..11d468b12 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -3,12 +3,12 @@ package constructors import ( libp2pHost "github.com/libp2p/go-libp2p/core/host" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" - runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" "github.com/pokt-network/pocket/internal/testutil" p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" + runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" "github.com/pokt-network/pocket/p2p" "github.com/pokt-network/pocket/runtime/genesis" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" @@ -19,16 +19,20 @@ import ( type serviceURLStr = string // NewP2PMocknetModules returns a map of peer IDs to P2PModules using libp2p mocknet hosts. -func NewP2PModulesAndMocknet( +func NewBusesMocknetAndP2PModules( t gocuke.TestingT, count int, genesisState *genesis.GenesisState, busEventHandlerFactory testutil.BusEventHandlerFactory, ) ( buses map[serviceURLStr]*mock_modules.MockBus, - p2pModules map[serviceURLStr]modules.P2PModule, libp2pNetworkMock mocknet.Mocknet, + p2pModules map[serviceURLStr]modules.P2PModule, ) { + // TODO_THIS_COMMIT: refafactor + dnsSrv, dnsDone := testutil.MinimalDNSMock(t) + t.Cleanup(dnsDone) + libp2pNetworkMock = mocknet.New() // destroy mocknet on test cleanup t.Cleanup(func() { @@ -39,29 +43,50 @@ func NewP2PModulesAndMocknet( // CONSIDERATION: using an iterator/generator would prevent unintentional // ID collisions privKeys := testutil.LoadLocalnetPrivateKeys(t, count) + serviceURLs := p2p_testutil.SequentialServiceURLs(t, count) + for i, serviceURL := range serviceURLs { + if len(privKeys) <= i { + t.Logf("WARNING: not enough private keys for %d service URLs", len(serviceURLs)) + break + } + + privKey := privKeys[i] + busMock := NewBus(t, privKey, serviceURL, genesisState, busEventHandlerFactory) + host := p2p_testutil.NewMocknetHost(t, libp2pNetworkMock, privKey) + + // MUST register DNS before instantiating P2PModule + testutil.AddServiceURLZone(t, dnsSrv, serviceURL) + p2pModules[serviceURL] = NewP2PModuleWithHost(t, busMock, host) + } + return buses, libp2pNetworkMock, p2pModules +} + +// TODO_THIS_TEST: need this? +func NewP2PModules( + t gocuke.TestingT, + privKeys []cryptoPocket.PrivateKey, + busMock *mock_modules.MockBus, + libp2pNetworkMock mocknet.Mocknet, +) ( + p2pModules map[serviceURLStr]modules.P2PModule, +) { // CONSIDERATION: using an iterator/generator would prevent unintentional // serviceURL collisions - serviceURLs := p2p_testutil.SequentialServiceURLs(t, count) + serviceURLs := p2p_testutil.SequentialServiceURLs(t, len(privKeys)) _ = p2p_testutil.SetupMockNetPeers(t, libp2pNetworkMock, privKeys, serviceURLs) for i, serviceURL := range serviceURLs { - // TECHDEBT: refactor - t.Logf("hosts: %v", libp2pNetworkMock.Hosts()) host := libp2pNetworkMock.Hosts()[i] - buses[serviceURL], p2pModules[serviceURL] = NewP2PModuleWithHost( - t, privKeys[i], - serviceURLs[i], - host, - genesisState, - busEventHandlerFactory, - ) + // TECHDEBT: refactor + p2pModules[serviceURL] = NewP2PModuleWithHost(t, busMock, host) } - return buses, p2pModules, libp2pNetworkMock + return p2pModules } +// TODO_THIS_TEST: need this? // TODO_THIS_COMMIT: consider following create factory convention (?) -func NewP2PModuleWithHost( +func NewBusesAndP2PModuleWithHost( t gocuke.TestingT, privKey cryptoPocket.PrivateKey, serviceURL string, @@ -71,12 +96,34 @@ func NewP2PModuleWithHost( ) (*mock_modules.MockBus, modules.P2PModule) { t.Helper() + busMock := NewBus(t, privKey, serviceURL, genesisState, busEventHandlerFactory) + return busMock, NewP2PModuleWithHost(t, busMock, host) +} + +func NewBus( + t gocuke.TestingT, + privKey cryptoPocket.PrivateKey, + serviceURL string, + genesisState *genesis.GenesisState, + busEventHandlerFactory testutil.BusEventHandlerFactory, +) *mock_modules.MockBus { + t.Helper() + runtimeMgrMock := runtime_testutil.BaseRuntimeManagerMock(t, privKey, serviceURL, genesisState) busMock := testutil.BusMockWithEventHandler(t, runtimeMgrMock, busEventHandlerFactory) busMock.EXPECT().GetRuntimeMgr().Return(runtimeMgrMock).AnyTimes() + return busMock +} + +func NewP2PModuleWithHost( + t gocuke.TestingT, + busMock *mock_modules.MockBus, + host libp2pHost.Host, +) modules.P2PModule { + t.Helper() mod, err := p2p.Create(busMock, p2p.WithHostOption(host)) require.NoError(t, err) - return busMock, mod.(modules.P2PModule) + return mod.(modules.P2PModule) } diff --git a/internal/testutil/mockdns.go b/internal/testutil/mockdns.go index 019a97860..4d8cc674e 100644 --- a/internal/testutil/mockdns.go +++ b/internal/testutil/mockdns.go @@ -2,41 +2,51 @@ package testutil import ( "fmt" + "net" + "github.com/foxcpp/go-mockdns" "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" - "net" - "net/url" ) -func PrepareDNSMockFromServiceURLs(t gocuke.TestingT, serviceURLs []string) (srv *mockdns.Server, done func()) { +func DNSMockFromServiceURLs(t gocuke.TestingT, serviceURLs []string) (srv *mockdns.Server, done func()) { t.Helper() - zones := make(map[string]mockdns.Zone) - for i, u := range serviceURLs { - // Perpend `scheme://` as serviceURLs are currently scheme-less. - // Required for parsing to produce useful results. - // (see: https://pkg.go.dev/net/url@go1.20.2#URL) - serviceURL, err := url.Parse(fmt.Sprintf("scheme://%s", u)) - require.NoError(t, err) + srv, done = MinimalDNSMock(t) + for _, serviceURL := range serviceURLs { + AddServiceURLZone(t, srv, serviceURL) + } + return srv, done +} - ipStr := fmt.Sprintf("10.0.0.%d", i+1) +func AddServiceURLZone(t gocuke.TestingT, srv *mockdns.Server, serviceURL string) { + t.Helper() - if i >= 254 { - panic(fmt.Sprintf("would generate invalid IPv4 address: %s", ipStr)) - } + // TODO_THIS_COMMIT: move & de-dup + hostname, _, err := net.SplitHostPort(serviceURL) + require.NoError(t, err) - zones[fmt.Sprintf("%s.", serviceURL.Hostname())] = mockdns.Zone{ - A: []string{ipStr}, - } + zone := mockdns.Zone{ + A: []string{"10.0.0.1"}, } - return PrepareDNSMock(t, zones) + err = srv.AddZone(fmt.Sprintf("%s.", hostname), zone) + require.NoError(t, err) } -func PrepareDNSMock(t gocuke.TestingT, zones map[string]mockdns.Zone) (srv *mockdns.Server, done func()) { +func MinimalDNSMock(t gocuke.TestingT) (srv *mockdns.Server, done func()) { t.Helper() + return BaseDNSMock(t, nil) +} + +func BaseDNSMock(t gocuke.TestingT, zones map[string]mockdns.Zone) (srv *mockdns.Server, done func()) { + t.Helper() + + if zones == nil { + zones = make(map[string]mockdns.Zone) + } + srv, _ = mockdns.NewServerWithLogger(zones, noopLogger{}, false) srv.PatchNet(net.DefaultResolver) return srv, func() { diff --git a/internal/testutil/p2p/network.go b/internal/testutil/p2p/network.go index 67cd69d05..1e4525046 100644 --- a/internal/testutil/p2p/network.go +++ b/internal/testutil/p2p/network.go @@ -3,9 +3,11 @@ package p2p_testutil import ( "fmt" "github.com/libp2p/go-libp2p/core/crypto" + libp2pHost "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" libp2pPeer "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/net/mock" + "github.com/multiformats/go-multiaddr" "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" @@ -15,6 +17,7 @@ import ( cryptoPocket "github.com/pokt-network/pocket/shared/crypto" ) +// TODO: remove if not needed func NewMocknetWithNPeers(t gocuke.TestingT, peerCount int) (mocknet.Mocknet, []string) { t.Helper() @@ -27,6 +30,25 @@ func NewMocknetWithNPeers(t gocuke.TestingT, peerCount int) (mocknet.Mocknet, [] return libp2pNetworkMock, serviceURLs } +func NewMocknetHost( + t gocuke.TestingT, + netMock mocknet.Mocknet, + privKey cryptoPocket.PrivateKey, +) libp2pHost.Host { + t.Helper() + + addrMock, err := multiaddr.NewMultiaddr("/ip4/10.0.0.1/tcp/0") + require.NoError(t, err) + + libp2pPrivKey, err := crypto.UnmarshalEd25519PrivateKey(privKey.Bytes()) + require.NoError(t, err) + + host, err := netMock.AddPeer(libp2pPrivKey, addrMock) + require.NoError(t, err) + + return host +} + func SetupMockNetPeers( t gocuke.TestingT, netMock mocknet.Mocknet, @@ -39,7 +61,7 @@ func SetupMockNetPeers( // // MUST add mockdns before any libp2p host comes online. Otherwise, it will // error while attempting to resolve its own hostname. - _, dnsSrvDone := testutil.PrepareDNSMockFromServiceURLs(t, serviceURLs) + _, dnsSrvDone := testutil.DNSMockFromServiceURLs(t, serviceURLs) t.Cleanup(dnsSrvDone) // Add a libp2p peers/hosts to the `MockNet` with the keypairs corresponding @@ -48,6 +70,7 @@ func SetupMockNetPeers( libp2pPrivKey, err := crypto.UnmarshalEd25519PrivateKey(privKeys[i].Bytes()) require.NoError(t, err) + // TODO_THIS_COMMIT: add mock DNS zone per peer instead of all at once _, err = netMock.AddPeer(libp2pPrivKey, peerInfo.Addrs[0]) require.NoError(t, err) @@ -92,7 +115,11 @@ func PeersFromPrivKeysAndServiceURLs( return peersInfo } -func peerFromPrivKeyAndServiceURL(t gocuke.TestingT, privKey cryptoPocket.PrivateKey, serviceURL string) libp2pPeer.AddrInfo { +func peerFromPrivKeyAndServiceURL( + t gocuke.TestingT, + privKey cryptoPocket.PrivateKey, + serviceURL string, +) libp2pPeer.AddrInfo { t.Helper() peerInfo, err := utils.Libp2pAddrInfoFromPeer(&types.NetworkPeer{ diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index 4c53a6f79..63ece3122 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -238,7 +238,7 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te } } - busMocks, p2pModules, _ := constructors.NewP2PModulesAndMocknet( + busMocks, _, p2pModules := constructors.NewBusesMocknetAndP2PModules( t, numValidators, genesisMock, busEventHandlerFactory, diff --git a/p2p/raintree/peers_manager_test.go b/p2p/raintree/peers_manager_test.go index 742080088..79e0f197c 100644 --- a/p2p/raintree/peers_manager_test.go +++ b/p2p/raintree/peers_manager_test.go @@ -356,6 +356,6 @@ func mockAlphabetValidatorServiceURLsDNS(t *testing.T) (done func()) { } } - _, done = testutil.PrepareDNSMock(t, zones) + _, done = testutil.BaseDNSMock(t, zones) return done } diff --git a/p2p/transport_encryption_test.go b/p2p/transport_encryption_test.go index 407f18e09..7b46d4d0d 100644 --- a/p2p/transport_encryption_test.go +++ b/p2p/transport_encryption_test.go @@ -1,4 +1,4 @@ -package p2p +package p2p_test import ( "context" @@ -8,6 +8,7 @@ import ( "github.com/pokt-network/pocket/internal/testutil/persistence" "github.com/pokt-network/pocket/internal/testutil/runtime" "github.com/pokt-network/pocket/internal/testutil/telemetry" + "github.com/pokt-network/pocket/p2p" "testing" "time" @@ -67,7 +68,7 @@ func TestP2pModule_Insecure_Error(t *testing.T) { dnsDone := testutil.PrepareDNSMockFromServiceURLs(t, serviceURLs) t.Cleanup(dnsDone) - p2pMod, err := Create(busMock) + p2pMod, err := p2p.Create(busMock) require.NoError(t, err) err = p2pMod.Start() From e7f057bae1afb1006932f26b2fb3e59058a51cd8 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 11 May 2023 22:33:08 +0200 Subject: [PATCH 05/82] wip: checkpoint --- internal/testutil/consensus/mocks.go | 5 +- .../testutil/constructors/constructors.go | 14 +++- internal/testutil/p2p/network.go | 1 + internal/testutil/runtime/genesis.go | 2 +- .../testutil/telemetry/event_metrics_agent.go | 19 ++++++ internal/testutil/telemetry/mocks.go | 15 +++-- p2p/module_raintree_test.go | 66 ++++++++++--------- p2p/transport_encryption_test.go | 4 +- p2p/utils_test.go | 28 ++++---- 9 files changed, 97 insertions(+), 57 deletions(-) diff --git a/internal/testutil/consensus/mocks.go b/internal/testutil/consensus/mocks.go index 64888cfcb..660bb0a38 100644 --- a/internal/testutil/consensus/mocks.go +++ b/internal/testutil/consensus/mocks.go @@ -1,16 +1,15 @@ package consensus_testutil import ( - "testing" - "github.com/golang/mock/gomock" + "github.com/regen-network/gocuke" "github.com/pokt-network/pocket/shared/modules" "github.com/pokt-network/pocket/shared/modules/mocks" ) // Consensus mock - only needed for validatorMap access -func PrepareConsensusMock(t *testing.T, busMock *mock_modules.MockBus) *mock_modules.MockConsensusModule { +func PrepareConsensusMock(t gocuke.TestingT, busMock *mock_modules.MockBus) *mock_modules.MockConsensusModule { ctrl := gomock.NewController(t) consensusMock := mock_modules.NewMockConsensusModule(ctrl) consensusMock.EXPECT().CurrentHeight().Return(uint64(1)).AnyTimes() diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 11d468b12..63975c874 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -3,6 +3,7 @@ package constructors import ( libp2pHost "github.com/libp2p/go-libp2p/core/host" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" + consensus_testutil "github.com/pokt-network/pocket/internal/testutil/consensus" "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" @@ -29,7 +30,7 @@ func NewBusesMocknetAndP2PModules( libp2pNetworkMock mocknet.Mocknet, p2pModules map[serviceURLStr]modules.P2PModule, ) { - // TODO_THIS_COMMIT: refafactor + // TODO_THIS_COMMIT: refactor dnsSrv, dnsDone := testutil.MinimalDNSMock(t) t.Cleanup(dnsDone) @@ -40,9 +41,13 @@ func NewBusesMocknetAndP2PModules( require.NoError(t, err) }) + buses = make(map[serviceURLStr]*mock_modules.MockBus) + p2pModules = make(map[serviceURLStr]modules.P2PModule) // CONSIDERATION: using an iterator/generator would prevent unintentional // ID collisions privKeys := testutil.LoadLocalnetPrivateKeys(t, count) + // CONSIDERATION: using an iterator/generator would prevent unintentional + // serviceURL collisions serviceURLs := p2p_testutil.SequentialServiceURLs(t, count) for i, serviceURL := range serviceURLs { if len(privKeys) <= i { @@ -52,11 +57,16 @@ func NewBusesMocknetAndP2PModules( privKey := privKeys[i] busMock := NewBus(t, privKey, serviceURL, genesisState, busEventHandlerFactory) - host := p2p_testutil.NewMocknetHost(t, libp2pNetworkMock, privKey) + buses[serviceURL] = busMock + + // TODO_THIS_COMMIT: refactor + consensusMock := consensus_testutil.PrepareConsensusMock(t, busMock) + busMock.EXPECT().GetConsensusModule().Return(consensusMock).AnyTimes() // MUST register DNS before instantiating P2PModule testutil.AddServiceURLZone(t, dnsSrv, serviceURL) + host := p2p_testutil.NewMocknetHost(t, libp2pNetworkMock, privKey) p2pModules[serviceURL] = NewP2PModuleWithHost(t, busMock, host) } return buses, libp2pNetworkMock, p2pModules diff --git a/internal/testutil/p2p/network.go b/internal/testutil/p2p/network.go index 1e4525046..5b1177525 100644 --- a/internal/testutil/p2p/network.go +++ b/internal/testutil/p2p/network.go @@ -37,6 +37,7 @@ func NewMocknetHost( ) libp2pHost.Host { t.Helper() + // TODO_THIS_COMMIT: move to const addrMock, err := multiaddr.NewMultiaddr("/ip4/10.0.0.1/tcp/0") require.NoError(t, err) diff --git a/internal/testutil/runtime/genesis.go b/internal/testutil/runtime/genesis.go index b1d30deb5..e7ea512ae 100644 --- a/internal/testutil/runtime/genesis.go +++ b/internal/testutil/runtime/genesis.go @@ -38,7 +38,7 @@ func GenesisWithSequentialServiceURLs(t gocuke.TestingT, valKeys []cryptoPocket. serviceURLs := make([]string, len(valKeys)) for i := range valKeys { - serviceURLs[i] = p2p_testutil.NewServiceURL(i) + serviceURLs[i] = p2p_testutil.NewServiceURL(i + 1) } return BaseGenesisStateMock(t, valKeys, serviceURLs) } diff --git a/internal/testutil/telemetry/event_metrics_agent.go b/internal/testutil/telemetry/event_metrics_agent.go index f100c994a..69c5d7035 100644 --- a/internal/testutil/telemetry/event_metrics_agent.go +++ b/internal/testutil/telemetry/event_metrics_agent.go @@ -36,3 +36,22 @@ func PrepareEventMetricsAgentMock(t *testing.T, valId string, wg *sync.WaitGroup return eventMetricsAgentMock } + +func EventMetricsAgentMockWithHandler( + t gocuke.TestingT, + label string, + // TODO_THIS_COMMIT: consider refactoring as a type + handler func(namespace, event_name string, labels ...any), + times int, +) *mock_modules.MockEventMetricsAgent { + ctrl := gomock.NewController(t) + eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) + + // TODO_THIS_COMMIT: scrutinize these & their order + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Eq(label), gomock.Any()).Do(handler).Times(times) + // TODO_THIS_COMMIT: is this really needed? + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Not(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).AnyTimes() + + return eventMetricsAgentMock +} diff --git a/internal/testutil/telemetry/mocks.go b/internal/testutil/telemetry/mocks.go index fa68c690b..e695a8343 100644 --- a/internal/testutil/telemetry/mocks.go +++ b/internal/testutil/telemetry/mocks.go @@ -8,7 +8,10 @@ import ( "github.com/pokt-network/pocket/shared/modules/mocks" ) -func MinimalTelemetryMock(t gocuke.TestingT) modules.TelemetryModule { +func MinimalTelemetryMock( + t gocuke.TestingT, + busMock *mock_modules.MockBus, +) modules.TelemetryModule { t.Helper() ctrl := gomock.NewController(t) @@ -18,15 +21,19 @@ func MinimalTelemetryMock(t gocuke.TestingT) modules.TelemetryModule { telemetryMock.EXPECT().SetBus(gomock.Any()).Return().AnyTimes() telemetryMock.EXPECT().GetModuleName().Return(modules.TelemetryModuleName).AnyTimes() + busMock.RegisterModule(telemetryMock) return telemetryMock } -func BaseTelemetryMock(t gocuke.TestingT) modules.TelemetryModule { +func BaseTelemetryMock( + t gocuke.TestingT, + busMock *mock_modules.MockBus, +) modules.TelemetryModule { t.Helper() - return WithTimeSeriesAgent(t, WithEventMetricsAgent(t, MinimalTelemetryMock(t))) + return WithTimeSeriesAgent(t, WithEventMetricsAgent(t, MinimalTelemetryMock(t, busMock))) } -func WithTimeSeriesAgent(t gocuke.TestingT, telemetryMod modules.TelemetryModule) modules.TelemetryModule { +func WithTimeSeriesAgent(t gocuke.TestingT, telemetryMod modules.TelemetryModule) *mock_modules.MockTelemetryModule { t.Helper() telemetryMock := telemetryMod.(*mock_modules.MockTelemetryModule) diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index 63ece3122..fad6f43ec 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -3,28 +3,26 @@ package p2p_test import ( - "github.com/pokt-network/pocket/internal/testutil" - "github.com/pokt-network/pocket/internal/testutil/constructors" - "github.com/pokt-network/pocket/internal/testutil/p2p" - "github.com/pokt-network/pocket/shared/messaging" - "github.com/pokt-network/pocket/shared/modules" - "github.com/regen-network/gocuke" "log" "os" "path/filepath" "regexp" - "sort" "strconv" "sync" "testing" + "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/anypb" - consensus_testutil "github.com/pokt-network/pocket/internal/testutil/consensus" + "github.com/pokt-network/pocket/internal/testutil" + "github.com/pokt-network/pocket/internal/testutil/constructors" + "github.com/pokt-network/pocket/internal/testutil/p2p" persistence_testutil "github.com/pokt-network/pocket/internal/testutil/persistence" "github.com/pokt-network/pocket/p2p/protocol" "github.com/pokt-network/pocket/p2p/raintree" + "github.com/pokt-network/pocket/shared/messaging" + "github.com/pokt-network/pocket/shared/modules" ) // TODO(#314): Add the tooling and instructions on how to generate unit tests in this file. @@ -232,6 +230,7 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te //busMocks := createMockBuses(t, runtimeConfigs, &wg) busEventHandlerFactory := func(t gocuke.TestingT, bus modules.Bus) testutil.BusEventHandler { return func(data *messaging.PocketEnvelope) { + // TODO: decide - is mutually exclusive w/ telemetry based wg.Done() below // `p2pModule#handleNetworkData()` calls `modules.Bus#PublishEventToBus()` // assumes that P2P module is the only event producer running during the test wg.Done() @@ -244,42 +243,47 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te busEventHandlerFactory, ) - valIds := make([]string, 0, numValidators) - for valId := range networkSimulationConfig { - valIds = append(valIds, valId) - } - - // TODO_THIS_COMMIT: need this? - // sort `valIds` in ascending order - sort.Slice(valIds, func(i, j int) bool { - iId := extractNumericId(valIds[i]) - jId := extractNumericId(valIds[j]) - return iId < jId - }) + //serviceURLs := make([]string, 0, numValidators) + //for valId := range networkSimulationConfig { + // serviceURLs = append(serviceURLs, valId) + //} + // + //// TODO_THIS_COMMIT: need this? + //// sort `serviceURLs` in ascending order + //sort.Slice(serviceURLs, func(i, j int) bool { + // iId := extractNumericId(serviceURLs[i]) + // jId := extractNumericId(serviceURLs[j]) + // return iId < jId + //}) // Create connection and bus mocks along with a shared WaitGroup to track the number of expected // reads and writes throughout the mocked local network - for _, valId := range valIds { - expectedCall := networkSimulationConfig[valId] + for serviceURL, busMock := range busMocks { + expectedCall := networkSimulationConfig[serviceURL] expectedReads := expectedCall.numNetworkReads expectedWrites := expectedCall.numNetworkWrites - log.Printf("[valId: %s] expected reads: %d\n", valId, expectedReads) - log.Printf("[valId: %s] expected writes: %d\n", valId, expectedWrites) + log.Printf("[serviceURL: %s] expected reads: %d\n", serviceURL, expectedReads) + log.Printf("[serviceURL: %s] expected writes: %d\n", serviceURL, expectedWrites) wg.Add(expectedReads) wg.Add(expectedWrites) - persistenceMock := persistence_testutil.BasePersistenceMock(t, busMocks[valId], genesisMock) - consensusMock := consensus_testutil.PrepareConsensusMock(t, busMocks[valId]) - telemetryMock := prepareTelemetryMock(t, busMocks[valId], valId, &wg, expectedWrites) + // TODO_THIS_COMMIT: + //if serviceURL == origNode { + // ... + //} + + persistenceMock := persistence_testutil.BasePersistenceMock(t, busMock, genesisMock) + //consensusMock := consensus_testutil.PrepareConsensusMock(t, busMocks[serviceURL]) + telemetryMock := prepareTelemetryMock(t, busMock, serviceURL, &wg, expectedWrites) - busMocks[valId].EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() - busMocks[valId].EXPECT().GetConsensusModule().Return(consensusMock).AnyTimes() - busMocks[valId].EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() + busMock.EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() + //busMocks[serviceURL].EXPECT().GetConsensusModule().Return(consensusMock).AnyTimes() + busMock.EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() } // Inject the connection and bus mocks into the P2P modules - //p2pModules := createP2PModules(t, busMocks, libp2pNetworkMock, valIds) + //p2pModules := createP2PModules(t, busMocks, libp2pNetworkMock, serviceURLs) for _, p2pMod := range p2pModules { err := p2pMod.Start() diff --git a/p2p/transport_encryption_test.go b/p2p/transport_encryption_test.go index 7b46d4d0d..a5dd7f236 100644 --- a/p2p/transport_encryption_test.go +++ b/p2p/transport_encryption_test.go @@ -49,10 +49,10 @@ func TestP2pModule_Insecure_Error(t *testing.T) { }, }).AnyTimes() - telemetryMock := telemetry_testutil.BaseTelemetryMock(t) - busMock := testutil.BaseBusMock(t, runtimeMgrMock) busMock.EXPECT().GetConsensusModule().Return(mockConsensusModule).AnyTimes() + + telemetryMock := telemetry_testutil.BaseTelemetryMock(t, busMock) busMock.EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() keys := testutil.LoadLocalnetPrivateKeys(t, 1) diff --git a/p2p/utils_test.go b/p2p/utils_test.go index 7de0f6429..bf225bc75 100644 --- a/p2p/utils_test.go +++ b/p2p/utils_test.go @@ -8,13 +8,13 @@ import ( "github.com/pokt-network/pocket/internal/testutil/telemetry" "github.com/pokt-network/pocket/p2p" "github.com/pokt-network/pocket/shared/messaging" + "github.com/pokt-network/pocket/telemetry" "github.com/regen-network/gocuke" "sort" "sync" "testing" "time" - "github.com/golang/mock/gomock" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" @@ -133,23 +133,23 @@ func createMockBuses(t *testing.T, runtimeMgrs []modules.RuntimeMgr, wg *sync.Wa return mockBuses } -// TODO_THIS_COMMIT: refactor +// TODO_THIS_COMMIT: refactor / move // Telemetry mock - Needed to help with proper counts for number of expected network writes func prepareTelemetryMock(t *testing.T, busMock *mockModules.MockBus, valId string, wg *sync.WaitGroup, expectedNumNetworkWrites int) *mockModules.MockTelemetryModule { - ctrl := gomock.NewController(t) - telemetryMock := mockModules.NewMockTelemetryModule(ctrl) + telemetryMock := telemetry_testutil.WithTimeSeriesAgent(t, + telemetry_testutil.MinimalTelemetryMock(t, busMock), + ) + + eventMetricsAgentMock := telemetry_testutil.EventMetricsAgentMockWithHandler( + t, telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL, + func(namesapce, event_name string, labels ...any) { + t.Logf("[valId: %s] Write\n", valId) + wg.Done() + }, + expectedNumNetworkWrites, + ) - timeSeriesAgentMock := telemetry_testutil.BaseTimeSeriesAgentMock(t) - // TODO_THIS_COMMIT: refactor - eventMetricsAgentMock := telemetry_testutil.PrepareEventMetricsAgentMock(t, valId, wg, expectedNumNetworkWrites) - - telemetryMock.EXPECT().GetTimeSeriesAgent().Return(timeSeriesAgentMock).AnyTimes() telemetryMock.EXPECT().GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() - - telemetryMock.EXPECT().GetModuleName().Return(modules.TelemetryModuleName).AnyTimes() - telemetryMock.EXPECT().GetBus().Return(busMock).AnyTimes() - telemetryMock.EXPECT().SetBus(busMock).AnyTimes() busMock.RegisterModule(telemetryMock) - return telemetryMock } From 70a0fc34dacc7db25663867bef9452b21f12fc3c Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:02:56 +0200 Subject: [PATCH 06/82] wip: testutils --- internal/testutil/bus.go | 2 +- internal/testutil/consensus/mocks.go | 5 +++-- .../testutil/constructors/constructors.go | 19 ++++++++++++----- internal/testutil/mockdns.go | 21 +++++++++++-------- internal/testutil/p2p/network.go | 10 ++++----- internal/testutil/persistence/mocks.go | 18 ++++++++-------- internal/testutil/runtime/genesis.go | 6 +++--- internal/testutil/telemetry/mocks.go | 3 ++- 8 files changed, 49 insertions(+), 35 deletions(-) diff --git a/internal/testutil/bus.go b/internal/testutil/bus.go index 6a0ea6102..972965eca 100644 --- a/internal/testutil/bus.go +++ b/internal/testutil/bus.go @@ -13,7 +13,7 @@ import ( ) type BusEventHandler func(*messaging.PocketEnvelope) -type BusEventHandlerFactory func(t gocuke.TestingT, bus modules.Bus) BusEventHandler +type BusEventHandlerFactory func(t gocuke.TestingT, busMock *mock_modules.MockBus) BusEventHandler // MinimalBusMock returns a bus mock with a module registry and minimal // expectations registered to maximize re-usability. diff --git a/internal/testutil/consensus/mocks.go b/internal/testutil/consensus/mocks.go index 660bb0a38..51bdc3338 100644 --- a/internal/testutil/consensus/mocks.go +++ b/internal/testutil/consensus/mocks.go @@ -9,7 +9,7 @@ import ( ) // Consensus mock - only needed for validatorMap access -func PrepareConsensusMock(t gocuke.TestingT, busMock *mock_modules.MockBus) *mock_modules.MockConsensusModule { +func BaseConsensusMock(t gocuke.TestingT, busMock *mock_modules.MockBus) *mock_modules.MockConsensusModule { ctrl := gomock.NewController(t) consensusMock := mock_modules.NewMockConsensusModule(ctrl) consensusMock.EXPECT().CurrentHeight().Return(uint64(1)).AnyTimes() @@ -17,7 +17,8 @@ func PrepareConsensusMock(t gocuke.TestingT, busMock *mock_modules.MockBus) *moc consensusMock.EXPECT().GetBus().Return(busMock).AnyTimes() consensusMock.EXPECT().SetBus(busMock).AnyTimes() consensusMock.EXPECT().GetModuleName().Return(modules.ConsensusModuleName).AnyTimes() - busMock.RegisterModule(consensusMock) + busMock.EXPECT().GetConsensusModule().Return(consensusMock).AnyTimes() + //busMock.RegisterModule(consensusMock) return consensusMock } diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 63975c874..9f78c7448 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -4,6 +4,8 @@ import ( libp2pHost "github.com/libp2p/go-libp2p/core/host" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" consensus_testutil "github.com/pokt-network/pocket/internal/testutil/consensus" + persistence_testutil "github.com/pokt-network/pocket/internal/testutil/persistence" + telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" @@ -31,8 +33,7 @@ func NewBusesMocknetAndP2PModules( p2pModules map[serviceURLStr]modules.P2PModule, ) { // TODO_THIS_COMMIT: refactor - dnsSrv, dnsDone := testutil.MinimalDNSMock(t) - t.Cleanup(dnsDone) + dnsSrv := testutil.MinimalDNSMock(t) libp2pNetworkMock = mocknet.New() // destroy mocknet on test cleanup @@ -60,8 +61,9 @@ func NewBusesMocknetAndP2PModules( buses[serviceURL] = busMock // TODO_THIS_COMMIT: refactor - consensusMock := consensus_testutil.PrepareConsensusMock(t, busMock) - busMock.EXPECT().GetConsensusModule().Return(consensusMock).AnyTimes() + _ = consensus_testutil.BaseConsensusMock(t, busMock) + _ = persistence_testutil.BasePersistenceMock(t, busMock, genesisState) + _ = telemetry_testutil.BaseTelemetryMock(t, busMock) // MUST register DNS before instantiating P2PModule testutil.AddServiceURLZone(t, dnsSrv, serviceURL) @@ -69,6 +71,9 @@ func NewBusesMocknetAndP2PModules( host := p2p_testutil.NewMocknetHost(t, libp2pNetworkMock, privKey) p2pModules[serviceURL] = NewP2PModuleWithHost(t, busMock, host) } + err := libp2pNetworkMock.LinkAll() + require.NoError(t, err) + return buses, libp2pNetworkMock, p2pModules } @@ -119,7 +124,11 @@ func NewBus( ) *mock_modules.MockBus { t.Helper() - runtimeMgrMock := runtime_testutil.BaseRuntimeManagerMock(t, privKey, serviceURL, genesisState) + runtimeMgrMock := runtime_testutil.BaseRuntimeManagerMock( + t, privKey, + serviceURL, + genesisState, + ) busMock := testutil.BusMockWithEventHandler(t, runtimeMgrMock, busEventHandlerFactory) busMock.EXPECT().GetRuntimeMgr().Return(runtimeMgrMock).AnyTimes() return busMock diff --git a/internal/testutil/mockdns.go b/internal/testutil/mockdns.go index 4d8cc674e..2adc4ab74 100644 --- a/internal/testutil/mockdns.go +++ b/internal/testutil/mockdns.go @@ -9,14 +9,14 @@ import ( "github.com/stretchr/testify/require" ) -func DNSMockFromServiceURLs(t gocuke.TestingT, serviceURLs []string) (srv *mockdns.Server, done func()) { +func DNSMockFromServiceURLs(t gocuke.TestingT, serviceURLs []string) *mockdns.Server { t.Helper() - srv, done = MinimalDNSMock(t) + srv := MinimalDNSMock(t) for _, serviceURL := range serviceURLs { AddServiceURLZone(t, srv, serviceURL) } - return srv, done + return srv } func AddServiceURLZone(t gocuke.TestingT, srv *mockdns.Server, serviceURL string) { @@ -34,25 +34,28 @@ func AddServiceURLZone(t gocuke.TestingT, srv *mockdns.Server, serviceURL string require.NoError(t, err) } -func MinimalDNSMock(t gocuke.TestingT) (srv *mockdns.Server, done func()) { +func MinimalDNSMock(t gocuke.TestingT) *mockdns.Server { t.Helper() return BaseDNSMock(t, nil) } -func BaseDNSMock(t gocuke.TestingT, zones map[string]mockdns.Zone) (srv *mockdns.Server, done func()) { +func BaseDNSMock(t gocuke.TestingT, zones map[string]mockdns.Zone) *mockdns.Server { t.Helper() if zones == nil { zones = make(map[string]mockdns.Zone) } - srv, _ = mockdns.NewServerWithLogger(zones, noopLogger{}, false) + srv, _ := mockdns.NewServerWithLogger(zones, noopLogger{}, false) srv.PatchNet(net.DefaultResolver) - return srv, func() { - _ = srv.Close() + t.Cleanup(func() { + err := srv.Close() + require.NoError(t, err) mockdns.UnpatchNet(net.DefaultResolver) - } + }) + + return srv } // NB: default logging behavior is too noisy. diff --git a/internal/testutil/p2p/network.go b/internal/testutil/p2p/network.go index 5b1177525..77fdb9edb 100644 --- a/internal/testutil/p2p/network.go +++ b/internal/testutil/p2p/network.go @@ -8,6 +8,7 @@ import ( libp2pPeer "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/multiformats/go-multiaddr" + "github.com/pokt-network/pocket/runtime/defaults" "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" @@ -32,19 +33,19 @@ func NewMocknetWithNPeers(t gocuke.TestingT, peerCount int) (mocknet.Mocknet, [] func NewMocknetHost( t gocuke.TestingT, - netMock mocknet.Mocknet, + libp2pNetworkMock mocknet.Mocknet, privKey cryptoPocket.PrivateKey, ) libp2pHost.Host { t.Helper() // TODO_THIS_COMMIT: move to const - addrMock, err := multiaddr.NewMultiaddr("/ip4/10.0.0.1/tcp/0") + addrMock, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/10.0.0.1/tcp/%d", defaults.DefaultP2PPort)) require.NoError(t, err) libp2pPrivKey, err := crypto.UnmarshalEd25519PrivateKey(privKey.Bytes()) require.NoError(t, err) - host, err := netMock.AddPeer(libp2pPrivKey, addrMock) + host, err := libp2pNetworkMock.AddPeer(libp2pPrivKey, addrMock) require.NoError(t, err) return host @@ -62,8 +63,7 @@ func SetupMockNetPeers( // // MUST add mockdns before any libp2p host comes online. Otherwise, it will // error while attempting to resolve its own hostname. - _, dnsSrvDone := testutil.DNSMockFromServiceURLs(t, serviceURLs) - t.Cleanup(dnsSrvDone) + _ = testutil.DNSMockFromServiceURLs(t, serviceURLs) // Add a libp2p peers/hosts to the `MockNet` with the keypairs corresponding // to the genesis validators' keypairs diff --git a/internal/testutil/persistence/mocks.go b/internal/testutil/persistence/mocks.go index 07702b867..f64c9bab1 100644 --- a/internal/testutil/persistence/mocks.go +++ b/internal/testutil/persistence/mocks.go @@ -2,21 +2,20 @@ package persistence_testutil import ( "fmt" - "github.com/pokt-network/pocket/persistence/types/mocks" - "github.com/pokt-network/pocket/shared/codec" - "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/utils" - "testing" - "github.com/golang/mock/gomock" + "github.com/regen-network/gocuke" + "github.com/pokt-network/pocket/persistence/types/mocks" "github.com/pokt-network/pocket/runtime/genesis" + "github.com/pokt-network/pocket/shared/codec" + "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/modules" "github.com/pokt-network/pocket/shared/modules/mocks" + "github.com/pokt-network/pocket/shared/utils" ) // Persistence mock - only needed for validatorMap access -func BasePersistenceMock(t *testing.T, busMock *mock_modules.MockBus, genesisState *genesis.GenesisState) *mock_modules.MockPersistenceModule { +func BasePersistenceMock(t gocuke.TestingT, busMock *mock_modules.MockBus, genesisState *genesis.GenesisState) *mock_modules.MockPersistenceModule { ctrl := gomock.NewController(t) persistenceModuleMock := mock_modules.NewMockPersistenceModule(ctrl) @@ -29,13 +28,14 @@ func BasePersistenceMock(t *testing.T, busMock *mock_modules.MockBus, genesisSta persistenceModuleMock.EXPECT().GetBus().Return(busMock).AnyTimes() persistenceModuleMock.EXPECT().SetBus(busMock).AnyTimes() persistenceModuleMock.EXPECT().GetModuleName().Return(modules.PersistenceModuleName).AnyTimes() - busMock.RegisterModule(persistenceModuleMock) + busMock.EXPECT().GetPersistenceModule().Return(persistenceModuleMock).AnyTimes() + //busMock.RegisterModule(persistenceModuleMock) return persistenceModuleMock } // Creates a persistence module mock with mock implementations of some basic functionality -func PersistenceMockWithBlockStore(t *testing.T, _ modules.EventsChannel, bus modules.Bus) *mock_modules.MockPersistenceModule { +func PersistenceMockWithBlockStore(t gocuke.TestingT, _ modules.EventsChannel, bus modules.Bus) *mock_modules.MockPersistenceModule { ctrl := gomock.NewController(t) persistenceMock := mock_modules.NewMockPersistenceModule(ctrl) persistenceReadContextMock := mock_modules.NewMockPersistenceReadContext(ctrl) diff --git a/internal/testutil/runtime/genesis.go b/internal/testutil/runtime/genesis.go index e7ea512ae..b14aac47d 100644 --- a/internal/testutil/runtime/genesis.go +++ b/internal/testutil/runtime/genesis.go @@ -9,7 +9,7 @@ import ( "github.com/regen-network/gocuke" ) -func BaseGenesisStateMock(t gocuke.TestingT, valKeys []cryptoPocket.PrivateKey, serviceURLs []string) *genesis.GenesisState { +func BaseGenesisStateMock(t gocuke.TestingT, valKeys []cryptoPocket.PublicKey, serviceURLs []string) *genesis.GenesisState { t.Helper() genesisState := new(genesis.GenesisState) @@ -19,7 +19,7 @@ func BaseGenesisStateMock(t gocuke.TestingT, valKeys []cryptoPocket.PrivateKey, mockActor := &types.Actor{ ActorType: types.ActorType_ACTOR_TYPE_VAL, Address: addr, - PublicKey: valKey.PublicKey().String(), + PublicKey: valKey.String(), ServiceUrl: serviceURLs[i], StakedAmount: test_artifacts.DefaultStakeAmountString, PausedHeight: int64(0), @@ -33,7 +33,7 @@ func BaseGenesisStateMock(t gocuke.TestingT, valKeys []cryptoPocket.PrivateKey, return genesisState } -func GenesisWithSequentialServiceURLs(t gocuke.TestingT, valKeys []cryptoPocket.PrivateKey) *genesis.GenesisState { +func GenesisWithSequentialServiceURLs(t gocuke.TestingT, valKeys []cryptoPocket.PublicKey) *genesis.GenesisState { t.Helper() serviceURLs := make([]string, len(valKeys)) diff --git a/internal/testutil/telemetry/mocks.go b/internal/testutil/telemetry/mocks.go index e695a8343..795c73aed 100644 --- a/internal/testutil/telemetry/mocks.go +++ b/internal/testutil/telemetry/mocks.go @@ -20,8 +20,9 @@ func MinimalTelemetryMock( telemetryMock.EXPECT().Start().Return(nil).AnyTimes() telemetryMock.EXPECT().SetBus(gomock.Any()).Return().AnyTimes() telemetryMock.EXPECT().GetModuleName().Return(modules.TelemetryModuleName).AnyTimes() + busMock.EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() + //busMock.RegisterModule(telemetryMock) - busMock.RegisterModule(telemetryMock) return telemetryMock } From 430477b928d275d15c45ddd25e57416ce25d0d60 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:05:18 +0200 Subject: [PATCH 07/82] wip: utils/host --- p2p/utils/host.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/p2p/utils/host.go b/p2p/utils/host.go index e9c6e130b..b016bcc47 100644 --- a/p2p/utils/host.go +++ b/p2p/utils/host.go @@ -94,6 +94,17 @@ func Libp2pSendToPeer(host libp2pHost.Host, data []byte, peer typesP2P.Peer) err logger.Global.Debug().Err(err).Msg("logging resource scope stats") } + // TODO: remove me! + //pstore := host.Peerstore() + //for _, peerID := range pstore.Peers() { + // addr := pstore.Addrs(peerID)[0] + // logger.Global.Debug(). + // Str("peerID", peerID.String()). + // Str("addr", addr.String()). + // Msg("peerstore") + //} + // -- end TODO + stream, err := host.NewStream(ctx, peerInfo.ID, protocol.PoktProtocolID) if err != nil { return fmt.Errorf("opening stream: %w", err) From 86a067d5c0ff401078be3c0a0789b3fa0d340f84 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 16 May 2023 12:16:14 +0200 Subject: [PATCH 08/82] wip: checkpoint --- .../testutil/constructors/constructors.go | 11 +- .../testutil/telemetry/event_metrics_agent.go | 13 ++- internal/testutil/telemetry/mocks.go | 4 +- p2p/module_raintree_test.go | 100 ++++++++++++++++-- p2p/module_test.go | 10 +- p2p/raintree/peers_manager_test.go | 2 +- p2p/testutil.go | 7 ++ 7 files changed, 126 insertions(+), 21 deletions(-) diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 9f78c7448..094813ec3 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -63,7 +63,16 @@ func NewBusesMocknetAndP2PModules( // TODO_THIS_COMMIT: refactor _ = consensus_testutil.BaseConsensusMock(t, busMock) _ = persistence_testutil.BasePersistenceMock(t, busMock, genesisState) - _ = telemetry_testutil.BaseTelemetryMock(t, busMock) + _ = telemetry_testutil.WithTimeSeriesAgent( + t, telemetry_testutil.MinimalTelemetryMock(t, busMock), + ) + //_ = telemetry_testutil.BaseTelemetryMock(t, busMock) + //telemetryMock.GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent).EXPECT().EmitEvent( + // gomock.Any(), + // gomock.Any(), + // gomock.Any(), + // gomock.Any(), + //).AnyTimes() // MUST register DNS before instantiating P2PModule testutil.AddServiceURLZone(t, dnsSrv, serviceURL) diff --git a/internal/testutil/telemetry/event_metrics_agent.go b/internal/testutil/telemetry/event_metrics_agent.go index 69c5d7035..0b77fe482 100644 --- a/internal/testutil/telemetry/event_metrics_agent.go +++ b/internal/testutil/telemetry/event_metrics_agent.go @@ -17,6 +17,7 @@ func BaseEventMetricsAgentMock(t gocuke.TestingT) *mock_modules.MockEventMetrics ctrl := gomock.NewController(t) eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() return eventMetricsAgentMock } @@ -27,12 +28,16 @@ func PrepareEventMetricsAgentMock(t *testing.T, valId string, wg *sync.WaitGroup ctrl := gomock.NewController(t) eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) - eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + logEvent := func(n, e string, l ...any) { + t.Logf("n: %s, e: %s, l: %v\n", n, e, l) + } + + //eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(logEvent).AnyTimes() eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Eq(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).Do(func(n, e any, l ...any) { log.Printf("[valId: %s] Write\n", valId) wg.Done() - }).Times(expectedNumNetworkWrites) - eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Not(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).AnyTimes() + }).Do(logEvent).Times(expectedNumNetworkWrites) + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Not(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).Do(logEvent).AnyTimes() return eventMetricsAgentMock } @@ -41,7 +46,7 @@ func EventMetricsAgentMockWithHandler( t gocuke.TestingT, label string, // TODO_THIS_COMMIT: consider refactoring as a type - handler func(namespace, event_name string, labels ...any), + handler func(namespace, eventName string, labels ...any), times int, ) *mock_modules.MockEventMetricsAgent { ctrl := gomock.NewController(t) diff --git a/internal/testutil/telemetry/mocks.go b/internal/testutil/telemetry/mocks.go index 795c73aed..a369bcbd5 100644 --- a/internal/testutil/telemetry/mocks.go +++ b/internal/testutil/telemetry/mocks.go @@ -19,6 +19,8 @@ func MinimalTelemetryMock( telemetryMock.EXPECT().Start().Return(nil).AnyTimes() telemetryMock.EXPECT().SetBus(gomock.Any()).Return().AnyTimes() + // TODO_THIS_COMMIT: which one ^ v ? + //telemetryMock.EXPECT().SetBus(busMock).Return().AnyTimes() telemetryMock.EXPECT().GetModuleName().Return(modules.TelemetryModuleName).AnyTimes() busMock.EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() //busMock.RegisterModule(telemetryMock) @@ -39,10 +41,8 @@ func WithTimeSeriesAgent(t gocuke.TestingT, telemetryMod modules.TelemetryModule telemetryMock := telemetryMod.(*mock_modules.MockTelemetryModule) timeSeriesAgentMock := BaseTimeSeriesAgentMock(t) - eventMetricsAgentMock := BaseEventMetricsAgentMock(t) telemetryMock.EXPECT().GetTimeSeriesAgent().Return(timeSeriesAgentMock).AnyTimes() - telemetryMock.EXPECT().GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() return telemetryMock } diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index fad6f43ec..24bc2b6f1 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -3,6 +3,10 @@ package p2p_test import ( + runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" + telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" + mock_modules "github.com/pokt-network/pocket/shared/modules/mocks" "log" "os" "path/filepath" @@ -22,7 +26,6 @@ import ( "github.com/pokt-network/pocket/p2p/protocol" "github.com/pokt-network/pocket/p2p/raintree" "github.com/pokt-network/pocket/shared/messaging" - "github.com/pokt-network/pocket/shared/modules" ) // TODO(#314): Add the tooling and instructions on how to generate unit tests in this file. @@ -223,26 +226,45 @@ func TestRainTreeNetworkCompleteTwentySevenNodes(t *testing.T) { func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig TestNetworkSimulationConfig) { // Configure & prepare test module numValidators := len(networkSimulationConfig) - runtimeConfigs := createMockRuntimeMgrs(t, numValidators) - genesisMock := runtimeConfigs[0].GetGenesis() + //runtimeConfigs := createMockRuntimeMgrs(t, numValidators) + //genesisState := runtimeConfigs[0].GetGenesis() + + privKeys := testutil.LoadLocalnetPrivateKeys(t, numValidators) + pubKeys := make([]cryptoPocket.PublicKey, len(privKeys)) + for i, privKey := range privKeys { + pubKeys[i] = privKey.PublicKey() + } + + genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(t, pubKeys) var wg sync.WaitGroup //busMocks := createMockBuses(t, runtimeConfigs, &wg) - busEventHandlerFactory := func(t gocuke.TestingT, bus modules.Bus) testutil.BusEventHandler { + busEventHandlerFactory := func(t gocuke.TestingT, busMock *mock_modules.MockBus) testutil.BusEventHandler { return func(data *messaging.PocketEnvelope) { // TODO: decide - is mutually exclusive w/ telemetry based wg.Done() below // `p2pModule#handleNetworkData()` calls `modules.Bus#PublishEventToBus()` // assumes that P2P module is the only event producer running during the test + t.Log("bus event received") wg.Done() } } busMocks, _, p2pModules := constructors.NewBusesMocknetAndP2PModules( t, numValidators, - genesisMock, + genesisState, busEventHandlerFactory, ) + //for _, busMock := range busMocks { + // telemetryMock := busMock.GetTelemetryModule().(*mock_modules.MockTelemetryModule) + // telemetryMock.GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent).EXPECT().EmitEvent( + // gomock.Any(), + // gomock.Any(), + // gomock.Any(), + // gomock.Any(), + // ).AnyTimes() + //} + //serviceURLs := make([]string, 0, numValidators) //for valId := range networkSimulationConfig { // serviceURLs = append(serviceURLs, valId) @@ -273,13 +295,57 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te // ... //} - persistenceMock := persistence_testutil.BasePersistenceMock(t, busMock, genesisMock) - //consensusMock := consensus_testutil.PrepareConsensusMock(t, busMocks[serviceURL]) - telemetryMock := prepareTelemetryMock(t, busMock, serviceURL, &wg, expectedWrites) + // TODO_THIS_COMMIT: MOVE + //telemetryEventHandler := func(namespace, eventName string, labels ...any) { + // t.Log("telemetry event received") + // wg.Done() + //} - busMock.EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() - //busMocks[serviceURL].EXPECT().GetConsensusModule().Return(consensusMock).AnyTimes() - busMock.EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() + //privKey, err := cryptoPocket.NewPrivateKey(busMock.GetRuntimeMgr().GetConfig().P2P.PrivateKey) + //require.NoError(t, err) + // + // TODO_THIS_COMMIT: move & de-dup + //libp2pPrivKey, err := libp2pCrypto.UnmarshalEd25519PrivateKey(privKey.Bytes()) + //require.NoError(t, err) + // + //libp2pPeerID, err := libp2pPeer.IDFromPrivateKey(libp2pPrivKey) + //require.NoError(t, err) + // END TODO + // + //sURL := strings.Clone(serviceURL) + //host := libp2pNetworkMock.Host(libp2pPeerID) + //host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { + // t.Logf("[valID: %s] Read\n", sURL) + // p2pModules[serviceURL].(*p2p.P2PModule).HandleStream(stream) + // wg.Done() + //}) + + eventMetricsAgentMock := telemetry_testutil.PrepareEventMetricsAgentMock(t, serviceURL, &wg, expectedWrites) + busMock.GetTelemetryModule().(*mock_modules.MockTelemetryModule).EXPECT(). + GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() + + _ = persistence_testutil.BasePersistenceMock(t, busMock, genesisState) + //telemetryMock := prepareTelemetryMock(t, busMock, serviceURL, &wg, expectedWrites) + //_ = telemetry_testutil.BaseTelemetryMock(t, busMock) + //telemetryMock.GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent).EXPECT().EmitEvent( + // gomock.Any(), + // gomock.Any(), + // gomock.Eq(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), + // gomock.Any(), + // //).Do(telemetryEventHandler).Times(expectedWrites) + //).Do(telemetryEventHandler).AnyTimes() + ////telemetryMock.GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent).EXPECT().EmitEvent( + //// gomock.Eq("one"), + //// gomock.Eq("two"), + //// gomock.Eq("three"), + ////).Do(telemetryEventHandler).Times(expectedWrites) + ////telemetryMock. + //// GetEventMetricsAgent(). + //// EmitEvent( + //// "one", + //// "two", + //// "three", + //// ) } // Inject the connection and bus mocks into the P2P modules @@ -312,6 +378,18 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te p := &anypb.Any{} p2pMod := p2pModules[origNode] require.NoError(t, p2pMod.Broadcast(p)) + + //for _, busMock := range busMocks { + // busMock. + //busMocks[testutil.GetKeys(busMocks)[0]]. + // GetTelemetryModule(). + // GetEventMetricsAgent(). + // EmitEvent( + // "one", + // "two", + // "three", + // ) + //} } func extractNumericId(valId string) int64 { diff --git a/p2p/module_test.go b/p2p/module_test.go index 69bedb1a9..c2503f699 100644 --- a/p2p/module_test.go +++ b/p2p/module_test.go @@ -120,8 +120,14 @@ func Test_Create_configureBootstrapNodes(t *testing.T) { mockRuntimeMgr := mockModules.NewMockRuntimeMgr(ctrl) mockBus := testutil.BaseBusMock(t, mockRuntimeMgr) - serviceURLs := p2p_testutil.SequentialServiceURLs(t, len(keys)) - genesisStateMock := runtime_testutil.BaseGenesisStateMock(t, keys, serviceURLs) + // TODO_THIS_COMMIT: refactor + pubKeys := make([]cryptoPocket.PublicKey, len(keys)) + for i, privKey := range keys { + pubKeys[i] = privKey.PublicKey() + } + + serviceURLs := p2p_testutil.SequentialServiceURLs(t, len(pubKeys)) + genesisStateMock := runtime_testutil.BaseGenesisStateMock(t, pubKeys, serviceURLs) persistenceMock := persistence_testutil.BasePersistenceMock(t, mockBus, genesisStateMock) mockBus.EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() diff --git a/p2p/raintree/peers_manager_test.go b/p2p/raintree/peers_manager_test.go index 79e0f197c..caca32f5b 100644 --- a/p2p/raintree/peers_manager_test.go +++ b/p2p/raintree/peers_manager_test.go @@ -356,6 +356,6 @@ func mockAlphabetValidatorServiceURLsDNS(t *testing.T) (done func()) { } } - _, done = testutil.BaseDNSMock(t, zones) + _ = testutil.BaseDNSMock(t, zones) return done } diff --git a/p2p/testutil.go b/p2p/testutil.go index a030d8682..b455024bf 100644 --- a/p2p/testutil.go +++ b/p2p/testutil.go @@ -2,5 +2,12 @@ package p2p +import libp2pNetwork "github.com/libp2p/go-libp2p/core/network" + // P2PModule exports the `p2pModule` type for use in tests type P2PModule = p2pModule + +// HandleStream exports the `handleStream` method for use in tests +func (m *p2pModule) HandleStream(stream libp2pNetwork.Stream) { + m.handleStream(stream) +} From 83e80e6c9d7d707d5f8052b8748ad4278f981e80 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 16 May 2023 12:34:51 +0200 Subject: [PATCH 09/82] wip: checkpoint --- .../testutil/constructors/constructors.go | 7 -- .../testutil/telemetry/event_metrics_agent.go | 8 +- p2p/module_raintree_test.go | 81 +++++-------------- p2p/testutil.go | 7 -- 4 files changed, 25 insertions(+), 78 deletions(-) diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 094813ec3..744acf85e 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -66,13 +66,6 @@ func NewBusesMocknetAndP2PModules( _ = telemetry_testutil.WithTimeSeriesAgent( t, telemetry_testutil.MinimalTelemetryMock(t, busMock), ) - //_ = telemetry_testutil.BaseTelemetryMock(t, busMock) - //telemetryMock.GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent).EXPECT().EmitEvent( - // gomock.Any(), - // gomock.Any(), - // gomock.Any(), - // gomock.Any(), - //).AnyTimes() // MUST register DNS before instantiating P2PModule testutil.AddServiceURLZone(t, dnsSrv, serviceURL) diff --git a/internal/testutil/telemetry/event_metrics_agent.go b/internal/testutil/telemetry/event_metrics_agent.go index 0b77fe482..ff665b0a1 100644 --- a/internal/testutil/telemetry/event_metrics_agent.go +++ b/internal/testutil/telemetry/event_metrics_agent.go @@ -1,7 +1,6 @@ package telemetry_testutil import ( - "log" "sync" "testing" @@ -28,13 +27,14 @@ func PrepareEventMetricsAgentMock(t *testing.T, valId string, wg *sync.WaitGroup ctrl := gomock.NewController(t) eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) + // TODO_THIS_COMMIT: remove logEvent := func(n, e string, l ...any) { - t.Logf("n: %s, e: %s, l: %v\n", n, e, l) + //t.Logf("n: %s, e: %s, l: %v\n", n, e, l) } - //eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(logEvent).AnyTimes() + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(logEvent).AnyTimes() eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Eq(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).Do(func(n, e any, l ...any) { - log.Printf("[valId: %s] Write\n", valId) + t.Logf("[valId: %s] Write", valId) wg.Done() }).Do(logEvent).Times(expectedNumNetworkWrites) eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Not(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).Do(logEvent).AnyTimes() diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index 24bc2b6f1..98a8d4297 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -6,6 +6,7 @@ import ( runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/pocket/shared/modules" mock_modules "github.com/pokt-network/pocket/shared/modules/mocks" "log" "os" @@ -23,8 +24,6 @@ import ( "github.com/pokt-network/pocket/internal/testutil/constructors" "github.com/pokt-network/pocket/internal/testutil/p2p" persistence_testutil "github.com/pokt-network/pocket/internal/testutil/persistence" - "github.com/pokt-network/pocket/p2p/protocol" - "github.com/pokt-network/pocket/p2p/raintree" "github.com/pokt-network/pocket/shared/messaging" ) @@ -237,19 +236,25 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(t, pubKeys) - var wg sync.WaitGroup + var ( + wg sync.WaitGroup + busMocks map[string]*mock_modules.MockBus + p2pModules map[string]modules.P2PModule + ) //busMocks := createMockBuses(t, runtimeConfigs, &wg) busEventHandlerFactory := func(t gocuke.TestingT, busMock *mock_modules.MockBus) testutil.BusEventHandler { return func(data *messaging.PocketEnvelope) { - // TODO: decide - is mutually exclusive w/ telemetry based wg.Done() below + p2pCfg := busMock.GetRuntimeMgr().GetConfig().P2P + // `p2pModule#handleNetworkData()` calls `modules.Bus#PublishEventToBus()` - // assumes that P2P module is the only event producer running during the test - t.Log("bus event received") + // assumes that P2P module is the only bus event producer running during + // the test. + t.Logf("[valId: %s:%d] Read", p2pCfg.Hostname, p2pCfg.Port) wg.Done() } } - busMocks, _, p2pModules := constructors.NewBusesMocknetAndP2PModules( + busMocks, _, p2pModules = constructors.NewBusesMocknetAndP2PModules( t, numValidators, genesisState, busEventHandlerFactory, @@ -301,25 +306,6 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te // wg.Done() //} - //privKey, err := cryptoPocket.NewPrivateKey(busMock.GetRuntimeMgr().GetConfig().P2P.PrivateKey) - //require.NoError(t, err) - // - // TODO_THIS_COMMIT: move & de-dup - //libp2pPrivKey, err := libp2pCrypto.UnmarshalEd25519PrivateKey(privKey.Bytes()) - //require.NoError(t, err) - // - //libp2pPeerID, err := libp2pPeer.IDFromPrivateKey(libp2pPrivKey) - //require.NoError(t, err) - // END TODO - // - //sURL := strings.Clone(serviceURL) - //host := libp2pNetworkMock.Host(libp2pPeerID) - //host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { - // t.Logf("[valID: %s] Read\n", sURL) - // p2pModules[serviceURL].(*p2p.P2PModule).HandleStream(stream) - // wg.Done() - //}) - eventMetricsAgentMock := telemetry_testutil.PrepareEventMetricsAgentMock(t, serviceURL, &wg, expectedWrites) busMock.GetTelemetryModule().(*mock_modules.MockTelemetryModule).EXPECT(). GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() @@ -334,34 +320,21 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te // gomock.Any(), // //).Do(telemetryEventHandler).Times(expectedWrites) //).Do(telemetryEventHandler).AnyTimes() - ////telemetryMock.GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent).EXPECT().EmitEvent( - //// gomock.Eq("one"), - //// gomock.Eq("two"), - //// gomock.Eq("three"), - ////).Do(telemetryEventHandler).Times(expectedWrites) - ////telemetryMock. - //// GetEventMetricsAgent(). - //// EmitEvent( - //// "one", - //// "two", - //// "three", - //// ) } - // Inject the connection and bus mocks into the P2P modules - //p2pModules := createP2PModules(t, busMocks, libp2pNetworkMock, serviceURLs) - + // Start all p2p modules for _, p2pMod := range p2pModules { err := p2pMod.Start() require.NoError(t, err) - sURL := strings.Clone(serviceURL) - mod := *p2pMod - p2pMod.host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { - log.Printf("[valID: %s] Read\n", sURL) - (&mod).router.(*raintree.RainTreeRouter).HandleStream(stream) - wg.Done() - }) + // TODO_THIS_COMMIT: decide where to `wg.Done()` + //sURL := strings.Clone(serviceURL) + //mod := *p2pMod + //p2pMod.host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { + // log.Printf("[valID: %s] Read\n", sURL) + // (&mod).router.(*raintree.RainTreeRouter).HandleStream(stream) + // wg.Done() + //}) } // Wait for completion @@ -378,18 +351,6 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te p := &anypb.Any{} p2pMod := p2pModules[origNode] require.NoError(t, p2pMod.Broadcast(p)) - - //for _, busMock := range busMocks { - // busMock. - //busMocks[testutil.GetKeys(busMocks)[0]]. - // GetTelemetryModule(). - // GetEventMetricsAgent(). - // EmitEvent( - // "one", - // "two", - // "three", - // ) - //} } func extractNumericId(valId string) int64 { diff --git a/p2p/testutil.go b/p2p/testutil.go index b455024bf..a030d8682 100644 --- a/p2p/testutil.go +++ b/p2p/testutil.go @@ -2,12 +2,5 @@ package p2p -import libp2pNetwork "github.com/libp2p/go-libp2p/core/network" - // P2PModule exports the `p2pModule` type for use in tests type P2PModule = p2pModule - -// HandleStream exports the `handleStream` method for use in tests -func (m *p2pModule) HandleStream(stream libp2pNetwork.Stream) { - m.handleStream(stream) -} From e46f386af7b14584b2e6bbafe142c2764f47cf46 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:08:18 +0200 Subject: [PATCH 10/82] wip: testutils --- internal/testutil/constructors/constructors.go | 1 + internal/testutil/telemetry/event_metrics_agent.go | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 744acf85e..57990e03b 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -63,6 +63,7 @@ func NewBusesMocknetAndP2PModules( // TODO_THIS_COMMIT: refactor _ = consensus_testutil.BaseConsensusMock(t, busMock) _ = persistence_testutil.BasePersistenceMock(t, busMock, genesisState) + //_ = telemetry_testutil.BaseTelemetryMock(t, busMock) _ = telemetry_testutil.WithTimeSeriesAgent( t, telemetry_testutil.MinimalTelemetryMock(t, busMock), ) diff --git a/internal/testutil/telemetry/event_metrics_agent.go b/internal/testutil/telemetry/event_metrics_agent.go index ff665b0a1..4060ac4ae 100644 --- a/internal/testutil/telemetry/event_metrics_agent.go +++ b/internal/testutil/telemetry/event_metrics_agent.go @@ -1,11 +1,9 @@ package telemetry_testutil import ( - "sync" - "testing" - "github.com/golang/mock/gomock" "github.com/regen-network/gocuke" + "sync" "github.com/pokt-network/pocket/shared/modules/mocks" "github.com/pokt-network/pocket/telemetry" @@ -17,13 +15,18 @@ func BaseEventMetricsAgentMock(t gocuke.TestingT) *mock_modules.MockEventMetrics ctrl := gomock.NewController(t) eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + // TODO_THIS_COMMIT: remove v -- may represent failure condition w/ reused nonces.. + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + // END TODO + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() return eventMetricsAgentMock } // TODO_THIS_COMMIT: refactor... // Events metric mock - Needed to help with proper counts for number of expected network writes -func PrepareEventMetricsAgentMock(t *testing.T, valId string, wg *sync.WaitGroup, expectedNumNetworkWrites int) *mock_modules.MockEventMetricsAgent { +func PrepareEventMetricsAgentMock(t gocuke.TestingT, valId string, wg *sync.WaitGroup, expectedNumNetworkWrites int) *mock_modules.MockEventMetricsAgent { ctrl := gomock.NewController(t) eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) @@ -56,7 +59,7 @@ func EventMetricsAgentMockWithHandler( eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Eq(label), gomock.Any()).Do(handler).Times(times) // TODO_THIS_COMMIT: is this really needed? - eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Not(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).AnyTimes() + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Not(label), gomock.Any()).AnyTimes() return eventMetricsAgentMock } From dc435633cf4f011c107c688c449bc6b6368914e7 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Mon, 22 May 2023 10:57:15 +0200 Subject: [PATCH 11/82] wip: checkpoint --- internal/testutil/persistence/mocks.go | 4 ++-- p2p/transport_encryption_test.go | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/internal/testutil/persistence/mocks.go b/internal/testutil/persistence/mocks.go index f64c9bab1..2e18385b6 100644 --- a/internal/testutil/persistence/mocks.go +++ b/internal/testutil/persistence/mocks.go @@ -5,7 +5,7 @@ import ( "github.com/golang/mock/gomock" "github.com/regen-network/gocuke" - "github.com/pokt-network/pocket/persistence/types/mocks" + persistence_mocks "github.com/pokt-network/pocket/persistence/types/mocks" "github.com/pokt-network/pocket/runtime/genesis" "github.com/pokt-network/pocket/shared/codec" "github.com/pokt-network/pocket/shared/core/types" @@ -47,7 +47,7 @@ func PersistenceMockWithBlockStore(t gocuke.TestingT, _ modules.EventsChannel, b persistenceMock.EXPECT().ReleaseWriteContext().Return(nil).AnyTimes() - blockStoreMock := mock_kvstore.NewMockKVStore(ctrl) + blockStoreMock := persistence_mocks.NewMockBlockStore(ctrl) blockStoreMock.EXPECT().Get(gomock.Any()).DoAndReturn(func(height []byte) ([]byte, error) { heightInt := utils.HeightFromBytes(height) diff --git a/p2p/transport_encryption_test.go b/p2p/transport_encryption_test.go index a5dd7f236..f2f2dbec7 100644 --- a/p2p/transport_encryption_test.go +++ b/p2p/transport_encryption_test.go @@ -61,12 +61,8 @@ func TestP2pModule_Insecure_Error(t *testing.T) { persistenceMock := persistence_testutil.BasePersistenceMock(t, busMock, genesisStateMock) busMock.EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() - serviceURLs := make([]string, len(genesisStateMock.Validators)) - for i, actor := range genesisStateMock.Validators { - serviceURLs[i] = actor.ServiceUrl - } - dnsDone := testutil.PrepareDNSMockFromServiceURLs(t, serviceURLs) - t.Cleanup(dnsDone) + // mock DNS for service URL hostnames + _ = testutil.DNSMockFromServiceURLs(t, serviceURLs) p2pMod, err := p2p.Create(busMock) require.NoError(t, err) From 262a77095dc67e6afdc277d25b139cc4a2ff55ac Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:16:41 +0200 Subject: [PATCH 12/82] wip: testutils --- .../testutil/constructors/constructors.go | 12 ++- .../testutil/telemetry/event_metrics_agent.go | 51 ++++++++++- internal/testutil/telemetry/mocks.go | 19 +++-- p2p/utils_test.go | 84 ------------------- 4 files changed, 68 insertions(+), 98 deletions(-) diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 57990e03b..6c95ad422 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -63,10 +63,14 @@ func NewBusesMocknetAndP2PModules( // TODO_THIS_COMMIT: refactor _ = consensus_testutil.BaseConsensusMock(t, busMock) _ = persistence_testutil.BasePersistenceMock(t, busMock, genesisState) - //_ = telemetry_testutil.BaseTelemetryMock(t, busMock) - _ = telemetry_testutil.WithTimeSeriesAgent( - t, telemetry_testutil.MinimalTelemetryMock(t, busMock), - ) + + // -- option 1 + _ = telemetry_testutil.BaseTelemetryMock(t, busMock) + + // -- option 2 + //_ = telemetry_testutil.WithTimeSeriesAgent( + // t, telemetry_testutil.MinimalTelemetryMock(t, busMock), + //) // MUST register DNS before instantiating P2PModule testutil.AddServiceURLZone(t, dnsSrv, serviceURL) diff --git a/internal/testutil/telemetry/event_metrics_agent.go b/internal/testutil/telemetry/event_metrics_agent.go index 4060ac4ae..5de2987c0 100644 --- a/internal/testutil/telemetry/event_metrics_agent.go +++ b/internal/testutil/telemetry/event_metrics_agent.go @@ -9,11 +9,12 @@ import ( "github.com/pokt-network/pocket/telemetry" ) -func BaseEventMetricsAgentMock(t gocuke.TestingT) *mock_modules.MockEventMetricsAgent { +func WithP2PIntegrationEvents( + t gocuke.TestingT, + eventMetricsAgentMock *mock_modules.MockEventMetricsAgent, +) *mock_modules.MockEventMetricsAgent { t.Helper() - ctrl := gomock.NewController(t) - eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() // TODO_THIS_COMMIT: remove v -- may represent failure condition w/ reused nonces.. @@ -45,6 +46,29 @@ func PrepareEventMetricsAgentMock(t gocuke.TestingT, valId string, wg *sync.Wait return eventMetricsAgentMock } +func WhyEventMetricsAgentMock( + t gocuke.TestingT, + eventMetricsAgentMock *mock_modules.MockEventMetricsAgent, + valId string, + wg *sync.WaitGroup, + //handler func(namespace, eventName string, labels ...any), + expectedNumNetworkWrites int, +) *mock_modules.MockEventMetricsAgent { + // TODO_THIS_COMMIT: remove + logEvent := func(n, e string, l ...any) { + //t.Logf("n: %s, e: %s, l: %v\n", n, e, l) + } + + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(logEvent).AnyTimes() + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Eq(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).Do(func(n, e any, l ...any) { + t.Logf("[valId: %s] Write", valId) + wg.Done() + }).Do(logEvent).Times(expectedNumNetworkWrites) + eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Not(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).Do(logEvent).AnyTimes() + + return eventMetricsAgentMock +} + func EventMetricsAgentMockWithHandler( t gocuke.TestingT, label string, @@ -52,9 +76,30 @@ func EventMetricsAgentMockWithHandler( handler func(namespace, eventName string, labels ...any), times int, ) *mock_modules.MockEventMetricsAgent { + t.Helper() + ctrl := gomock.NewController(t) eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) + return WithEventMetricsHandler(t, eventMetricsAgentMock, label, handler, times) +} + +func WithEventMetricsHandler( + t gocuke.TestingT, + eventMetricsAgentMock *mock_modules.MockEventMetricsAgent, + label string, + handler func(namespace, eventName string, labels ...any), + times int, +) *mock_modules.MockEventMetricsAgent { + t.Helper() + + //eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(logEvent).AnyTimes() + //eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Eq(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).Do(func(n, e any, l ...any) { + // t.Logf("[valId: %s] Write", valId) + // wg.Done() + //}).Do(logEvent).Times(expectedNumNetworkWrites) + //eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Not(telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL), gomock.Any()).Do(logEvent).AnyTimes() + // TODO_THIS_COMMIT: scrutinize these & their order eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() eventMetricsAgentMock.EXPECT().EmitEvent(gomock.Any(), gomock.Any(), gomock.Eq(label), gomock.Any()).Do(handler).Times(times) diff --git a/internal/testutil/telemetry/mocks.go b/internal/testutil/telemetry/mocks.go index a369bcbd5..3271e9767 100644 --- a/internal/testutil/telemetry/mocks.go +++ b/internal/testutil/telemetry/mocks.go @@ -11,7 +11,7 @@ import ( func MinimalTelemetryMock( t gocuke.TestingT, busMock *mock_modules.MockBus, -) modules.TelemetryModule { +) *mock_modules.MockTelemetryModule { t.Helper() ctrl := gomock.NewController(t) @@ -31,26 +31,31 @@ func MinimalTelemetryMock( func BaseTelemetryMock( t gocuke.TestingT, busMock *mock_modules.MockBus, -) modules.TelemetryModule { +) *mock_modules.MockTelemetryModule { t.Helper() return WithTimeSeriesAgent(t, WithEventMetricsAgent(t, MinimalTelemetryMock(t, busMock))) } -func WithTimeSeriesAgent(t gocuke.TestingT, telemetryMod modules.TelemetryModule) *mock_modules.MockTelemetryModule { +func WithTimeSeriesAgent( + t gocuke.TestingT, + telemetryMock *mock_modules.MockTelemetryModule, +) *mock_modules.MockTelemetryModule { t.Helper() - telemetryMock := telemetryMod.(*mock_modules.MockTelemetryModule) timeSeriesAgentMock := BaseTimeSeriesAgentMock(t) telemetryMock.EXPECT().GetTimeSeriesAgent().Return(timeSeriesAgentMock).AnyTimes() return telemetryMock } -func WithEventMetricsAgent(t gocuke.TestingT, telemetryMod modules.TelemetryModule) modules.TelemetryModule { +func WithEventMetricsAgent( + t gocuke.TestingT, + telemetryMock *mock_modules.MockTelemetryModule, +) *mock_modules.MockTelemetryModule { t.Helper() - telemetryMock := telemetryMod.(*mock_modules.MockTelemetryModule) - eventMetricsAgentMock := BaseEventMetricsAgentMock(t) + ctrl := gomock.NewController(t) + eventMetricsAgentMock := mock_modules.NewMockEventMetricsAgent(ctrl) telemetryMock.EXPECT().GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() return telemetryMock diff --git a/p2p/utils_test.go b/p2p/utils_test.go index bf225bc75..57f5729d2 100644 --- a/p2p/utils_test.go +++ b/p2p/utils_test.go @@ -3,31 +3,17 @@ package p2p_test import ( - "github.com/pokt-network/pocket/internal/testutil" - "github.com/pokt-network/pocket/internal/testutil/runtime" - "github.com/pokt-network/pocket/internal/testutil/telemetry" - "github.com/pokt-network/pocket/p2p" - "github.com/pokt-network/pocket/shared/messaging" - "github.com/pokt-network/pocket/telemetry" - "github.com/regen-network/gocuke" "sort" "sync" "testing" "time" - mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" - "github.com/stretchr/testify/require" - - p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" - "github.com/pokt-network/pocket/shared/modules" - mockModules "github.com/pokt-network/pocket/shared/modules/mocks" ) // ~~~~~~ RainTree Unit Test Configurations ~~~~~~ const ( - eventsChannelSize = 10000 // Since we simulate up to a 27 node network, we will pre-generate a n >= 27 number of keys to avoid generation // every time. The genesis config seed start is set for deterministic key generation and 42 was chosen arbitrarily. genesisConfigSeedStart = 42 @@ -83,73 +69,3 @@ func waitForNetworkSimulationCompletion(t *testing.T, wg *sync.WaitGroup) { t.Fatal("Timeout waiting for message to be handled") } } - -// ~~~~~~ RainTree Unit Test Mocks ~~~~~~ - -// createP2PModules returns a map of configured p2pModules keyed by an incremental naming convention (eg: `val_1`, `val_2`, etc.) -func createP2PModules(t *testing.T, busMocks []*mockModules.MockBus, netMock mocknet.Mocknet, serviceURLs []string) (p2pModules map[string]*p2p.P2PModule) { - t.Helper() - - require.GreaterOrEqualf(t, len(serviceURLs), len(busMocks), "number of bus mocks must be less than or equal to the number of service URLs") - - peerIDs := p2p_testutil.SetupMockNetPeers(t, netMock, keys[:len(busMocks)], serviceURLs) - p2pModules = make(map[string]*p2p.P2PModule, len(busMocks)) - for i := range busMocks { - host := netMock.Host(peerIDs[i]) - p2pMod, err := p2p.Create(busMocks[i], p2p.WithHostOption(host)) - require.NoError(t, err) - p2pModules[serviceURLs[i]] = p2pMod.(*p2p.P2PModule) - } - return -} - -// createMockRuntimeMgrs creates `numValidators` instances of mocked `RuntimeMgr` that are essentially -// representing the runtime environments of the validators that we will use in our tests -func createMockRuntimeMgrs(t *testing.T, numValidators int) []modules.RuntimeMgr { - mockRuntimeMgrs := make([]modules.RuntimeMgr, numValidators) - valKeys := make([]cryptoPocket.PrivateKey, numValidators) - copy(valKeys, keys[:numValidators]) - mockGenesisState := runtime_testutil.GenesisWithSequentialServiceURLs(t, valKeys) - for i := range mockRuntimeMgrs { - mockRuntimeMgrs[i] = runtime_testutil.BaseRuntimeManagerMock( - t, valKeys[i], - p2p_testutil.NewServiceURL(i+1), - mockGenesisState, - ) - } - return mockRuntimeMgrs -} - -func createMockBuses(t *testing.T, runtimeMgrs []modules.RuntimeMgr, wg *sync.WaitGroup) []*mockModules.MockBus { - mockBuses := make([]*mockModules.MockBus, len(runtimeMgrs)) - for i := range mockBuses { - handlerFactory := func(t gocuke.TestingT, bus modules.Bus) testutil.BusEventHandler { - return func(data *messaging.PocketEnvelope) { - wg.Done() - } - } - mockBuses[i] = testutil.BusMockWithEventHandler(t, runtimeMgrs[i], handlerFactory) - } - return mockBuses -} - -// TODO_THIS_COMMIT: refactor / move -// Telemetry mock - Needed to help with proper counts for number of expected network writes -func prepareTelemetryMock(t *testing.T, busMock *mockModules.MockBus, valId string, wg *sync.WaitGroup, expectedNumNetworkWrites int) *mockModules.MockTelemetryModule { - telemetryMock := telemetry_testutil.WithTimeSeriesAgent(t, - telemetry_testutil.MinimalTelemetryMock(t, busMock), - ) - - eventMetricsAgentMock := telemetry_testutil.EventMetricsAgentMockWithHandler( - t, telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL, - func(namesapce, event_name string, labels ...any) { - t.Logf("[valId: %s] Write\n", valId) - wg.Done() - }, - expectedNumNetworkWrites, - ) - - telemetryMock.EXPECT().GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() - busMock.RegisterModule(telemetryMock) - return telemetryMock -} From 0a27fadc6f2671cc009c220cbf14ea8eb77b62a6 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:22:21 +0200 Subject: [PATCH 13/82] wip: testutils --- internal/testutil/keys.go | 2 +- internal/testutil/telemetry/mocks.go | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/internal/testutil/keys.go b/internal/testutil/keys.go index 5c621fe11..5f8b6accc 100644 --- a/internal/testutil/keys.go +++ b/internal/testutil/keys.go @@ -25,7 +25,7 @@ func LoadLocalnetPrivateKeys(t require.TestingT, keyCount int) (privKeys []crypt privKeys = make([]cryptoPocket.PrivateKey, 0, keyCount) - // scann through file & extract private keys + // scan through file & extract private keys scanner := bufio.NewScanner(privKeyManifest) scanner.Split(bufio.ScanLines) diff --git a/internal/testutil/telemetry/mocks.go b/internal/testutil/telemetry/mocks.go index 3271e9767..633d03129 100644 --- a/internal/testutil/telemetry/mocks.go +++ b/internal/testutil/telemetry/mocks.go @@ -2,6 +2,7 @@ package telemetry_testutil import ( "github.com/golang/mock/gomock" + "github.com/pokt-network/pocket/internal/testutil" "github.com/regen-network/gocuke" "github.com/pokt-network/pocket/shared/modules" @@ -17,13 +18,20 @@ func MinimalTelemetryMock( ctrl := gomock.NewController(t) telemetryMock := mock_modules.NewMockTelemetryModule(ctrl) + busMock.EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() + + return telemetryMock +} + +func BehavesLikeBaseTelemetryMock( + t gocuke.TestingT, + telemetryMock *mock_modules.MockTelemetryModule, +) *mock_modules.MockTelemetryModule { + t.Helper() + telemetryMock.EXPECT().Start().Return(nil).AnyTimes() telemetryMock.EXPECT().SetBus(gomock.Any()).Return().AnyTimes() - // TODO_THIS_COMMIT: which one ^ v ? - //telemetryMock.EXPECT().SetBus(busMock).Return().AnyTimes() telemetryMock.EXPECT().GetModuleName().Return(modules.TelemetryModuleName).AnyTimes() - busMock.EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() - //busMock.RegisterModule(telemetryMock) return telemetryMock } @@ -33,7 +41,16 @@ func BaseTelemetryMock( busMock *mock_modules.MockBus, ) *mock_modules.MockTelemetryModule { t.Helper() - return WithTimeSeriesAgent(t, WithEventMetricsAgent(t, MinimalTelemetryMock(t, busMock))) + + return testutil.PipeTwoToOne[ + gocuke.TestingT, + *mock_modules.MockTelemetryModule + ]( + t, MinimalTelemetryMock(t, busMock), + BehavesLikeBaseTelemetryMock, + WithEventMetricsAgent, + WithTimeSeriesAgent, + ) } func WithTimeSeriesAgent( From 7fc3c3d97eadba614a4d8a755926c4ed27e0d8f5 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:25:49 +0200 Subject: [PATCH 14/82] wip: testutils - refactor & add to generics --- internal/testutil/composition.go | 30 +++++++++++++++++++++++ internal/testutil/generics/composition.go | 30 +++++++++++++++++++++++ internal/testutil/{ => generics}/map.go | 0 internal/testutil/generics/proxy.go | 3 +++ 4 files changed, 63 insertions(+) create mode 100644 internal/testutil/composition.go create mode 100644 internal/testutil/generics/composition.go rename internal/testutil/{ => generics}/map.go (100%) create mode 100644 internal/testutil/generics/proxy.go diff --git a/internal/testutil/composition.go b/internal/testutil/composition.go new file mode 100644 index 000000000..5e4d66b0f --- /dev/null +++ b/internal/testutil/composition.go @@ -0,0 +1,30 @@ +package testutil + +// PipeTwoToOne threads two values of any type (T and U) through a pipeline of +// functions and returns the result of any type (U). Each function in the pipeline +// takes two arguments of type T and U, and returns a value of type U. +// +// Applies each function in the pipeline to the current value of U and the +// constant value of T, effectively "threading" the initial U value through the +// pipeline of functions. +// +// Does *not* mutate the original U value. Instead, it operates on a reference +// to U, ensuring that value types (non-pointer types) are not mutated. +// +// Returns the final value of U after it has been threaded through all the functions in the pipeline. +// +// Usage: +// +// result := PipeTwo(initialT, initialU, func1, func2, func3) +// +// In this example, initialT and initialU are the initial values of T and U, and func1, func2, and func3 +// are functions that take two arguments of type T and U and return a value of type U. +func PipeTwoToOne[T, U any](t T, u U, pipeline ...func(T, U) U) U { + // NB: don't mutate potential value type `u` (i.e. non-pointer) + uRef := u + for _, fn := range pipeline { + uRef = fn(t, uRef) + } + + return u +} diff --git a/internal/testutil/generics/composition.go b/internal/testutil/generics/composition.go new file mode 100644 index 000000000..5e4d66b0f --- /dev/null +++ b/internal/testutil/generics/composition.go @@ -0,0 +1,30 @@ +package testutil + +// PipeTwoToOne threads two values of any type (T and U) through a pipeline of +// functions and returns the result of any type (U). Each function in the pipeline +// takes two arguments of type T and U, and returns a value of type U. +// +// Applies each function in the pipeline to the current value of U and the +// constant value of T, effectively "threading" the initial U value through the +// pipeline of functions. +// +// Does *not* mutate the original U value. Instead, it operates on a reference +// to U, ensuring that value types (non-pointer types) are not mutated. +// +// Returns the final value of U after it has been threaded through all the functions in the pipeline. +// +// Usage: +// +// result := PipeTwo(initialT, initialU, func1, func2, func3) +// +// In this example, initialT and initialU are the initial values of T and U, and func1, func2, and func3 +// are functions that take two arguments of type T and U and return a value of type U. +func PipeTwoToOne[T, U any](t T, u U, pipeline ...func(T, U) U) U { + // NB: don't mutate potential value type `u` (i.e. non-pointer) + uRef := u + for _, fn := range pipeline { + uRef = fn(t, uRef) + } + + return u +} diff --git a/internal/testutil/map.go b/internal/testutil/generics/map.go similarity index 100% rename from internal/testutil/map.go rename to internal/testutil/generics/map.go diff --git a/internal/testutil/generics/proxy.go b/internal/testutil/generics/proxy.go new file mode 100644 index 000000000..8dd837735 --- /dev/null +++ b/internal/testutil/generics/proxy.go @@ -0,0 +1,3 @@ +package generics_testutil + +type ProxyFactory[T any] func(target T) (proxy T) From 02b6b1e9fb03844dc21179031242bfd74556f67b Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:28:54 +0200 Subject: [PATCH 15/82] wip: testutils --- .../testutil/constructors/constructors.go | 25 +++---------------- shared/messaging/proto/debug_message.proto | 4 +++ 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 6c95ad422..3fd96a39a 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -3,6 +3,7 @@ package constructors import ( libp2pHost "github.com/libp2p/go-libp2p/core/host" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" + "github.com/pokt-network/pocket/internal/testutil/bus" consensus_testutil "github.com/pokt-network/pocket/internal/testutil/consensus" persistence_testutil "github.com/pokt-network/pocket/internal/testutil/persistence" telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" @@ -11,7 +12,6 @@ import ( "github.com/pokt-network/pocket/internal/testutil" p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" - runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" "github.com/pokt-network/pocket/p2p" "github.com/pokt-network/pocket/runtime/genesis" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" @@ -57,7 +57,7 @@ func NewBusesMocknetAndP2PModules( } privKey := privKeys[i] - busMock := NewBus(t, privKey, serviceURL, genesisState, busEventHandlerFactory) + busMock := bus_testutil.NewBus(t, privKey, serviceURL, genesisState, busEventHandlerFactory) buses[serviceURL] = busMock // TODO_THIS_COMMIT: refactor @@ -118,29 +118,10 @@ func NewBusesAndP2PModuleWithHost( ) (*mock_modules.MockBus, modules.P2PModule) { t.Helper() - busMock := NewBus(t, privKey, serviceURL, genesisState, busEventHandlerFactory) + busMock := bus_testutil.NewBus(t, privKey, serviceURL, genesisState, busEventHandlerFactory) return busMock, NewP2PModuleWithHost(t, busMock, host) } -func NewBus( - t gocuke.TestingT, - privKey cryptoPocket.PrivateKey, - serviceURL string, - genesisState *genesis.GenesisState, - busEventHandlerFactory testutil.BusEventHandlerFactory, -) *mock_modules.MockBus { - t.Helper() - - runtimeMgrMock := runtime_testutil.BaseRuntimeManagerMock( - t, privKey, - serviceURL, - genesisState, - ) - busMock := testutil.BusMockWithEventHandler(t, runtimeMgrMock, busEventHandlerFactory) - busMock.EXPECT().GetRuntimeMgr().Return(runtimeMgrMock).AnyTimes() - return busMock -} - func NewP2PModuleWithHost( t gocuke.TestingT, busMock *mock_modules.MockBus, diff --git a/shared/messaging/proto/debug_message.proto b/shared/messaging/proto/debug_message.proto index 7ce079afa..7e42f7ddd 100644 --- a/shared/messaging/proto/debug_message.proto +++ b/shared/messaging/proto/debug_message.proto @@ -30,6 +30,10 @@ message DebugMessage { google.protobuf.Any message = 3; } +message DebugStringMessage { + string value = 1; +} + // NB: See https://en.wikipedia.org/wiki/Routing for more info on routing and delivery schemes. enum DebugMessageRoutingType { DEBUG_MESSAGE_TYPE_UNKNOWN = 0; From f0aee7207feb37acbb97789dc69db04448568fe5 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:31:46 +0200 Subject: [PATCH 16/82] wip: testutils --- internal/testutil/bus.go | 7 +- .../testutil/constructors/constructors.go | 135 +++++++------- internal/testutil/module.go | 41 +++++ internal/testutil/network.go | 73 ++++++++ internal/testutil/p2p/network.go | 49 ++---- internal/testutil/runtime/genesis.go | 28 ++- p2p/module_raintree_test.go | 165 +++++++++--------- p2p/module_test.go | 3 +- p2p/transport_encryption_test.go | 11 +- 9 files changed, 322 insertions(+), 190 deletions(-) create mode 100644 internal/testutil/module.go create mode 100644 internal/testutil/network.go diff --git a/internal/testutil/bus.go b/internal/testutil/bus.go index 972965eca..1f83597a3 100644 --- a/internal/testutil/bus.go +++ b/internal/testutil/bus.go @@ -75,8 +75,11 @@ func WithBusEventHandler( ) *mock_modules.MockBus { t.Helper() - handler := handlerFactory(t, busMock) - busMock.EXPECT().PublishEventToBus(gomock.Any()).Do(handler).AnyTimes() + if handlerFactory != nil { + handler := handlerFactory(t, busMock) + busMock.EXPECT().PublishEventToBus(gomock.Any()).Do(handler).AnyTimes() + } + return busMock } diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 3fd96a39a..5491c981a 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -1,6 +1,7 @@ package constructors import ( + "github.com/foxcpp/go-mockdns" libp2pHost "github.com/libp2p/go-libp2p/core/host" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/pokt-network/pocket/internal/testutil/bus" @@ -25,6 +26,7 @@ type serviceURLStr = string func NewBusesMocknetAndP2PModules( t gocuke.TestingT, count int, + dnsSrv *mockdns.Server, genesisState *genesis.GenesisState, busEventHandlerFactory testutil.BusEventHandlerFactory, ) ( @@ -32,78 +34,92 @@ func NewBusesMocknetAndP2PModules( libp2pNetworkMock mocknet.Mocknet, p2pModules map[serviceURLStr]modules.P2PModule, ) { - // TODO_THIS_COMMIT: refactor - dnsSrv := testutil.MinimalDNSMock(t) - - libp2pNetworkMock = mocknet.New() - // destroy mocknet on test cleanup - t.Cleanup(func() { - err := libp2pNetworkMock.Close() - require.NoError(t, err) - }) - - buses = make(map[serviceURLStr]*mock_modules.MockBus) - p2pModules = make(map[serviceURLStr]modules.P2PModule) - // CONSIDERATION: using an iterator/generator would prevent unintentional - // ID collisions - privKeys := testutil.LoadLocalnetPrivateKeys(t, count) - // CONSIDERATION: using an iterator/generator would prevent unintentional - // serviceURL collisions - serviceURLs := p2p_testutil.SequentialServiceURLs(t, count) - for i, serviceURL := range serviceURLs { - if len(privKeys) <= i { - t.Logf("WARNING: not enough private keys for %d service URLs", len(serviceURLs)) - break - } - - privKey := privKeys[i] - busMock := bus_testutil.NewBus(t, privKey, serviceURL, genesisState, busEventHandlerFactory) - buses[serviceURL] = busMock - - // TODO_THIS_COMMIT: refactor - _ = consensus_testutil.BaseConsensusMock(t, busMock) - _ = persistence_testutil.BasePersistenceMock(t, busMock, genesisState) - - // -- option 1 - _ = telemetry_testutil.BaseTelemetryMock(t, busMock) - - // -- option 2 - //_ = telemetry_testutil.WithTimeSeriesAgent( - // t, telemetry_testutil.MinimalTelemetryMock(t, busMock), - //) - - // MUST register DNS before instantiating P2PModule - testutil.AddServiceURLZone(t, dnsSrv, serviceURL) - - host := p2p_testutil.NewMocknetHost(t, libp2pNetworkMock, privKey) - p2pModules[serviceURL] = NewP2PModuleWithHost(t, busMock, host) - } + libp2pNetworkMock = p2p_testutil.NewLibp2pNetworkMock(t) + serviceURLKeyMap := testutil.SequentialServiceURLPrivKeyMap(t, count) + + buses, p2pModules = NewBusesAndP2PModules( + t, busEventHandlerFactory, + dnsSrv, + genesisState, + libp2pNetworkMock, + serviceURLKeyMap, + ) err := libp2pNetworkMock.LinkAll() require.NoError(t, err) return buses, libp2pNetworkMock, p2pModules } -// TODO_THIS_TEST: need this? -func NewP2PModules( +// TODO_THIS_COMMIT: rename / move, if possible +func NewP2PModule( t gocuke.TestingT, - privKeys []cryptoPocket.PrivateKey, + // TODO_THIS_COMMIT: get these from the bus instead + serviceURL string, + privKey cryptoPocket.PrivateKey, + // -- busMock *mock_modules.MockBus, + genesisState *genesis.GenesisState, + dnsSrv *mockdns.Server, + libp2pNetworkMock mocknet.Mocknet, + // TODO_THIS_COMMIT: consider *p2p.P2PModule instead +) modules.P2PModule { + _ = consensus_testutil.BaseConsensusMock(t, busMock) + _ = persistence_testutil.BasePersistenceMock(t, busMock, genesisState) + + // -- option 1 + _ = telemetry_testutil.BaseTelemetryMock(t, busMock) + + // -- option 2 + //_ = telemetry_testutil.WithTimeSeriesAgent( + // t, telemetry_testutil.MinimalTelemetryMock(t, busMock), + //) + + // MUST register DNS before instantiating P2PModule + testutil.AddServiceURLZone(t, dnsSrv, serviceURL) + + host := testutil.NewMocknetHost(t, libp2pNetworkMock, privKey) + return NewP2PModuleWithHost(t, busMock, host) +} + +// TODO_THIS_TEST: need this? +func NewBusesAndP2PModules( + t gocuke.TestingT, + busEventHandlerFactory testutil.BusEventHandlerFactory, + dnsSrv *mockdns.Server, + genesisState *genesis.GenesisState, libp2pNetworkMock mocknet.Mocknet, + serviceURLKeyMap map[serviceURLStr]cryptoPocket.PrivateKey, ) ( + busMocks map[serviceURLStr]*mock_modules.MockBus, + // TODO_THIS_COMMIT: consider *p2p.P2PModule instead p2pModules map[serviceURLStr]modules.P2PModule, ) { - // CONSIDERATION: using an iterator/generator would prevent unintentional - // serviceURL collisions - serviceURLs := p2p_testutil.SequentialServiceURLs(t, len(privKeys)) - _ = p2p_testutil.SetupMockNetPeers(t, libp2pNetworkMock, privKeys, serviceURLs) - - for i, serviceURL := range serviceURLs { - host := libp2pNetworkMock.Hosts()[i] - // TECHDEBT: refactor - p2pModules[serviceURL] = NewP2PModuleWithHost(t, busMock, host) + busMocks = make(map[serviceURLStr]*mock_modules.MockBus) + p2pModules = make(map[serviceURLStr]modules.P2PModule) + + for serviceURL, privKey := range serviceURLKeyMap { + busMock := bus_testutil.NewBus( + t, privKey, + serviceURL, + genesisState, + busEventHandlerFactory, + ) + busMocks[serviceURL] = busMock + + p2pModules[serviceURL] = NewP2PModule( + t, serviceURL, + // TODO_THIS_COMMIT: send via busMock instead + privKey, + busMock, + // TODO_THIS_COMMIT: send via busMock instead + genesisState, + // TODO_THIS_COMMIT: send via busMock instead (?) + dnsSrv, + // TODO_THIS_COMMIT: send via busMock instead (?) + libp2pNetworkMock, + ) } - return p2pModules + return busMocks, p2pModules } // TODO_THIS_TEST: need this? @@ -122,6 +138,7 @@ func NewBusesAndP2PModuleWithHost( return busMock, NewP2PModuleWithHost(t, busMock, host) } +// TODO_THIS_COMMIT: rename; consider returning *p2p.P2PModule instead func NewP2PModuleWithHost( t gocuke.TestingT, busMock *mock_modules.MockBus, diff --git a/internal/testutil/module.go b/internal/testutil/module.go new file mode 100644 index 000000000..a59710b02 --- /dev/null +++ b/internal/testutil/module.go @@ -0,0 +1,41 @@ +package testutil + +import ( + "github.com/foxcpp/go-mockdns" + mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" + "github.com/pokt-network/pocket/runtime/genesis" + "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" +) + +// TODO_THIS_COMMIT: is this helpful? +const TestModuleName = "testModule" + +var ( + _ modules.Module = &TestModule{} + _ modules.ModuleFactoryWithOptions = &TestModule{} +) + +type TestModule struct { + base_modules.IntegratableModule + base_modules.InterruptableModule + + DNS *mockdns.Server + Genesis *genesis.GenesisState + Libp2pNetworkMock mocknet.Mocknet +} + +func (m *TestModule) GetModuleName() string { + return TestModuleName +} + +func (m *TestModule) Create( + bus modules.Bus, + opts ...modules.ModuleOption, +) (modules.Module, error) { + panic("implement me") +} + +func (m *TestModule) GetDNS() *mockdns.Server { + return m.DNS +} diff --git a/internal/testutil/network.go b/internal/testutil/network.go new file mode 100644 index 000000000..1403dca46 --- /dev/null +++ b/internal/testutil/network.go @@ -0,0 +1,73 @@ +package testutil + +import ( + "fmt" + + crypto2 "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/p2p/net/mock" + "github.com/multiformats/go-multiaddr" + "github.com/regen-network/gocuke" + "github.com/stretchr/testify/require" + + "github.com/pokt-network/pocket/runtime/defaults" + "github.com/pokt-network/pocket/shared/crypto" +) + +const ServiceURLFormat = "node%d.consensus:42069" + +func NewMocknetHost( + t gocuke.TestingT, + libp2pNetworkMock mocknet.Mocknet, + privKey crypto.PrivateKey, +) host.Host { + t.Helper() + + // TODO_THIS_COMMIT: move to const + addrMock, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/10.0.0.1/tcp/%d", defaults.DefaultP2PPort)) + require.NoError(t, err) + + libp2pPrivKey, err := crypto2.UnmarshalEd25519PrivateKey(privKey.Bytes()) + require.NoError(t, err) + + host, err := libp2pNetworkMock.AddPeer(libp2pPrivKey, addrMock) + require.NoError(t, err) + + return host +} + +func SequentialServiceURLPrivKeyMap(t gocuke.TestingT, count int) map[string]crypto.PrivateKey { + t.Helper() + + // CONSIDERATION: using an iterator/generator would prevent unintentional + // ID collisions + privKeys := LoadLocalnetPrivateKeys(t, count) + // CONSIDERATION: using an iterator/generator would prevent unintentional + // serviceURL collisions + serviceURLs := SequentialServiceURLs(t, count) + + require.GreaterOrEqualf(t, len(privKeys), len(serviceURLs), "not enough private keys for service URLs") + + serviceURLKeysMap := make(map[string]crypto.PrivateKey, len(serviceURLs)) + + for i, serviceURL := range serviceURLs { + serviceURLKeysMap[serviceURL] = privKeys[i] + } + return serviceURLKeysMap +} + +// CONSIDERATION: serviceURLs are only unique within their respective slice; +// consider building an iterator/generator instead. +func SequentialServiceURLs(t gocuke.TestingT, count int) (serviceURLs []string) { + t.Helper() + + for i := 0; i < count; i++ { + serviceURLs = append(serviceURLs, NewServiceURL(i+1)) + } + return serviceURLs +} + +// TECHDEBT: rename `validatorId()` to `serviceURL()` +func NewServiceURL(i int) string { + return fmt.Sprintf(ServiceURLFormat, i) +} diff --git a/internal/testutil/p2p/network.go b/internal/testutil/p2p/network.go index 77fdb9edb..23196a6c8 100644 --- a/internal/testutil/p2p/network.go +++ b/internal/testutil/p2p/network.go @@ -1,14 +1,10 @@ package p2p_testutil import ( - "fmt" "github.com/libp2p/go-libp2p/core/crypto" - libp2pHost "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" libp2pPeer "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/net/mock" - "github.com/multiformats/go-multiaddr" - "github.com/pokt-network/pocket/runtime/defaults" "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" @@ -25,30 +21,23 @@ func NewMocknetWithNPeers(t gocuke.TestingT, peerCount int) (mocknet.Mocknet, [] // load pre-generated validator keypairs libp2pNetworkMock := mocknet.New() privKeys := testutil.LoadLocalnetPrivateKeys(t, peerCount) - serviceURLs := SequentialServiceURLs(t, peerCount) + serviceURLs := testutil.SequentialServiceURLs(t, peerCount) _ = SetupMockNetPeers(t, libp2pNetworkMock, privKeys, serviceURLs) return libp2pNetworkMock, serviceURLs } -func NewMocknetHost( - t gocuke.TestingT, - libp2pNetworkMock mocknet.Mocknet, - privKey cryptoPocket.PrivateKey, -) libp2pHost.Host { +func NewLibp2pNetworkMock(t gocuke.TestingT) mocknet.Mocknet { t.Helper() - // TODO_THIS_COMMIT: move to const - addrMock, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/10.0.0.1/tcp/%d", defaults.DefaultP2PPort)) - require.NoError(t, err) - - libp2pPrivKey, err := crypto.UnmarshalEd25519PrivateKey(privKey.Bytes()) - require.NoError(t, err) - - host, err := libp2pNetworkMock.AddPeer(libp2pPrivKey, addrMock) - require.NoError(t, err) + libp2pNetworkMock := mocknet.New() + // destroy mocknet on test cleanup + t.Cleanup(func() { + err := libp2pNetworkMock.Close() + require.NoError(t, err) + }) - return host + return libp2pNetworkMock } func SetupMockNetPeers( @@ -85,17 +74,6 @@ func SetupMockNetPeers( return peerIDs } -// CONSIDERATION: serviceURLs are only unique within their respective slice; -// consider building an iterator/generator instead. -func SequentialServiceURLs(t gocuke.TestingT, count int) (serviceURLs []string) { - t.Helper() - - for i := 0; i < count; i++ { - serviceURLs = append(serviceURLs, NewServiceURL(i+1)) - } - return serviceURLs -} - func PeersFromPrivKeysAndServiceURLs( t gocuke.TestingT, privKeys []cryptoPocket.PrivateKey, @@ -110,7 +88,7 @@ func PeersFromPrivKeysAndServiceURLs( } for i, privKey := range privKeys[:maxCount] { - peerInfo := peerFromPrivKeyAndServiceURL(t, privKey, NewServiceURL(i+1)) + peerInfo := peerFromPrivKeyAndServiceURL(t, privKey, testutil.NewServiceURL(i+1)) peersInfo = append(peersInfo, peerInfo) } return peersInfo @@ -132,10 +110,3 @@ func peerFromPrivKeyAndServiceURL( return peerInfo } - -const ServiceURLFormat = "node%d.consensus:42069" - -// TECHDEBT: rename `validatorId()` to `serviceURL()` -func NewServiceURL(i int) string { - return fmt.Sprintf(ServiceURLFormat, i) -} diff --git a/internal/testutil/runtime/genesis.go b/internal/testutil/runtime/genesis.go index b14aac47d..47da6caed 100644 --- a/internal/testutil/runtime/genesis.go +++ b/internal/testutil/runtime/genesis.go @@ -1,7 +1,7 @@ package runtime_testutil import ( - "github.com/pokt-network/pocket/internal/testutil/p2p" + "github.com/pokt-network/pocket/internal/testutil" "github.com/pokt-network/pocket/runtime/genesis" "github.com/pokt-network/pocket/runtime/test_artifacts" "github.com/pokt-network/pocket/shared/core/types" @@ -33,12 +33,36 @@ func BaseGenesisStateMock(t gocuke.TestingT, valKeys []cryptoPocket.PublicKey, s return genesisState } +func BaseGenesisStateMockFromServiceURLKeyMap(t gocuke.TestingT, serviceURLKeyMap map[string]cryptoPocket.PrivateKey) *genesis.GenesisState { + t.Helper() + + var validators []*types.Actor + genesisState := new(genesis.GenesisState) + for serviceURL, privKey := range serviceURLKeyMap { + addr := privKey.Address().String() + mockValidator := &types.Actor{ + ActorType: types.ActorType_ACTOR_TYPE_VAL, + Address: addr, + PublicKey: privKey.PublicKey().String(), + ServiceUrl: serviceURL, + StakedAmount: test_artifacts.DefaultStakeAmountString, + PausedHeight: int64(0), + UnstakingHeight: int64(0), + Output: addr, + } + validators = append(validators, mockValidator) + } + genesisState.Validators = validators + + return genesisState +} + func GenesisWithSequentialServiceURLs(t gocuke.TestingT, valKeys []cryptoPocket.PublicKey) *genesis.GenesisState { t.Helper() serviceURLs := make([]string, len(valKeys)) for i := range valKeys { - serviceURLs[i] = p2p_testutil.NewServiceURL(i + 1) + serviceURLs[i] = testutil.NewServiceURL(i + 1) } return BaseGenesisStateMock(t, valKeys, serviceURLs) } diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index 98a8d4297..e8058a672 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -22,7 +22,6 @@ import ( "github.com/pokt-network/pocket/internal/testutil" "github.com/pokt-network/pocket/internal/testutil/constructors" - "github.com/pokt-network/pocket/internal/testutil/p2p" persistence_testutil "github.com/pokt-network/pocket/internal/testutil/persistence" "github.com/pokt-network/pocket/shared/messaging" ) @@ -44,7 +43,7 @@ func TestMain(m *testing.M) { // ### RainTree Unit Tests ### func TestRainTreeNetworkCompleteOneNodes(t *testing.T) { // val_1 - originatorNode := p2p_testutil.NewServiceURL(1) + originatorNode := testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ originatorNode: {0, 0}, // val_1, the originator, does 0 network reads or writes } @@ -55,13 +54,13 @@ func TestRainTreeNetworkCompleteTwoNodes(t *testing.T) { // val_1 // └───────┐ // val_2 - originatorNode := p2p_testutil.NewServiceURL(1) + originatorNode := testutil.NewServiceURL(1) // Per the diagram above, in the case of a 2 node network, the originator node (val_1) does a // single write to another node (val_2), also the // originator node and never performs any reads or writes during a RainTree broadcast. expectedCalls := TestNetworkSimulationConfig{ // Attempt: I think Validator 1 is sending a message in a 2 (including self) node network originatorNode: {0, 1}, // val_1 does a single network write (to val_2) - p2p_testutil.NewServiceURL(2): {1, 0}, // val_2 does a single network read (from val_1) + testutil.NewServiceURL(2): {1, 0}, // val_2 does a single network read (from val_1) } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -70,11 +69,11 @@ func TestRainTreeNetworkCompleteThreeNodes(t *testing.T) { // val_1 // ┌───────┴────┬─────────┐ // val_2 val_1 val_3 - originatorNode := p2p_testutil.NewServiceURL(1) + originatorNode := testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {0, 2}, // val_1 does two network writes (to val_2 and val_3) - p2p_testutil.NewServiceURL(2): {1, 0}, // val_2 does a single network read (from val_1) - p2p_testutil.NewServiceURL(3): {1, 0}, // val_2 does a single network read (from val_3) + originatorNode: {0, 2}, // val_1 does two network writes (to val_2 and val_3) + testutil.NewServiceURL(2): {1, 0}, // val_2 does a single network read (from val_1) + testutil.NewServiceURL(3): {1, 0}, // val_2 does a single network read (from val_3) } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -86,12 +85,12 @@ func TestRainTreeNetworkCompleteFourNodes(t *testing.T) { // val_2 val_1 val_3 // └───────┐ └───────┐ └───────┐ // val_3 val_2 val_4 - originatorNode := p2p_testutil.NewServiceURL(1) + originatorNode := testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {0, 3}, // val_1 does 3 network writes (two to val_2 and 1 to val_3) - p2p_testutil.NewServiceURL(2): {2, 1}, // val_2 does 2 network reads (both from val_1) and 1 network write (to val_3) - p2p_testutil.NewServiceURL(3): {2, 1}, // val_2 does 2 network reads (from val_1 and val_2) and 1 network write (to val_4) - p2p_testutil.NewServiceURL(4): {1, 0}, // val_2 does 1 network read (from val_3) + originatorNode: {0, 3}, // val_1 does 3 network writes (two to val_2 and 1 to val_3) + testutil.NewServiceURL(2): {2, 1}, // val_2 does 2 network reads (both from val_1) and 1 network write (to val_3) + testutil.NewServiceURL(3): {2, 1}, // val_2 does 2 network reads (from val_1 and val_2) and 1 network write (to val_4) + testutil.NewServiceURL(4): {1, 0}, // val_2 does 1 network read (from val_3) } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -102,17 +101,17 @@ func TestRainTreeNetworkCompleteNineNodes(t *testing.T) { // val_4 val_1 val_7 // ┌───────┴────┬─────────┐ ┌───────┴────┬─────────┐ ┌───────┴────┬─────────┐ // val_6 val_4 val_8 val_3 val_1 val_5 val_9 val_7 val_2 - originatorNode := p2p_testutil.NewServiceURL(1) + originatorNode := testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {0, 4}, - p2p_testutil.NewServiceURL(2): {1, 0}, - p2p_testutil.NewServiceURL(3): {1, 0}, - p2p_testutil.NewServiceURL(4): {1, 2}, - p2p_testutil.NewServiceURL(5): {1, 0}, - p2p_testutil.NewServiceURL(6): {1, 0}, - p2p_testutil.NewServiceURL(7): {1, 2}, - p2p_testutil.NewServiceURL(8): {1, 0}, - p2p_testutil.NewServiceURL(9): {1, 0}, + originatorNode: {0, 4}, + testutil.NewServiceURL(2): {1, 0}, + testutil.NewServiceURL(3): {1, 0}, + testutil.NewServiceURL(4): {1, 2}, + testutil.NewServiceURL(5): {1, 0}, + testutil.NewServiceURL(6): {1, 0}, + testutil.NewServiceURL(7): {1, 2}, + testutil.NewServiceURL(8): {1, 0}, + testutil.NewServiceURL(9): {1, 0}, } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -126,20 +125,20 @@ func TestRainTreeNetworkCompleteNineNodes(t *testing.T) { // val_8 val_7 val_10 val_6 val_5 val_8 val_11 val_10 val_5 val_4 val_3 val_6 val_2 val_1 val_4 val_7 val_6 val_1 val_12 val_11 val_2 val_10 val_9 val_12 val_3 val_2 val_9 func TestRainTreeCompleteTwelveNodes(t *testing.T) { - originatorNode := p2p_testutil.NewServiceURL(1) + originatorNode := testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {1, 6}, - p2p_testutil.NewServiceURL(2): {3, 2}, - p2p_testutil.NewServiceURL(3): {2, 2}, - p2p_testutil.NewServiceURL(4): {2, 0}, - p2p_testutil.NewServiceURL(5): {2, 4}, - p2p_testutil.NewServiceURL(6): {3, 2}, - p2p_testutil.NewServiceURL(7): {2, 2}, - p2p_testutil.NewServiceURL(8): {2, 0}, - p2p_testutil.NewServiceURL(9): {2, 4}, - p2p_testutil.NewServiceURL(10): {3, 2}, - p2p_testutil.NewServiceURL(11): {2, 2}, - p2p_testutil.NewServiceURL(12): {2, 0}, + originatorNode: {1, 6}, + testutil.NewServiceURL(2): {3, 2}, + testutil.NewServiceURL(3): {2, 2}, + testutil.NewServiceURL(4): {2, 0}, + testutil.NewServiceURL(5): {2, 4}, + testutil.NewServiceURL(6): {3, 2}, + testutil.NewServiceURL(7): {2, 2}, + testutil.NewServiceURL(8): {2, 0}, + testutil.NewServiceURL(9): {2, 4}, + testutil.NewServiceURL(10): {3, 2}, + testutil.NewServiceURL(11): {2, 2}, + testutil.NewServiceURL(12): {2, 0}, } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -152,26 +151,26 @@ func TestRainTreeNetworkCompleteEighteenNodes(t *testing.T) { // val_11 val_7 val_15 val_5 val_1 val_9 val_17 val_13 val_3 // ┌────────┴─────┬───────────┐ ┌───────┴────┬──────────┐ ┌────────┴─────┬──────────┐ ┌───────┴────┬──────────┐ ┌───────┴────┬─────────┐ ┌────────┴────┬─────────┐ ┌───────┴─────┬──────────┐ ┌────────┴─────┬───────────┐ ┌───────┴────┬──────────┐ // val_13 val_11 val_16 val_9 val_7 val_12 val_17 val_15 val_8 val_7 val_5 val_10 val_3 val_1 val_6 val_11 val_9 val_2 val_1 val_17 val_4 val_15 val_13 val_18 val_5 val_3 val_14 - originatorNode := p2p_testutil.NewServiceURL(1) + originatorNode := testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {1, 6}, - p2p_testutil.NewServiceURL(2): {1, 0}, - p2p_testutil.NewServiceURL(3): {2, 2}, - p2p_testutil.NewServiceURL(4): {1, 0}, - p2p_testutil.NewServiceURL(5): {2, 2}, - p2p_testutil.NewServiceURL(6): {1, 0}, - p2p_testutil.NewServiceURL(7): {2, 4}, - p2p_testutil.NewServiceURL(8): {1, 0}, - p2p_testutil.NewServiceURL(9): {2, 2}, - p2p_testutil.NewServiceURL(10): {1, 0}, - p2p_testutil.NewServiceURL(11): {2, 2}, - p2p_testutil.NewServiceURL(12): {1, 0}, - p2p_testutil.NewServiceURL(13): {2, 4}, - p2p_testutil.NewServiceURL(14): {1, 0}, - p2p_testutil.NewServiceURL(15): {2, 2}, - p2p_testutil.NewServiceURL(16): {1, 0}, - p2p_testutil.NewServiceURL(17): {2, 2}, - p2p_testutil.NewServiceURL(18): {1, 0}, + originatorNode: {1, 6}, + testutil.NewServiceURL(2): {1, 0}, + testutil.NewServiceURL(3): {2, 2}, + testutil.NewServiceURL(4): {1, 0}, + testutil.NewServiceURL(5): {2, 2}, + testutil.NewServiceURL(6): {1, 0}, + testutil.NewServiceURL(7): {2, 4}, + testutil.NewServiceURL(8): {1, 0}, + testutil.NewServiceURL(9): {2, 2}, + testutil.NewServiceURL(10): {1, 0}, + testutil.NewServiceURL(11): {2, 2}, + testutil.NewServiceURL(12): {1, 0}, + testutil.NewServiceURL(13): {2, 4}, + testutil.NewServiceURL(14): {1, 0}, + testutil.NewServiceURL(15): {2, 2}, + testutil.NewServiceURL(16): {1, 0}, + testutil.NewServiceURL(17): {2, 2}, + testutil.NewServiceURL(18): {1, 0}, } testRainTreeCalls(t, originatorNode, expectedCalls) } @@ -184,35 +183,35 @@ func TestRainTreeNetworkCompleteTwentySevenNodes(t *testing.T) { // val_16 val_10 val_22 val_7 val_1 val_13 val_25 val_19 val_4 // ┌────────┴─────┬───────────┐ ┌────────┴─────┬───────────┐ ┌────────┴─────┬───────────┐ ┌────────┴────┬──────────┐ ┌───────┴────┬─────────┐ ┌────────┴─────┬──────────┐ ┌───────┴─────┬──────────┐ ┌────────┴─────┬───────────┐ ┌───────┴────┬──────────┐ // val_20 val_16 val_24 val_14 val_10 val_18 val_26 val_22 val_12 val_11 val_7 val_15 val_5 val_1 val_9 val_17 val_13 val_3 val_2 val_25 val_6 val_23 val_19 val_27 val_8 val_4 val_21 - originatorNode := p2p_testutil.NewServiceURL(1) + originatorNode := testutil.NewServiceURL(1) expectedCalls := TestNetworkSimulationConfig{ - originatorNode: {0, 6}, - p2p_testutil.NewServiceURL(2): {1, 0}, - p2p_testutil.NewServiceURL(3): {1, 0}, - p2p_testutil.NewServiceURL(4): {1, 2}, - p2p_testutil.NewServiceURL(5): {1, 0}, - p2p_testutil.NewServiceURL(6): {1, 0}, - p2p_testutil.NewServiceURL(7): {1, 2}, - p2p_testutil.NewServiceURL(8): {1, 0}, - p2p_testutil.NewServiceURL(9): {1, 0}, - p2p_testutil.NewServiceURL(10): {1, 4}, - p2p_testutil.NewServiceURL(11): {1, 0}, - p2p_testutil.NewServiceURL(12): {1, 0}, - p2p_testutil.NewServiceURL(13): {1, 2}, - p2p_testutil.NewServiceURL(14): {1, 0}, - p2p_testutil.NewServiceURL(15): {1, 0}, - p2p_testutil.NewServiceURL(16): {1, 2}, - p2p_testutil.NewServiceURL(17): {1, 0}, - p2p_testutil.NewServiceURL(18): {1, 0}, - p2p_testutil.NewServiceURL(19): {1, 4}, - p2p_testutil.NewServiceURL(20): {1, 0}, - p2p_testutil.NewServiceURL(21): {1, 0}, - p2p_testutil.NewServiceURL(22): {1, 2}, - p2p_testutil.NewServiceURL(23): {1, 0}, - p2p_testutil.NewServiceURL(24): {1, 0}, - p2p_testutil.NewServiceURL(25): {1, 2}, - p2p_testutil.NewServiceURL(26): {1, 0}, - p2p_testutil.NewServiceURL(27): {1, 0}, + originatorNode: {0, 6}, + testutil.NewServiceURL(2): {1, 0}, + testutil.NewServiceURL(3): {1, 0}, + testutil.NewServiceURL(4): {1, 2}, + testutil.NewServiceURL(5): {1, 0}, + testutil.NewServiceURL(6): {1, 0}, + testutil.NewServiceURL(7): {1, 2}, + testutil.NewServiceURL(8): {1, 0}, + testutil.NewServiceURL(9): {1, 0}, + testutil.NewServiceURL(10): {1, 4}, + testutil.NewServiceURL(11): {1, 0}, + testutil.NewServiceURL(12): {1, 0}, + testutil.NewServiceURL(13): {1, 2}, + testutil.NewServiceURL(14): {1, 0}, + testutil.NewServiceURL(15): {1, 0}, + testutil.NewServiceURL(16): {1, 2}, + testutil.NewServiceURL(17): {1, 0}, + testutil.NewServiceURL(18): {1, 0}, + testutil.NewServiceURL(19): {1, 4}, + testutil.NewServiceURL(20): {1, 0}, + testutil.NewServiceURL(21): {1, 0}, + testutil.NewServiceURL(22): {1, 2}, + testutil.NewServiceURL(23): {1, 0}, + testutil.NewServiceURL(24): {1, 0}, + testutil.NewServiceURL(25): {1, 2}, + testutil.NewServiceURL(26): {1, 0}, + testutil.NewServiceURL(27): {1, 0}, } testRainTreeCalls(t, originatorNode, expectedCalls) } diff --git a/p2p/module_test.go b/p2p/module_test.go index c2503f699..d39c9f097 100644 --- a/p2p/module_test.go +++ b/p2p/module_test.go @@ -12,7 +12,6 @@ import ( "github.com/stretchr/testify/require" "github.com/pokt-network/pocket/internal/testutil" - p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" "github.com/pokt-network/pocket/internal/testutil/persistence" "github.com/pokt-network/pocket/internal/testutil/runtime" typesP2P "github.com/pokt-network/pocket/p2p/types" @@ -126,7 +125,7 @@ func Test_Create_configureBootstrapNodes(t *testing.T) { pubKeys[i] = privKey.PublicKey() } - serviceURLs := p2p_testutil.SequentialServiceURLs(t, len(pubKeys)) + serviceURLs := testutil.SequentialServiceURLs(t, len(pubKeys)) genesisStateMock := runtime_testutil.BaseGenesisStateMock(t, pubKeys, serviceURLs) persistenceMock := persistence_testutil.BasePersistenceMock(t, mockBus, genesisStateMock) mockBus.EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() diff --git a/p2p/transport_encryption_test.go b/p2p/transport_encryption_test.go index f2f2dbec7..86e3c0f1d 100644 --- a/p2p/transport_encryption_test.go +++ b/p2p/transport_encryption_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "github.com/pokt-network/pocket/internal/testutil" - p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" "github.com/pokt-network/pocket/internal/testutil/persistence" "github.com/pokt-network/pocket/internal/testutil/runtime" "github.com/pokt-network/pocket/internal/testutil/telemetry" @@ -56,8 +55,14 @@ func TestP2pModule_Insecure_Error(t *testing.T) { busMock.EXPECT().GetTelemetryModule().Return(telemetryMock).AnyTimes() keys := testutil.LoadLocalnetPrivateKeys(t, 1) - serviceURLs := p2p_testutil.SequentialServiceURLs(t, 1) - genesisStateMock := runtime_testutil.BaseGenesisStateMock(t, keys, serviceURLs) + + // TODO_THIS_COMMIT: refactor + pubKeys := make([]cryptoPocket.PublicKey, len(keys)) + for i, privKey := range keys { + pubKeys[i] = privKey.PublicKey() + } + serviceURLs := testutil.SequentialServiceURLs(t, len(pubKeys)) + genesisStateMock := runtime_testutil.BaseGenesisStateMock(t, pubKeys, serviceURLs) persistenceMock := persistence_testutil.BasePersistenceMock(t, busMock, genesisStateMock) busMock.EXPECT().GetPersistenceModule().Return(persistenceMock).AnyTimes() From d0acc92476211936b6b1682d20a25dabd1ba42fe Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:34:10 +0200 Subject: [PATCH 17/82] wip: testutils --- .../testutil/constructors/constructors.go | 27 +++++++---------- internal/testutil/network.go | 29 +++++++++++++++++++ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 5491c981a..19175f8bd 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -10,6 +10,8 @@ import ( telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" + "net" + "strconv" "github.com/pokt-network/pocket/internal/testutil" p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" @@ -53,16 +55,13 @@ func NewBusesMocknetAndP2PModules( // TODO_THIS_COMMIT: rename / move, if possible func NewP2PModule( t gocuke.TestingT, - // TODO_THIS_COMMIT: get these from the bus instead - serviceURL string, - privKey cryptoPocket.PrivateKey, - // -- busMock *mock_modules.MockBus, - genesisState *genesis.GenesisState, dnsSrv *mockdns.Server, libp2pNetworkMock mocknet.Mocknet, // TODO_THIS_COMMIT: consider *p2p.P2PModule instead ) modules.P2PModule { + genesisState := busMock.GetRuntimeMgr().GetGenesis() + _ = consensus_testutil.BaseConsensusMock(t, busMock) _ = persistence_testutil.BasePersistenceMock(t, busMock, genesisState) @@ -74,6 +73,11 @@ func NewP2PModule( // t, telemetry_testutil.MinimalTelemetryMock(t, busMock), //) + p2pCfg := busMock.GetRuntimeMgr().GetConfig().P2P + serviceURL := net.JoinHostPort(p2pCfg.Hostname, strconv.Itoa(int(p2pCfg.Port))) + privKey, err := cryptoPocket.NewPrivateKey(p2pCfg.PrivateKey) + require.NoError(t, err) + // MUST register DNS before instantiating P2PModule testutil.AddServiceURLZone(t, dnsSrv, serviceURL) @@ -106,18 +110,7 @@ func NewBusesAndP2PModules( ) busMocks[serviceURL] = busMock - p2pModules[serviceURL] = NewP2PModule( - t, serviceURL, - // TODO_THIS_COMMIT: send via busMock instead - privKey, - busMock, - // TODO_THIS_COMMIT: send via busMock instead - genesisState, - // TODO_THIS_COMMIT: send via busMock instead (?) - dnsSrv, - // TODO_THIS_COMMIT: send via busMock instead (?) - libp2pNetworkMock, - ) + p2pModules[serviceURL] = NewP2PModule(t, busMock, dnsSrv, libp2pNetworkMock) } return busMocks, p2pModules } diff --git a/internal/testutil/network.go b/internal/testutil/network.go index 1403dca46..e59dd2ab0 100644 --- a/internal/testutil/network.go +++ b/internal/testutil/network.go @@ -2,6 +2,7 @@ package testutil import ( "fmt" + "github.com/libp2p/go-libp2p/core/network" crypto2 "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/host" @@ -71,3 +72,31 @@ func SequentialServiceURLs(t gocuke.TestingT, count int) (serviceURLs []string) func NewServiceURL(i int) string { return fmt.Sprintf(ServiceURLFormat, i) } + +// TODO_THIS_COMMIT: move +func NewDebugNotifee(t gocuke.TestingT) network.Notifiee { + t.Helper() + + return &network.NotifyBundle{ + ConnectedF: func(_ network.Network, conn network.Conn) { + t.Logf("connected: local: %s; remote: %s", + conn.LocalPeer().String(), + conn.RemotePeer().String(), + ) + //bootstrapPeerIDCh <- conn.RemotePeer().String() + //bootstrapWaitgroup.Done() + }, + DisconnectedF: func(_ network.Network, conn network.Conn) { + t.Logf("disconnected: local: %s; remote: %s", + conn.LocalPeer().String(), + conn.RemotePeer().String(), + ) + }, + ListenF: func(_ network.Network, addr multiaddr.Multiaddr) { + t.Logf("listening: %s", addr.String()) + }, + ListenCloseF: func(_ network.Network, addr multiaddr.Multiaddr) { + t.Logf("closed: %s", addr.String()) + }, + } +} From 7f699101855e045396d8915a4e1f6b5160d6318c Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:34:43 +0200 Subject: [PATCH 18/82] wip: testutils --- .../testutil/constructors/constructors.go | 14 ++++++++++++-- internal/testutil/network.go | 19 ++++++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/internal/testutil/constructors/constructors.go b/internal/testutil/constructors/constructors.go index 19175f8bd..2d6e7900f 100644 --- a/internal/testutil/constructors/constructors.go +++ b/internal/testutil/constructors/constructors.go @@ -3,6 +3,7 @@ package constructors import ( "github.com/foxcpp/go-mockdns" libp2pHost "github.com/libp2p/go-libp2p/core/host" + libp2pNetwork "github.com/libp2p/go-libp2p/core/network" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/pokt-network/pocket/internal/testutil/bus" consensus_testutil "github.com/pokt-network/pocket/internal/testutil/consensus" @@ -31,6 +32,7 @@ func NewBusesMocknetAndP2PModules( dnsSrv *mockdns.Server, genesisState *genesis.GenesisState, busEventHandlerFactory testutil.BusEventHandlerFactory, + notifiee libp2pNetwork.Notifiee, ) ( buses map[serviceURLStr]*mock_modules.MockBus, libp2pNetworkMock mocknet.Mocknet, @@ -45,6 +47,7 @@ func NewBusesMocknetAndP2PModules( genesisState, libp2pNetworkMock, serviceURLKeyMap, + notifiee, ) err := libp2pNetworkMock.LinkAll() require.NoError(t, err) @@ -58,6 +61,7 @@ func NewP2PModule( busMock *mock_modules.MockBus, dnsSrv *mockdns.Server, libp2pNetworkMock mocknet.Mocknet, + notifiee libp2pNetwork.Notifiee, // TODO_THIS_COMMIT: consider *p2p.P2PModule instead ) modules.P2PModule { genesisState := busMock.GetRuntimeMgr().GetGenesis() @@ -81,7 +85,7 @@ func NewP2PModule( // MUST register DNS before instantiating P2PModule testutil.AddServiceURLZone(t, dnsSrv, serviceURL) - host := testutil.NewMocknetHost(t, libp2pNetworkMock, privKey) + host := testutil.NewMocknetHost(t, libp2pNetworkMock, privKey, notifiee) return NewP2PModuleWithHost(t, busMock, host) } @@ -93,6 +97,7 @@ func NewBusesAndP2PModules( genesisState *genesis.GenesisState, libp2pNetworkMock mocknet.Mocknet, serviceURLKeyMap map[serviceURLStr]cryptoPocket.PrivateKey, + notifiee libp2pNetwork.Notifiee, ) ( busMocks map[serviceURLStr]*mock_modules.MockBus, // TODO_THIS_COMMIT: consider *p2p.P2PModule instead @@ -110,7 +115,12 @@ func NewBusesAndP2PModules( ) busMocks[serviceURL] = busMock - p2pModules[serviceURL] = NewP2PModule(t, busMock, dnsSrv, libp2pNetworkMock) + p2pModules[serviceURL] = NewP2PModule( + t, busMock, + dnsSrv, + libp2pNetworkMock, + notifiee, + ) } return busMocks, p2pModules } diff --git a/internal/testutil/network.go b/internal/testutil/network.go index e59dd2ab0..f7943156d 100644 --- a/internal/testutil/network.go +++ b/internal/testutil/network.go @@ -2,10 +2,10 @@ package testutil import ( "fmt" - "github.com/libp2p/go-libp2p/core/network" crypto2 "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/host" + libp2pNetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/multiformats/go-multiaddr" "github.com/regen-network/gocuke" @@ -21,6 +21,7 @@ func NewMocknetHost( t gocuke.TestingT, libp2pNetworkMock mocknet.Mocknet, privKey crypto.PrivateKey, + notifiee libp2pNetwork.Notifiee, ) host.Host { t.Helper() @@ -34,6 +35,10 @@ func NewMocknetHost( host, err := libp2pNetworkMock.AddPeer(libp2pPrivKey, addrMock) require.NoError(t, err) + if notifiee != nil { + host.Network().Notify(notifiee) + } + return host } @@ -74,11 +79,11 @@ func NewServiceURL(i int) string { } // TODO_THIS_COMMIT: move -func NewDebugNotifee(t gocuke.TestingT) network.Notifiee { +func NewDebugNotifee(t gocuke.TestingT) libp2pNetwork.Notifiee { t.Helper() - return &network.NotifyBundle{ - ConnectedF: func(_ network.Network, conn network.Conn) { + return &libp2pNetwork.NotifyBundle{ + ConnectedF: func(_ libp2pNetwork.Network, conn libp2pNetwork.Conn) { t.Logf("connected: local: %s; remote: %s", conn.LocalPeer().String(), conn.RemotePeer().String(), @@ -86,16 +91,16 @@ func NewDebugNotifee(t gocuke.TestingT) network.Notifiee { //bootstrapPeerIDCh <- conn.RemotePeer().String() //bootstrapWaitgroup.Done() }, - DisconnectedF: func(_ network.Network, conn network.Conn) { + DisconnectedF: func(_ libp2pNetwork.Network, conn libp2pNetwork.Conn) { t.Logf("disconnected: local: %s; remote: %s", conn.LocalPeer().String(), conn.RemotePeer().String(), ) }, - ListenF: func(_ network.Network, addr multiaddr.Multiaddr) { + ListenF: func(_ libp2pNetwork.Network, addr multiaddr.Multiaddr) { t.Logf("listening: %s", addr.String()) }, - ListenCloseF: func(_ network.Network, addr multiaddr.Multiaddr) { + ListenCloseF: func(_ libp2pNetwork.Network, addr multiaddr.Multiaddr) { t.Logf("closed: %s", addr.String()) }, } From 2e4f26a8b50bcaec3a89c419bba0bda50a713b45 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:37:09 +0200 Subject: [PATCH 19/82] wip: testutils - p2p module test refactor --- p2p/module_raintree_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index e8058a672..c67c6fb34 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -222,6 +222,8 @@ func TestRainTreeNetworkCompleteTwentySevenNodes(t *testing.T) { // 1. It creates and configures a "real" P2P module where all the other components of the node are mocked. // 2. It then triggers a single message and waits for all of the expected messages transmission to complete before announcing failure. func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig TestNetworkSimulationConfig) { + dnsSrv := testutil.MinimalDNSMock(t) + // Configure & prepare test module numValidators := len(networkSimulationConfig) //runtimeConfigs := createMockRuntimeMgrs(t, numValidators) @@ -255,8 +257,10 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te busMocks, _, p2pModules = constructors.NewBusesMocknetAndP2PModules( t, numValidators, + dnsSrv, genesisState, busEventHandlerFactory, + nil, ) //for _, busMock := range busMocks { From f956fcc2a85b4d4902445637e942ce58d0e01d59 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:37:50 +0200 Subject: [PATCH 20/82] wip: testutils --- internal/testutil/network.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/testutil/network.go b/internal/testutil/network.go index f7943156d..a86d5a866 100644 --- a/internal/testutil/network.go +++ b/internal/testutil/network.go @@ -88,8 +88,6 @@ func NewDebugNotifee(t gocuke.TestingT) libp2pNetwork.Notifiee { conn.LocalPeer().String(), conn.RemotePeer().String(), ) - //bootstrapPeerIDCh <- conn.RemotePeer().String() - //bootstrapWaitgroup.Done() }, DisconnectedF: func(_ libp2pNetwork.Network, conn libp2pNetwork.Conn) { t.Logf("disconnected: local: %s; remote: %s", From b7203cb5ca68d3af28febba59bb39a57e4a501cd Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 09:07:15 +0200 Subject: [PATCH 21/82] wip: testutils --- internal/testutil/p2p/network.go | 28 +++++++ p2p/background/router_test.go | 124 ++++++++++++++++++++----------- 2 files changed, 109 insertions(+), 43 deletions(-) diff --git a/internal/testutil/p2p/network.go b/internal/testutil/p2p/network.go index 23196a6c8..2fd47f150 100644 --- a/internal/testutil/p2p/network.go +++ b/internal/testutil/p2p/network.go @@ -1,17 +1,22 @@ package p2p_testutil import ( + "testing" + "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" libp2pPeer "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" "github.com/pokt-network/pocket/internal/testutil" "github.com/pokt-network/pocket/p2p/types" "github.com/pokt-network/pocket/p2p/utils" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/pocket/shared/messaging" ) // TODO: remove if not needed @@ -110,3 +115,26 @@ func peerFromPrivKeyAndServiceURL( return peerInfo } + +func NewTestPoktEnvelopeBz(t *testing.T, msg string) []byte { + debugMsg := NewDebugStringMessage(t, msg) + + poktEnvelope, err := messaging.PackMessage(debugMsg) + require.NoError(t, err) + + poktEnvelopeBz, err := proto.Marshal(poktEnvelope) + require.NoError(t, err) + + return poktEnvelopeBz +} + +func NewDebugStringMessage(t gocuke.TestingT, msg string) *messaging.DebugMessage { + debugStringMsg, err := anypb.New(&messaging.DebugStringMessage{Value: msg}) + require.NoError(t, err) + + return &messaging.DebugMessage{ + Action: messaging.DebugMessageAction_DEBUG_ACTION_UNKNOWN, + Type: messaging.DebugMessageRoutingType_DEBUG_MESSAGE_TYPE_BROADCAST, + Message: debugStringMsg, + } +} diff --git a/p2p/background/router_test.go b/p2p/background/router_test.go index a1a0fe40b..6b3219d12 100644 --- a/p2p/background/router_test.go +++ b/p2p/background/router_test.go @@ -14,7 +14,11 @@ import ( libp2pPeer "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/multiformats/go-multiaddr" - "github.com/pokt-network/pocket/internal/testutil" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + "github.com/pokt-network/pocket/internal/testutil/generics" + "github.com/pokt-network/pocket/internal/testutil/p2p" "github.com/pokt-network/pocket/p2p/config" typesP2P "github.com/pokt-network/pocket/p2p/types" mock_types "github.com/pokt-network/pocket/p2p/types/mocks" @@ -22,16 +26,28 @@ import ( "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/runtime/defaults" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/pocket/shared/messaging" mockModules "github.com/pokt-network/pocket/shared/modules/mocks" - "github.com/stretchr/testify/require" ) // https://www.rfc-editor.org/rfc/rfc3986#section-3.2.2 -const testIP6ServiceURL = "[2a00:1450:4005:802::2004]:8080" +const ( + testIP6ServiceURL = "[2a00:1450:4005:802::2004]:8080" + numPeers = 4 + testMsg = "test messsage" + testTimeoutDuration = time.Second * 2 +) // TECHDEBT(#609): move & de-dup. var testLocalServiceURL = fmt.Sprintf("127.0.0.1:%d", defaults.DefaultP2PPort) +func TestBackgroundRouter_InvalidConfig(t *testing.T) { + t.Skip("pending") + //busMock := bus_testutil.NewBus(t) + // + //router, err := NewBackgroundRouter() +} + func TestBackgroundRouter_AddPeer(t *testing.T) { testRouter := newTestRouter(t, nil) libp2pPStore := testRouter.host.Peerstore() @@ -115,19 +131,15 @@ func TestBackgroundRouter_RemovePeer(t *testing.T) { } func TestBackgroundRouter_Broadcast(t *testing.T) { - const ( - numPeers = 4 - testMsg = "test messsage" - testTimeoutDuration = time.Second * 5 - ) - var ( ctx = context.Background() // mutex preventing concurrent writes to `seenMessages` - seenMessagesMutext sync.Mutex + seenMessagesMutex sync.Mutex // map used as a set to collect IDs of peers which have received a message seenMessages = make(map[string]struct{}) bootstrapWaitgroup = sync.WaitGroup{} + bootstrapPeerIDCh = make(chan string) + bootstrapPeerIDs = make(map[string]struct{}) broadcastWaitgroup = sync.WaitGroup{} broadcastDone = make(chan struct{}, 1) testTimeout = time.After(testTimeoutDuration) @@ -148,9 +160,31 @@ func TestBackgroundRouter_Broadcast(t *testing.T) { testHosts = append(testHosts, host) expectedPeerIDs[i] = host.ID().String() rtr := newRouterWithSelfPeerAndHost(t, selfPeer, host) - go readSubscription(t, ctx, &broadcastWaitgroup, rtr, &seenMessagesMutext, seenMessages) + rtr.HandlerProxy(t, func(origHandler typesP2P.RouterHandler) typesP2P.RouterHandler { + return func(data []byte) error { + seenMessagesMutex.Lock() + broadcastWaitgroup.Done() + seenMessages[rtr.host.ID().String()] = struct{}{} + seenMessagesMutex.Unlock() + + return origHandler(data) + } + }) } + // concurrently update the set of bootstrapped peer IDs as they connect + go func() { + for { + peerIDStr := <-bootstrapPeerIDCh + if _, ok := bootstrapPeerIDs[peerIDStr]; ok { + // already connected to this peer during bootstrapping + continue + } + bootstrapPeerIDs[peerIDStr] = struct{}{} + bootstrapWaitgroup.Done() + } + }() + // bootstrap off of arbitrary testHost privKey, selfPeer := newTestPeer(t) @@ -166,9 +200,14 @@ func TestBackgroundRouter_Broadcast(t *testing.T) { // setup notifee/notify BEFORE bootstrapping notifee := &libp2pNetwork.NotifyBundle{ - ConnectedF: func(_ libp2pNetwork.Network, _ libp2pNetwork.Conn) { + ConnectedF: func(_ libp2pNetwork.Network, conn libp2pNetwork.Conn) { t.Logf("connected!") - bootstrapWaitgroup.Done() + t.Logf("local PeerID %s; remote PeerID: %s", + conn.LocalPeer().String(), + conn.RemotePeer().String(), + ) + bootstrapPeerIDCh <- conn.RemotePeer().String() + //bootstrapWaitgroup.Done() }, } testRouter.host.Network().Notify(notifee) @@ -189,7 +228,8 @@ func TestBackgroundRouter_Broadcast(t *testing.T) { // broadcast message t.Log("broadcasting...") - err := testRouter.Broadcast([]byte(testMsg)) + testPoktEnvelopeBz := p2p_testutil.NewTestPoktEnvelopeBz(t, testMsg) + err = testRouter.Broadcast(testPoktEnvelopeBz) require.NoError(t, err) // wait for broadcast to be received by all peers @@ -200,6 +240,7 @@ func TestBackgroundRouter_Broadcast(t *testing.T) { // waitgroup broadcastDone or timeout select { case <-testTimeout: + seenMessagesMutex.Lock() t.Fatalf( "timed out waiting for all expected messages: got %d; wanted %d", len(seenMessages), @@ -208,7 +249,10 @@ func TestBackgroundRouter_Broadcast(t *testing.T) { case <-broadcastDone: } - actualPeerIDs = testutil.GetKeys[string](seenMessages) + seenMessagesMutex.Lock() + defer seenMessagesMutex.Unlock() + + actualPeerIDs = generics_testutil.GetKeys[string](seenMessages) require.ElementsMatchf(t, expectedPeerIDs, actualPeerIDs, "peerIDs don't match") } @@ -284,11 +328,33 @@ func newRouterWithSelfPeerAndHost(t *testing.T, selfPeer typesP2P.Peer, host lib err := pstore.AddPeer(selfPeer) require.NoError(t, err) + handler := func(poktEnvelopeBz []byte) error { + poktEnvelope := &messaging.PocketEnvelope{} + err := proto.Unmarshal(poktEnvelopeBz, poktEnvelope) + require.NoError(t, err) + + require.NotEmpty(t, poktEnvelope.Nonce) + require.NotEmpty(t, poktEnvelope.Content) + + debugMsg := &messaging.DebugMessage{} + err = poktEnvelope.Content.UnmarshalTo(debugMsg) + require.NoError(t, err) + + debugStringMsg := &messaging.DebugStringMessage{} + err = debugMsg.Message.UnmarshalTo(debugStringMsg) + require.NoError(t, err) + + require.Equal(t, testMsg, debugStringMsg.Value, "debug string messages don't match") + + return nil + } + router, err := NewBackgroundRouter(busMock, &config.BackgroundConfig{ Addr: selfPeer.GetAddress(), PeerstoreProvider: pstoreProviderMock, CurrentHeightProvider: consensusMock, Host: host, + Handler: handler, }) require.NoError(t, err) @@ -345,31 +411,3 @@ func newTestHost(t *testing.T, mockNet mocknet.Mocknet, privKey cryptoPocket.Pri // construct mock host return newMockNetHostFromPeer(t, mockNet, privKey, peer) } - -func readSubscription( - t *testing.T, - ctx context.Context, - broadcastWaitGroup *sync.WaitGroup, - rtr *backgroundRouter, - mu *sync.Mutex, - seenMsgs map[string]struct{}, -) { - t.Helper() - - for { - if err := ctx.Err(); err != nil { - if err != context.Canceled || err != context.DeadlineExceeded { - require.NoError(t, err) - } - return - } - - _, err := rtr.subscription.Next(ctx) - require.NoError(t, err) - - mu.Lock() - broadcastWaitGroup.Done() - seenMsgs[rtr.host.ID().String()] = struct{}{} - mu.Unlock() - } -} From cb4bf216d5ad4cbd3c24efb032723c1f5ed6d0c5 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 13:26:08 +0200 Subject: [PATCH 22/82] wip: testutils --- internal/testutil/bus/bus.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 internal/testutil/bus/bus.go diff --git a/internal/testutil/bus/bus.go b/internal/testutil/bus/bus.go new file mode 100644 index 000000000..f83e27735 --- /dev/null +++ b/internal/testutil/bus/bus.go @@ -0,0 +1,30 @@ +package bus_testutil + +import ( + "github.com/regen-network/gocuke" + + "github.com/pokt-network/pocket/internal/testutil" + "github.com/pokt-network/pocket/internal/testutil/runtime" + "github.com/pokt-network/pocket/runtime/genesis" + "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/pocket/shared/modules/mocks" +) + +func NewBus( + t gocuke.TestingT, + privKey crypto.PrivateKey, + serviceURL string, + genesisState *genesis.GenesisState, + busEventHandlerFactory testutil.BusEventHandlerFactory, +) *mock_modules.MockBus { + t.Helper() + + runtimeMgrMock := runtime_testutil.BaseRuntimeManagerMock( + t, privKey, + serviceURL, + genesisState, + ) + busMock := testutil.BusMockWithEventHandler(t, runtimeMgrMock, busEventHandlerFactory) + busMock.EXPECT().GetRuntimeMgr().Return(runtimeMgrMock).AnyTimes() + return busMock +} From 3a4db57743c59228d37c7ced08195f5bc5809292 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Fri, 5 May 2023 12:20:19 +0200 Subject: [PATCH 23/82] test: add background gossip feature --- p2p/integration/background_gossip.feature | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 p2p/integration/background_gossip.feature diff --git a/p2p/integration/background_gossip.feature b/p2p/integration/background_gossip.feature new file mode 100644 index 000000000..4f77e7356 --- /dev/null +++ b/p2p/integration/background_gossip.feature @@ -0,0 +1,43 @@ +Feature: Background Gossip Broadcast + + Scenario Outline: Complete broadcast + Given a fully connected network of peers + When a node broadcasts a test message via its background router + Then number of nodes should receive the test message + + Examples: + | size | + | 4 | + | 6 | + | 12 | +# | 100 | +# | 1024 | + + Scenario Outline: Partial broadcast + Given a faulty network of peers + And number of faulty peers + When a node broadcasts a test message via its background router + Then number of nodes should receive the test message + + Examples: + | size | faulty | received | + | 4 | 2 | 2 | + | 6 | 3 | 3 | + | 12 | 8 | 4 | +# | 100 | 75 | 25 | +# | 1024 | 1000 | 24 | + + Scenario Outline: Broadcast during churn + Given a fully connected network of peers + When number of nodes join the network + And number of nodes leave the network + And a node broadcasts a test message via its background router + Then number of nodes should receive the test message + + Examples: + | size | leave | join | received | + | 4 | 2 | 2 | 4 | + | 4 | 3 | 4 | 5 | + | 12 | 6 | 6 | 12 | +# | 100 | 50 | 55 | 105 | +# | 1024 | 1000 | 1200 | 1224 | From 0569a5e54d6c34a7a277978b3bdf99e8ee97070c Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:55:13 +0200 Subject: [PATCH 24/82] wip: background gossip integration test --- p2p/integration/background_gossip_test.go | 122 ++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 p2p/integration/background_gossip_test.go diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go new file mode 100644 index 000000000..12c800e89 --- /dev/null +++ b/p2p/integration/background_gossip_test.go @@ -0,0 +1,122 @@ +package integration + +import ( + libp2pPeer "github.com/libp2p/go-libp2p/core/peer" + p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" + "github.com/pokt-network/pocket/shared/modules" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/anypb" + "testing" + + "github.com/regen-network/gocuke" +) + +const backgroundGossipFeaturePath = "background_gossip.feature" + +func TestMinimal(t *testing.T) { + // a new step definition suite is constructed for every scenario + gocuke.NewRunner(t, &suite{}).Path(backgroundGossipFeaturePath).Run() +} + +type suite struct { + // special arguments like TestingT are injected automatically into exported fields + gocuke.TestingT + + // seenMessages is used as a map to track which messages have been seen + // by which nodes + seenMessages map[libp2pPeer.ID]struct{} + p2pModules []modules.P2PModule +} + +func (s *suite) AFaultyNetworkOfPeers(a int64) { + panic("PENDING") +} + +func (s *suite) NumberOfFaultyPeers(a int64) { + panic("PENDING") +} + +func (s *suite) NumberOfNodesJoinTheNetwork(a int64) { + panic("PENDING") +} + +func (s *suite) NumberOfNodesLeaveTheNetwork(a int64) { + panic("PENDING") +} + +func (s *suite) AFullyConnectedNetworkOfPeers(peerCount int64) { + // setup mock network + s.p2pModules = p2p_testutil.NewP2PModules(s, int(peerCount)) +} + +func (s *suite) ANodeBroadcastsATestMessageViaItsBackgroundRouter() { + // select arbitrary sender & store in context for reference later + sender := s.p2pModules[0] + + // broadcast a test message + msg := &anypb.Any{} + err := sender.Broadcast(msg) + require.NoError(s, err) +} + +func (s *suite) NumberOfNodesShouldReceiveTheTestMessage(receivedCount int64) { + done := make(chan struct{}, 1) + + go func() { + s.wg.Wait() + require.Len(s.seenMessages, receivedCount) + done <- struct{}{} + }() + +} + +//func TestBackgroundGossipIntegration(t *testing.T) { +// t.Parallel() +// +// testutil.RunGherkinFeature(t, backgroundGossipFeaturePath, initBackgroundGossipScenarios(t)) +//} +// +//func aNodeBroadcastsATestMessageViaItsBackgroundRouter(ctx context.Context) (context.Context, error) { +// t, err := testutil.GetTestingTFromContext(ctx) +// if err != nil { +// return ctx, err +// } +// +// p2pModules := ctx.Value("p2pModules").([]modules.P2PModule) +// +// // select arbitrary sender & store in context for reference later +// sender := p2pModules[0] +// ctx = context.WithValue(ctx, "sender", sender) +// +// // broadcast a test message +// msg := &anypb.Any{} +// err = sender.Broadcast(msg) +// require.NoError(t, err) +// +// return ctx, nil +//} +// +//func numberOfFaultyPeers(arg1 int) error { +// return godog.ErrPending +//} +// +//func numberOfNodesShouldReceiveTheTestMessage(expectedReceivedCount int) error { +// // wait for all nodes to receive the test message +// // TODO +// +// //require.Equal(t) +// +// return godog.ErrPending +//} +// +//func initBackgroundGossipScenarios(t *testing.T) func(ctx *godog.ScenarioContext) { +// return func(ctx *godog.ScenarioContext) { +// ctx.Step(`^a faulty network of (\d+) peers$`, aFaultyNetworkOfPeers) +// ctx.Step(`^a fully connected network of (\d+) peers$`, aFullyConnectedNetworkOfPeers) +// ctx.Step(`^a node broadcasts a test message via its background router$`, aNodeBroadcastsATestMessageViaItsBackgroundRouter) +// ctx.Step(`^(\d+) number of faulty peers$`, numberOfFaultyPeers) +// ctx.Step(`^(\d+) number of nodes join the network$`, numberOfNodesJoinTheNetwork) +// ctx.Step(`^(\d+) number of nodes leave the network$`, numberOfNodesLeaveTheNetwork) +// ctx.Step(`^(\d+) number of nodes should receive the test message$`, numberOfNodesShouldReceiveTheTestMessage) +// } +//} From 70a9b514b5311951ac4e046e508d5349eb77e716 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:01:27 +0200 Subject: [PATCH 25/82] wip: background gossip integration test --- p2p/integration/background_gossip_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index 12c800e89..7a3662097 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -1,3 +1,5 @@ +//go:build integration + package integration import ( From 598eb62fbc20057eabe2db80624fa047da202ca5 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:08:37 +0200 Subject: [PATCH 26/82] wip: background gossip integration test --- p2p/integration/background_gossip.feature | 67 ++++++----- p2p/integration/background_gossip_test.go | 135 +++++++++++++++++++--- 2 files changed, 155 insertions(+), 47 deletions(-) diff --git a/p2p/integration/background_gossip.feature b/p2p/integration/background_gossip.feature index 4f77e7356..6abdc0346 100644 --- a/p2p/integration/background_gossip.feature +++ b/p2p/integration/background_gossip.feature @@ -3,41 +3,44 @@ Feature: Background Gossip Broadcast Scenario Outline: Complete broadcast Given a fully connected network of peers When a node broadcasts a test message via its background router - Then number of nodes should receive the test message + Then minus one number of nodes should receive the test message Examples: | size | + | 3 | | 4 | - | 6 | + | 9 | | 12 | -# | 100 | -# | 1024 | + | 18 | + | 27 | + | 100 | +## | 1024 | - Scenario Outline: Partial broadcast - Given a faulty network of peers - And number of faulty peers - When a node broadcasts a test message via its background router - Then number of nodes should receive the test message - - Examples: - | size | faulty | received | - | 4 | 2 | 2 | - | 6 | 3 | 3 | - | 12 | 8 | 4 | -# | 100 | 75 | 25 | -# | 1024 | 1000 | 24 | - - Scenario Outline: Broadcast during churn - Given a fully connected network of peers - When number of nodes join the network - And number of nodes leave the network - And a node broadcasts a test message via its background router - Then number of nodes should receive the test message - - Examples: - | size | leave | join | received | - | 4 | 2 | 2 | 4 | - | 4 | 3 | 4 | 5 | - | 12 | 6 | 6 | 12 | -# | 100 | 50 | 55 | 105 | -# | 1024 | 1000 | 1200 | 1224 | +# Scenario Outline: Partial broadcast +# Given a faulty network of peers +# And number of faulty peers +# When a node broadcasts a test message via its background router +# Then number of nodes should receive the test message +# +# Examples: +# | size | faulty | received | +# | 4 | 2 | 2 | +# | 6 | 3 | 3 | +# | 12 | 8 | 4 | +## | 100 | 75 | 25 | +## | 1024 | 1000 | 24 | +# +# Scenario Outline: Broadcast during churn +# Given a fully connected network of peers +# When number of nodes join the network +# And number of nodes leave the network +# And a node broadcasts a test message via its background router +# Then number of nodes should receive the test message +# +# Examples: +# | size | leave | join | received | +# | 4 | 2 | 2 | 4 | +# | 4 | 3 | 4 | 5 | +# | 12 | 6 | 6 | 12 | +## | 100 | 50 | 55 | 105 | +## | 1024 | 1000 | 1200 | 1224 | diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index 7a3662097..e7ccb276e 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -1,21 +1,38 @@ -//go:build integration +//go:build integration && test package integration import ( - libp2pPeer "github.com/libp2p/go-libp2p/core/peer" - p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" - "github.com/pokt-network/pocket/shared/modules" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/types/known/anypb" + "fmt" + telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" + "github.com/pokt-network/pocket/p2p" + "github.com/pokt-network/pocket/runtime/defaults" + "sync" "testing" + "time" + libp2pMocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/regen-network/gocuke" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/pokt-network/pocket/internal/testutil" + "github.com/pokt-network/pocket/internal/testutil/constructors" + runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/pocket/shared/messaging" + "github.com/pokt-network/pocket/shared/modules" + mock_modules "github.com/pokt-network/pocket/shared/modules/mocks" ) -const backgroundGossipFeaturePath = "background_gossip.feature" +const ( + backgroundGossipFeaturePath = "background_gossip.feature" + broadcastTimeoutDuration = time.Second * 2 +) func TestMinimal(t *testing.T) { + t.Parallel() + // a new step definition suite is constructed for every scenario gocuke.NewRunner(t, &suite{}).Path(backgroundGossipFeaturePath).Run() } @@ -24,10 +41,18 @@ type suite struct { // special arguments like TestingT are injected automatically into exported fields gocuke.TestingT - // seenMessages is used as a map to track which messages have been seen + timeoutDuration time.Duration + // TODO_THIS_COMMIT: rename + mu sync.Mutex + // seenServiceURLs is used as a map to track which messages have been seen // by which nodes - seenMessages map[libp2pPeer.ID]struct{} - p2pModules []modules.P2PModule + seenServiceURLs map[string]struct{} + receivedCount int + p2pModules map[string]modules.P2PModule + busMocks map[string]*mock_modules.MockBus + libp2pNetworkMock libp2pMocknet.Mocknet + // TODO_THIS_COMMIT: reanme + wg sync.WaitGroup } func (s *suite) AFaultyNetworkOfPeers(a int64) { @@ -46,14 +71,77 @@ func (s *suite) NumberOfNodesLeaveTheNetwork(a int64) { panic("PENDING") } -func (s *suite) AFullyConnectedNetworkOfPeers(peerCount int64) { +func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { + var ( + peerCount = int(count) + pubKeys = make([]cryptoPocket.PublicKey, peerCount) + ) + s.wg.Add(peerCount - 1) + s.seenServiceURLs = make(map[string]struct{}) + + for i, privKey := range testutil.LoadLocalnetPrivateKeys(s, peerCount) { + pubKeys[i] = privKey.PublicKey() + } + genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(s, pubKeys) + busEventHandlerFactory := func(t gocuke.TestingT, busMock *mock_modules.MockBus) testutil.BusEventHandler { + // event handler is called when a p2p module receives a network message + return func(data *messaging.PocketEnvelope) { + s.mu.Lock() + defer s.mu.Unlock() + + defer func() { + if r := recover(); r != nil { + t.Logf("seenServiceURLs: %v", s.seenServiceURLs) + //panic(r) + t.Fatalf("panic: %v", r) + } + }() + + p2pCfg := busMock.GetRuntimeMgr().GetConfig().P2P + serviceURL := fmt.Sprintf("%s:%d", p2pCfg.Hostname, defaults.DefaultP2PPort) + t.Logf("received message by %s", serviceURL) + if _, ok := s.seenServiceURLs[serviceURL]; ok { + return + } + + s.receivedCount++ + s.seenServiceURLs[serviceURL] = struct{}{} + s.wg.Done() + } + } + // setup mock network - s.p2pModules = p2p_testutil.NewP2PModules(s, int(peerCount)) + s.busMocks, s.libp2pNetworkMock, s.p2pModules = constructors.NewBusesMocknetAndP2PModules( + s, peerCount, + genesisState, + busEventHandlerFactory, + ) + + for _, p2pModule := range s.p2pModules { + err := p2pModule.(*p2p.P2PModule).Start() + require.NoError(s, err) + } + + for _, busMock := range s.busMocks { + //eventMetricsAgentMock := telemetry_testutil.PrepareEventMetricsAgentMock(s, serviceURL, &s.wg, peerCount) + eventMetricsAgentMock := telemetry_testutil.BaseEventMetricsAgentMock(s) + busMock.GetTelemetryModule().(*mock_modules.MockTelemetryModule).EXPECT(). + GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() + } + + //for _, host := range libp2pNetworkMock.Hosts() { + // host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { + // s.Logf("inbound stream protocol: %s", stream.Protocol()) + // s.seenServiceURLs[stream.Conn().RemotePeer()] = struct{}{} + // }) + //} } func (s *suite) ANodeBroadcastsATestMessageViaItsBackgroundRouter() { + s.timeoutDuration = broadcastTimeoutDuration + // select arbitrary sender & store in context for reference later - sender := s.p2pModules[0] + sender := s.p2pModules[testutil.GetKeys(s.p2pModules)[0]].(*p2p.P2PModule) // broadcast a test message msg := &anypb.Any{} @@ -61,15 +149,32 @@ func (s *suite) ANodeBroadcastsATestMessageViaItsBackgroundRouter() { require.NoError(s, err) } -func (s *suite) NumberOfNodesShouldReceiveTheTestMessage(receivedCount int64) { +func (s *suite) MinusOneNumberOfNodesShouldReceiveTheTestMessage(receivedCountPlus1 int64) { done := make(chan struct{}, 1) go func() { s.wg.Wait() - require.Len(s.seenMessages, receivedCount) + s.mu.Lock() + defer s.mu.Unlock() + + require.Lenf( + s, s.seenServiceURLs, + int(receivedCountPlus1-1), + "expected to see %d peers, got: %v", + receivedCountPlus1-1, + len(s.seenServiceURLs), + ) done <- struct{}{} }() + select { + case <-time.After(s.timeoutDuration): + s.mu.Lock() + defer s.mu.Unlock() + + s.Fatalf("timed out waiting for messages to be received; received: %d; seenServiceURLs: %v", s.receivedCount, s.seenServiceURLs) + case <-done: + } } //func TestBackgroundGossipIntegration(t *testing.T) { From 61d529b13f792e97f3fe1339926a0b0c0b009d5d Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:17:06 +0200 Subject: [PATCH 27/82] wip: background gossip integration test --- p2p/integration/background_gossip_test.go | 26 +++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index e7ccb276e..688f5300b 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -35,6 +35,7 @@ func TestMinimal(t *testing.T) { // a new step definition suite is constructed for every scenario gocuke.NewRunner(t, &suite{}).Path(backgroundGossipFeaturePath).Run() + t.Fatalf("fail") } type suite struct { @@ -117,18 +118,22 @@ func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { busEventHandlerFactory, ) + // add expectations for P2P events to telemetry module's event metrics agent + for _, busMock := range s.busMocks { + eventMetricsAgentMock := busMock. + GetTelemetryModule(). + GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent) + + telemetry_testutil.WithP2PIntegrationEvents( + s, eventMetricsAgentMock, + ) + } + for _, p2pModule := range s.p2pModules { err := p2pModule.(*p2p.P2PModule).Start() require.NoError(s, err) } - for _, busMock := range s.busMocks { - //eventMetricsAgentMock := telemetry_testutil.PrepareEventMetricsAgentMock(s, serviceURL, &s.wg, peerCount) - eventMetricsAgentMock := telemetry_testutil.BaseEventMetricsAgentMock(s) - busMock.GetTelemetryModule().(*mock_modules.MockTelemetryModule).EXPECT(). - GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() - } - //for _, host := range libp2pNetworkMock.Hosts() { // host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { // s.Logf("inbound stream protocol: %s", stream.Protocol()) @@ -157,12 +162,11 @@ func (s *suite) MinusOneNumberOfNodesShouldReceiveTheTestMessage(receivedCountPl s.mu.Lock() defer s.mu.Unlock() + receivedCount := int(receivedCountPlus1 - 1) require.Lenf( - s, s.seenServiceURLs, - int(receivedCountPlus1-1), + s, s.seenServiceURLs, receivedCount, "expected to see %d peers, got: %v", - receivedCountPlus1-1, - len(s.seenServiceURLs), + receivedCount, len(s.seenServiceURLs), ) done <- struct{}{} }() From 8dd960981fe31958df009d665c04bf7c60805c54 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:24:09 +0200 Subject: [PATCH 28/82] wip: background gossip integration test --- p2p/integration/background_gossip.feature | 2 +- p2p/integration/background_gossip_test.go | 37 ++++++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/p2p/integration/background_gossip.feature b/p2p/integration/background_gossip.feature index 6abdc0346..d76cf2bc3 100644 --- a/p2p/integration/background_gossip.feature +++ b/p2p/integration/background_gossip.feature @@ -13,7 +13,7 @@ Feature: Background Gossip Broadcast | 12 | | 18 | | 27 | - | 100 | +# | 100 | ## | 1024 | # Scenario Outline: Partial broadcast diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index 688f5300b..98112209c 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -4,6 +4,9 @@ package integration import ( "fmt" + dht "github.com/libp2p/go-libp2p-kad-dht" + pubsub "github.com/libp2p/go-libp2p-pubsub" + libp2pNetwork "github.com/libp2p/go-libp2p/core/network" telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" "github.com/pokt-network/pocket/p2p" "github.com/pokt-network/pocket/runtime/defaults" @@ -35,7 +38,6 @@ func TestMinimal(t *testing.T) { // a new step definition suite is constructed for every scenario gocuke.NewRunner(t, &suite{}).Path(backgroundGossipFeaturePath).Run() - t.Fatalf("fail") } type suite struct { @@ -129,17 +131,38 @@ func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { ) } + // TODO_THIS_COMMIT: bus event handler based wg.Done()! + + // start P2P modules of all peers for _, p2pModule := range s.p2pModules { + // (NOPE) WIP: pubsub-level intercept... + //p2pModule.GetBackgroundRouter().Get + err := p2pModule.(*p2p.P2PModule).Start() require.NoError(s, err) } - //for _, host := range libp2pNetworkMock.Hosts() { - // host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { - // s.Logf("inbound stream protocol: %s", stream.Protocol()) - // s.seenServiceURLs[stream.Conn().RemotePeer()] = struct{}{} - // }) - //} + // (NOPE) WIP: host-level intercept... + for _, host := range s.libp2pNetworkMock.Hosts() { + s.Logf("host protocols: %v", host.Mux().Protocols()) + //host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { + host.SetStreamHandler(pubsub.FloodSubID, func(stream libp2pNetwork.Stream) { + s.Logf("inbound stream protocol: %s", stream.Protocol()) + // //s.seenServiceURLs[stream.Conn().RemotePeer()] = struct{}{} + // data, err := io.ReadAll(stream) + // require.NoError(s, err) + // + // s.Logf("stream data: %s", data) + }) + host.SetStreamHandler(dht.ProtocolDHT, func(stream libp2pNetwork.Stream) { + s.Logf("inbound stream protocol: %s", stream.Protocol()) + //s.seenServiceURLs[stream.Conn().RemotePeer()] = struct{}{} + //data, err := io.ReadAll(stream) + //require.NoError(s, err) + // + //s.Logf("stream data: %s", data) + }) + } } func (s *suite) ANodeBroadcastsATestMessageViaItsBackgroundRouter() { From cc85184964256152cbc37571c0e286f3fe0d96df Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:35:16 +0200 Subject: [PATCH 29/82] wip: background gossip integration test --- p2p/integration/background_gossip.feature | 18 ++- p2p/integration/background_gossip_test.go | 181 +++++++++++----------- 2 files changed, 106 insertions(+), 93 deletions(-) diff --git a/p2p/integration/background_gossip.feature b/p2p/integration/background_gossip.feature index d76cf2bc3..1799fbf23 100644 --- a/p2p/integration/background_gossip.feature +++ b/p2p/integration/background_gossip.feature @@ -8,11 +8,19 @@ Feature: Background Gossip Broadcast Examples: | size | | 3 | - | 4 | - | 9 | - | 12 | - | 18 | - | 27 | +# | 4 | +# | 5 | +# | 6 | +# | 7 | +# | 8 | +# | 9 | +# | 10 | +# | 11 | +# TODO_THIS_COMMIT: figure out why these are failing +# | 12 | +# | 13 | +# | 18 | +# | 27 | # | 100 | ## | 1024 | diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index 98112209c..25feac695 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -4,12 +4,7 @@ package integration import ( "fmt" - dht "github.com/libp2p/go-libp2p-kad-dht" - pubsub "github.com/libp2p/go-libp2p-pubsub" - libp2pNetwork "github.com/libp2p/go-libp2p/core/network" - telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" - "github.com/pokt-network/pocket/p2p" - "github.com/pokt-network/pocket/runtime/defaults" + typesP2P "github.com/pokt-network/pocket/p2p/types" "sync" "testing" "time" @@ -21,7 +16,11 @@ import ( "github.com/pokt-network/pocket/internal/testutil" "github.com/pokt-network/pocket/internal/testutil/constructors" + "github.com/pokt-network/pocket/internal/testutil/generics" runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" + telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" + "github.com/pokt-network/pocket/p2p" + "github.com/pokt-network/pocket/runtime/defaults" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/pocket/shared/messaging" "github.com/pokt-network/pocket/shared/modules" @@ -54,6 +53,7 @@ type suite struct { p2pModules map[string]modules.P2PModule busMocks map[string]*mock_modules.MockBus libp2pNetworkMock libp2pMocknet.Mocknet + sender *p2p.P2PModule // TODO_THIS_COMMIT: reanme wg sync.WaitGroup } @@ -77,15 +77,21 @@ func (s *suite) NumberOfNodesLeaveTheNetwork(a int64) { func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { var ( peerCount = int(count) - pubKeys = make([]cryptoPocket.PublicKey, peerCount) + //pubKeys = make([]cryptoPocket.PublicKey, peerCount) ) + s.Logf("ADDING peerCount - 1: %d", peerCount-1) s.wg.Add(peerCount - 1) + s.mu.Lock() + defer s.mu.Unlock() s.seenServiceURLs = make(map[string]struct{}) - for i, privKey := range testutil.LoadLocalnetPrivateKeys(s, peerCount) { - pubKeys[i] = privKey.PublicKey() - } - genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(s, pubKeys) + //for i, privKey := range testutil.LoadLocalnetPrivateKeys(s, peerCount) { + // pubKeys[i] = privKey.PublicKey() + //} + //genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(s, pubKeys) + // TODO_THIS_COMMIT: explain + genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(s, nil) + busEventHandlerFactory := func(t gocuke.TestingT, busMock *mock_modules.MockBus) testutil.BusEventHandler { // event handler is called when a p2p module receives a network message return func(data *messaging.PocketEnvelope) { @@ -94,7 +100,7 @@ func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { defer func() { if r := recover(); r != nil { - t.Logf("seenServiceURLs: %v", s.seenServiceURLs) + t.Logf("seenCount: %d; seenServiceURLs: %v", len(s.seenServiceURLs), s.seenServiceURLs) //panic(r) t.Fatalf("panic: %v", r) } @@ -103,7 +109,20 @@ func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { p2pCfg := busMock.GetRuntimeMgr().GetConfig().P2P serviceURL := fmt.Sprintf("%s:%d", p2pCfg.Hostname, defaults.DefaultP2PPort) t.Logf("received message by %s", serviceURL) + + peerPrivKey, err := cryptoPocket.NewPrivateKey(p2pCfg.PrivateKey) + require.NoError(t, err) + + senderAddr, err := s.sender.GetAddress() + require.NoError(t, err) + + if senderAddr.Equals(peerPrivKey.Address()) { + t.Logf("SELF: %s", serviceURL) + return + } + if _, ok := s.seenServiceURLs[serviceURL]; ok { + t.Logf("DUPLICATE SERVICE URL: %s", serviceURL) return } @@ -134,46 +153,82 @@ func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { // TODO_THIS_COMMIT: bus event handler based wg.Done()! // start P2P modules of all peers + handleCount := 0 for _, p2pModule := range s.p2pModules { - // (NOPE) WIP: pubsub-level intercept... - //p2pModule.GetBackgroundRouter().Get - err := p2pModule.(*p2p.P2PModule).Start() require.NoError(s, err) + + handlerProxyFactory := func( + origHandler typesP2P.RouterHandler, + ) (proxyHandler typesP2P.RouterHandler) { + return func(data []byte) error { + s.mu.Lock() + handleCount++ + s.mu.Unlock() + + s.Logf("handleCount: %d", handleCount) + //s.wg.Done() + return origHandler(data) + + //return nil + } + } + + // TODO_THIS_COMMIT: look into go-libp2p-pubsub tracing + // (see: https://github.com/libp2p/go-libp2p-pubsub#tracing) + noopHandlerProxyFactory := func(_ typesP2P.RouterHandler) typesP2P.RouterHandler { + return func(_ []byte) error { + // noop + return nil + } + } + + p2pModule.(*p2p.P2PModule).GetRainTreeRouter().HandlerProxy( + s, noopHandlerProxyFactory, + ) + p2pModule.(*p2p.P2PModule).GetBackgroundRouter().HandlerProxy( + s, handlerProxyFactory, + ) } + //time.Sleep(time.Millisecond * 500) + // (NOPE) WIP: host-level intercept... - for _, host := range s.libp2pNetworkMock.Hosts() { - s.Logf("host protocols: %v", host.Mux().Protocols()) - //host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { - host.SetStreamHandler(pubsub.FloodSubID, func(stream libp2pNetwork.Stream) { - s.Logf("inbound stream protocol: %s", stream.Protocol()) - // //s.seenServiceURLs[stream.Conn().RemotePeer()] = struct{}{} - // data, err := io.ReadAll(stream) - // require.NoError(s, err) - // - // s.Logf("stream data: %s", data) - }) - host.SetStreamHandler(dht.ProtocolDHT, func(stream libp2pNetwork.Stream) { - s.Logf("inbound stream protocol: %s", stream.Protocol()) - //s.seenServiceURLs[stream.Conn().RemotePeer()] = struct{}{} - //data, err := io.ReadAll(stream) - //require.NoError(s, err) - // - //s.Logf("stream data: %s", data) - }) - } + //for _, host := range s.libp2pNetworkMock.Hosts() { + // //s.Logf("host protocols: %v", host.Mux().Protocols()) + // //host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { + // host.SetStreamHandler(pubsub.FloodSubID, func(stream libp2pNetwork.Stream) { + // //s.Logf("inbound stream protocol: %s", stream.Protocol()) + // // //s.seenServiceURLs[stream.Conn().RemotePeer()] = struct{}{} + // // data, err := io.ReadAll(stream) + // // require.NoError(s, err) + // // + // // s.Logf("stream data: %s", data) + // }) + // host.SetStreamHandler(dht.ProtocolDHT, func(stream libp2pNetwork.Stream) { + // //s.Logf("inbound stream protocol: %s", stream.Protocol()) + // //s.seenServiceURLs[stream.Conn().RemotePeer()] = struct{}{} + // //data, err := io.ReadAll(stream) + // //require.NoError(s, err) + // // + // //s.Logf("stream data: %s", data) + // }) + //} } func (s *suite) ANodeBroadcastsATestMessageViaItsBackgroundRouter() { s.timeoutDuration = broadcastTimeoutDuration // select arbitrary sender & store in context for reference later - sender := s.p2pModules[testutil.GetKeys(s.p2pModules)[0]].(*p2p.P2PModule) + s.sender = s.p2pModules[generics_testutil.GetKeys(s.p2pModules)[0]].(*p2p.P2PModule) // broadcast a test message msg := &anypb.Any{} - err := sender.Broadcast(msg) + + // TODO: + // - disable raintree router OR broadcast w/ bg router only + + err := s.sender.Broadcast(msg) require.NoError(s, err) } @@ -201,56 +256,6 @@ func (s *suite) MinusOneNumberOfNodesShouldReceiveTheTestMessage(receivedCountPl s.Fatalf("timed out waiting for messages to be received; received: %d; seenServiceURLs: %v", s.receivedCount, s.seenServiceURLs) case <-done: + s.Logf("seenCount: %d; seenServiceURLs: %v", len(s.seenServiceURLs), s.seenServiceURLs) } } - -//func TestBackgroundGossipIntegration(t *testing.T) { -// t.Parallel() -// -// testutil.RunGherkinFeature(t, backgroundGossipFeaturePath, initBackgroundGossipScenarios(t)) -//} -// -//func aNodeBroadcastsATestMessageViaItsBackgroundRouter(ctx context.Context) (context.Context, error) { -// t, err := testutil.GetTestingTFromContext(ctx) -// if err != nil { -// return ctx, err -// } -// -// p2pModules := ctx.Value("p2pModules").([]modules.P2PModule) -// -// // select arbitrary sender & store in context for reference later -// sender := p2pModules[0] -// ctx = context.WithValue(ctx, "sender", sender) -// -// // broadcast a test message -// msg := &anypb.Any{} -// err = sender.Broadcast(msg) -// require.NoError(t, err) -// -// return ctx, nil -//} -// -//func numberOfFaultyPeers(arg1 int) error { -// return godog.ErrPending -//} -// -//func numberOfNodesShouldReceiveTheTestMessage(expectedReceivedCount int) error { -// // wait for all nodes to receive the test message -// // TODO -// -// //require.Equal(t) -// -// return godog.ErrPending -//} -// -//func initBackgroundGossipScenarios(t *testing.T) func(ctx *godog.ScenarioContext) { -// return func(ctx *godog.ScenarioContext) { -// ctx.Step(`^a faulty network of (\d+) peers$`, aFaultyNetworkOfPeers) -// ctx.Step(`^a fully connected network of (\d+) peers$`, aFullyConnectedNetworkOfPeers) -// ctx.Step(`^a node broadcasts a test message via its background router$`, aNodeBroadcastsATestMessageViaItsBackgroundRouter) -// ctx.Step(`^(\d+) number of faulty peers$`, numberOfFaultyPeers) -// ctx.Step(`^(\d+) number of nodes join the network$`, numberOfNodesJoinTheNetwork) -// ctx.Step(`^(\d+) number of nodes leave the network$`, numberOfNodesLeaveTheNetwork) -// ctx.Step(`^(\d+) number of nodes should receive the test message$`, numberOfNodesShouldReceiveTheTestMessage) -// } -//} From bc60f114f4e406baace6501decf576422a45b1ee Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:54:45 +0200 Subject: [PATCH 30/82] wip: background gossip integration test --- p2p/integration/background_gossip_test.go | 253 ++++++++++++++++------ 1 file changed, 186 insertions(+), 67 deletions(-) diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index 25feac695..47382a518 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -4,6 +4,8 @@ package integration import ( "fmt" + libp2pNetwork "github.com/libp2p/go-libp2p/core/network" + libp2pPeer "github.com/libp2p/go-libp2p/core/peer" typesP2P "github.com/pokt-network/pocket/p2p/types" "sync" "testing" @@ -27,9 +29,12 @@ import ( mock_modules "github.com/pokt-network/pocket/shared/modules/mocks" ) +type PeerIDSet map[libp2pPeer.ID]struct{} + const ( backgroundGossipFeaturePath = "background_gossip.feature" - broadcastTimeoutDuration = time.Second * 2 + //broadcastTimeoutDuration = time.Millisecond * 250 + broadcastTimeoutDuration = time.Second * 5 ) func TestMinimal(t *testing.T) { @@ -46,16 +51,32 @@ type suite struct { timeoutDuration time.Duration // TODO_THIS_COMMIT: rename mu sync.Mutex - // seenServiceURLs is used as a map to track which messages have been seen - // by which nodes - seenServiceURLs map[string]struct{} - receivedCount int - p2pModules map[string]modules.P2PModule - busMocks map[string]*mock_modules.MockBus - libp2pNetworkMock libp2pMocknet.Mocknet - sender *p2p.P2PModule - // TODO_THIS_COMMIT: reanme - wg sync.WaitGroup + // receivedFromServiceURLMap is used as a map to track which messages have been + // received by which nodes. + receivedFromServiceURLMap map[string]struct{} + // bootstrapPeerIDChMap is a mapping between the peerID string of each node to + // a channel that will be used to signal the peer ID strings of each node it + // has discovered. + bootstrapPeerIDChMap map[libp2pPeer.ID]chan libp2pPeer.ID + // bootstrapPeerIDsMap is a mapping between the peerID string of each node to a + // set of peerID strings that node has discovered. This set is represented as + // a map with the peerID string as the key and an empty struct as the value. + bootstrapPeerIDsMap map[libp2pPeer.ID]PeerIDSet + bootstrapNetworkWaitGroup sync.WaitGroup + receivedCount int + receivedWaitGroup sync.WaitGroup + p2pModules map[string]modules.P2PModule + busMocks map[string]*mock_modules.MockBus + libp2pNetworkMock libp2pMocknet.Mocknet + sender *p2p.P2PModule +} + +func (s *suite) Before(scenario gocuke.Scenario) { + s.mu.Lock() + defer s.mu.Unlock() + + s.bootstrapPeerIDChMap = make(map[libp2pPeer.ID]chan libp2pPeer.ID) + s.receivedFromServiceURLMap = make(map[string]struct{}) } func (s *suite) AFaultyNetworkOfPeers(a int64) { @@ -77,21 +98,21 @@ func (s *suite) NumberOfNodesLeaveTheNetwork(a int64) { func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { var ( peerCount = int(count) - //pubKeys = make([]cryptoPocket.PublicKey, peerCount) + pubKeys = make([]cryptoPocket.PublicKey, peerCount) ) s.Logf("ADDING peerCount - 1: %d", peerCount-1) - s.wg.Add(peerCount - 1) + s.receivedWaitGroup.Add(peerCount - 1) s.mu.Lock() defer s.mu.Unlock() - s.seenServiceURLs = make(map[string]struct{}) - //for i, privKey := range testutil.LoadLocalnetPrivateKeys(s, peerCount) { - // pubKeys[i] = privKey.PublicKey() - //} - //genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(s, pubKeys) + for i, privKey := range testutil.LoadLocalnetPrivateKeys(s, peerCount) { + pubKeys[i] = privKey.PublicKey() + } + genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(s, pubKeys) // TODO_THIS_COMMIT: explain - genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(s, nil) + //genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(s, nil) + // TODO_THIS_COMMIT: refactor busEventHandlerFactory := func(t gocuke.TestingT, busMock *mock_modules.MockBus) testutil.BusEventHandler { // event handler is called when a p2p module receives a network message return func(data *messaging.PocketEnvelope) { @@ -100,7 +121,7 @@ func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { defer func() { if r := recover(); r != nil { - t.Logf("seenCount: %d; seenServiceURLs: %v", len(s.seenServiceURLs), s.seenServiceURLs) + t.Logf("seenCount: %d; receivedFromServiceURLMap: %v", len(s.receivedFromServiceURLMap), s.receivedFromServiceURLMap) //panic(r) t.Fatalf("panic: %v", r) } @@ -121,16 +142,17 @@ func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { return } - if _, ok := s.seenServiceURLs[serviceURL]; ok { + if _, ok := s.receivedFromServiceURLMap[serviceURL]; ok { t.Logf("DUPLICATE SERVICE URL: %s", serviceURL) return } s.receivedCount++ - s.seenServiceURLs[serviceURL] = struct{}{} - s.wg.Done() + s.receivedFromServiceURLMap[serviceURL] = struct{}{} + s.receivedWaitGroup.Done() } } + // -- // setup mock network s.busMocks, s.libp2pNetworkMock, s.p2pModules = constructors.NewBusesMocknetAndP2PModules( @@ -139,38 +161,84 @@ func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { busEventHandlerFactory, ) + // add number of combinations of peers given number of peers + //s.Logf("bootstrapNetworkWaitGroup.Add: %d", peerCount*(peerCount-1)/2) + //s.Logf("bootstrapNetworkWaitGroup.Add: %d", peerCount*(peerCount-1)) + //s.Logf("bootstrapNetworkWaitGroup.Add: %d", peerCount*peerCount) + //s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1) / 2) + //s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1)) + //s.bootstrapNetworkWaitGroup.Add(peerCount * peerCount) + s.bootstrapNetworkWaitGroup.Add(peerCount) + // add expectations for P2P events to telemetry module's event metrics agent for _, busMock := range s.busMocks { + // TODO_THIS_COMMIT: ?? eventMetricsAgentMock := busMock. GetTelemetryModule(). GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent) + // TODO_THIS_COMMIT: ?? telemetry_testutil.WithP2PIntegrationEvents( s, eventMetricsAgentMock, ) } - // TODO_THIS_COMMIT: bus event handler based wg.Done()! + // TODO_THIS_COMMIT: bus event handler based receivedWaitGroup.Done()! // start P2P modules of all peers - handleCount := 0 - for _, p2pModule := range s.p2pModules { - err := p2pModule.(*p2p.P2PModule).Start() + //handleCount := 0 + for _, module := range s.p2pModules { + //countz := 0 + p2pModule := module.(*p2p.P2PModule) + + p2pCfg := p2pModule.GetBus().GetRuntimeMgr().GetConfig().P2P + serviceURL := fmt.Sprintf("%s:%d", p2pCfg.Hostname, p2pCfg.Port) + s.Logf("serviceURL: %s; peer ID: %s", serviceURL, p2pModule.GetHost().ID()) + + s.initBootstrapPeerIDChMap(p2pModule) + + // concurrently update `s.bootstrapPeerIDsMap` by receiving from the + // corresponding channel from `s.bootstrapPeerIDChMap` that `notifee` + // is sending on. + go s.trackBootstrapProgress(p2pModule, peerCount-1) + + // TODO_THIS_COMMIT: refactor + notifee := &libp2pNetwork.NotifyBundle{ + DisconnectedF: func(network libp2pNetwork.Network, conn libp2pNetwork.Conn) { + s.Logf("disconnected: %s", conn.RemotePeer()) + }, + ConnectedF: func(_ libp2pNetwork.Network, conn libp2pNetwork.Conn) { + s.Logf("connected: %s", conn.RemotePeer()) + //s.Logf("pstore size: %d", len(p2pModule.GetHost().Peerstore().Peers())) + s.bootstrapPeerIDChMap[p2pModule.GetHost().ID()] <- conn.RemotePeer() + s.Logf("bootstrap peer ID sent on channel") + //if len(p2pModule.GetHost().Peerstore().Peers()) == peerCount { + // countz++ + // s.Logf("count: %d", countz) + // //s.bootstrapNetworkWaitGroup.Done() + // //s.bootstrapNetworkWaitGroup.Done() + // //s.bootstrapNetworkWaitGroup.Done() + //} + }, + } + p2pModule.GetHost().Network().Notify(notifee) + // -- + + err := p2pModule.Start() require.NoError(s, err) + // TODO_THIS_COMMIT: fix + //s.Cleanup(func() { + // err := p2pModule.Stop() + // require.NoError(s, err) + //}) + handlerProxyFactory := func( origHandler typesP2P.RouterHandler, ) (proxyHandler typesP2P.RouterHandler) { return func(data []byte) error { - s.mu.Lock() - handleCount++ - s.mu.Unlock() - - s.Logf("handleCount: %d", handleCount) - //s.wg.Done() + //s.receivedWaitGroup.Done() return origHandler(data) - - //return nil } } @@ -183,40 +251,34 @@ func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { } } - p2pModule.(*p2p.P2PModule).GetRainTreeRouter().HandlerProxy( + p2pModule.GetRainTreeRouter().HandlerProxy( s, noopHandlerProxyFactory, ) - p2pModule.(*p2p.P2PModule).GetBackgroundRouter().HandlerProxy( + + p2pModule.GetBackgroundRouter().HandlerProxy( s, handlerProxyFactory, ) + } - //time.Sleep(time.Millisecond * 500) - - // (NOPE) WIP: host-level intercept... - //for _, host := range s.libp2pNetworkMock.Hosts() { - // //s.Logf("host protocols: %v", host.Mux().Protocols()) - // //host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { - // host.SetStreamHandler(pubsub.FloodSubID, func(stream libp2pNetwork.Stream) { - // //s.Logf("inbound stream protocol: %s", stream.Protocol()) - // // //s.seenServiceURLs[stream.Conn().RemotePeer()] = struct{}{} - // // data, err := io.ReadAll(stream) - // // require.NoError(s, err) - // // - // // s.Logf("stream data: %s", data) - // }) - // host.SetStreamHandler(dht.ProtocolDHT, func(stream libp2pNetwork.Stream) { - // //s.Logf("inbound stream protocol: %s", stream.Protocol()) - // //s.seenServiceURLs[stream.Conn().RemotePeer()] = struct{}{} - // //data, err := io.ReadAll(stream) - // //require.NoError(s, err) - // // - // //s.Logf("stream data: %s", data) - // }) - //} + // wait for bootstrapping to complete + // TODO_THIS_COMMIT: move + bootstrapTimeoutDuration := time.Second * 5 + bootstrapDone := make(chan struct{}, 0) + go func() { + s.bootstrapNetworkWaitGroup.Wait() + close(bootstrapDone) + }() + + select { + case <-time.After(bootstrapTimeoutDuration): + s.Fatal("timed out waiting for bootstrapping") + case <-bootstrapDone: + } } func (s *suite) ANodeBroadcastsATestMessageViaItsBackgroundRouter() { + // TODO_THIS_COMMIT: refactor s.timeoutDuration = broadcastTimeoutDuration // select arbitrary sender & store in context for reference later @@ -225,8 +287,9 @@ func (s *suite) ANodeBroadcastsATestMessageViaItsBackgroundRouter() { // broadcast a test message msg := &anypb.Any{} - // TODO: - // - disable raintree router OR broadcast w/ bg router only + // TODO_THIS_COMMIT: try to remove... + // wait for DHT bootstrapping + time.Sleep(time.Millisecond * 250) err := s.sender.Broadcast(msg) require.NoError(s, err) @@ -236,15 +299,15 @@ func (s *suite) MinusOneNumberOfNodesShouldReceiveTheTestMessage(receivedCountPl done := make(chan struct{}, 1) go func() { - s.wg.Wait() + s.receivedWaitGroup.Wait() s.mu.Lock() defer s.mu.Unlock() receivedCount := int(receivedCountPlus1 - 1) require.Lenf( - s, s.seenServiceURLs, receivedCount, + s, s.receivedFromServiceURLMap, receivedCount, "expected to see %d peers, got: %v", - receivedCount, len(s.seenServiceURLs), + receivedCount, len(s.receivedFromServiceURLMap), ) done <- struct{}{} }() @@ -254,8 +317,64 @@ func (s *suite) MinusOneNumberOfNodesShouldReceiveTheTestMessage(receivedCountPl s.mu.Lock() defer s.mu.Unlock() - s.Fatalf("timed out waiting for messages to be received; received: %d; seenServiceURLs: %v", s.receivedCount, s.seenServiceURLs) + s.Fatalf("timed out waiting for messages to be received; received: %d; receivedFromServiceURLMap: %v", s.receivedCount, s.receivedFromServiceURLMap) case <-done: - s.Logf("seenCount: %d; seenServiceURLs: %v", len(s.seenServiceURLs), s.seenServiceURLs) + s.Logf("seenCount: %d; receivedFromServiceURLMap: %v", len(s.receivedFromServiceURLMap), s.receivedFromServiceURLMap) + } +} + +func (s *suite) initBootstrapPeerIDChMap(p2pModule *p2p.P2PModule) { + selfID := p2pModule.GetHost().ID() + // initialize `s.bootstrapPeerIDChMap` for each p2pModule + if _, ok := s.bootstrapPeerIDChMap[selfID]; !ok { + s.bootstrapPeerIDChMap[selfID] = make(chan libp2pPeer.ID) + } +} + +func (s *suite) initBootstrapPeerIDsMap(p2pModule *p2p.P2PModule) { + selfID := p2pModule.GetHost().ID() + // initialize `s.bootstrapPeerIDsMap` + if s.bootstrapPeerIDsMap == nil { + s.bootstrapPeerIDsMap = make(map[libp2pPeer.ID]PeerIDSet) + } + + // initialize `s.bootstrapPeerIDsMap` for each p2pModule + if _, ok := s.bootstrapPeerIDsMap[selfID]; !ok { + s.bootstrapPeerIDsMap[selfID] = make(PeerIDSet) + } +} + +func (s *suite) trackBootstrapProgress(p2pModule *p2p.P2PModule, peerCount int) { + s.initBootstrapPeerIDsMap(p2pModule) + selfID := p2pModule.GetHost().ID() + + // add unique bootstrap peer IDs to `bootstrapPeerIDsMap` for this + // p2pModule (`selfID`) as they connect + for { + newBootstrapPeerID := <-s.bootstrapPeerIDChMap[selfID] + if selfID == newBootstrapPeerID { + // don't count self as a bootstrap peer + s.Logf("self bootstrap peer ID: %s", newBootstrapPeerID) + continue + } + + if _, ok := s.bootstrapPeerIDsMap[selfID][newBootstrapPeerID]; ok { + // already connected to this peer during bootstrapping + s.Logf("duplicate bootstrap peer ID: %s", newBootstrapPeerID) + continue + } + s.bootstrapPeerIDsMap[selfID][newBootstrapPeerID] = struct{}{} + + if len(p2pModule.GetHost().Network().Conns()) == peerCount { + s.Logf("bootstrap peer connections len: %d", len(p2pModule.GetHost().Network().Conns())) + connections := p2pModule.GetHost().Network().Conns() + remoteConnPeers := make([]libp2pPeer.ID, len(connections)) + for i, conn := range connections { + remoteConnPeers[i] = conn.RemotePeer() + } + s.Logf("p2pModule.GetHost().Network().Conns(): %v", remoteConnPeers) + s.Logf("s.bootstrapPeerIDsMap[selfID]: %v", s.bootstrapPeerIDsMap[selfID]) + s.bootstrapNetworkWaitGroup.Done() + } } } From c0a8f689460a699f398e18af798f4593369c3b6c Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:32:06 +0200 Subject: [PATCH 31/82] wip: background gossip integration test --- p2p/integration/background_gossip_test.go | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index 47382a518..9549375bc 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -41,10 +41,10 @@ func TestMinimal(t *testing.T) { t.Parallel() // a new step definition suite is constructed for every scenario - gocuke.NewRunner(t, &suite{}).Path(backgroundGossipFeaturePath).Run() + gocuke.NewRunner(t, new(backgroundGossipSuite)).Path(backgroundGossipFeaturePath).Run() } -type suite struct { +type backgroundGossipSuite struct { // special arguments like TestingT are injected automatically into exported fields gocuke.TestingT @@ -71,7 +71,7 @@ type suite struct { sender *p2p.P2PModule } -func (s *suite) Before(scenario gocuke.Scenario) { +func (s *backgroundGossipSuite) Before(scenario gocuke.Scenario) { s.mu.Lock() defer s.mu.Unlock() @@ -79,23 +79,23 @@ func (s *suite) Before(scenario gocuke.Scenario) { s.receivedFromServiceURLMap = make(map[string]struct{}) } -func (s *suite) AFaultyNetworkOfPeers(a int64) { +func (s *backgroundGossipSuite) AFaultyNetworkOfPeers(a int64) { panic("PENDING") } -func (s *suite) NumberOfFaultyPeers(a int64) { +func (s *backgroundGossipSuite) NumberOfFaultyPeers(a int64) { panic("PENDING") } -func (s *suite) NumberOfNodesJoinTheNetwork(a int64) { +func (s *backgroundGossipSuite) NumberOfNodesJoinTheNetwork(a int64) { panic("PENDING") } -func (s *suite) NumberOfNodesLeaveTheNetwork(a int64) { +func (s *backgroundGossipSuite) NumberOfNodesLeaveTheNetwork(a int64) { panic("PENDING") } -func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { +func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { var ( peerCount = int(count) pubKeys = make([]cryptoPocket.PublicKey, peerCount) @@ -277,7 +277,7 @@ func (s *suite) AFullyConnectedNetworkOfPeers(count int64) { } } -func (s *suite) ANodeBroadcastsATestMessageViaItsBackgroundRouter() { +func (s *backgroundGossipSuite) ANodeBroadcastsATestMessageViaItsBackgroundRouter() { // TODO_THIS_COMMIT: refactor s.timeoutDuration = broadcastTimeoutDuration @@ -295,7 +295,7 @@ func (s *suite) ANodeBroadcastsATestMessageViaItsBackgroundRouter() { require.NoError(s, err) } -func (s *suite) MinusOneNumberOfNodesShouldReceiveTheTestMessage(receivedCountPlus1 int64) { +func (s *backgroundGossipSuite) MinusOneNumberOfNodesShouldReceiveTheTestMessage(receivedCountPlus1 int64) { done := make(chan struct{}, 1) go func() { @@ -323,7 +323,7 @@ func (s *suite) MinusOneNumberOfNodesShouldReceiveTheTestMessage(receivedCountPl } } -func (s *suite) initBootstrapPeerIDChMap(p2pModule *p2p.P2PModule) { +func (s *backgroundGossipSuite) initBootstrapPeerIDChMap(p2pModule *p2p.P2PModule) { selfID := p2pModule.GetHost().ID() // initialize `s.bootstrapPeerIDChMap` for each p2pModule if _, ok := s.bootstrapPeerIDChMap[selfID]; !ok { @@ -331,7 +331,7 @@ func (s *suite) initBootstrapPeerIDChMap(p2pModule *p2p.P2PModule) { } } -func (s *suite) initBootstrapPeerIDsMap(p2pModule *p2p.P2PModule) { +func (s *backgroundGossipSuite) initBootstrapPeerIDsMap(p2pModule *p2p.P2PModule) { selfID := p2pModule.GetHost().ID() // initialize `s.bootstrapPeerIDsMap` if s.bootstrapPeerIDsMap == nil { @@ -344,7 +344,7 @@ func (s *suite) initBootstrapPeerIDsMap(p2pModule *p2p.P2PModule) { } } -func (s *suite) trackBootstrapProgress(p2pModule *p2p.P2PModule, peerCount int) { +func (s *backgroundGossipSuite) trackBootstrapProgress(p2pModule *p2p.P2PModule, peerCount int) { s.initBootstrapPeerIDsMap(p2pModule) selfID := p2pModule.GetHost().ID() From 2c22f01d9f1a6ecc96cd1193188916915278720c Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:34:18 +0200 Subject: [PATCH 32/82] wip: background gossip integration test --- p2p/integration/background_gossip_test.go | 49 ++++++++++++++++------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index 9549375bc..4c2770f68 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -4,6 +4,7 @@ package integration import ( "fmt" + "github.com/foxcpp/go-mockdns" libp2pNetwork "github.com/libp2p/go-libp2p/core/network" libp2pPeer "github.com/libp2p/go-libp2p/core/peer" typesP2P "github.com/pokt-network/pocket/p2p/types" @@ -34,7 +35,9 @@ type PeerIDSet map[libp2pPeer.ID]struct{} const ( backgroundGossipFeaturePath = "background_gossip.feature" //broadcastTimeoutDuration = time.Millisecond * 250 - broadcastTimeoutDuration = time.Second * 5 + broadcastTimeoutDuration = time.Second * 3 + // TODO_THIS_COMMIT: move + bootstrapTimeoutDuration = time.Second * 3 ) func TestMinimal(t *testing.T) { @@ -47,6 +50,7 @@ func TestMinimal(t *testing.T) { type backgroundGossipSuite struct { // special arguments like TestingT are injected automatically into exported fields gocuke.TestingT + dnsSrv *mockdns.Server timeoutDuration time.Duration // TODO_THIS_COMMIT: rename @@ -54,6 +58,7 @@ type backgroundGossipSuite struct { // receivedFromServiceURLMap is used as a map to track which messages have been // received by which nodes. receivedFromServiceURLMap map[string]struct{} + bootstrapMutex sync.Mutex // bootstrapPeerIDChMap is a mapping between the peerID string of each node to // a channel that will be used to signal the peer ID strings of each node it // has discovered. @@ -75,6 +80,7 @@ func (s *backgroundGossipSuite) Before(scenario gocuke.Scenario) { s.mu.Lock() defer s.mu.Unlock() + s.dnsSrv = testutil.MinimalDNSMock(s) s.bootstrapPeerIDChMap = make(map[libp2pPeer.ID]chan libp2pPeer.ID) s.receivedFromServiceURLMap = make(map[string]struct{}) } @@ -100,7 +106,9 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { peerCount = int(count) pubKeys = make([]cryptoPocket.PublicKey, peerCount) ) - s.Logf("ADDING peerCount - 1: %d", peerCount-1) + + //s.Logf("ADDING peerCount - 1: %d", peerCount-1) + s.bootstrapNetworkWaitGroup.Add(peerCount) s.receivedWaitGroup.Add(peerCount - 1) s.mu.Lock() defer s.mu.Unlock() @@ -157,6 +165,7 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { // setup mock network s.busMocks, s.libp2pNetworkMock, s.p2pModules = constructors.NewBusesMocknetAndP2PModules( s, peerCount, + s.dnsSrv, genesisState, busEventHandlerFactory, ) @@ -168,7 +177,6 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { //s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1) / 2) //s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1)) //s.bootstrapNetworkWaitGroup.Add(peerCount * peerCount) - s.bootstrapNetworkWaitGroup.Add(peerCount) // add expectations for P2P events to telemetry module's event metrics agent for _, busMock := range s.busMocks { @@ -191,9 +199,9 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { //countz := 0 p2pModule := module.(*p2p.P2PModule) - p2pCfg := p2pModule.GetBus().GetRuntimeMgr().GetConfig().P2P - serviceURL := fmt.Sprintf("%s:%d", p2pCfg.Hostname, p2pCfg.Port) - s.Logf("serviceURL: %s; peer ID: %s", serviceURL, p2pModule.GetHost().ID()) + //p2pCfg := p2pModule.GetBus().GetRuntimeMgr().GetConfig().P2P + //serviceURL := fmt.Sprintf("%s:%d", p2pCfg.Hostname, p2pCfg.Port) + //s.Logf("serviceURL: %s; peer ID: %s", serviceURL, p2pModule.GetHost().ID()) s.initBootstrapPeerIDChMap(p2pModule) @@ -203,15 +211,20 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { go s.trackBootstrapProgress(p2pModule, peerCount-1) // TODO_THIS_COMMIT: refactor + debugNotifee := testutil.NewDebugNotifee(s) notifee := &libp2pNetwork.NotifyBundle{ - DisconnectedF: func(network libp2pNetwork.Network, conn libp2pNetwork.Conn) { - s.Logf("disconnected: %s", conn.RemotePeer()) - }, - ConnectedF: func(_ libp2pNetwork.Network, conn libp2pNetwork.Conn) { - s.Logf("connected: %s", conn.RemotePeer()) + //DisconnectedF: func(network libp2pNetwork.Network, conn libp2pNetwork.Conn) { + // s.Logf("disconnected: %s", conn.RemotePeer()) + //}, + DisconnectedF: debugNotifee.Disconnected, + ConnectedF: func(net libp2pNetwork.Network, conn libp2pNetwork.Conn) { + //s.Logf("connected: %s", conn.RemotePeer()) //s.Logf("pstore size: %d", len(p2pModule.GetHost().Peerstore().Peers())) + s.bootstrapMutex.Lock() + defer s.bootstrapMutex.Unlock() + s.bootstrapPeerIDChMap[p2pModule.GetHost().ID()] <- conn.RemotePeer() - s.Logf("bootstrap peer ID sent on channel") + //s.Logf("bootstrap peer ID sent on channel") //if len(p2pModule.GetHost().Peerstore().Peers()) == peerCount { // countz++ // s.Logf("count: %d", countz) @@ -219,8 +232,12 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { // //s.bootstrapNetworkWaitGroup.Done() // //s.bootstrapNetworkWaitGroup.Done() //} + debugNotifee.Connected(net, conn) }, + ListenF: debugNotifee.Listen, + ListenCloseF: debugNotifee.ListenClose, } + //p2pModule.GetHost().Network().Notify(debugNotifee) p2pModule.GetHost().Network().Notify(notifee) // -- @@ -262,8 +279,6 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { } // wait for bootstrapping to complete - // TODO_THIS_COMMIT: move - bootstrapTimeoutDuration := time.Second * 5 bootstrapDone := make(chan struct{}, 0) go func() { s.bootstrapNetworkWaitGroup.Wait() @@ -324,6 +339,9 @@ func (s *backgroundGossipSuite) MinusOneNumberOfNodesShouldReceiveTheTestMessage } func (s *backgroundGossipSuite) initBootstrapPeerIDChMap(p2pModule *p2p.P2PModule) { + s.bootstrapMutex.Lock() + defer s.bootstrapMutex.Unlock() + selfID := p2pModule.GetHost().ID() // initialize `s.bootstrapPeerIDChMap` for each p2pModule if _, ok := s.bootstrapPeerIDChMap[selfID]; !ok { @@ -332,6 +350,9 @@ func (s *backgroundGossipSuite) initBootstrapPeerIDChMap(p2pModule *p2p.P2PModul } func (s *backgroundGossipSuite) initBootstrapPeerIDsMap(p2pModule *p2p.P2PModule) { + s.bootstrapMutex.Lock() + defer s.bootstrapMutex.Unlock() + selfID := p2pModule.GetHost().ID() // initialize `s.bootstrapPeerIDsMap` if s.bootstrapPeerIDsMap == nil { From e00031bbe88f7af407ed3d98b339484d24b2017d Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:34:54 +0200 Subject: [PATCH 33/82] wip: background gossip integration test --- p2p/integration/background_gossip_test.go | 217 +++++++++++++--------- 1 file changed, 129 insertions(+), 88 deletions(-) diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index 4c2770f68..f8716d958 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -47,6 +47,11 @@ func TestMinimal(t *testing.T) { gocuke.NewRunner(t, new(backgroundGossipSuite)).Path(backgroundGossipFeaturePath).Run() } +type peerConnectionEvent struct { + localID libp2pPeer.ID + remoteID libp2pPeer.ID +} + type backgroundGossipSuite struct { // special arguments like TestingT are injected automatically into exported fields gocuke.TestingT @@ -59,14 +64,19 @@ type backgroundGossipSuite struct { // received by which nodes. receivedFromServiceURLMap map[string]struct{} bootstrapMutex sync.Mutex + // bootstrapPeerIDChMap is a mapping between the peerID string of each node to // a channel that will be used to signal the peer ID strings of each node it // has discovered. - bootstrapPeerIDChMap map[libp2pPeer.ID]chan libp2pPeer.ID + //bootstrapPeerIDChMap map[libp2pPeer.ID]chan libp2pPeer.ID + + bootstrapPeerIDCh chan peerConnectionEvent + // bootstrapPeerIDsMap is a mapping between the peerID string of each node to a // set of peerID strings that node has discovered. This set is represented as // a map with the peerID string as the key and an empty struct as the value. - bootstrapPeerIDsMap map[libp2pPeer.ID]PeerIDSet + bootstrapPeerIDsMap map[libp2pPeer.ID]PeerIDSet + bootstrapNetworkWaitGroup sync.WaitGroup receivedCount int receivedWaitGroup sync.WaitGroup @@ -81,7 +91,8 @@ func (s *backgroundGossipSuite) Before(scenario gocuke.Scenario) { defer s.mu.Unlock() s.dnsSrv = testutil.MinimalDNSMock(s) - s.bootstrapPeerIDChMap = make(map[libp2pPeer.ID]chan libp2pPeer.ID) + //s.bootstrapPeerIDChMap = make(map[libp2pPeer.ID]chan libp2pPeer.ID) + s.bootstrapPeerIDCh = make(chan peerConnectionEvent) s.receivedFromServiceURLMap = make(map[string]struct{}) } @@ -102,23 +113,17 @@ func (s *backgroundGossipSuite) NumberOfNodesLeaveTheNetwork(a int64) { } func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { - var ( - peerCount = int(count) - pubKeys = make([]cryptoPocket.PublicKey, peerCount) - ) + peerCount := int(count) - //s.Logf("ADDING peerCount - 1: %d", peerCount-1) - s.bootstrapNetworkWaitGroup.Add(peerCount) + //s.bootstrapNetworkWaitGroup.Add(peerCount) + //s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1) / 2) + s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1)) s.receivedWaitGroup.Add(peerCount - 1) s.mu.Lock() defer s.mu.Unlock() - for i, privKey := range testutil.LoadLocalnetPrivateKeys(s, peerCount) { - pubKeys[i] = privKey.PublicKey() - } - genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(s, pubKeys) - // TODO_THIS_COMMIT: explain - //genesisState := runtime_testutil.GenesisWithSequentialServiceURLs(s, nil) + serviceURLKeyMap := testutil.SequentialServiceURLPrivKeyMap(s, peerCount) + genesisState := runtime_testutil.BaseGenesisStateMockFromServiceURLKeyMap(s, serviceURLKeyMap) // TODO_THIS_COMMIT: refactor busEventHandlerFactory := func(t gocuke.TestingT, busMock *mock_modules.MockBus) testutil.BusEventHandler { @@ -162,12 +167,44 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { } // -- + // TODO_THIS_COMMIT: refactor + debugNotifiee := testutil.NewDebugNotifee(s) + notifiee := &libp2pNetwork.NotifyBundle{ + //DisconnectedF: func(network libp2pNetwork.Network, conn libp2pNetwork.Conn) { + // s.Logf("disconnected: %s", conn.RemotePeer()) + //}, + DisconnectedF: debugNotifiee.Disconnected, + ConnectedF: func(net libp2pNetwork.Network, conn libp2pNetwork.Conn) { + //s.Logf("connected: %s", conn.RemotePeer()) + //s.Logf("pstore size: %d", len(p2pModule.GetHost().Peerstore().Peers())) + + s.bootstrapPeerIDCh <- peerConnectionEvent{ + localID: conn.LocalPeer(), + remoteID: conn.RemotePeer(), + } + //s.bootstrapPeerIDChMap[p2pModule.GetHost().ID()] <- conn.RemotePeer() + //s.Logf("bootstrap peer ID sent on channel") + //if len(p2pModule.GetHost().Peerstore().Peers()) == peerCount { + // countz++ + // s.Logf("count: %d", countz) + // //s.bootstrapNetworkWaitGroup.Done() + // //s.bootstrapNetworkWaitGroup.Done() + // //s.bootstrapNetworkWaitGroup.Done() + //} + debugNotifiee.Connected(net, conn) + }, + ListenF: debugNotifiee.Listen, + ListenCloseF: debugNotifiee.ListenClose, + } + // -- + // setup mock network s.busMocks, s.libp2pNetworkMock, s.p2pModules = constructors.NewBusesMocknetAndP2PModules( s, peerCount, s.dnsSrv, genesisState, busEventHandlerFactory, + notifiee, ) // add number of combinations of peers given number of peers @@ -193,52 +230,50 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { // TODO_THIS_COMMIT: bus event handler based receivedWaitGroup.Done()! + // concurrently update `s.bootstrapPeerIDsMap` by receiving from the + // corresponding channel from `s.bootstrapPeerIDChMap` that `notifee` + // is sending on. + go s.trackBootstrapProgress(peerCount - 1) + // start P2P modules of all peers //handleCount := 0 for _, module := range s.p2pModules { //countz := 0 p2pModule := module.(*p2p.P2PModule) - //p2pCfg := p2pModule.GetBus().GetRuntimeMgr().GetConfig().P2P - //serviceURL := fmt.Sprintf("%s:%d", p2pCfg.Hostname, p2pCfg.Port) - //s.Logf("serviceURL: %s; peer ID: %s", serviceURL, p2pModule.GetHost().ID()) - - s.initBootstrapPeerIDChMap(p2pModule) - - // concurrently update `s.bootstrapPeerIDsMap` by receiving from the - // corresponding channel from `s.bootstrapPeerIDChMap` that `notifee` - // is sending on. - go s.trackBootstrapProgress(p2pModule, peerCount-1) - // TODO_THIS_COMMIT: refactor - debugNotifee := testutil.NewDebugNotifee(s) - notifee := &libp2pNetwork.NotifyBundle{ - //DisconnectedF: func(network libp2pNetwork.Network, conn libp2pNetwork.Conn) { - // s.Logf("disconnected: %s", conn.RemotePeer()) - //}, - DisconnectedF: debugNotifee.Disconnected, - ConnectedF: func(net libp2pNetwork.Network, conn libp2pNetwork.Conn) { - //s.Logf("connected: %s", conn.RemotePeer()) - //s.Logf("pstore size: %d", len(p2pModule.GetHost().Peerstore().Peers())) - s.bootstrapMutex.Lock() - defer s.bootstrapMutex.Unlock() - - s.bootstrapPeerIDChMap[p2pModule.GetHost().ID()] <- conn.RemotePeer() - //s.Logf("bootstrap peer ID sent on channel") - //if len(p2pModule.GetHost().Peerstore().Peers()) == peerCount { - // countz++ - // s.Logf("count: %d", countz) - // //s.bootstrapNetworkWaitGroup.Done() - // //s.bootstrapNetworkWaitGroup.Done() - // //s.bootstrapNetworkWaitGroup.Done() - //} - debugNotifee.Connected(net, conn) - }, - ListenF: debugNotifee.Listen, - ListenCloseF: debugNotifee.ListenClose, - } - //p2pModule.GetHost().Network().Notify(debugNotifee) - p2pModule.GetHost().Network().Notify(notifee) + //debugNotifiee := testutil.NewDebugNotifee(s) + //notifee := &libp2pNetwork.NotifyBundle{ + // //DisconnectedF: func(network libp2pNetwork.Network, conn libp2pNetwork.Conn) { + // // s.Logf("disconnected: %s", conn.RemotePeer()) + // //}, + // DisconnectedF: debugNotifiee.Disconnected, + // ConnectedF: func(net libp2pNetwork.Network, conn libp2pNetwork.Conn) { + // //s.Logf("connected: %s", conn.RemotePeer()) + // //s.Logf("pstore size: %d", len(p2pModule.GetHost().Peerstore().Peers())) + // s.bootstrapMutex.Lock() + // defer s.bootstrapMutex.Unlock() + // + // s.bootstrapPeerIDCh <- peerConnectionEvent{ + // localID: conn.LocalPeer(), + // remoteID: conn.RemotePeer(), + // } + // //s.bootstrapPeerIDChMap[p2pModule.GetHost().ID()] <- conn.RemotePeer() + // //s.Logf("bootstrap peer ID sent on channel") + // //if len(p2pModule.GetHost().Peerstore().Peers()) == peerCount { + // // countz++ + // // s.Logf("count: %d", countz) + // // //s.bootstrapNetworkWaitGroup.Done() + // // //s.bootstrapNetworkWaitGroup.Done() + // // //s.bootstrapNetworkWaitGroup.Done() + // //} + // debugNotifiee.Connected(net, conn) + // }, + // ListenF: debugNotifiee.Listen, + // ListenCloseF: debugNotifiee.ListenClose, + //} + ////p2pModule.GetHost().Network().Notify(debugNotifiee) + //p2pModule.GetHost().Network().Notify(notifee) // -- err := p2pModule.Start() @@ -338,22 +373,22 @@ func (s *backgroundGossipSuite) MinusOneNumberOfNodesShouldReceiveTheTestMessage } } -func (s *backgroundGossipSuite) initBootstrapPeerIDChMap(p2pModule *p2p.P2PModule) { - s.bootstrapMutex.Lock() - defer s.bootstrapMutex.Unlock() - - selfID := p2pModule.GetHost().ID() - // initialize `s.bootstrapPeerIDChMap` for each p2pModule - if _, ok := s.bootstrapPeerIDChMap[selfID]; !ok { - s.bootstrapPeerIDChMap[selfID] = make(chan libp2pPeer.ID) - } -} - -func (s *backgroundGossipSuite) initBootstrapPeerIDsMap(p2pModule *p2p.P2PModule) { +//func (s *backgroundGossipSuite) initBootstrapPeerIDChMap(p2pModule *p2p.P2PModule) { +// s.bootstrapMutex.Lock() +// defer s.bootstrapMutex.Unlock() +// +// selfID := p2pModule.GetHost().ID() +// // initialize `s.bootstrapPeerIDChMap` for each p2pModule +// if _, ok := s.bootstrapPeerIDChMap[selfID]; !ok { +// s.bootstrapPeerIDChMap[selfID] = make(chan libp2pPeer.ID) +// } +//} + +func (s *backgroundGossipSuite) initBootstrapPeerIDsMap(selfID libp2pPeer.ID) { + // TODO_THIS_COMMIT: need this? s.bootstrapMutex.Lock() defer s.bootstrapMutex.Unlock() - selfID := p2pModule.GetHost().ID() // initialize `s.bootstrapPeerIDsMap` if s.bootstrapPeerIDsMap == nil { s.bootstrapPeerIDsMap = make(map[libp2pPeer.ID]PeerIDSet) @@ -365,37 +400,43 @@ func (s *backgroundGossipSuite) initBootstrapPeerIDsMap(p2pModule *p2p.P2PModule } } -func (s *backgroundGossipSuite) trackBootstrapProgress(p2pModule *p2p.P2PModule, peerCount int) { - s.initBootstrapPeerIDsMap(p2pModule) - selfID := p2pModule.GetHost().ID() +func (s *backgroundGossipSuite) trackBootstrapProgress(peerCount int) { + //selfID := p2pModule.GetHost().ID() // add unique bootstrap peer IDs to `bootstrapPeerIDsMap` for this // p2pModule (`selfID`) as they connect for { - newBootstrapPeerID := <-s.bootstrapPeerIDChMap[selfID] - if selfID == newBootstrapPeerID { + //newBootstrapPeerID := <-s.bootstrapPeerIDChMap[selfID] + newPeerConnectionEvent := <-s.bootstrapPeerIDCh + localID, remoteID := newPeerConnectionEvent.localID, newPeerConnectionEvent.remoteID + + s.initBootstrapPeerIDsMap(localID) + + if localID == remoteID { // don't count self as a bootstrap peer - s.Logf("self bootstrap peer ID: %s", newBootstrapPeerID) + s.Logf("self bootstrap peer ID: %s") continue } - if _, ok := s.bootstrapPeerIDsMap[selfID][newBootstrapPeerID]; ok { + if _, ok := s.bootstrapPeerIDsMap[localID][remoteID]; ok { // already connected to this peer during bootstrapping - s.Logf("duplicate bootstrap peer ID: %s", newBootstrapPeerID) + s.Logf("duplicate bootstrap peer ID: %s", remoteID) continue } - s.bootstrapPeerIDsMap[selfID][newBootstrapPeerID] = struct{}{} - - if len(p2pModule.GetHost().Network().Conns()) == peerCount { - s.Logf("bootstrap peer connections len: %d", len(p2pModule.GetHost().Network().Conns())) - connections := p2pModule.GetHost().Network().Conns() - remoteConnPeers := make([]libp2pPeer.ID, len(connections)) - for i, conn := range connections { - remoteConnPeers[i] = conn.RemotePeer() - } - s.Logf("p2pModule.GetHost().Network().Conns(): %v", remoteConnPeers) - s.Logf("s.bootstrapPeerIDsMap[selfID]: %v", s.bootstrapPeerIDsMap[selfID]) - s.bootstrapNetworkWaitGroup.Done() - } + s.bootstrapPeerIDsMap[localID][remoteID] = struct{}{} + s.bootstrapNetworkWaitGroup.Done() + + //p2pModule := s.p2pModules[serviceURL].(*p2p.P2PModule) + //if len(p2pModule.GetHost().Network().Conns()) == peerCount { + // s.Logf("bootstrap peer connections len: %d", len(p2pModule.GetHost().Network().Conns())) + // connections := p2pModule.GetHost().Network().Conns() + // remoteConnPeers := make([]libp2pPeer.ID, len(connections)) + // for i, conn := range connections { + // remoteConnPeers[i] = conn.RemotePeer() + // } + // s.Logf("p2pModule.GetHost().Network().Conns(): %v", remoteConnPeers) + // s.Logf("s.bootstrapPeerIDsMap[selfID]: %v", s.bootstrapPeerIDsMap[localID]) + // s.bootstrapNetworkWaitGroup.Done() + //} } } From b0d25e7ff4d670d859fa46e9d7d62f5ed0048eb6 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:36:43 +0200 Subject: [PATCH 34/82] wip: background gossip integration test --- p2p/integration/background_gossip_test.go | 80 +++++++++++++---------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index f8716d958..994489901 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -59,11 +59,12 @@ type backgroundGossipSuite struct { timeoutDuration time.Duration // TODO_THIS_COMMIT: rename - mu sync.Mutex - // receivedFromServiceURLMap is used as a map to track which messages have been + mu sync.Mutex + receivedServiceURLCh chan string + // receivedServiceURLMap is used as a map to track which messages have been // received by which nodes. - receivedFromServiceURLMap map[string]struct{} - bootstrapMutex sync.Mutex + receivedServiceURLMap map[string]struct{} + bootstrapMutex sync.Mutex // bootstrapPeerIDChMap is a mapping between the peerID string of each node to // a channel that will be used to signal the peer ID strings of each node it @@ -86,14 +87,15 @@ type backgroundGossipSuite struct { sender *p2p.P2PModule } -func (s *backgroundGossipSuite) Before(scenario gocuke.Scenario) { +func (s *backgroundGossipSuite) Before(_ gocuke.Scenario) { s.mu.Lock() defer s.mu.Unlock() s.dnsSrv = testutil.MinimalDNSMock(s) //s.bootstrapPeerIDChMap = make(map[libp2pPeer.ID]chan libp2pPeer.ID) s.bootstrapPeerIDCh = make(chan peerConnectionEvent) - s.receivedFromServiceURLMap = make(map[string]struct{}) + s.receivedServiceURLCh = make(chan string) + s.receivedServiceURLMap = make(map[string]struct{}) } func (s *backgroundGossipSuite) AFaultyNetworkOfPeers(a int64) { @@ -115,6 +117,7 @@ func (s *backgroundGossipSuite) NumberOfNodesLeaveTheNetwork(a int64) { func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { peerCount := int(count) + // TODO_THIS_COMMOT: comment, explain //s.bootstrapNetworkWaitGroup.Add(peerCount) //s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1) / 2) s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1)) @@ -126,23 +129,39 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { genesisState := runtime_testutil.BaseGenesisStateMockFromServiceURLKeyMap(s, serviceURLKeyMap) // TODO_THIS_COMMIT: refactor + go func() { + for serviceURL := range s.receivedServiceURLCh { + // use a channel instead of a map; send a struct like w/ trackBootstrapProgress() + // THIS IS PER BUS (i.e. run in each node)!! + _, ok := s.receivedServiceURLMap[serviceURL] + require.Falsef(s, ok, "received message from duplicate serviceURL: %s", serviceURL) + + s.receivedCount++ + s.receivedServiceURLMap[serviceURL] = struct{}{} + s.receivedWaitGroup.Done() + } + }() + + s.Cleanup(func() { + close(s.receivedServiceURLCh) + }) + busEventHandlerFactory := func(t gocuke.TestingT, busMock *mock_modules.MockBus) testutil.BusEventHandler { // event handler is called when a p2p module receives a network message return func(data *messaging.PocketEnvelope) { s.mu.Lock() defer s.mu.Unlock() - defer func() { - if r := recover(); r != nil { - t.Logf("seenCount: %d; receivedFromServiceURLMap: %v", len(s.receivedFromServiceURLMap), s.receivedFromServiceURLMap) - //panic(r) - t.Fatalf("panic: %v", r) - } - }() + //defer func() { + // if r := recover(); r != nil { + // t.Logf("seenCount: %d; receivedServiceURLMap: %v", len(s.receivedServiceURLMap), s.receivedServiceURLMap) + // //panic(r) + // t.Fatalf("panic: %v", r) + // } + //}() p2pCfg := busMock.GetRuntimeMgr().GetConfig().P2P serviceURL := fmt.Sprintf("%s:%d", p2pCfg.Hostname, defaults.DefaultP2PPort) - t.Logf("received message by %s", serviceURL) peerPrivKey, err := cryptoPocket.NewPrivateKey(p2pCfg.PrivateKey) require.NoError(t, err) @@ -151,18 +170,16 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { require.NoError(t, err) if senderAddr.Equals(peerPrivKey.Address()) { - t.Logf("SELF: %s", serviceURL) + t.Logf("ignoring message from self: %s", serviceURL) return } + t.Logf("received message from %s", serviceURL) - if _, ok := s.receivedFromServiceURLMap[serviceURL]; ok { - t.Logf("DUPLICATE SERVICE URL: %s", serviceURL) - return - } + // TODO: RESUME HERE!!! + // TODO: RESUME HERE!!! + // TODO: RESUME HERE!!! - s.receivedCount++ - s.receivedFromServiceURLMap[serviceURL] = struct{}{} - s.receivedWaitGroup.Done() + s.receivedServiceURLCh <- serviceURL } } // -- @@ -207,14 +224,6 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { notifiee, ) - // add number of combinations of peers given number of peers - //s.Logf("bootstrapNetworkWaitGroup.Add: %d", peerCount*(peerCount-1)/2) - //s.Logf("bootstrapNetworkWaitGroup.Add: %d", peerCount*(peerCount-1)) - //s.Logf("bootstrapNetworkWaitGroup.Add: %d", peerCount*peerCount) - //s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1) / 2) - //s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1)) - //s.bootstrapNetworkWaitGroup.Add(peerCount * peerCount) - // add expectations for P2P events to telemetry module's event metrics agent for _, busMock := range s.busMocks { // TODO_THIS_COMMIT: ?? @@ -355,9 +364,9 @@ func (s *backgroundGossipSuite) MinusOneNumberOfNodesShouldReceiveTheTestMessage receivedCount := int(receivedCountPlus1 - 1) require.Lenf( - s, s.receivedFromServiceURLMap, receivedCount, + s, s.receivedServiceURLMap, receivedCount, "expected to see %d peers, got: %v", - receivedCount, len(s.receivedFromServiceURLMap), + receivedCount, len(s.receivedServiceURLMap), ) done <- struct{}{} }() @@ -367,9 +376,9 @@ func (s *backgroundGossipSuite) MinusOneNumberOfNodesShouldReceiveTheTestMessage s.mu.Lock() defer s.mu.Unlock() - s.Fatalf("timed out waiting for messages to be received; received: %d; receivedFromServiceURLMap: %v", s.receivedCount, s.receivedFromServiceURLMap) + s.Fatalf("timed out waiting for messages to be received; received: %d; receivedServiceURLMap: %v", s.receivedCount, s.receivedServiceURLMap) case <-done: - s.Logf("seenCount: %d; receivedFromServiceURLMap: %v", len(s.receivedFromServiceURLMap), s.receivedFromServiceURLMap) + s.Logf("seenCount: %d; receivedServiceURLMap: %v", len(s.receivedServiceURLMap), s.receivedServiceURLMap) } } @@ -405,9 +414,8 @@ func (s *backgroundGossipSuite) trackBootstrapProgress(peerCount int) { // add unique bootstrap peer IDs to `bootstrapPeerIDsMap` for this // p2pModule (`selfID`) as they connect - for { + for newPeerConnectionEvent := range s.bootstrapPeerIDCh { //newBootstrapPeerID := <-s.bootstrapPeerIDChMap[selfID] - newPeerConnectionEvent := <-s.bootstrapPeerIDCh localID, remoteID := newPeerConnectionEvent.localID, newPeerConnectionEvent.remoteID s.initBootstrapPeerIDsMap(localID) From a33bafe7254e6d5c09bbd529af2167d5a7518dfd Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:41:52 +0200 Subject: [PATCH 35/82] wip: background gossip integration test --- p2p/integration/background_gossip_test.go | 74 ++++++++++++++--------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index 994489901..1dff25d49 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -35,9 +35,9 @@ type PeerIDSet map[libp2pPeer.ID]struct{} const ( backgroundGossipFeaturePath = "background_gossip.feature" //broadcastTimeoutDuration = time.Millisecond * 250 - broadcastTimeoutDuration = time.Second * 3 + broadcastTimeoutDuration = time.Second * 1 // TODO_THIS_COMMIT: move - bootstrapTimeoutDuration = time.Second * 3 + bootstrapTimeoutMinSeconds = 3 ) func TestMinimal(t *testing.T) { @@ -79,12 +79,13 @@ type backgroundGossipSuite struct { bootstrapPeerIDsMap map[libp2pPeer.ID]PeerIDSet bootstrapNetworkWaitGroup sync.WaitGroup - receivedCount int - receivedWaitGroup sync.WaitGroup - p2pModules map[string]modules.P2PModule - busMocks map[string]*mock_modules.MockBus - libp2pNetworkMock libp2pMocknet.Mocknet - sender *p2p.P2PModule + // TODO_THIS_COMMIT: use this? + receivedCount int + receivedWaitGroup sync.WaitGroup + p2pModules map[string]modules.P2PModule + busMocks map[string]*mock_modules.MockBus + libp2pNetworkMock libp2pMocknet.Mocknet + sender *p2p.P2PModule } func (s *backgroundGossipSuite) Before(_ gocuke.Scenario) { @@ -98,6 +99,10 @@ func (s *backgroundGossipSuite) Before(_ gocuke.Scenario) { s.receivedServiceURLMap = make(map[string]struct{}) } +func (s *backgroundGossipSuite) After(_ gocuke.Scenario) { + close(s.receivedServiceURLCh) +} + func (s *backgroundGossipSuite) AFaultyNetworkOfPeers(a int64) { panic("PENDING") } @@ -121,7 +126,6 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { //s.bootstrapNetworkWaitGroup.Add(peerCount) //s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1) / 2) s.bootstrapNetworkWaitGroup.Add(peerCount * (peerCount - 1)) - s.receivedWaitGroup.Add(peerCount - 1) s.mu.Lock() defer s.mu.Unlock() @@ -142,9 +146,9 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { } }() - s.Cleanup(func() { - close(s.receivedServiceURLCh) - }) + //s.Cleanup(func() { + // close(s.receivedServiceURLCh) + //}) busEventHandlerFactory := func(t gocuke.TestingT, busMock *mock_modules.MockBus) testutil.BusEventHandler { // event handler is called when a p2p module receives a network message @@ -175,22 +179,18 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { } t.Logf("received message from %s", serviceURL) - // TODO: RESUME HERE!!! - // TODO: RESUME HERE!!! - // TODO: RESUME HERE!!! - s.receivedServiceURLCh <- serviceURL } } // -- // TODO_THIS_COMMIT: refactor - debugNotifiee := testutil.NewDebugNotifee(s) + //debugNotifiee := testutil.NewDebugNotifee(s) notifiee := &libp2pNetwork.NotifyBundle{ //DisconnectedF: func(network libp2pNetwork.Network, conn libp2pNetwork.Conn) { // s.Logf("disconnected: %s", conn.RemotePeer()) //}, - DisconnectedF: debugNotifiee.Disconnected, + //DisconnectedF: debugNotifiee.Disconnected, ConnectedF: func(net libp2pNetwork.Network, conn libp2pNetwork.Conn) { //s.Logf("connected: %s", conn.RemotePeer()) //s.Logf("pstore size: %d", len(p2pModule.GetHost().Peerstore().Peers())) @@ -208,10 +208,10 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { // //s.bootstrapNetworkWaitGroup.Done() // //s.bootstrapNetworkWaitGroup.Done() //} - debugNotifiee.Connected(net, conn) + //debugNotifiee.Connected(net, conn) }, - ListenF: debugNotifiee.Listen, - ListenCloseF: debugNotifiee.ListenClose, + //ListenF: debugNotifiee.Listen, + //ListenCloseF: debugNotifiee.ListenClose, } // -- @@ -329,10 +329,21 @@ func (s *backgroundGossipSuite) AFullyConnectedNetworkOfPeers(count int64) { close(bootstrapDone) }() + var bootstrapTimeoutCh <-chan time.Time + if peerCount <= 20 { + bootstrapTimeoutCh = time.After(time.Second * bootstrapTimeoutMinSeconds) + } else { + largeNetworkTimeoutMilliseconds := (bootstrapTimeoutMinSeconds * 1000) + (250 * peerCount) + bootstrapTimeoutCh = time.After(time.Millisecond * time.Duration(largeNetworkTimeoutMilliseconds)) + } + select { - case <-time.After(bootstrapTimeoutDuration): + case <-bootstrapTimeoutCh: s.Fatal("timed out waiting for bootstrapping") case <-bootstrapDone: + // TODO_THIS_COMMIT: try to remove... + // wait for DHT bootstrapping + time.Sleep(time.Millisecond * 500) } } @@ -346,12 +357,16 @@ func (s *backgroundGossipSuite) ANodeBroadcastsATestMessageViaItsBackgroundRoute // broadcast a test message msg := &anypb.Any{} - // TODO_THIS_COMMIT: try to remove... - // wait for DHT bootstrapping - time.Sleep(time.Millisecond * 250) + s.receivedWaitGroup.Add(1) + // TODO_THIS_COMMIT: refactor + //for i := 0; i > 10; i++ { + s.receivedWaitGroup.Add(len(s.p2pModules) - 1) err := s.sender.Broadcast(msg) require.NoError(s, err) + //} + + s.receivedWaitGroup.Done() } func (s *backgroundGossipSuite) MinusOneNumberOfNodesShouldReceiveTheTestMessage(receivedCountPlus1 int64) { @@ -363,10 +378,9 @@ func (s *backgroundGossipSuite) MinusOneNumberOfNodesShouldReceiveTheTestMessage defer s.mu.Unlock() receivedCount := int(receivedCountPlus1 - 1) - require.Lenf( - s, s.receivedServiceURLMap, receivedCount, - "expected to see %d peers, got: %v", - receivedCount, len(s.receivedServiceURLMap), + require.Equalf( + s, receivedCount, s.receivedCount, + "received messages count doesn't match", ) done <- struct{}{} }() @@ -428,7 +442,7 @@ func (s *backgroundGossipSuite) trackBootstrapProgress(peerCount int) { if _, ok := s.bootstrapPeerIDsMap[localID][remoteID]; ok { // already connected to this peer during bootstrapping - s.Logf("duplicate bootstrap peer ID: %s", remoteID) + //s.Logf("duplicate bootstrap peer ID: %s", remoteID) continue } s.bootstrapPeerIDsMap[localID][remoteID] = struct{}{} From 9d3a18da0f5f1464a66e4ddb2adfb8c70373db2e Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:05:06 +0200 Subject: [PATCH 36/82] wip: raintree router --- p2p/raintree/router.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/p2p/raintree/router.go b/p2p/raintree/router.go index 707f0a0a9..4eff54b56 100644 --- a/p2p/raintree/router.go +++ b/p2p/raintree/router.go @@ -169,7 +169,6 @@ func (rtr *rainTreeRouter) sendInternal(data []byte, address cryptoPocket.Addres utils.LogOutgoingMsg(rtr.logger, hostname, peer) if err := utils.Libp2pSendToPeer(rtr.host, data, peer); err != nil { - rtr.logger.Debug().Err(err).Msg("from libp2pSendInternal") return err } @@ -360,6 +359,20 @@ func (rtr *rainTreeRouter) setupDependencies() error { return err } + // TODO: remove me + //debugFieldsMap := map[string]any{"selfPeerID": rtr.host.ID()} + //for _, peer := range pstore.GetPeerList() { + // peerInfo, err := utils.Libp2pAddrInfoFromPeer(peer) + // if err != nil { + // panic(err) + // } + // + // debugFieldsMap[peerInfo.ID.String()] = peer.GetAddress().String() + //} + // + //rtr.logger.Debug().Fields(debugFieldsMap).Msg("peer IDs to pocket addresses") + // END TODO + if err := utils.PopulateLibp2pHost(rtr.host, pstore); err != nil { return err } From 8e98698f5315fe72f07ec36dc115b28496ce5561 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:09:27 +0200 Subject: [PATCH 37/82] wip: bg router integration - p2p module --- p2p/module.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/p2p/module.go b/p2p/module.go index 44095b53b..8e5d44c6b 100644 --- a/p2p/module.go +++ b/p2p/module.go @@ -180,6 +180,10 @@ func (m *p2pModule) Stop() error { } func (m *p2pModule) Broadcast(msg *anypb.Any) error { + if m.router == nil { + return fmt.Errorf("router not started") + } + c := &messaging.PocketEnvelope{ Content: msg, Nonce: cryptoPocket.GetNonce(), From 4b95a557828d43d53fa384e134cc4eeaae472429 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:09:36 +0200 Subject: [PATCH 38/82] wip: p2p module tests --- p2p/module_raintree_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index c67c6fb34..289c16552 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -299,9 +299,12 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te wg.Add(expectedWrites) // TODO_THIS_COMMIT: - //if serviceURL == origNode { - // ... - //} + if serviceURL == origNode { + t.Log("SELF SEND!!!! 1") + t.Log("SELF SEND!!!! 2") + t.Log("SELF SEND!!!! 3") + //... + } // TODO_THIS_COMMIT: MOVE //telemetryEventHandler := func(namespace, eventName string, labels ...any) { From 4afe27bacf42b4ec81c8dcdeed46e7470eb90e5b Mon Sep 17 00:00:00 2001 From: Bryan White Date: Mon, 22 May 2023 10:59:02 +0200 Subject: [PATCH 39/82] chore: update go.mod --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index 272661180..e6bfcade0 100644 --- a/go.sum +++ b/go.sum @@ -694,8 +694,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pokt-network/go-mockdns v0.0.1 h1:1Kb/kIFH6bNtY9F1bFhJyMRMCc7WyiqfGg0hotBZ1JI= -github.com/pokt-network/go-mockdns v0.0.1/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= github.com/pokt-network/gocuke v0.0.1 h1:qJ/Ryf+hi5L6T9lsOZDNbiAclHkLlDio5/eVKQEYhgE= github.com/pokt-network/gocuke v0.0.1/go.mod h1:BowLKW4++696gTTU33teodtIhjjyaphEbhQT9D5Refw= github.com/pokt-network/smt v0.4.0 h1:fodLphes/EtQw+nr6Zc5giSJdnjIkM2BY6cGTOfTaqo= From be68644d4854913d82a0a0021108fe3d903a389e Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:10:34 +0200 Subject: [PATCH 40/82] wip: background router testutil --- p2p/background/testutil.go | 1 + 1 file changed, 1 insertion(+) create mode 100644 p2p/background/testutil.go diff --git a/p2p/background/testutil.go b/p2p/background/testutil.go new file mode 100644 index 000000000..313dbd6c5 --- /dev/null +++ b/p2p/background/testutil.go @@ -0,0 +1 @@ +package background From 442303c21e6c9dc1e464b8c91d0399b02b2757ac Mon Sep 17 00:00:00 2001 From: Bryan White Date: Mon, 22 May 2023 11:35:51 +0200 Subject: [PATCH 41/82] wip: background router testutils --- p2p/background/testutil.go | 5 +++++ p2p/module_raintree_test.go | 34 +++++++++++++++++++--------------- p2p/testutil.go | 20 ++++++++++++++++++++ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/p2p/background/testutil.go b/p2p/background/testutil.go index 313dbd6c5..c7fd8a166 100644 --- a/p2p/background/testutil.go +++ b/p2p/background/testutil.go @@ -1 +1,6 @@ +//go:build test + package background + +// BackgroundRouter exports `backgroundRouter` for testing purposes. +type BackgroundRouter = backgroundRouter diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index 289c16552..a0dfb1256 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -3,8 +3,11 @@ package p2p_test import ( + libp2pNetwork "github.com/libp2p/go-libp2p/core/network" runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" + "github.com/pokt-network/pocket/p2p" + "github.com/pokt-network/pocket/p2p/protocol" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/pocket/shared/modules" mock_modules "github.com/pokt-network/pocket/shared/modules/mocks" @@ -13,6 +16,7 @@ import ( "path/filepath" "regexp" "strconv" + "strings" "sync" "testing" @@ -245,13 +249,13 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te //busMocks := createMockBuses(t, runtimeConfigs, &wg) busEventHandlerFactory := func(t gocuke.TestingT, busMock *mock_modules.MockBus) testutil.BusEventHandler { return func(data *messaging.PocketEnvelope) { - p2pCfg := busMock.GetRuntimeMgr().GetConfig().P2P - - // `p2pModule#handleNetworkData()` calls `modules.Bus#PublishEventToBus()` - // assumes that P2P module is the only bus event producer running during - // the test. - t.Logf("[valId: %s:%d] Read", p2pCfg.Hostname, p2pCfg.Port) - wg.Done() + //p2pCfg := busMock.GetRuntimeMgr().GetConfig().P2P + // + //// `p2pModule#handleNetworkData()` calls `modules.Bus#PublishEventToBus()` + //// assumes that P2P module is the only bus event producer running during + //// the test. + //t.Logf("[valId: %s:%d] Read", p2pCfg.Hostname, p2pCfg.Port) + //wg.Done() } } @@ -329,18 +333,18 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te } // Start all p2p modules - for _, p2pMod := range p2pModules { + for serviceURL, p2pMod := range p2pModules { err := p2pMod.Start() require.NoError(t, err) // TODO_THIS_COMMIT: decide where to `wg.Done()` - //sURL := strings.Clone(serviceURL) - //mod := *p2pMod - //p2pMod.host.SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { - // log.Printf("[valID: %s] Read\n", sURL) - // (&mod).router.(*raintree.RainTreeRouter).HandleStream(stream) - // wg.Done() - //}) + sURL := strings.Clone(serviceURL) + mod := *(p2pMod.(*p2p.P2PModule)) + mod.GetHost().SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { + log.Printf("[valID: %s] Read\n", sURL) + (&mod).GetRainTreeRouter().HandleStream(stream) + wg.Done() + }) } // Wait for completion diff --git a/p2p/testutil.go b/p2p/testutil.go index a030d8682..fd8f49442 100644 --- a/p2p/testutil.go +++ b/p2p/testutil.go @@ -2,5 +2,25 @@ package p2p +import ( + libp2pHost "github.com/libp2p/go-libp2p/core/host" + + "github.com/pokt-network/pocket/p2p/raintree" +) + // P2PModule exports the `p2pModule` type for use in tests type P2PModule = p2pModule + +func (m *p2pModule) GetHost() libp2pHost.Host { + return m.host +} + +// GetRainTreeRouter returns the `RainTreeRouter` for use in integration tests +func (m *p2pModule) GetRainTreeRouter() *raintree.RainTreeRouter { + return m.router.(*raintree.RainTreeRouter) +} + +// GetBackgroundRouter returns the `BackgroundRouter` for use in integration tests +//func (m *p2pModule) GetBackgroundRouter() background.BackgroundRouter { +// return m.backgroundRouter +//} From 2cda45fd62742e6f0865a684af0317fed8def5a7 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:17:14 +0200 Subject: [PATCH 42/82] wip: p2p module tests --- p2p/module_raintree_test.go | 53 +++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index a0dfb1256..3fc433dcc 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -311,18 +311,61 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te } // TODO_THIS_COMMIT: MOVE + // -- option 2a //telemetryEventHandler := func(namespace, eventName string, labels ...any) { // t.Log("telemetry event received") // wg.Done() //} - eventMetricsAgentMock := telemetry_testutil.PrepareEventMetricsAgentMock(t, serviceURL, &wg, expectedWrites) - busMock.GetTelemetryModule().(*mock_modules.MockTelemetryModule).EXPECT(). - GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() + // TODO_THIS_COMMIT: refactor + // -- option 1 + //eventMetricsAgentMock := telemetry_testutil.PrepareEventMetricsAgentMock(t, serviceURL, &wg, expectedWrites) + //busMock.GetTelemetryModule().(*mock_modules.MockTelemetryModule).EXPECT(). + // GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() + + // option 1.5 + eventMetricsAgentMock := busMock. + GetTelemetryModule(). + GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent) + + //telemetry_testutil.WithEventMetricsHandler( + telemetry_testutil.WhyEventMetricsAgentMock( + t, eventMetricsAgentMock, + serviceURL, &wg, + //telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL, + //telemetryEventHandler, + expectedWrites, + ) + // -- _ = persistence_testutil.BasePersistenceMock(t, busMock, genesisState) //telemetryMock := prepareTelemetryMock(t, busMock, serviceURL, &wg, expectedWrites) - //_ = telemetry_testutil.BaseTelemetryMock(t, busMock) + + //telemetryMock := telemetry_testutil.WithTimeSeriesAgent( + // t, telemetry_testutil.MinimalTelemetryMock(t, busMock)) + // + //eventMetricsAgentMock := telemetry_testutil.EventMetricsAgentMockWithHandler( + // t, telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL, + // telemetryEventHandler, + // expectedWrites, + //) + //telemetryMock.EXPECT().GetEventMetricsAgent().Return(eventMetricsAgentMock).AnyTimes() + + // -- option 2b + //eventMetricsAgentMock := busMock. + // GetTelemetryModule(). + // GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent) + // + ////telemetry_testutil.WithEventMetricsHandler( + //telemetry_testutil.WhyEventMetricsAgentMock( + // t, eventMetricsAgentMock, + // //telemetry.P2P_RAINTREE_MESSAGE_EVENT_METRIC_SEND_LABEL, + // telemetryEventHandler, + // expectedWrites, + //) + // -- + + //telemetryMock := telemetry_testutil.BaseTelemetryMock(t, busMock) //telemetryMock.GetEventMetricsAgent().(*mock_modules.MockEventMetricsAgent).EXPECT().EmitEvent( // gomock.Any(), // gomock.Any(), @@ -337,7 +380,7 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te err := p2pMod.Start() require.NoError(t, err) - // TODO_THIS_COMMIT: decide where to `wg.Done()` + // TODO_THIS_COMMIT: consider using BusEventHandler instead... sURL := strings.Clone(serviceURL) mod := *(p2pMod.(*p2p.P2PModule)) mod.GetHost().SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { From 663859faa19c9fd4d34177a2f92b54b0f2efe8e8 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:21:37 +0200 Subject: [PATCH 43/82] wip: move RainTreeMessage & add BackgroundMessage proto types --- Makefile | 2 +- p2p/event_handler.go | 8 ++++---- p2p/types/proto/background.proto | 8 ++++++++ p2p/{raintree => }/types/proto/raintree.proto | 0 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 p2p/types/proto/background.proto rename p2p/{raintree => }/types/proto/raintree.proto (100%) diff --git a/Makefile b/Makefile index 4b2472608..b39d54f26 100644 --- a/Makefile +++ b/Makefile @@ -297,7 +297,7 @@ protogen_local: go_protoc-go-inject-tag ## Generate go structures for all of the $(PROTOC_SHARED) -I=./consensus/types/proto --go_out=./consensus/types ./consensus/types/proto/*.proto # P2P - $(PROTOC_SHARED) -I=./p2p/raintree/types/proto --go_out=./p2p/types ./p2p/raintree/types/proto/*.proto + $(PROTOC_SHARED) -I=./p2p/types/proto --go_out=./p2p/types ./p2p/types/proto/*.proto # echo "View generated proto files by running: make protogen_show" diff --git a/p2p/event_handler.go b/p2p/event_handler.go index 48e1a7d73..8a2b30473 100644 --- a/p2p/event_handler.go +++ b/p2p/event_handler.go @@ -23,7 +23,7 @@ func (m *p2pModule) HandleEvent(event *anypb.Any) error { return fmt.Errorf("failed to cast event to ConsensusNewHeightEvent") } - oldPeerList := m.router.GetPeerstore().GetPeerList() + oldPeerList := m.stakedActorRouter.GetPeerstore().GetPeerList() updatedPeerstore, err := m.pstoreProvider.GetStakedPeerstoreAtHeight(consensusNewHeightEvent.Height) if err != nil { return err @@ -31,12 +31,12 @@ func (m *p2pModule) HandleEvent(event *anypb.Any) error { added, removed := oldPeerList.Delta(updatedPeerstore.GetPeerList()) for _, add := range added { - if err := m.router.AddPeer(add); err != nil { + if err := m.stakedActorRouter.AddPeer(add); err != nil { return err } } for _, rm := range removed { - if err := m.router.RemovePeer(rm); err != nil { + if err := m.stakedActorRouter.RemovePeer(rm); err != nil { return err } } @@ -50,7 +50,7 @@ func (m *p2pModule) HandleEvent(event *anypb.Any) error { m.logger.Debug().Fields(messaging.TransitionEventToMap(stateMachineTransitionEvent)).Msg("Received state machine transition event") if stateMachineTransitionEvent.NewState == string(coreTypes.StateMachineState_P2P_Bootstrapping) { - if m.router.GetPeerstore().Size() == 0 { + if m.stakedActorRouter.GetPeerstore().Size() == 0 { m.logger.Warn().Msg("No peers in addrbook, bootstrapping") if err := m.bootstrap(); err != nil { diff --git a/p2p/types/proto/background.proto b/p2p/types/proto/background.proto new file mode 100644 index 000000000..d5046ff71 --- /dev/null +++ b/p2p/types/proto/background.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +package background; + +option go_package = "github.com/pokt-network/pocket/p2p/types"; + +message BackgroundMessage { + bytes data = 1; +} diff --git a/p2p/raintree/types/proto/raintree.proto b/p2p/types/proto/raintree.proto similarity index 100% rename from p2p/raintree/types/proto/raintree.proto rename to p2p/types/proto/raintree.proto From 5c50916e3394a6b2bfe5b0eff0669774b4482a15 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:22:52 +0200 Subject: [PATCH 44/82] wip: bg router handle messages --- p2p/background/router.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/p2p/background/router.go b/p2p/background/router.go index 199190b21..f924a83de 100644 --- a/p2p/background/router.go +++ b/p2p/background/router.go @@ -9,6 +9,9 @@ import ( dht "github.com/libp2p/go-libp2p-kad-dht" pubsub "github.com/libp2p/go-libp2p-pubsub" libp2pHost "github.com/libp2p/go-libp2p/core/host" + "github.com/pokt-network/pocket/shared/messaging" + "google.golang.org/protobuf/proto" + "github.com/pokt-network/pocket/logger" "github.com/pokt-network/pocket/p2p/config" "github.com/pokt-network/pocket/p2p/protocol" @@ -29,6 +32,8 @@ type backgroundRouter struct { base_modules.IntegratableModule logger *modules.Logger + // handler is the function to call when a message is received. + handler typesP2P.RouterHandler // host represents a libp2p network node, it encapsulates a libp2p peerstore // & connection manager. `libp2p.New` configures and starts listening // according to options. @@ -110,6 +115,8 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 pstore: pstore, } + go rtr.readSubscription(ctx) + return rtr, nil } @@ -166,6 +173,35 @@ func (rtr *backgroundRouter) RemovePeer(peer typesP2P.Peer) error { return rtr.pstore.RemovePeer(peer.GetAddress()) } +func (rtr *backgroundRouter) readSubscription(ctx context.Context) { + for msg, err := rtr.subscription.Next(ctx); ctx.Err() == nil; { + if err != nil { + rtr.logger.Error().Err(err). + Msg("error reading from background topic subscription") + continue + } + + // TECHDEBT/DISCUSS: telemetry + if err := rtr.handleBackgroundMsg(msg.Data); err != nil { + rtr.logger.Error().Err(err).Msg("error handling background message") + continue + } + } +} + +func (rtr *backgroundRouter) handleBackgroundMsg(data []byte) error { + var backgroundMsg typesP2P.BackgroundMessage + if err := proto.Unmarshal(data, &backgroundMsg); err != nil { + return err + } + + networkMessage := messaging.PocketEnvelope{} + if err := proto.Unmarshal(data, &networkMessage); err != nil { + return err + } + return nil +} + // isClientDebugMode returns the value of `ClientDebugMode` in the base config func isClientDebugMode(bus modules.Bus) bool { return bus.GetRuntimeMgr().GetConfig().ClientDebugMode From ae0b7aa8b20bea3e1d2c016652ed98efe89a4f5b Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:23:52 +0200 Subject: [PATCH 45/82] wip: integrate bg router - module --- p2p/bootstrap.go | 4 ++-- p2p/module.go | 51 ++++++++++++++++++++++++++++++++++++------------ p2p/testutil.go | 2 +- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/p2p/bootstrap.go b/p2p/bootstrap.go index f9f76a049..00a89de2f 100644 --- a/p2p/bootstrap.go +++ b/p2p/bootstrap.go @@ -76,14 +76,14 @@ func (m *p2pModule) bootstrap() error { for _, peer := range pstore.GetPeerList() { m.logger.Debug().Str("address", peer.GetAddress().String()).Msg("Adding peer to router") - if err := m.router.AddPeer(peer); err != nil { + if err := m.stakedActorRouter.AddPeer(peer); err != nil { m.logger.Error().Err(err). Str("pokt_address", peer.GetAddress().String()). Msg("adding peer") } } - if m.router.GetPeerstore().Size() == 0 { + if m.stakedActorRouter.GetPeerstore().Size() == 0 { return fmt.Errorf("bootstrap failed") } return nil diff --git a/p2p/module.go b/p2p/module.go index 8e5d44c6b..4d24f02fa 100644 --- a/p2p/module.go +++ b/p2p/module.go @@ -3,10 +3,16 @@ package p2p import ( "errors" "fmt" + + "github.com/hashicorp/go-multierror" "github.com/libp2p/go-libp2p" libp2pHost "github.com/libp2p/go-libp2p/core/host" "github.com/multiformats/go-multiaddr" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + "github.com/pokt-network/pocket/logger" + "github.com/pokt-network/pocket/p2p/background" "github.com/pokt-network/pocket/p2p/config" "github.com/pokt-network/pocket/p2p/providers" "github.com/pokt-network/pocket/p2p/providers/current_height_provider" @@ -24,8 +30,6 @@ import ( "github.com/pokt-network/pocket/shared/modules" "github.com/pokt-network/pocket/shared/modules/base_modules" "github.com/pokt-network/pocket/telemetry" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" ) var _ modules.P2PModule = &p2pModule{} @@ -47,8 +51,9 @@ type p2pModule struct { nonceDeduper *mempool.GenericFIFOSet[uint64, uint64] // Assigned during `#Start()`. TLDR; `host` listens on instantiation. - // and `router` depends on `host`. - router typesP2P.Router + // `stakedActorRouter` and `unstakedActorRouter` depends on `host`. + stakedActorRouter typesP2P.Router + unstakedActorRouter typesP2P.Router // host represents a libp2p network node, it encapsulates a libp2p peerstore // & connection manager. `libp2p.New` configures and starts listening // according to options. Assigned via `#Start()` (starts on instantiation). @@ -135,7 +140,7 @@ func (m *p2pModule) GetModuleName() string { } // Start instantiates and assigns `m.host`, unless one already exists, and -// `m.router` (which depends on `m.host` as a required config field). +// `m.stakedActorRouter` (which depends on `m.host` as a required config field). func (m *p2pModule) Start() (err error) { m.GetBus(). GetTelemetryModule(). @@ -160,10 +165,16 @@ func (m *p2pModule) Start() (err error) { } } - if err := m.setupRouter(); err != nil { - return fmt.Errorf("setting up router: %w", err) + if err := m.setupRouters(); err != nil { + return fmt.Errorf("setting up routers: %w", err) } + // TODO_THIS_COMMIT: ensure this is addressed + //// Don't handle incoming streams in client debug mode. + //if !m.isClientDebugMode() { + // m.host.SetStreamHandler(protocol.PoktProtocolID, m.handleStream) + //} + m.GetBus(). GetTelemetryModule(). GetTimeSeriesAgent(). @@ -180,7 +191,7 @@ func (m *p2pModule) Stop() error { } func (m *p2pModule) Broadcast(msg *anypb.Any) error { - if m.router == nil { + if m.stakedActorRouter == nil { return fmt.Errorf("router not started") } @@ -193,7 +204,9 @@ func (m *p2pModule) Broadcast(msg *anypb.Any) error { return err } - return m.router.Broadcast(data) + stakedBroadcastErr := m.stakedActorRouter.Broadcast(data) + unstakedBroadcastErr := m.unstakedActorRouter.Broadcast(data) + return multierror.Append(err, stakedBroadcastErr, unstakedBroadcastErr).ErrorOrNil() } func (m *p2pModule) Send(addr cryptoPocket.Address, msg *anypb.Any) error { @@ -207,7 +220,8 @@ func (m *p2pModule) Send(addr cryptoPocket.Address, msg *anypb.Any) error { return err } - return m.router.Send(data, addr) + // TODO_THIS_COMMIT: send using "appropriate" router... + return m.stakedActorRouter.Send(data, addr) } // TECHDEBT(#348): Define what the node identity is throughout the codebase @@ -287,9 +301,9 @@ func (m *p2pModule) setupNonceDeduper() error { return nil } -// setupRouter instantiates the configured router implementation. -func (m *p2pModule) setupRouter() (err error) { - m.router, err = raintree.NewRainTreeRouter( +// setupRouters instantiates the configured router implementations. +func (m *p2pModule) setupRouters() (err error) { + m.stakedActorRouter, err = raintree.NewRainTreeRouter( m.GetBus(), &config.RainTreeConfig{ Addr: m.address, @@ -299,6 +313,17 @@ func (m *p2pModule) setupRouter() (err error) { Handler: m.handlePocketEnvelope, }, ) + + m.unstakedActorRouter, err = background.NewBackgroundRouter( + m.GetBus(), + &config.BackgroundConfig{ + Addr: m.address, + CurrentHeightProvider: m.currentHeightProvider, + PeerstoreProvider: m.pstoreProvider, + Host: m.host, + Handler: m.handleAppData, + }, + ) return err } diff --git a/p2p/testutil.go b/p2p/testutil.go index fd8f49442..5235fed9c 100644 --- a/p2p/testutil.go +++ b/p2p/testutil.go @@ -17,7 +17,7 @@ func (m *p2pModule) GetHost() libp2pHost.Host { // GetRainTreeRouter returns the `RainTreeRouter` for use in integration tests func (m *p2pModule) GetRainTreeRouter() *raintree.RainTreeRouter { - return m.router.(*raintree.RainTreeRouter) + return m.stakedActorRouter.(*raintree.RainTreeRouter) } // GetBackgroundRouter returns the `BackgroundRouter` for use in integration tests From ce65e6fd755c82b01955d7a47d5b36eff6414d9b Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:24:37 +0200 Subject: [PATCH 46/82] wip: add comment to shared/k8s/debug.go --- shared/k8s/debug.go | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/k8s/debug.go b/shared/k8s/debug.go index 531c2ab7a..a58785c24 100644 --- a/shared/k8s/debug.go +++ b/shared/k8s/debug.go @@ -31,6 +31,7 @@ func init() { // FetchValidatorPrivateKeys returns a map corresponding to the data section of // the validator private keys k8s secret (yaml), located at `privateKeysSecretResourceName`. +// NB: depends on running k8s cluster. func FetchValidatorPrivateKeys(clientset *kubernetes.Clientset) (map[string]string, error) { validatorKeysMap := make(map[string]string) From 63a03fbc211b1fa7aba12745c6d1938bda916579 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 23 May 2023 22:14:20 +0200 Subject: [PATCH 47/82] fixup: refactor move generics pkg --- internal/testutil/generics/composition.go | 2 +- internal/testutil/generics/map.go | 2 +- internal/testutil/telemetry/mocks.go | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/testutil/generics/composition.go b/internal/testutil/generics/composition.go index 5e4d66b0f..efff575d8 100644 --- a/internal/testutil/generics/composition.go +++ b/internal/testutil/generics/composition.go @@ -1,4 +1,4 @@ -package testutil +package generics_testutil // PipeTwoToOne threads two values of any type (T and U) through a pipeline of // functions and returns the result of any type (U). Each function in the pipeline diff --git a/internal/testutil/generics/map.go b/internal/testutil/generics/map.go index 55ef6617f..32639577c 100644 --- a/internal/testutil/generics/map.go +++ b/internal/testutil/generics/map.go @@ -1,4 +1,4 @@ -package testutil +package generics_testutil func GetKeys[K comparable, V any](keyMap map[K]V) []K { var ( diff --git a/internal/testutil/telemetry/mocks.go b/internal/testutil/telemetry/mocks.go index 633d03129..0f88302bf 100644 --- a/internal/testutil/telemetry/mocks.go +++ b/internal/testutil/telemetry/mocks.go @@ -2,7 +2,7 @@ package telemetry_testutil import ( "github.com/golang/mock/gomock" - "github.com/pokt-network/pocket/internal/testutil" + "github.com/pokt-network/pocket/internal/testutil/generics" "github.com/regen-network/gocuke" "github.com/pokt-network/pocket/shared/modules" @@ -42,9 +42,9 @@ func BaseTelemetryMock( ) *mock_modules.MockTelemetryModule { t.Helper() - return testutil.PipeTwoToOne[ + return generics_testutil.PipeTwoToOne[ gocuke.TestingT, - *mock_modules.MockTelemetryModule + *mock_modules.MockTelemetryModule, ]( t, MinimalTelemetryMock(t, busMock), BehavesLikeBaseTelemetryMock, From b40a4de525e8b07e3add36a21a5550976d445022 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 23 May 2023 22:18:31 +0200 Subject: [PATCH 48/82] chore: remove unused `backgroundRouter#HandleNetworkData()` method --- p2p/background/router.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/p2p/background/router.go b/p2p/background/router.go index f924a83de..7e25f7b11 100644 --- a/p2p/background/router.go +++ b/p2p/background/router.go @@ -139,11 +139,6 @@ func (rtr *backgroundRouter) Send(data []byte, address cryptoPocket.Address) err return nil } -// HandleNetworkData implements the respective `typesP2P.Router` interface method. -func (rtr *backgroundRouter) HandleNetworkData(data []byte) ([]byte, error) { - return data, nil // intentional passthrough -} - // GetPeerstore implements the respective `typesP2P.Router` interface method. func (rtr *backgroundRouter) GetPeerstore() typesP2P.Peerstore { return rtr.pstore From a6c5949b3f78685eddea9c61bd283ef49decaa93 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:29:25 +0200 Subject: [PATCH 49/82] wip: update multierr in go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index e64de0142..7116b1cdb 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/dgraph-io/badger/v3 v3.2103.2 github.com/foxcpp/go-mockdns v1.0.0 github.com/getkin/kin-openapi v0.107.0 + github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/vault/api v1.9.0 github.com/jackc/pgconn v1.13.0 github.com/jordanorelli/lexnum v0.0.0-20141216151731-460eeb125754 @@ -112,7 +113,6 @@ require ( github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.6.6 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect From 0ebe5ff83ac0d589c0b5a7de84af83945393473a Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:30:24 +0200 Subject: [PATCH 50/82] wip: background router --- p2p/background/router.go | 56 +++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/p2p/background/router.go b/p2p/background/router.go index 7e25f7b11..c69e9d62a 100644 --- a/p2p/background/router.go +++ b/p2p/background/router.go @@ -5,11 +5,11 @@ package background import ( "context" "fmt" + "sync" dht "github.com/libp2p/go-libp2p-kad-dht" pubsub "github.com/libp2p/go-libp2p-pubsub" libp2pHost "github.com/libp2p/go-libp2p/core/host" - "github.com/pokt-network/pocket/shared/messaging" "google.golang.org/protobuf/proto" "github.com/pokt-network/pocket/logger" @@ -65,6 +65,10 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 networkLogger := logger.Global.CreateLoggerForModule("backgroundRouter") networkLogger.Info().Msg("Initializing background router") + if err := cfg.IsValid(); err != nil { + return nil, err + } + // seed initial peerstore with current on-chain peer info (i.e. staked actors) pstore, err := cfg.PeerstoreProvider.GetStakedPeerstoreAtHeight( cfg.CurrentHeightProvider.CurrentHeight(), @@ -74,7 +78,9 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 } // CONSIDERATION: If switching to `NewRandomSub`, there will be a max size - gossipSub, err := pubsub.NewGossipSub(ctx, cfg.Host) + gossipSub, err := pubsub.NewGossipSub(ctx, cfg.Host) //pubsub.WithFloodPublish(false), + //pubsub.WithMaxMessageSize(256), + if err != nil { return nil, fmt.Errorf("creating gossip pubsub: %w", err) } @@ -100,7 +106,10 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 // > output buffer. The default length is 32 but it can be configured to avoid // > dropping messages if the consumer is not reading fast enough. // (see: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub#WithBufferSize) - subscription, err := topic.Subscribe() + subscription, err := topic.Subscribe( + //pubsub.WithBufferSize(10), + //pubsub.With + ) if err != nil { return nil, fmt.Errorf("subscribing to background topic: %w", err) } @@ -113,6 +122,7 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 subscription: subscription, logger: networkLogger, pstore: pstore, + handler: cfg.Handler, } go rtr.readSubscription(ctx) @@ -122,8 +132,18 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 // Broadcast implements the respective `typesP2P.Router` interface method. func (rtr *backgroundRouter) Broadcast(data []byte) error { + // CONSIDERATION: validate as PocketEnvelopeBz here (?) + // TODO_THIS_COMMIT: wrap in BackgroundMessage + backgroundMsg := &typesP2P.BackgroundMessage{ + Data: data, + } + backgroundMsgBz, err := proto.Marshal(backgroundMsg) + if err != nil { + return err + } + // TECHDEBT(#595): add ctx to interface methods and propagate down. - return rtr.topic.Publish(context.TODO(), data) + return rtr.topic.Publish(context.TODO(), backgroundMsgBz) } // Send implements the respective `typesP2P.Router` interface method. @@ -168,8 +188,24 @@ func (rtr *backgroundRouter) RemovePeer(peer typesP2P.Peer) error { return rtr.pstore.RemovePeer(peer.GetAddress()) } +var readCountMu sync.Mutex + func (rtr *backgroundRouter) readSubscription(ctx context.Context) { - for msg, err := rtr.subscription.Next(ctx); ctx.Err() == nil; { + // TODO_THIS_COMMIT: look into "topic validaton" + // (see: https://github.com/libp2p/specs/tree/master/pubsub#topic-validation) + readCount := 0 + for { + readCountMu.Lock() + readCount++ + fmt.Printf("readCount: %d\n", readCount) + readCountMu.Unlock() + + msg, err := rtr.subscription.Next(ctx) + if ctx.Err() != nil { + fmt.Printf("error: %s\n", ctx.Err()) + return + } + if err != nil { rtr.logger.Error().Err(err). Msg("error reading from background topic subscription") @@ -184,17 +220,13 @@ func (rtr *backgroundRouter) readSubscription(ctx context.Context) { } } -func (rtr *backgroundRouter) handleBackgroundMsg(data []byte) error { +func (rtr *backgroundRouter) handleBackgroundMsg(backgroundMsgBz []byte) error { var backgroundMsg typesP2P.BackgroundMessage - if err := proto.Unmarshal(data, &backgroundMsg); err != nil { + if err := proto.Unmarshal(backgroundMsgBz, &backgroundMsg); err != nil { return err } - networkMessage := messaging.PocketEnvelope{} - if err := proto.Unmarshal(data, &networkMessage); err != nil { - return err - } - return nil + return rtr.handler(backgroundMsg.Data) } // isClientDebugMode returns the value of `ClientDebugMode` in the base config From 0d49f29ffb8be4786e7d4cf89555b824c0e90623 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:33:48 +0200 Subject: [PATCH 51/82] wip bg router testing --- p2p/background/testutil.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/p2p/background/testutil.go b/p2p/background/testutil.go index c7fd8a166..c6de4fc61 100644 --- a/p2p/background/testutil.go +++ b/p2p/background/testutil.go @@ -2,5 +2,25 @@ package background +import ( + "github.com/pokt-network/pocket/internal/testutil/generics" + typesP2P "github.com/pokt-network/pocket/p2p/types" + "github.com/regen-network/gocuke" +) + // BackgroundRouter exports `backgroundRouter` for testing purposes. type BackgroundRouter = backgroundRouter + +// TOOD_THIS_COMMIT: move & dedup +type routerHandlerProxyFactory = generics_testutil.ProxyFactory[typesP2P.RouterHandler] + +func (rtr *backgroundRouter) HandlerProxy( + t gocuke.TestingT, + handlerProxyFactory routerHandlerProxyFactory, +) { + t.Helper() + + // pass original handler to proxy factory & replace it with the proxy + rtr.handler = handlerProxyFactory(rtr.handler) +} + From c125019c34b9ceb930e1cef8592bf512670a3025 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:34:13 +0200 Subject: [PATCH 52/82] wip: handler fixes --- p2p/config/config.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/p2p/config/config.go b/p2p/config/config.go index e64fe8519..2ff62e966 100644 --- a/p2p/config/config.go +++ b/p2p/config/config.go @@ -20,6 +20,7 @@ type baseConfig struct { Addr crypto.Address CurrentHeightProvider providers.CurrentHeightProvider PeerstoreProvider providers.PeerstoreProvider + Handler func(data []byte) error } // BackgroundConfig implements `RouterConfig` for use with `BackgroundRouter`. @@ -57,6 +58,10 @@ func (cfg *baseConfig) IsValid() (err error) { if cfg.PeerstoreProvider == nil { err = multierr.Append(err, fmt.Errorf("peerstore provider not configured")) } + + if cfg.Handler == nil { + err = multierr.Append(err, fmt.Errorf("handler not configured")) + } return err } @@ -67,6 +72,7 @@ func (cfg *BackgroundConfig) IsValid() (err error) { Addr: cfg.Addr, CurrentHeightProvider: cfg.CurrentHeightProvider, PeerstoreProvider: cfg.PeerstoreProvider, + Handler: cfg.Handler, } return multierr.Append(err, baseCfg.IsValid()) } @@ -78,6 +84,7 @@ func (cfg *RainTreeConfig) IsValid() (err error) { Addr: cfg.Addr, CurrentHeightProvider: cfg.CurrentHeightProvider, PeerstoreProvider: cfg.PeerstoreProvider, + Handler: cfg.Handler, } return multierr.Append(err, baseCfg.IsValid()) } From c61e2bd7af47fd44fe99abdf84e69181932b01a3 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:36:01 +0200 Subject: [PATCH 53/82] wip: integrate bg router - module --- p2p/module.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/p2p/module.go b/p2p/module.go index 4d24f02fa..7aefc4120 100644 --- a/p2p/module.go +++ b/p2p/module.go @@ -192,9 +192,14 @@ func (m *p2pModule) Stop() error { func (m *p2pModule) Broadcast(msg *anypb.Any) error { if m.stakedActorRouter == nil { - return fmt.Errorf("router not started") + return fmt.Errorf("staked actor router not started") } + if m.unstakedActorRouter == nil { + return fmt.Errorf("unstaked actor router not started") + } + + // TODO: rename `c` c := &messaging.PocketEnvelope{ Content: msg, Nonce: cryptoPocket.GetNonce(), @@ -207,9 +212,11 @@ func (m *p2pModule) Broadcast(msg *anypb.Any) error { stakedBroadcastErr := m.stakedActorRouter.Broadcast(data) unstakedBroadcastErr := m.unstakedActorRouter.Broadcast(data) return multierror.Append(err, stakedBroadcastErr, unstakedBroadcastErr).ErrorOrNil() + //return multierror.Append(err, unstakedBroadcastErr).ErrorOrNil() } func (m *p2pModule) Send(addr cryptoPocket.Address, msg *anypb.Any) error { + // TODO: rename `c` c := &messaging.PocketEnvelope{ Content: msg, Nonce: cryptoPocket.GetNonce(), @@ -313,6 +320,9 @@ func (m *p2pModule) setupRouters() (err error) { Handler: m.handlePocketEnvelope, }, ) + if err != nil { + return fmt.Errorf("staked actor router: %w", err) + } m.unstakedActorRouter, err = background.NewBackgroundRouter( m.GetBus(), @@ -324,7 +334,10 @@ func (m *p2pModule) setupRouters() (err error) { Handler: m.handleAppData, }, ) - return err + if err != nil { + return fmt.Errorf("unstaked actor router: %w", err) + } + return nil } // setupHost creates a new libp2p host and assignes it to `m.host`. Libp2p host From 39b8510cdffe88e40665680acbdf972ef2d4a98c Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:36:20 +0200 Subject: [PATCH 54/82] wip: integrate bg router - module test --- p2p/module_raintree_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/p2p/module_raintree_test.go b/p2p/module_raintree_test.go index 3fc433dcc..b8a87b696 100644 --- a/p2p/module_raintree_test.go +++ b/p2p/module_raintree_test.go @@ -3,14 +3,14 @@ package p2p_test import ( - libp2pNetwork "github.com/libp2p/go-libp2p/core/network" runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" "github.com/pokt-network/pocket/p2p" - "github.com/pokt-network/pocket/p2p/protocol" + typesP2P "github.com/pokt-network/pocket/p2p/types" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/pocket/shared/modules" mock_modules "github.com/pokt-network/pocket/shared/modules/mocks" + "google.golang.org/protobuf/types/known/anypb" "log" "os" "path/filepath" @@ -20,14 +20,12 @@ import ( "sync" "testing" - "github.com/regen-network/gocuke" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/types/known/anypb" - "github.com/pokt-network/pocket/internal/testutil" "github.com/pokt-network/pocket/internal/testutil/constructors" persistence_testutil "github.com/pokt-network/pocket/internal/testutil/persistence" "github.com/pokt-network/pocket/shared/messaging" + "github.com/regen-network/gocuke" + "github.com/stretchr/testify/require" ) // TODO(#314): Add the tooling and instructions on how to generate unit tests in this file. @@ -383,10 +381,12 @@ func testRainTreeCalls(t *testing.T, origNode string, networkSimulationConfig Te // TODO_THIS_COMMIT: consider using BusEventHandler instead... sURL := strings.Clone(serviceURL) mod := *(p2pMod.(*p2p.P2PModule)) - mod.GetHost().SetStreamHandler(protocol.PoktProtocolID, func(stream libp2pNetwork.Stream) { - log.Printf("[valID: %s] Read\n", sURL) - (&mod).GetRainTreeRouter().HandleStream(stream) - wg.Done() + mod.GetRainTreeRouter().HandlerProxy(t, func(origHandler typesP2P.RouterHandler) typesP2P.RouterHandler { + return func(data []byte) error { + log.Printf("[valID: %s] Read\n", sURL) + wg.Done() + return nil + } }) } From db1fafdeccf513267c48eea61dd74ca52654c037 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:37:14 +0200 Subject: [PATCH 55/82] wip: raintree comment --- p2p/raintree/router.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/p2p/raintree/router.go b/p2p/raintree/router.go index 4eff54b56..828a23fae 100644 --- a/p2p/raintree/router.go +++ b/p2p/raintree/router.go @@ -210,11 +210,13 @@ func (rtr *rainTreeRouter) handleRainTreeMsg(data []byte) ([]byte, error) { return nil, err } + // TECHDEBT(#763): refactor as "pre-propagation validation" networkMessage := messaging.PocketEnvelope{} if err := proto.Unmarshal(rainTreeMsg.Data, &networkMessage); err != nil { rtr.logger.Error().Err(err).Msg("Error decoding network message") return nil, err } + // -- // Continue RainTree propagation if rainTreeMsg.Level > 0 { From 3eaecce918713f1775cbebc43302755d3f92967c Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:37:45 +0200 Subject: [PATCH 56/82] wip: integrate bg router - raintree test --- p2p/raintree/testutil.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/p2p/raintree/testutil.go b/p2p/raintree/testutil.go index ebcb464b5..ef19b19cc 100644 --- a/p2p/raintree/testutil.go +++ b/p2p/raintree/testutil.go @@ -2,12 +2,30 @@ package raintree -import libp2pNetwork "github.com/libp2p/go-libp2p/core/network" +import ( + libp2pNetwork "github.com/libp2p/go-libp2p/core/network" + generics_testutil "github.com/pokt-network/pocket/internal/testutil/generics" + typesP2P "github.com/pokt-network/pocket/p2p/types" + "github.com/regen-network/gocuke" +) // RainTreeRouter exports `rainTreeRouter` for testing purposes. type RainTreeRouter = rainTreeRouter +// TOOD_THIS_COMMIT: move & dedup +type routerHandlerProxyFactory = generics_testutil.ProxyFactory[typesP2P.RouterHandler] + // HandleStream exports `rainTreeRouter#handleStream` for testing purposes. func (rtr *rainTreeRouter) HandleStream(stream libp2pNetwork.Stream) { rtr.handleStream(stream) } +func (rtr *rainTreeRouter) HandlerProxy( + t gocuke.TestingT, + handlerProxyFactory routerHandlerProxyFactory, +) { + t.Helper() + + // pass original handler to proxy factory & replace it with the proxy + origHandler := rtr.handler + rtr.handler = handlerProxyFactory(origHandler) +} From d4b28be01066c701b9e296c8ef49f7a58ef36185 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:38:01 +0200 Subject: [PATCH 57/82] wip: integrate bg router - module test --- p2p/testutil.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/p2p/testutil.go b/p2p/testutil.go index 5235fed9c..7cf01cf83 100644 --- a/p2p/testutil.go +++ b/p2p/testutil.go @@ -4,6 +4,7 @@ package p2p import ( libp2pHost "github.com/libp2p/go-libp2p/core/host" + "github.com/pokt-network/pocket/p2p/background" "github.com/pokt-network/pocket/p2p/raintree" ) @@ -21,6 +22,6 @@ func (m *p2pModule) GetRainTreeRouter() *raintree.RainTreeRouter { } // GetBackgroundRouter returns the `BackgroundRouter` for use in integration tests -//func (m *p2pModule) GetBackgroundRouter() background.BackgroundRouter { -// return m.backgroundRouter -//} +func (m *p2pModule) GetBackgroundRouter() *background.BackgroundRouter { + return m.unstakedActorRouter.(*background.BackgroundRouter) +} From 90bcf667dffe25ef4db1527df3840175010b892e Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:38:51 +0200 Subject: [PATCH 58/82] wip: generate nonce in `messaging.PackMessage()` --- shared/messaging/envelope.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shared/messaging/envelope.go b/shared/messaging/envelope.go index 4150fbfba..f65304007 100644 --- a/shared/messaging/envelope.go +++ b/shared/messaging/envelope.go @@ -4,6 +4,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/anypb" + + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" ) // PackMessage returns a *PocketEnvelope after having packed the message supplied as an argument @@ -12,7 +14,10 @@ func PackMessage(message proto.Message) (*PocketEnvelope, error) { if err != nil { return nil, err } - return &PocketEnvelope{Content: anyMsg}, nil + return &PocketEnvelope{ + Content: anyMsg, + Nonce: cryptoPocket.GetNonce(), + }, nil } // UnpackMessage extracts the message inside the PocketEnvelope decorating it with typing information From 709f78ce5634a6901fc519a935cfb20e32e692a9 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:54:15 +0200 Subject: [PATCH 59/82] fixup: bg router --- p2p/background/router.go | 73 +++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/p2p/background/router.go b/p2p/background/router.go index c69e9d62a..232343bfa 100644 --- a/p2p/background/router.go +++ b/p2p/background/router.go @@ -5,8 +5,6 @@ package background import ( "context" "fmt" - "sync" - dht "github.com/libp2p/go-libp2p-kad-dht" pubsub "github.com/libp2p/go-libp2p-pubsub" libp2pHost "github.com/libp2p/go-libp2p/core/host" @@ -15,6 +13,7 @@ import ( "github.com/pokt-network/pocket/logger" "github.com/pokt-network/pocket/p2p/config" "github.com/pokt-network/pocket/p2p/protocol" + "github.com/pokt-network/pocket/p2p/providers" typesP2P "github.com/pokt-network/pocket/p2p/types" "github.com/pokt-network/pocket/p2p/utils" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" @@ -52,7 +51,7 @@ type backgroundRouter struct { kadDHT *dht.IpfsDHT // TECHDEBT: `pstore` will likely be removed in future refactoring / simplification // of the `Router` interface. - // pstore is the background router's peerstore. + // pstore is the background router's peerstore. Assigned in `backgroundRouter#setupPeerstore()`. pstore typesP2P.Peerstore } @@ -69,14 +68,6 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 return nil, err } - // seed initial peerstore with current on-chain peer info (i.e. staked actors) - pstore, err := cfg.PeerstoreProvider.GetStakedPeerstoreAtHeight( - cfg.CurrentHeightProvider.CurrentHeight(), - ) - if err != nil { - return nil, err - } - // CONSIDERATION: If switching to `NewRandomSub`, there will be a max size gossipSub, err := pubsub.NewGossipSub(ctx, cfg.Host) //pubsub.WithFloodPublish(false), //pubsub.WithMaxMessageSize(256), @@ -96,6 +87,10 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 return nil, fmt.Errorf("creating DHT: %w", err) } + //if err := kadDHT.Bootstrap(ctx); err != nil { + // return nil, fmt.Errorf("bootstrapping DHT: %w", err) + //} + topic, err := gossipSub.Join(protocol.BackgroundTopicStr) if err != nil { return nil, fmt.Errorf("joining background topic: %w", err) @@ -121,10 +116,16 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 topic: topic, subscription: subscription, logger: networkLogger, - pstore: pstore, handler: cfg.Handler, } + if err := rtr.setupPeerstore( + cfg.PeerstoreProvider, + cfg.CurrentHeightProvider, + ); err != nil { + return nil, err + } + go rtr.readSubscription(ctx) return rtr, nil @@ -188,18 +189,52 @@ func (rtr *backgroundRouter) RemovePeer(peer typesP2P.Peer) error { return rtr.pstore.RemovePeer(peer.GetAddress()) } -var readCountMu sync.Mutex +func (rtr *backgroundRouter) setupPeerstore( + pstoreProvider providers.PeerstoreProvider, + currentHeightProvider providers.CurrentHeightProvider, +) (err error) { + // seed initial peerstore with current on-chain peer info (i.e. staked actors) + rtr.pstore, err = pstoreProvider.GetStakedPeerstoreAtHeight( + currentHeightProvider.CurrentHeight(), + ) + if err != nil { + return err + } + + // CONSIDERATION: add `GetPeers` method to `PeerstoreProvider` interface + // to avoid this loop. + for _, peer := range rtr.pstore.GetPeerList() { + if err := utils.AddPeerToLibp2pHost(rtr.host, peer); err != nil { + return err + } + + // TODO: refactor: #bootstrap() + libp2pPeer, err := utils.Libp2pAddrInfoFromPeer(peer) + if err != nil { + return fmt.Errorf( + "converting peer info, pokt address: %s: %w", + peer.GetAddress(), + err, + ) + } + + // don't attempt to connect to self + if rtr.host.ID() == libp2pPeer.ID { + return nil + } + + // TECHDEBT(#595): add ctx to interface methods and propagate down. + if err := rtr.host.Connect(context.TODO(), libp2pPeer); err != nil { + return fmt.Errorf("connecting to peer: %w", err) + } + } + return nil +} func (rtr *backgroundRouter) readSubscription(ctx context.Context) { // TODO_THIS_COMMIT: look into "topic validaton" // (see: https://github.com/libp2p/specs/tree/master/pubsub#topic-validation) - readCount := 0 for { - readCountMu.Lock() - readCount++ - fmt.Printf("readCount: %d\n", readCount) - readCountMu.Unlock() - msg, err := rtr.subscription.Next(ctx) if ctx.Err() != nil { fmt.Printf("error: %s\n", ctx.Err()) From f44aaa9505cce9ae7bb2cee3ac62912e2acf361b Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:54:31 +0200 Subject: [PATCH 60/82] fixup: bg router test --- p2p/background/kad_discovery_baseline_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/background/kad_discovery_baseline_test.go b/p2p/background/kad_discovery_baseline_test.go index fd352456c..7ee351d8a 100644 --- a/p2p/background/kad_discovery_baseline_test.go +++ b/p2p/background/kad_discovery_baseline_test.go @@ -54,7 +54,7 @@ func TestLibp2pKademliaPeerDiscovery(t *testing.T) { expectedPeerIDs = append(expectedPeerIDs, host4.ID()) // TECHDEBT: consider using `host.ConnManager().Notifee()` to avoid sleeping here - time.Sleep(time.Millisecond * 500) + time.Sleep(dhtUpdateSleepDuration) // new host discovers existing hosts... host4DiscoveredHost2Addrs := host4.Peerstore().Addrs(host2.ID()) From 667a082e9857c46da5a98f0bf424cd7790db5f13 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:54:59 +0200 Subject: [PATCH 61/82] fixup: integrate bg router - module --- p2p/module.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/p2p/module.go b/p2p/module.go index 7aefc4120..2758596b7 100644 --- a/p2p/module.go +++ b/p2p/module.go @@ -209,10 +209,10 @@ func (m *p2pModule) Broadcast(msg *anypb.Any) error { return err } - stakedBroadcastErr := m.stakedActorRouter.Broadcast(data) + //stakedBroadcastErr := m.stakedActorRouter.Broadcast(data) unstakedBroadcastErr := m.unstakedActorRouter.Broadcast(data) - return multierror.Append(err, stakedBroadcastErr, unstakedBroadcastErr).ErrorOrNil() - //return multierror.Append(err, unstakedBroadcastErr).ErrorOrNil() + //return multierror.Append(err, stakedBroadcastErr, unstakedBroadcastErr).ErrorOrNil() + return multierror.Append(err, unstakedBroadcastErr).ErrorOrNil() } func (m *p2pModule) Send(addr cryptoPocket.Address, msg *anypb.Any) error { From 2a33404be3a18978051ac7213c743bc0f52bc788 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:32:42 +0200 Subject: [PATCH 62/82] wip: integrate bg router - module --- p2p/module.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/module.go b/p2p/module.go index 2758596b7..fe99cba27 100644 --- a/p2p/module.go +++ b/p2p/module.go @@ -331,7 +331,7 @@ func (m *p2pModule) setupRouters() (err error) { CurrentHeightProvider: m.currentHeightProvider, PeerstoreProvider: m.pstoreProvider, Host: m.host, - Handler: m.handleAppData, + Handler: m.handlePocketEnvelope, }, ) if err != nil { From 585fcc6be2c8000ad4e4d9b8f15b4e0ed3a60aab Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:33:13 +0200 Subject: [PATCH 63/82] wip: update go.mod add go-mockdns --- go.sum | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.sum b/go.sum index e6bfcade0..272661180 100644 --- a/go.sum +++ b/go.sum @@ -694,6 +694,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pokt-network/go-mockdns v0.0.1 h1:1Kb/kIFH6bNtY9F1bFhJyMRMCc7WyiqfGg0hotBZ1JI= +github.com/pokt-network/go-mockdns v0.0.1/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= github.com/pokt-network/gocuke v0.0.1 h1:qJ/Ryf+hi5L6T9lsOZDNbiAclHkLlDio5/eVKQEYhgE= github.com/pokt-network/gocuke v0.0.1/go.mod h1:BowLKW4++696gTTU33teodtIhjjyaphEbhQT9D5Refw= github.com/pokt-network/smt v0.4.0 h1:fodLphes/EtQw+nr6Zc5giSJdnjIkM2BY6cGTOfTaqo= From 63bcc1557c8e6fc5f85f32657e6bdbf5573d103e Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:39:12 +0200 Subject: [PATCH 64/82] chore: add `typesP2P.Router#Close()` interface method --- p2p/types/router.go | 1 + 1 file changed, 1 insertion(+) diff --git a/p2p/types/router.go b/p2p/types/router.go index f9c7f2d74..915755c18 100644 --- a/p2p/types/router.go +++ b/p2p/types/router.go @@ -14,6 +14,7 @@ type Router interface { Broadcast(data []byte) error Send(data []byte, address cryptoPocket.Address) error + Close() error // Address book helpers // TECHDEBT: simplify - remove `GetPeerstore` From c90d9fd3bb986cb5afadfd5d2ad873164bb7ddbb Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:40:05 +0200 Subject: [PATCH 65/82] chore: implement `backgroundRouter#Close()` --- p2p/background/router.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/p2p/background/router.go b/p2p/background/router.go index 232343bfa..a42095ec8 100644 --- a/p2p/background/router.go +++ b/p2p/background/router.go @@ -5,6 +5,7 @@ package background import ( "context" "fmt" + "github.com/hashicorp/go-multierror" dht "github.com/libp2p/go-libp2p-kad-dht" pubsub "github.com/libp2p/go-libp2p-pubsub" libp2pHost "github.com/libp2p/go-libp2p/core/host" @@ -189,6 +190,15 @@ func (rtr *backgroundRouter) RemovePeer(peer typesP2P.Peer) error { return rtr.pstore.RemovePeer(peer.GetAddress()) } +func (rtr *backgroundRouter) Close() error { + rtr.subscription.Cancel() + + return multierror.Append( + rtr.topic.Close(), + rtr.kadDHT.Close(), + ) +} + func (rtr *backgroundRouter) setupPeerstore( pstoreProvider providers.PeerstoreProvider, currentHeightProvider providers.CurrentHeightProvider, From 566f849c804ca4ec2e6b577b08309be59d380267 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:41:00 +0200 Subject: [PATCH 66/82] chore: integrate `typesP2P.Router#Close()` into p2p module --- p2p/module.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/p2p/module.go b/p2p/module.go index fe99cba27..11dbccc47 100644 --- a/p2p/module.go +++ b/p2p/module.go @@ -3,6 +3,7 @@ package p2p import ( "errors" "fmt" + "go.uber.org/multierr" "github.com/hashicorp/go-multierror" "github.com/libp2p/go-libp2p" @@ -183,7 +184,15 @@ func (m *p2pModule) Start() (err error) { } func (m *p2pModule) Stop() error { - err := m.host.Close() + routerCloseErrs := multierr.Append( + m.unstakedActorRouter.Close(), + m.stakedActorRouter.Close(), + ) + + err := multierr.Append( + routerCloseErrs, + m.host.Close(), + ) // Don't reuse closed host, `#Start()` will re-create. m.host = nil From f6ec1621996fd2d0cf27c06c4bd2624660706a15 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:41:16 +0200 Subject: [PATCH 67/82] chore: implement `rainTreeRouter#Close()` --- p2p/raintree/router.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/p2p/raintree/router.go b/p2p/raintree/router.go index 828a23fae..45b50c8be 100644 --- a/p2p/raintree/router.go +++ b/p2p/raintree/router.go @@ -271,6 +271,10 @@ func (rtr *rainTreeRouter) Size() int { return rtr.peersManager.GetPeerstore().Size() } +func (rtr *rainTreeRouter) Close() error { + return nil +} + // handleStream ensures the peerstore contains the remote peer and then reads // the incoming stream in a new go routine. func (rtr *rainTreeRouter) handleStream(stream libp2pNetwork.Stream) { From dc6e27fa31c2b380ff8ad12f7068628069accf1c Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:41:27 +0200 Subject: [PATCH 68/82] wip: bg router --- p2p/background/router.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/p2p/background/router.go b/p2p/background/router.go index a42095ec8..dab524da6 100644 --- a/p2p/background/router.go +++ b/p2p/background/router.go @@ -69,14 +69,6 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 return nil, err } - // CONSIDERATION: If switching to `NewRandomSub`, there will be a max size - gossipSub, err := pubsub.NewGossipSub(ctx, cfg.Host) //pubsub.WithFloodPublish(false), - //pubsub.WithMaxMessageSize(256), - - if err != nil { - return nil, fmt.Errorf("creating gossip pubsub: %w", err) - } - dhtMode := dht.ModeAutoServer // NB: don't act as a bootstrap node in peer discovery in client debug mode if isClientDebugMode(bus) { @@ -92,6 +84,14 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 // return nil, fmt.Errorf("bootstrapping DHT: %w", err) //} + // CONSIDERATION: If switching to `NewRandomSub`, there will be a max size + gossipSub, err := pubsub.NewGossipSub(ctx, cfg.Host) //pubsub.WithFloodPublish(false), + //pubsub.WithMaxMessageSize(256), + + if err != nil { + return nil, fmt.Errorf("creating gossip pubsub: %w", err) + } + topic, err := gossipSub.Join(protocol.BackgroundTopicStr) if err != nil { return nil, fmt.Errorf("joining background topic: %w", err) From 316789a75d5ab6a88b16bb86b147aec99819d4a7 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 09:08:45 +0200 Subject: [PATCH 69/82] wip: tracing --- p2p/background/router.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/p2p/background/router.go b/p2p/background/router.go index dab524da6..6db297b1d 100644 --- a/p2p/background/router.go +++ b/p2p/background/router.go @@ -85,7 +85,20 @@ func NewBackgroundRouter(bus modules.Bus, cfg *config.BackgroundConfig) (typesP2 //} // CONSIDERATION: If switching to `NewRandomSub`, there will be a max size - gossipSub, err := pubsub.NewGossipSub(ctx, cfg.Host) //pubsub.WithFloodPublish(false), + // TECHDEBT: integrate with go-libp2p-pubsub tracing + truncID := cfg.Host.ID().String()[:20] + jsonTracer, err := pubsub.NewJSONTracer(fmt.Sprintf("./pubsub-trace_%s.json", truncID)) + if err != nil { + return nil, fmt.Errorf("creating json tracer: %w", err) + } + //pbTracer, err := pubsub.NewPBTracer("./pubsub-trace.pb") + //if err != nil { + // return nil, fmt.Errorf("creating json tracer: %w", err) + //} + + tracerOpt := pubsub.WithEventTracer(jsonTracer) + //tracerOpt := pubsub.WithEventTracer(pbTracer) + gossipSub, err := pubsub.NewGossipSub(ctx, cfg.Host, tracerOpt) //pubsub.WithFloodPublish(false), //pubsub.WithMaxMessageSize(256), if err != nil { From 7093181de780b998efe919dec8997f72e3b10712 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 09:09:29 +0200 Subject: [PATCH 70/82] wip: misc --- p2p/background/router.go | 1 + p2p/raintree/router.go | 1 + 2 files changed, 2 insertions(+) diff --git a/p2p/background/router.go b/p2p/background/router.go index 6db297b1d..3b96d8167 100644 --- a/p2p/background/router.go +++ b/p2p/background/router.go @@ -5,6 +5,7 @@ package background import ( "context" "fmt" + "github.com/hashicorp/go-multierror" dht "github.com/libp2p/go-libp2p-kad-dht" pubsub "github.com/libp2p/go-libp2p-pubsub" diff --git a/p2p/raintree/router.go b/p2p/raintree/router.go index 45b50c8be..65a4da710 100644 --- a/p2p/raintree/router.go +++ b/p2p/raintree/router.go @@ -63,6 +63,7 @@ func NewRainTreeRouter(bus modules.Bus, cfg *config.RainTreeConfig) (typesP2P.Ro } func (*rainTreeRouter) Create(bus modules.Bus, cfg *config.RainTreeConfig) (typesP2P.Router, error) { + // TODO_THIS_COMMIT: rename logger to support multiple routers routerLogger := logger.Global.CreateLoggerForModule("router") routerLogger.Info().Msg("Initializing rainTreeRouter") From 75ba0bf6867704f3ea189737bfce1fc04f3fd2f5 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 09:09:59 +0200 Subject: [PATCH 71/82] wip: bg gossip test --- p2p/integration/background_gossip_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/p2p/integration/background_gossip_test.go b/p2p/integration/background_gossip_test.go index 1dff25d49..6ee5944bc 100644 --- a/p2p/integration/background_gossip_test.go +++ b/p2p/integration/background_gossip_test.go @@ -20,6 +20,7 @@ import ( "github.com/pokt-network/pocket/internal/testutil" "github.com/pokt-network/pocket/internal/testutil/constructors" "github.com/pokt-network/pocket/internal/testutil/generics" + p2p_testutil "github.com/pokt-network/pocket/internal/testutil/p2p" runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" telemetry_testutil "github.com/pokt-network/pocket/internal/testutil/telemetry" "github.com/pokt-network/pocket/p2p" @@ -355,14 +356,16 @@ func (s *backgroundGossipSuite) ANodeBroadcastsATestMessageViaItsBackgroundRoute s.sender = s.p2pModules[generics_testutil.GetKeys(s.p2pModules)[0]].(*p2p.P2PModule) // broadcast a test message - msg := &anypb.Any{} + debugStringMsg := p2p_testutil.NewDebugStringMessage(s, "test message") + msg, err := anypb.New(debugStringMsg) + require.NoError(s, err) s.receivedWaitGroup.Add(1) // TODO_THIS_COMMIT: refactor //for i := 0; i > 10; i++ { s.receivedWaitGroup.Add(len(s.p2pModules) - 1) - err := s.sender.Broadcast(msg) + err = s.sender.Broadcast(msg) require.NoError(s, err) //} From cc06619d876c2657f8647ba3b5abd36045cb61df Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 09:26:42 +0200 Subject: [PATCH 72/82] fixup: add LoadLocalNetKrivatekeys --- internal/testutil/keys.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/testutil/keys.go b/internal/testutil/keys.go index 5f8b6accc..a541f4205 100644 --- a/internal/testutil/keys.go +++ b/internal/testutil/keys.go @@ -7,15 +7,17 @@ import ( "regexp" "runtime" - cryptoPocket "github.com/pokt-network/pocket/shared/crypto" + "github.com/regen-network/gocuke" "github.com/stretchr/testify/require" + + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" ) var ( privKeyManifestKeyRegex = regexp.MustCompile(`\s+"\d+":\s+(\w+)\s+`) ) -func LoadLocalnetPrivateKeys(t require.TestingT, keyCount int) (privKeys []cryptoPocket.PrivateKey) { +func LoadLocalnetPrivateKeys(t gocuke.TestingT, keyCount int) (privKeys []cryptoPocket.PrivateKey) { _, filename, _, _ := runtime.Caller(0) pkgDir := filepath.Dir(filename) relativePathToKeys := filepath.Join(pkgDir, "..", "..", "build", "localnet", "manifests", "private-keys.yaml") From b3da937bf3c9b132135de4c6e7e4f8b3665e2e7f Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 10:13:43 +0200 Subject: [PATCH 73/82] fixup: integrate bg router --- p2p/background/router.go | 13 +++++++------ p2p/module.go | 19 ++++++++++++++++--- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/p2p/background/router.go b/p2p/background/router.go index 3b96d8167..0579e1d0b 100644 --- a/p2p/background/router.go +++ b/p2p/background/router.go @@ -6,7 +6,6 @@ import ( "context" "fmt" - "github.com/hashicorp/go-multierror" dht "github.com/libp2p/go-libp2p-kad-dht" pubsub "github.com/libp2p/go-libp2p-pubsub" libp2pHost "github.com/libp2p/go-libp2p/core/host" @@ -205,12 +204,14 @@ func (rtr *backgroundRouter) RemovePeer(peer typesP2P.Peer) error { } func (rtr *backgroundRouter) Close() error { - rtr.subscription.Cancel() + // TODO_THIS_COMMIT: why is this causing problems? + //rtr.subscription.Cancel() - return multierror.Append( - rtr.topic.Close(), - rtr.kadDHT.Close(), - ) + //return multierror.Append( + // rtr.topic.Close(), + // rtr.kadDHT.Close(), + //) + return nil } func (rtr *backgroundRouter) setupPeerstore( diff --git a/p2p/module.go b/p2p/module.go index 11dbccc47..29f58eb2c 100644 --- a/p2p/module.go +++ b/p2p/module.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "go.uber.org/multierr" + "sync/atomic" "github.com/hashicorp/go-multierror" "github.com/libp2p/go-libp2p" @@ -38,6 +39,7 @@ var _ modules.P2PModule = &p2pModule{} type p2pModule struct { base_modules.IntegratableModule + started atomic.Bool address cryptoPocket.Address logger *modules.Logger options []modules.ModuleOption @@ -143,6 +145,10 @@ func (m *p2pModule) GetModuleName() string { // Start instantiates and assigns `m.host`, unless one already exists, and // `m.stakedActorRouter` (which depends on `m.host` as a required config field). func (m *p2pModule) Start() (err error) { + if !m.started.CompareAndSwap(false, true) { + return fmt.Errorf("p2p module already started") + } + m.GetBus(). GetTelemetryModule(). GetTimeSeriesAgent(). @@ -184,6 +190,10 @@ func (m *p2pModule) Start() (err error) { } func (m *p2pModule) Stop() error { + if !m.started.CompareAndSwap(true, false) { + return fmt.Errorf("p2p module already stopped") + } + routerCloseErrs := multierr.Append( m.unstakedActorRouter.Close(), m.stakedActorRouter.Close(), @@ -196,6 +206,8 @@ func (m *p2pModule) Stop() error { // Don't reuse closed host, `#Start()` will re-create. m.host = nil + m.stakedActorRouter = nil + m.unstakedActorRouter = nil return err } @@ -218,10 +230,11 @@ func (m *p2pModule) Broadcast(msg *anypb.Any) error { return err } - //stakedBroadcastErr := m.stakedActorRouter.Broadcast(data) + stakedBroadcastErr := m.stakedActorRouter.Broadcast(data) unstakedBroadcastErr := m.unstakedActorRouter.Broadcast(data) - //return multierror.Append(err, stakedBroadcastErr, unstakedBroadcastErr).ErrorOrNil() - return multierror.Append(err, unstakedBroadcastErr).ErrorOrNil() + return multierror.Append(err, stakedBroadcastErr, unstakedBroadcastErr).ErrorOrNil() + //return multierror.Append(err, unstakedBroadcastErr).ErrorOrNil() + //return multierror.Append(err, stakedBroadcastErr).ErrorOrNil() } func (m *p2pModule) Send(addr cryptoPocket.Address, msg *anypb.Any) error { From a8206ecb42ed351e8a4c70ca98f3b3e9f61b095b Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 10:14:04 +0200 Subject: [PATCH 74/82] fixup: add handler router config fields --- p2p/raintree/peers_manager_test.go | 6 ++++++ p2p/raintree/router_test.go | 2 ++ 2 files changed, 8 insertions(+) diff --git a/p2p/raintree/peers_manager_test.go b/p2p/raintree/peers_manager_test.go index caca32f5b..1c199a5f4 100644 --- a/p2p/raintree/peers_manager_test.go +++ b/p2p/raintree/peers_manager_test.go @@ -101,6 +101,7 @@ func TestRainTree_Peerstore_HandleUpdate(t *testing.T) { Addr: pubKey.Address(), PeerstoreProvider: pstoreProviderMock, CurrentHeightProvider: currentHeightProviderMock, + Handler: noopHandler, } router, err := NewRainTreeRouter(mockBus, rtCfg) @@ -293,6 +294,7 @@ func testRainTreeMessageTargets(t *testing.T, expectedMsgProp *ExpectedRainTreeM Addr: []byte{expectedMsgProp.orig}, PeerstoreProvider: pstoreProviderMock, CurrentHeightProvider: currentHeightProviderMock, + Handler: noopHandler, } router, err := NewRainTreeRouter(busMock, rtCfg) @@ -359,3 +361,7 @@ func mockAlphabetValidatorServiceURLsDNS(t *testing.T) (done func()) { _ = testutil.BaseDNSMock(t, zones) return done } + +func noopHandler(data []byte) error { + return nil +} diff --git a/p2p/raintree/router_test.go b/p2p/raintree/router_test.go index 1ef093a20..2865a584b 100644 --- a/p2p/raintree/router_test.go +++ b/p2p/raintree/router_test.go @@ -57,6 +57,7 @@ func TestRainTreeRouter_AddPeer(t *testing.T) { Addr: selfAddr, PeerstoreProvider: peerstoreProviderMock, CurrentHeightProvider: currentHeightProviderMock, + Handler: noopHandler, } router, err := NewRainTreeRouter(busMock, rtCfg) @@ -119,6 +120,7 @@ func TestRainTreeRouter_RemovePeer(t *testing.T) { Addr: selfAddr, PeerstoreProvider: peerstoreProviderMock, CurrentHeightProvider: currentHeightProviderMock, + Handler: noopHandler, } router, err := NewRainTreeRouter(busMock, rtCfg) From 03d5c41884a7767d1ffc7a04b4606fc3607388c3 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Fri, 5 May 2023 12:22:43 +0200 Subject: [PATCH 75/82] test: add peer discovery feature --- p2p/integration/peer_discovery.feature | 63 ++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 p2p/integration/peer_discovery.feature diff --git a/p2p/integration/peer_discovery.feature b/p2p/integration/peer_discovery.feature new file mode 100644 index 000000000..74d27267e --- /dev/null +++ b/p2p/integration/peer_discovery.feature @@ -0,0 +1,63 @@ +Feature: Background Router Peer Discovery + # TODO_THIS_COMMIT: reword `node`; at this level, it represents (a) P2P + # module(s) and/or Router implementation(s). + +# TODO: more scenarios +# Scenario: Client joins network +# Scenario: Inactive nodes are removed from the peerstore + + Scenario Outline: Fully connected network bootstrapping + Given a "bootstrap" node + When number of nodes join the network + Then the "bootstrap" node should have number of peers in its peerstore + And the "bootstrap" node should not be included in its own peerstore + And other nodes should have number of peers in their peerstores + And other nodes should not be included in their respective peerstores + + Examples: + | size | + | 4 | + | 6 | + | 12 | +# | 100 | +# | 1024 | + + Scenario Outline: Fully connected network churning + Given a "bootstrap" node + When number of nodes join the network + And the "bootstrap" node leaves the network + And number of nodes leave the network + And number of nodes join the network + Then the network should contain number of nodes + And each node should have number of peers in their respective peerstores + And each node should not have any nodes which left in their peerstores + + Examples: + | initial | leave | join | final | + | 4 | 2 | 2 | 4 | + | 4 | 3 | 4 | 5 | + | 12 | 6 | 6 | 12 | +# | 100 | 50 | 55 | 105 | +# | 1024 | 1000 | 1200 | 1224 | + +# TODO_THIS_COMMIT: very similar test will exercise libp2p relaying.. + Scenario Outline: Discovery across pre-bootstrap network partitions + Given a "bootstrap_A" node in partition "A" + And a "bootstrap_B" node in partition "B" + And a "bootstrap_C" node in partition "C" + And number of nodes bootstrap in partition "A" + And number of nodes bootstrap in partition "B" + And number of nodes bootstrap in partition "C" + When a "bridge_AB" node joins partitions "A" and "B" + Then all nodes in partition "A" should discover all nodes in partition "B" + And all nodes in partition "B" should discover all nodes in partition "A" + When a "bridge_BC" node joins partitions "B" and "C" + Then all nodes in partition "A" should discover all nodes in partition "C" + And all nodes in partition "B" should discover all nodes in partition "C" + And all nodes in partition "C" should discover all nodes in partition "A" + And all nodes in partition "C" should discover all nodes in partition "B" + + Examples: + | size_a | size_b | size_c | + | 2 | 2 | 3 | + | 10 | 5 | 12 | From 7ab03ae527eb66196774f2e91208831895ed1d78 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:52:04 +0200 Subject: [PATCH 76/82] wip: peer discovery integration test --- p2p/integration/peer_discovery.feature | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/p2p/integration/peer_discovery.feature b/p2p/integration/peer_discovery.feature index 74d27267e..0f06ed36d 100644 --- a/p2p/integration/peer_discovery.feature +++ b/p2p/integration/peer_discovery.feature @@ -26,17 +26,17 @@ Feature: Background Router Peer Discovery Given a "bootstrap" node When number of nodes join the network And the "bootstrap" node leaves the network - And number of nodes leave the network - And number of nodes join the network + And number of nodes leave the network + And number of nodes join the network Then the network should contain number of nodes And each node should have number of peers in their respective peerstores - And each node should not have any nodes which left in their peerstores + And each node should not have any leavers in their peerstores Examples: - | initial | leave | join | final | - | 4 | 2 | 2 | 4 | - | 4 | 3 | 4 | 5 | - | 12 | 6 | 6 | 12 | + | initial | leavers | joiners | final | + | 4 | 2 | 2 | 4 | + | 4 | 3 | 4 | 5 | + | 12 | 6 | 6 | 12 | # | 100 | 50 | 55 | 105 | # | 1024 | 1000 | 1200 | 1224 | From 1ee07f04845b854aae79922b4d76de82bad472f3 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 11:55:22 +0200 Subject: [PATCH 77/82] wip: peer discovery integration test --- p2p/integration/peer_discovery_test.go | 84 ++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 p2p/integration/peer_discovery_test.go diff --git a/p2p/integration/peer_discovery_test.go b/p2p/integration/peer_discovery_test.go new file mode 100644 index 000000000..f2bde945a --- /dev/null +++ b/p2p/integration/peer_discovery_test.go @@ -0,0 +1,84 @@ +package integration + +import ( + "github.com/pokt-network/pocket/shared/modules" + "strings" + "testing" + + "github.com/cucumber/godog" + + "github.com/pokt-network/pocket/internal/testutil" +) + +const ( + peerDiscoveryFeaturePath = "peer_discovery.feature" + bootstrapNodePrefix = "bootstrap" +) + +var ( + testNodes map[string]modules.P2PModule +) + +func TestPeerDiscoveryIntegration(t *testing.T) { + t.Parallel() + + testutil.RunGherkinFeature(t, peerDiscoveryFeaturePath, initPeerDiscoveryScenarios) +} + +func aNode(nodeName string) error { + if strings.HasPrefix(bootstrapNodePrefix, nodeName) { + // -- + } + + return godog.ErrPending +} + +func allNodesInPartitionShouldDiscoverAllNodesInPartition(partitionA, partitionB string) error { + return godog.ErrPending +} + +func eachNodeShouldHaveNumberOfPeersInTheirRespectivePeerstores(expectedPeerCount int) error { + return godog.ErrPending +} + +func eachNodeShouldNotHaveAnyLeaversInTheirPeerstores() error { + return godog.ErrPending +} + +func otherNodesShouldHaveNumberOfPeersInTheirPeerstores(arg1 int) error { + return godog.ErrPending +} + +func otherNodesShouldNotBeIncludedInTheirRespectivePeerstores() error { + return godog.ErrPending +} + +func theNetworkShouldContainNumberOfNodes(arg1 int) error { + return godog.ErrPending +} + +func theNodeShouldHaveNumberOfPeersInItsPeerstore(arg1 string, arg2 int) error { + return godog.ErrPending +} + +func theNodeShouldNotBeIncludedInItsOwnPeerstore(arg1 string) error { + return godog.ErrPending +} + +func initPeerDiscoveryScenarios(ctx *godog.ScenarioContext) { + ctx.Step(`^a "([^"]*)" node$`, aNode) + ctx.Step(`^a "([^"]*)" node in partition "([^"]*)"$`, aNodeInPartition) + ctx.Step(`^a "([^"]*)" node joins partitions "([^"]*)" and "([^"]*)"$`, aNodeJoinsPartitionsAnd) + ctx.Step(`^all nodes in partition "([^"]*)" should discover all nodes in partition "([^"]*)"$`, allNodesInPartitionShouldDiscoverAllNodesInPartition) + ctx.Step(`^each node should have (\d+) number of peers in their respective peerstores$`, eachNodeShouldHaveNumberOfPeersInTheirRespectivePeerstores) + ctx.Step(`^each node should not have any leavers in their peerstores$`, eachNodeShouldNotHaveAnyLeaversInTheirPeerstores) + ctx.Step(`^(\d+) number of nodes bootstrap in partition "([^"]*)"$`, numberOfNodesBootstrapInPartition) + ctx.Step(`^(\d+) number of nodes join the network$`, numberOfNodesJoinTheNetwork) + ctx.Step(`^(\d+) number of nodes leave the network$`, numberOfNodesLeaveTheNetwork) + ctx.Step(`^other nodes should have (\d+) number of peers in their peerstores$`, otherNodesShouldHaveNumberOfPeersInTheirPeerstores) + ctx.Step(`^other nodes should not be included in their respective peerstores$`, otherNodesShouldNotBeIncludedInTheirRespectivePeerstores) + ctx.Step(`^the network should contain (\d+) number of nodes$`, theNetworkShouldContainNumberOfNodes) + ctx.Step(`^the "([^"]*)" node leaves the network$`, theNodeLeavesTheNetwork) + ctx.Step(`^the "([^"]*)" node should have (\d+) number of peers in its peerstore$`, theNodeShouldHaveNumberOfPeersInItsPeerstore) + ctx.Step(`^the "([^"]*)" node should not be included in its own peerstore$`, theNodeShouldNotBeIncludedInItsOwnPeerstore) +} From be68967d881e7528d6fcac5e12b204b14dbe0494 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:08:55 +0200 Subject: [PATCH 78/82] wip: peer discovery integration test --- p2p/integration/peer_discovery_test.go | 121 ++++++++++++------------- 1 file changed, 58 insertions(+), 63 deletions(-) diff --git a/p2p/integration/peer_discovery_test.go b/p2p/integration/peer_discovery_test.go index f2bde945a..c84d2d4ac 100644 --- a/p2p/integration/peer_discovery_test.go +++ b/p2p/integration/peer_discovery_test.go @@ -2,12 +2,7 @@ package integration import ( "github.com/pokt-network/pocket/shared/modules" - "strings" "testing" - - "github.com/cucumber/godog" - - "github.com/pokt-network/pocket/internal/testutil" ) const ( @@ -22,63 +17,63 @@ var ( func TestPeerDiscoveryIntegration(t *testing.T) { t.Parallel() - testutil.RunGherkinFeature(t, peerDiscoveryFeaturePath, initPeerDiscoveryScenarios) -} - -func aNode(nodeName string) error { - if strings.HasPrefix(bootstrapNodePrefix, nodeName) { - // -- - } - - return godog.ErrPending -} - -func allNodesInPartitionShouldDiscoverAllNodesInPartition(partitionA, partitionB string) error { - return godog.ErrPending -} - -func eachNodeShouldHaveNumberOfPeersInTheirRespectivePeerstores(expectedPeerCount int) error { - return godog.ErrPending -} - -func eachNodeShouldNotHaveAnyLeaversInTheirPeerstores() error { - return godog.ErrPending + //testutil.RunGherkinFeature(t, peerDiscoveryFeaturePath, initPeerDiscoveryScenarios) } -func otherNodesShouldHaveNumberOfPeersInTheirPeerstores(arg1 int) error { - return godog.ErrPending -} - -func otherNodesShouldNotBeIncludedInTheirRespectivePeerstores() error { - return godog.ErrPending -} - -func theNetworkShouldContainNumberOfNodes(arg1 int) error { - return godog.ErrPending -} - -func theNodeShouldHaveNumberOfPeersInItsPeerstore(arg1 string, arg2 int) error { - return godog.ErrPending -} - -func theNodeShouldNotBeIncludedInItsOwnPeerstore(arg1 string) error { - return godog.ErrPending -} - -func initPeerDiscoveryScenarios(ctx *godog.ScenarioContext) { - ctx.Step(`^a "([^"]*)" node$`, aNode) - ctx.Step(`^a "([^"]*)" node in partition "([^"]*)"$`, aNodeInPartition) - ctx.Step(`^a "([^"]*)" node joins partitions "([^"]*)" and "([^"]*)"$`, aNodeJoinsPartitionsAnd) - ctx.Step(`^all nodes in partition "([^"]*)" should discover all nodes in partition "([^"]*)"$`, allNodesInPartitionShouldDiscoverAllNodesInPartition) - ctx.Step(`^each node should have (\d+) number of peers in their respective peerstores$`, eachNodeShouldHaveNumberOfPeersInTheirRespectivePeerstores) - ctx.Step(`^each node should not have any leavers in their peerstores$`, eachNodeShouldNotHaveAnyLeaversInTheirPeerstores) - ctx.Step(`^(\d+) number of nodes bootstrap in partition "([^"]*)"$`, numberOfNodesBootstrapInPartition) - ctx.Step(`^(\d+) number of nodes join the network$`, numberOfNodesJoinTheNetwork) - ctx.Step(`^(\d+) number of nodes leave the network$`, numberOfNodesLeaveTheNetwork) - ctx.Step(`^other nodes should have (\d+) number of peers in their peerstores$`, otherNodesShouldHaveNumberOfPeersInTheirPeerstores) - ctx.Step(`^other nodes should not be included in their respective peerstores$`, otherNodesShouldNotBeIncludedInTheirRespectivePeerstores) - ctx.Step(`^the network should contain (\d+) number of nodes$`, theNetworkShouldContainNumberOfNodes) - ctx.Step(`^the "([^"]*)" node leaves the network$`, theNodeLeavesTheNetwork) - ctx.Step(`^the "([^"]*)" node should have (\d+) number of peers in its peerstore$`, theNodeShouldHaveNumberOfPeersInItsPeerstore) - ctx.Step(`^the "([^"]*)" node should not be included in its own peerstore$`, theNodeShouldNotBeIncludedInItsOwnPeerstore) -} +//func aNode(nodeName string) error { +// if strings.HasPrefix(bootstrapNodePrefix, nodeName) { +// // -- +// } +// +// return godog.ErrPending +//} +// +//func allNodesInPartitionShouldDiscoverAllNodesInPartition(partitionA, partitionB string) error { +// return godog.ErrPending +//} +// +//func eachNodeShouldHaveNumberOfPeersInTheirRespectivePeerstores(expectedPeerCount int) error { +// return godog.ErrPending +//} +// +//func eachNodeShouldNotHaveAnyLeaversInTheirPeerstores() error { +// return godog.ErrPending +//} +// +//func otherNodesShouldHaveNumberOfPeersInTheirPeerstores(arg1 int) error { +// return godog.ErrPending +//} +// +//func otherNodesShouldNotBeIncludedInTheirRespectivePeerstores() error { +// return godog.ErrPending +//} +// +//func theNetworkShouldContainNumberOfNodes(arg1 int) error { +// return godog.ErrPending +//} +// +//func theNodeShouldHaveNumberOfPeersInItsPeerstore(arg1 string, arg2 int) error { +// return godog.ErrPending +//} +// +//func theNodeShouldNotBeIncludedInItsOwnPeerstore(arg1 string) error { +// return godog.ErrPending +//} +// +//func initPeerDiscoveryScenarios(ctx *godog.ScenarioContext) { +// ctx.Step(`^a "([^"]*)" node$`, aNode) +// ctx.Step(`^a "([^"]*)" node in partition "([^"]*)"$`, aNodeInPartition) +// ctx.Step(`^a "([^"]*)" node joins partitions "([^"]*)" and "([^"]*)"$`, aNodeJoinsPartitionsAnd) +// ctx.Step(`^all nodes in partition "([^"]*)" should discover all nodes in partition "([^"]*)"$`, allNodesInPartitionShouldDiscoverAllNodesInPartition) +// ctx.Step(`^each node should have (\d+) number of peers in their respective peerstores$`, eachNodeShouldHaveNumberOfPeersInTheirRespectivePeerstores) +// ctx.Step(`^each node should not have any leavers in their peerstores$`, eachNodeShouldNotHaveAnyLeaversInTheirPeerstores) +// ctx.Step(`^(\d+) number of nodes bootstrap in partition "([^"]*)"$`, numberOfNodesBootstrapInPartition) +// ctx.Step(`^(\d+) number of nodes join the network$`, numberOfNodesJoinTheNetwork) +// ctx.Step(`^(\d+) number of nodes leave the network$`, numberOfNodesLeaveTheNetwork) +// ctx.Step(`^other nodes should have (\d+) number of peers in their peerstores$`, otherNodesShouldHaveNumberOfPeersInTheirPeerstores) +// ctx.Step(`^other nodes should not be included in their respective peerstores$`, otherNodesShouldNotBeIncludedInTheirRespectivePeerstores) +// ctx.Step(`^the network should contain (\d+) number of nodes$`, theNetworkShouldContainNumberOfNodes) +// ctx.Step(`^the "([^"]*)" node leaves the network$`, theNodeLeavesTheNetwork) +// ctx.Step(`^the "([^"]*)" node should have (\d+) number of peers in its peerstore$`, theNodeShouldHaveNumberOfPeersInItsPeerstore) +// ctx.Step(`^the "([^"]*)" node should not be included in its own peerstore$`, theNodeShouldNotBeIncludedInItsOwnPeerstore) +//} From 0f34a645502b93656557264633730e4007a7365e Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:32:20 +0200 Subject: [PATCH 79/82] wip: peer discovery integration test --- p2p/integration/peer_discovery.feature | 96 +++++---- p2p/integration/peer_discovery_test.go | 262 ++++++++++++++++++++----- 2 files changed, 256 insertions(+), 102 deletions(-) diff --git a/p2p/integration/peer_discovery.feature b/p2p/integration/peer_discovery.feature index 0f06ed36d..c968ca3ca 100644 --- a/p2p/integration/peer_discovery.feature +++ b/p2p/integration/peer_discovery.feature @@ -7,57 +7,55 @@ Feature: Background Router Peer Discovery # Scenario: Inactive nodes are removed from the peerstore Scenario Outline: Fully connected network bootstrapping - Given a "bootstrap" node - When number of nodes join the network - Then the "bootstrap" node should have number of peers in its peerstore - And the "bootstrap" node should not be included in its own peerstore - And other nodes should have number of peers in their peerstores - And other nodes should not be included in their respective peerstores + Given a network containing a "bootstrap" node + When number of "other" nodes join the network + Then the "bootstrap" node should have plus one number of peers in its peerstore + And other nodes should have plus one number of peers in their peerstores Examples: - | size | - | 4 | - | 6 | - | 12 | + | count | + | 4 | +# | 6 | +# | 12 | # | 100 | # | 1024 | - Scenario Outline: Fully connected network churning - Given a "bootstrap" node - When number of nodes join the network - And the "bootstrap" node leaves the network - And number of nodes leave the network - And number of nodes join the network - Then the network should contain number of nodes - And each node should have number of peers in their respective peerstores - And each node should not have any leavers in their peerstores - - Examples: - | initial | leavers | joiners | final | - | 4 | 2 | 2 | 4 | - | 4 | 3 | 4 | 5 | - | 12 | 6 | 6 | 12 | -# | 100 | 50 | 55 | 105 | -# | 1024 | 1000 | 1200 | 1224 | - -# TODO_THIS_COMMIT: very similar test will exercise libp2p relaying.. - Scenario Outline: Discovery across pre-bootstrap network partitions - Given a "bootstrap_A" node in partition "A" - And a "bootstrap_B" node in partition "B" - And a "bootstrap_C" node in partition "C" - And number of nodes bootstrap in partition "A" - And number of nodes bootstrap in partition "B" - And number of nodes bootstrap in partition "C" - When a "bridge_AB" node joins partitions "A" and "B" - Then all nodes in partition "A" should discover all nodes in partition "B" - And all nodes in partition "B" should discover all nodes in partition "A" - When a "bridge_BC" node joins partitions "B" and "C" - Then all nodes in partition "A" should discover all nodes in partition "C" - And all nodes in partition "B" should discover all nodes in partition "C" - And all nodes in partition "C" should discover all nodes in partition "A" - And all nodes in partition "C" should discover all nodes in partition "B" - - Examples: - | size_a | size_b | size_c | - | 2 | 2 | 3 | - | 10 | 5 | 12 | +# Scenario Outline: Fully connected network churning +# Given a "bootstrap" node +# When number of nodes join the network +# And the "bootstrap" node leaves the network +# And number of nodes leave the network +# And number of nodes join the network +# Then the network should contain number of nodes +# And each node should have number of peers in their respective peerstores +# And each node should not have any leavers in their peerstores +# +# Examples: +# | initial | leavers | joiners | final | +# | 4 | 2 | 2 | 4 | +# | 4 | 3 | 4 | 5 | +# | 12 | 6 | 6 | 12 | +## | 100 | 50 | 55 | 105 | +## | 1024 | 1000 | 1200 | 1224 | +# +## TODO_THIS_COMMIT: very similar test will exercise libp2p relaying.. +# Scenario Outline: Discovery across pre-bootstrap network partitions +# Given a "bootstrap_A" node in partition "A" +# And a "bootstrap_B" node in partition "B" +# And a "bootstrap_C" node in partition "C" +# And number of nodes bootstrap in partition "A" +# And number of nodes bootstrap in partition "B" +# And number of nodes bootstrap in partition "C" +# When a "bridge_AB" node joins partitions "A" and "B" +# Then all nodes in partition "A" should discover all nodes in partition "B" +# And all nodes in partition "B" should discover all nodes in partition "A" +# When a "bridge_BC" node joins partitions "B" and "C" +# Then all nodes in partition "A" should discover all nodes in partition "C" +# And all nodes in partition "B" should discover all nodes in partition "C" +# And all nodes in partition "C" should discover all nodes in partition "A" +# And all nodes in partition "C" should discover all nodes in partition "B" +# +# Examples: +# | size_a | size_b | size_c | +# | 2 | 2 | 3 | +# | 10 | 5 | 12 | diff --git a/p2p/integration/peer_discovery_test.go b/p2p/integration/peer_discovery_test.go index c84d2d4ac..564180096 100644 --- a/p2p/integration/peer_discovery_test.go +++ b/p2p/integration/peer_discovery_test.go @@ -1,79 +1,235 @@ +//go:build test + package integration import ( + "github.com/foxcpp/go-mockdns" + mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" + "github.com/pokt-network/pocket/internal/testutil" + "github.com/pokt-network/pocket/internal/testutil/constructors" + generics_testutil "github.com/pokt-network/pocket/internal/testutil/generics" + runtime_testutil "github.com/pokt-network/pocket/internal/testutil/runtime" + "github.com/pokt-network/pocket/p2p" + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/pocket/shared/modules" + mock_modules "github.com/pokt-network/pocket/shared/modules/mocks" + "github.com/regen-network/gocuke" + "github.com/stretchr/testify/require" "testing" + "time" ) const ( peerDiscoveryFeaturePath = "peer_discovery.feature" - bootstrapNodePrefix = "bootstrap" -) - -var ( - testNodes map[string]modules.P2PModule + bootstrapNodeLabelPrefix = "bootstrap" + otherNodeLabelPrefix = "other" ) func TestPeerDiscoveryIntegration(t *testing.T) { t.Parallel() - //testutil.RunGherkinFeature(t, peerDiscoveryFeaturePath, initPeerDiscoveryScenarios) + gocuke.NewRunner(t, new(backgroundPeerDiscoverySuite)).Path(peerDiscoveryFeaturePath).Run() } -//func aNode(nodeName string) error { -// if strings.HasPrefix(bootstrapNodePrefix, nodeName) { -// // -- -// } -// -// return godog.ErrPending -//} -// -//func allNodesInPartitionShouldDiscoverAllNodesInPartition(partitionA, partitionB string) error { -// return godog.ErrPending -//} -// -//func eachNodeShouldHaveNumberOfPeersInTheirRespectivePeerstores(expectedPeerCount int) error { -// return godog.ErrPending -//} -// -//func eachNodeShouldNotHaveAnyLeaversInTheirPeerstores() error { -// return godog.ErrPending +type backgroundPeerDiscoverySuite struct { + gocuke.TestingT + + dnsSrv *mockdns.Server + busMocks map[string]*mock_modules.MockBus + libp2pNetworkMock mocknet.Mocknet + p2pModules map[string]modules.P2PModule + + // TODO: something more sophisticated.. + // - what about a list of nodes sharing a label? + // + // labelServiceURLMap a list of serviceURLs to a set of labels + labelServiceURLMap map[string][]string +} + +func (s *backgroundPeerDiscoverySuite) Before(_ gocuke.Scenario) { + s.labelServiceURLMap = make(map[string][]string) +} + +func (s *backgroundPeerDiscoverySuite) ANetworkContainingANode(nodeLabel string) { + s.dnsSrv = testutil.MinimalDNSMock(s) + + s.busMocks, s.libp2pNetworkMock, s.p2pModules = constructors.NewBusesMocknetAndP2PModules( + s, 1, s.dnsSrv, nil, nil, + ) + + // i.e. "only" serviceURL as this step definition initializes the network + firstServiceURL := generics_testutil.GetKeys(s.busMocks)[0] + s.addServiceURLWithLabel(nodeLabel, firstServiceURL) + + err := s.p2pModules[firstServiceURL].Start() + require.NoError(s, err) + + // TODO_THIS_COMMIT: revisit... + time.Sleep(time.Second * 1) +} + +func (s *backgroundPeerDiscoverySuite) NumberOfNodesJoinTheNetwork(nodeCount int64, nodeLabel string) { + // TECHDEBT: use an iterator instead.. + // plus 1 to account for the bootstrap node serviceURL which we used earlier + serviceURLKeyMap := testutil.SequentialServiceURLPrivKeyMap(s, int(nodeCount+1)) + + // TODO_THIS_COMMIT: clarify how to use `bootstrapNodeLabelPrefix` / what it is for + bootstrapNodeServiceURLs := s.getServiceURLsWithLabel(bootstrapNodeLabelPrefix) + require.Equalf(s, 1, len(bootstrapNodeServiceURLs), "expected exactly one bootstrap node") + + bootstrapNodeServiceURL := bootstrapNodeServiceURLs[0] + + // bootstrap node is the only validator in genesis + genesisState := runtime_testutil.BaseGenesisStateMockFromServiceURLKeyMap( + s, map[string]cryptoPocket.PrivateKey{ + bootstrapNodeServiceURL: serviceURLKeyMap[bootstrapNodeServiceURL], + }, + ) + + // remove the bootstrap node from the map + delete(serviceURLKeyMap, bootstrapNodeServiceURL) + + for serviceURL := range serviceURLKeyMap { + s.addServiceURLWithLabel(otherNodeLabelPrefix, serviceURL) + } + + joinersBusMocks, joinersP2PModules := constructors.NewBusesAndP2PModules( + s, nil, + s.dnsSrv, + genesisState, + s.libp2pNetworkMock, + serviceURLKeyMap, + ) + + err := s.libp2pNetworkMock.LinkAll() + require.NoError(s, err) + + for _, p2pModule := range joinersP2PModules { + err := p2pModule.Start() + require.NoError(s, err) + } + + s.addBusMocks(joinersBusMocks) + s.addP2PModules(joinersP2PModules) + + // TODO_THIS_COMMIT: wait for bootstrapping + time.Sleep(time.Second * 1) +} + +// TODO_THIS_COMMIT: move below exported methods +func (s *backgroundPeerDiscoverySuite) addServiceURLWithLabel(nodeLabel, serviceURL string) { + serviceURLs := s.labelServiceURLMap[nodeLabel] + if serviceURLs == nil { + serviceURLs = make([]string, 0) + } + s.labelServiceURLMap[nodeLabel] = append(serviceURLs, serviceURL) +} +func (s *backgroundPeerDiscoverySuite) getServiceURLsWithLabel(nodeLabel string) []string { + serviceURLs, ok := s.labelServiceURLMap[nodeLabel] + if !ok { + return nil + } + + if len(serviceURLs) < 1 { + return nil + } + return serviceURLs +} + +func (s *backgroundPeerDiscoverySuite) TheNodeShouldHavePlusOneNumberOfPeersInItsPeerstore(nodeLabel string, peerCountMinus1 int64) { + labelServiceURLs := s.getServiceURLsWithLabel(nodeLabel) + //serviceURL, ok := s.labelServiceURLMap[nodeLabel] + require.NotEmptyf(s, labelServiceURLs, "node label %q not found", nodeLabel) + require.Equalf(s, 1, len(labelServiceURLs), "node label %q has more than one service url", nodeLabel) + + serviceURL := labelServiceURLs[0] + p2pModule, ok := s.p2pModules[serviceURL] + require.Truef(s, ok, "p2p module for service url %q not found", serviceURL) + + host := p2pModule.(*p2p.P2PModule).GetHost() + peers := host.Peerstore().Peers() + + require.Equalf(s, int(peerCountMinus1+1), len(peers), "unexpected number of peers in peerstore: %s", peers) +} + +func (s *backgroundPeerDiscoverySuite) OtherNodesShouldHavePlusOneNumberOfPeersInTheirPeerstores(peerCountMinus1 int64) { + // TODO_THIS_COMMIT: clarify how to use `bootstrapNodeLabelPrefix` / what it is for + otherNodeServiceURLs := s.getServiceURLsWithoutLabel(bootstrapNodeLabelPrefix) + + require.NotEmpty(s, otherNodeServiceURLs, "other nodes not found") + require.Equal(s, int(peerCountMinus1), len(otherNodeServiceURLs)) + + for _, serviceURL := range otherNodeServiceURLs { + p2pModule, ok := s.p2pModules[serviceURL] + require.Truef(s, ok, "p2p module for service url %q not found", serviceURL) + + host := p2pModule.(*p2p.P2PModule).GetHost() + peers := host.Peerstore().Peers() + + require.Equalf(s, int(peerCountMinus1+1), len(peers), "unexpected number of peers in peerstore: %s", peers) + } +} + +//func (s *backgroundPeerDiscoverySuite) EachNodeShouldNotHaveAnyLeaversInTheirPeerstores() { +// panic("PENDING") //} // -//func otherNodesShouldHaveNumberOfPeersInTheirPeerstores(arg1 int) error { -// return godog.ErrPending +//func (s *backgroundPeerDiscoverySuite) LeaverNumberOfNodesLeaveTheNetwork() { +// panic("PENDING") //} -// -//func otherNodesShouldNotBeIncludedInTheirRespectivePeerstores() error { -// return godog.ErrPending + +func (s *backgroundPeerDiscoverySuite) EachNodeShouldHaveNumberOfPeersInTheirRespectivePeerstores(peerCount int64) { + panic("PENDING") +} + +//func (s *backgroundPeerDiscoverySuite) NumberOfNodesBootstrapInPartition(a int64, b string) { +// panic("PENDING") //} -// -//func theNetworkShouldContainNumberOfNodes(arg1 int) error { -// return godog.ErrPending + +func (s *backgroundPeerDiscoverySuite) TheNetworkShouldContainNumberOfNodes(nodeCount int64) { + panic("PENDING") +} + +//func (s *backgroundPeerDiscoverySuite) ANodeInPartition(a string, b string) { +// panic("PENDING") //} // -//func theNodeShouldHaveNumberOfPeersInItsPeerstore(arg1 string, arg2 int) error { -// return godog.ErrPending +//func (s *backgroundPeerDiscoverySuite) ANodeJoinsPartitionsAnd(a string, b string, c string) { +// panic("PENDING") //} // -//func theNodeShouldNotBeIncludedInItsOwnPeerstore(arg1 string) error { -// return godog.ErrPending +//func (s *backgroundPeerDiscoverySuite) AllNodesInPartitionShouldDiscoverAllNodesInPartition(a string, b string) { +// panic("PENDING") //} -// -//func initPeerDiscoveryScenarios(ctx *godog.ScenarioContext) { -// ctx.Step(`^a "([^"]*)" node$`, aNode) -// ctx.Step(`^a "([^"]*)" node in partition "([^"]*)"$`, aNodeInPartition) -// ctx.Step(`^a "([^"]*)" node joins partitions "([^"]*)" and "([^"]*)"$`, aNodeJoinsPartitionsAnd) -// ctx.Step(`^all nodes in partition "([^"]*)" should discover all nodes in partition "([^"]*)"$`, allNodesInPartitionShouldDiscoverAllNodesInPartition) -// ctx.Step(`^each node should have (\d+) number of peers in their respective peerstores$`, eachNodeShouldHaveNumberOfPeersInTheirRespectivePeerstores) -// ctx.Step(`^each node should not have any leavers in their peerstores$`, eachNodeShouldNotHaveAnyLeaversInTheirPeerstores) -// ctx.Step(`^(\d+) number of nodes bootstrap in partition "([^"]*)"$`, numberOfNodesBootstrapInPartition) -// ctx.Step(`^(\d+) number of nodes join the network$`, numberOfNodesJoinTheNetwork) -// ctx.Step(`^(\d+) number of nodes leave the network$`, numberOfNodesLeaveTheNetwork) -// ctx.Step(`^other nodes should have (\d+) number of peers in their peerstores$`, otherNodesShouldHaveNumberOfPeersInTheirPeerstores) -// ctx.Step(`^other nodes should not be included in their respective peerstores$`, otherNodesShouldNotBeIncludedInTheirRespectivePeerstores) -// ctx.Step(`^the network should contain (\d+) number of nodes$`, theNetworkShouldContainNumberOfNodes) -// ctx.Step(`^the "([^"]*)" node leaves the network$`, theNodeLeavesTheNetwork) -// ctx.Step(`^the "([^"]*)" node should have (\d+) number of peers in its peerstore$`, theNodeShouldHaveNumberOfPeersInItsPeerstore) -// ctx.Step(`^the "([^"]*)" node should not be included in its own peerstore$`, theNodeShouldNotBeIncludedInItsOwnPeerstore) + +//func (s *backgroundPeerDiscoverySuite) TheNodeLeavesTheNetwork(a string) { +// panic("PENDING") //} + +func (s *backgroundPeerDiscoverySuite) addBusMocks(busMocks map[string]*mock_modules.MockBus) { + s.Helper() + + for serviceURL, busMock := range busMocks { + require.Nilf(s, s.busMocks[serviceURL], "busMock for serviceURL %s already exists", serviceURL) + s.busMocks[serviceURL] = busMock + } +} + +func (s *backgroundPeerDiscoverySuite) addP2PModules(p2pModules map[string]modules.P2PModule) { + s.Helper() + + for serviceURL, p2pModule := range p2pModules { + require.Nilf(s, s.p2pModules[serviceURL], "p2pModule for serviceURL %s already exists", serviceURL) + s.p2pModules[serviceURL] = p2pModule + } +} + +func (s *backgroundPeerDiscoverySuite) getServiceURLsWithoutLabel(nodeLabel string) (serviceURLs []string) { + for label, urls := range s.labelServiceURLMap { + if label == nodeLabel { + continue + } + serviceURLs = append(serviceURLs, urls...) + } + return serviceURLs +} From b213598cf512fae9bad343649dc43353b687b13a Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:34:24 +0200 Subject: [PATCH 80/82] wip: peer discovery integration test --- p2p/integration/peer_discovery_test.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/p2p/integration/peer_discovery_test.go b/p2p/integration/peer_discovery_test.go index 564180096..cf258c535 100644 --- a/p2p/integration/peer_discovery_test.go +++ b/p2p/integration/peer_discovery_test.go @@ -39,10 +39,9 @@ type backgroundPeerDiscoverySuite struct { libp2pNetworkMock mocknet.Mocknet p2pModules map[string]modules.P2PModule - // TODO: something more sophisticated.. - // - what about a list of nodes sharing a label? - // - // labelServiceURLMap a list of serviceURLs to a set of labels + // labelServiceURLMap a list of serviceURLs to a set of labels; intended to + // be access and updated via `#getServiceURLsWithLabel()` and + // `#addServiceURLWithLabel()`, respectively. labelServiceURLMap map[string][]string } @@ -61,7 +60,11 @@ func (s *backgroundPeerDiscoverySuite) ANetworkContainingANode(nodeLabel string) firstServiceURL := generics_testutil.GetKeys(s.busMocks)[0] s.addServiceURLWithLabel(nodeLabel, firstServiceURL) - err := s.p2pModules[firstServiceURL].Start() + debugNotifee := testutil.NewDebugNotifee(s) + bootstrapP2PModule := s.p2pModules[firstServiceURL] + bootstrapP2PModule.(*p2p.P2PModule).GetHost().Network().Notify(debugNotifee) + + err := bootstrapP2PModule.Start() require.NoError(s, err) // TODO_THIS_COMMIT: revisit... @@ -105,6 +108,9 @@ func (s *backgroundPeerDiscoverySuite) NumberOfNodesJoinTheNetwork(nodeCount int require.NoError(s, err) for _, p2pModule := range joinersP2PModules { + debugNotifee := testutil.NewDebugNotifee(s) + p2pModule.(*p2p.P2PModule).GetHost().Network().Notify(debugNotifee) + err := p2pModule.Start() require.NoError(s, err) } @@ -113,7 +119,9 @@ func (s *backgroundPeerDiscoverySuite) NumberOfNodesJoinTheNetwork(nodeCount int s.addP2PModules(joinersP2PModules) // TODO_THIS_COMMIT: wait for bootstrapping - time.Sleep(time.Second * 1) + s.Log("STARTING DELAY...") + time.Sleep(time.Second * 5) + s.Log("DELAY OVER") } // TODO_THIS_COMMIT: move below exported methods @@ -138,7 +146,6 @@ func (s *backgroundPeerDiscoverySuite) getServiceURLsWithLabel(nodeLabel string) func (s *backgroundPeerDiscoverySuite) TheNodeShouldHavePlusOneNumberOfPeersInItsPeerstore(nodeLabel string, peerCountMinus1 int64) { labelServiceURLs := s.getServiceURLsWithLabel(nodeLabel) - //serviceURL, ok := s.labelServiceURLMap[nodeLabel] require.NotEmptyf(s, labelServiceURLs, "node label %q not found", nodeLabel) require.Equalf(s, 1, len(labelServiceURLs), "node label %q has more than one service url", nodeLabel) From 333b1e909d3289c3b3962cf0729cbefc2eae6f9d Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:35:02 +0200 Subject: [PATCH 81/82] wip: peer discovery integration test --- p2p/integration/peer_discovery_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/p2p/integration/peer_discovery_test.go b/p2p/integration/peer_discovery_test.go index cf258c535..20ea09d26 100644 --- a/p2p/integration/peer_discovery_test.go +++ b/p2p/integration/peer_discovery_test.go @@ -53,7 +53,7 @@ func (s *backgroundPeerDiscoverySuite) ANetworkContainingANode(nodeLabel string) s.dnsSrv = testutil.MinimalDNSMock(s) s.busMocks, s.libp2pNetworkMock, s.p2pModules = constructors.NewBusesMocknetAndP2PModules( - s, 1, s.dnsSrv, nil, nil, + s, 1, s.dnsSrv, nil, nil, nil, ) // i.e. "only" serviceURL as this step definition initializes the network @@ -102,6 +102,7 @@ func (s *backgroundPeerDiscoverySuite) NumberOfNodesJoinTheNetwork(nodeCount int genesisState, s.libp2pNetworkMock, serviceURLKeyMap, + nil, ) err := s.libp2pNetworkMock.LinkAll() From a00b9d1ee2e8468fc0ac2423ff63ab532ee5f8b2 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 1 Jun 2023 12:42:02 +0200 Subject: [PATCH 82/82] wip: peer discovery integration test --- p2p/integration/peer_discovery_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/p2p/integration/peer_discovery_test.go b/p2p/integration/peer_discovery_test.go index 20ea09d26..b914622ba 100644 --- a/p2p/integration/peer_discovery_test.go +++ b/p2p/integration/peer_discovery_test.go @@ -60,9 +60,9 @@ func (s *backgroundPeerDiscoverySuite) ANetworkContainingANode(nodeLabel string) firstServiceURL := generics_testutil.GetKeys(s.busMocks)[0] s.addServiceURLWithLabel(nodeLabel, firstServiceURL) - debugNotifee := testutil.NewDebugNotifee(s) + //debugNotifee := testutil.NewDebugNotifee(s) bootstrapP2PModule := s.p2pModules[firstServiceURL] - bootstrapP2PModule.(*p2p.P2PModule).GetHost().Network().Notify(debugNotifee) + //bootstrapP2PModule.(*p2p.P2PModule).GetHost().Network().Notify(debugNotifee) err := bootstrapP2PModule.Start() require.NoError(s, err)