diff --git a/Makefile b/Makefile index b78e88a68..16504dde3 100644 --- a/Makefile +++ b/Makefile @@ -304,6 +304,11 @@ generate_cli_commands_docs: ## (Re)generates the CLI commands docs (this is mean cd app/client/cli/docgen && go run . echo "CLI commands docs generated in ${cli_docs_dir}" +.PHONY: generate_node_state_machine_diagram +generate_node_state_machine_diagram: ## (Re)generates the Node State Machine diagram + go run ./state_machine/visualizer/main.go + echo "Node State Machine diagram generated in state_machine/docs/state-machine.diagram.md" + .PHONY: test_all test_all: ## Run all go unit tests go test -p 1 -count=1 ./... diff --git a/app/client/doc/CHANGELOG.md b/app/client/doc/CHANGELOG.md index 983f0e91b..698b73929 100644 --- a/app/client/doc/CHANGELOG.md +++ b/app/client/doc/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.0.0.14] - 2023-02-16 +## [0.0.0.14] - 2023-02-17 - Introduced logical switch to handle parsing of the debug private keys from a local file OR from Kubernetes secret (PR #517) - Bugfix for `Stake` command. Address erroneously sent instead of the PublicKey. (PR #518) diff --git a/build/docs/CHANGELOG.md b/build/docs/CHANGELOG.md index aa13f5e16..20a0fc54c 100644 --- a/build/docs/CHANGELOG.md +++ b/build/docs/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.0.0.15] - 2023-02-16 +## [0.0.0.15] - 2023-02-17 - Added manifests to handle `Roles`, `RoleBindings` and `ServiceAccounts` and referenced them in the `Tiltfile` - Updated `cli-client.yaml` to bind the `debug-client-account` `ServiceAccount` that has permissions to read the private keys from the `Secret` diff --git a/consensus/debugging.go b/consensus/debugging.go index e6e5eec08..c921ae5ac 100644 --- a/consensus/debugging.go +++ b/consensus/debugging.go @@ -49,10 +49,12 @@ func (m *consensusModule) GetNodeState() typesCons.ConsensusNodeState { func (m *consensusModule) resetToGenesis(_ *messaging.DebugMessage) error { m.logger.Debug().Msg(typesCons.DebugResetToGenesis) - m.height = 0 + + m.SetHeight(0) m.ResetForNewHeight() m.clearLeader() m.clearMessagesPool() + m.GetBus().GetUtilityModule().GetMempool().Clear() if err := m.GetBus().GetPersistenceModule().HandleDebugMessage(&messaging.DebugMessage{ Action: messaging.DebugMessageAction_DEBUG_PERSISTENCE_RESET_TO_GENESIS, Message: nil, diff --git a/consensus/doc/CHANGELOG.md b/consensus/doc/CHANGELOG.md index 2e3e98095..3bc5b40e6 100644 --- a/consensus/doc/CHANGELOG.md +++ b/consensus/doc/CHANGELOG.md @@ -7,12 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.29] - 2023-02-17 + +- Modules embed `base_modules.IntegratableModule` and `base_modules.InterruptableModule` for DRYness +- Updated modules `Create` to accept generic options +- `resetToGenesis` clears the utility mempool as well +- Publishing `ConsensusNewHeightEvent` on new height + ## [0.0.0.28] - 2023-02-14 - Add a few `nolint` comments to fix the code on main ## [0.0.0.27] - 2023-02-09 - - Add `state_sync` submodule, with `state_sync` struct - Implement state sync server to advertise blocks and metadata - Create new `state_sync_handler.go` source file that handles `StateSyncMessage`s sent to the `Consensus` module diff --git a/consensus/e2e_tests/utils_test.go b/consensus/e2e_tests/utils_test.go index 646689158..f99600a9c 100644 --- a/consensus/e2e_tests/utils_test.go +++ b/consensus/e2e_tests/utils_test.go @@ -100,10 +100,9 @@ func CreateTestConsensusPocketNode( ) *shared.Node { // persistence is a dependency of consensus, so we need to create it first persistenceMock := basePersistenceMock(t, eventsChannel, bus) - err := (bus).RegisterModule(persistenceMock) - require.NoError(t, err) + bus.RegisterModule(persistenceMock) - _, err = consensus.Create(bus) + _, err := consensus.Create(bus) require.NoError(t, err) runtimeMgr := (bus).GetRuntimeMgr() @@ -114,16 +113,17 @@ func CreateTestConsensusPocketNode( telemetryMock := baseTelemetryMock(t, eventsChannel) loggerMock := baseLoggerMock(t, eventsChannel) rpcMock := baseRpcMock(t, eventsChannel) + stateMachineMock := baseStateMachineMock(t, eventsChannel) for _, module := range []modules.Module{ + stateMachineMock, p2pMock, utilityMock, telemetryMock, loggerMock, rpcMock, } { - err = (bus).RegisterModule(module) - require.NoError(t, err) + bus.RegisterModule(module) } require.NoError(t, err) @@ -423,6 +423,7 @@ func baseP2PMock(t *testing.T, eventsChannel modules.EventsChannel) *mockModules }). AnyTimes() p2pMock.EXPECT().GetModuleName().Return(modules.P2PModuleName).AnyTimes() + p2pMock.EXPECT().HandleEvent(gomock.Any()).Return(nil).AnyTimes() return p2pMock } @@ -494,6 +495,16 @@ func baseRpcMock(t *testing.T, _ modules.EventsChannel) *mockModules.MockRPCModu return rpcMock } +func baseStateMachineMock(t *testing.T, _ modules.EventsChannel) *mockModules.MockStateMachineModule { + ctrl := gomock.NewController(t) + stateMachineMock := mockModules.NewMockStateMachineModule(ctrl) + stateMachineMock.EXPECT().Start().Return(nil).AnyTimes() + stateMachineMock.EXPECT().SetBus(gomock.Any()).Return().AnyTimes() + stateMachineMock.EXPECT().GetModuleName().Return(modules.StateMachineModuleName).AnyTimes() + + return stateMachineMock +} + func baseTelemetryTimeSeriesAgentMock(t *testing.T) *mockModules.MockTimeSeriesAgent { ctrl := gomock.NewController(t) timeSeriesAgentMock := mockModules.NewMockTimeSeriesAgent(ctrl) @@ -521,6 +532,14 @@ func baseLoggerMock(t *testing.T, _ modules.EventsChannel) *mockModules.MockLogg func logTime(t *testing.T, clck *clock.Mock) { t.Helper() + defer func() { + // this is to recover from a panic that could happen if the goroutine tries to log after the test has finished + // cause of the panic: https://github.com/golang/go/blob/135c470b2277e1c9514ba8a5478408fea0dee8a2/src/testing/testing.go#L1003 + // + // spotted for the first time in our CI: https://github.com/pokt-network/pocket/actions/runs/4198025819/jobs/7281103860#step:8:1118 + //nolint:errcheck // ignoring completely + recover() + }() t.Logf("[⌚ CLOCK ⌚] the time is: %v ms from UNIX Epoch [%v]", clck.Now().UTC().UnixMilli(), clck.Now().UTC()) } diff --git a/consensus/events.go b/consensus/events.go new file mode 100644 index 000000000..0e31f8d72 --- /dev/null +++ b/consensus/events.go @@ -0,0 +1,14 @@ +package consensus + +import ( + "github.com/pokt-network/pocket/shared/messaging" +) + +// publishNewHeightEvent publishes a new height event to the bus so that other interested IntegratableModules can react to it if necessary +func (m *consensusModule) publishNewHeightEvent(height uint64) { + newHeightEvent, err := messaging.PackMessage(&messaging.ConsensusNewHeightEvent{Height: height}) + if err != nil { + m.logger.Fatal().Err(err).Msg("Failed to pack consensus new height event") + } + m.GetBus().PublishEventToBus(newHeightEvent) +} diff --git a/consensus/leader_election/module.go b/consensus/leader_election/module.go index 8d856dd24..2004bad28 100644 --- a/consensus/leader_election/module.go +++ b/consensus/leader_election/module.go @@ -2,8 +2,8 @@ package leader_election import ( typesCons "github.com/pokt-network/pocket/consensus/types" - "github.com/pokt-network/pocket/logger" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" ) type LeaderElectionModule interface { @@ -14,45 +14,29 @@ type LeaderElectionModule interface { var _ LeaderElectionModule = &leaderElectionModule{} type leaderElectionModule struct { - bus modules.Bus + base_modules.IntegratableModule + base_modules.InterruptableModule } func Create(bus modules.Bus) (modules.Module, error) { return new(leaderElectionModule).Create(bus) } -func (*leaderElectionModule) Create(bus modules.Bus) (modules.Module, error) { +func (*leaderElectionModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { m := &leaderElectionModule{} - if err := bus.RegisterModule(m); err != nil { - return nil, err - } - return m, nil -} -func (m *leaderElectionModule) Start() error { - // TODO(olshansky): Use persistence to create leader election module. - return nil -} + for _, option := range options { + option(m) + } -func (m *leaderElectionModule) Stop() error { - return nil + bus.RegisterModule(m) + return m, nil } func (m *leaderElectionModule) GetModuleName() string { return modules.LeaderElectionModuleName } -func (m *leaderElectionModule) SetBus(pocketBus modules.Bus) { - m.bus = pocketBus -} - -func (m *leaderElectionModule) GetBus() modules.Bus { - if m.bus == nil { - logger.Global.Fatal().Msg("PocketBus is not initialized") - } - return m.bus -} - func (m *leaderElectionModule) ElectNextLeader(message *typesCons.HotstuffMessage) (typesCons.NodeId, error) { nodeId, err := m.electNextLeaderDeterministicRoundRobin(message) if err != nil { diff --git a/consensus/module.go b/consensus/module.go index 4201a4a7e..dcb43f581 100644 --- a/consensus/module.go +++ b/consensus/module.go @@ -17,6 +17,7 @@ import ( coreTypes "github.com/pokt-network/pocket/shared/core/types" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" "google.golang.org/protobuf/types/known/anypb" ) @@ -30,7 +31,8 @@ var ( ) type consensusModule struct { - bus modules.Bus + base_modules.IntegratableModule + privateKey cryptoPocket.Ed25519PrivateKey consCfg *configs.ConsensusConfig @@ -90,6 +92,7 @@ type ConsensusDebugModule interface { func (m *consensusModule) SetHeight(height uint64) { m.height = height + m.publishNewHeightEvent(height) } func (m *consensusModule) SetRound(round uint64) { @@ -136,11 +139,11 @@ func (m *consensusModule) ClearLeaderMessagesPool() { m.clearMessagesPool() } -func Create(bus modules.Bus) (modules.Module, error) { - return new(consensusModule).Create(bus) +func Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(consensusModule).Create(bus, options...) } -func (*consensusModule) Create(bus modules.Bus) (modules.Module, error) { +func (*consensusModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { leaderElectionMod, err := leader_election.Create(bus) if err != nil { return nil, err @@ -179,10 +182,13 @@ func (*consensusModule) Create(bus modules.Bus) (modules.Module, error) { hotstuffMempool: make(map[typesCons.HotstuffStep]*hotstuffFIFOMempool), } - if err := bus.RegisterModule(m); err != nil { - return nil, err + + for _, option := range options { + option(m) } + bus.RegisterModule(m) + runtimeMgr := bus.GetRuntimeMgr() consensusCfg := runtimeMgr.GetConfig().Consensus @@ -259,15 +265,8 @@ func (m *consensusModule) GetModuleName() string { return modules.ConsensusModuleName } -func (m *consensusModule) GetBus() modules.Bus { - if m.bus == nil { - logger.Global.Fatal().Msg("PocketBus is not initialized") - } - return m.bus -} - func (m *consensusModule) SetBus(pocketBus modules.Bus) { - m.bus = pocketBus + m.IntegratableModule.SetBus(pocketBus) if m.paceMaker != nil { m.paceMaker.SetBus(pocketBus) } diff --git a/consensus/pacemaker/module.go b/consensus/pacemaker/module.go index 46d91c1bb..a7676e562 100644 --- a/consensus/pacemaker/module.go +++ b/consensus/pacemaker/module.go @@ -11,6 +11,7 @@ import ( "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/shared/codec" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" "google.golang.org/protobuf/types/known/anypb" ) @@ -44,7 +45,9 @@ type Pacemaker interface { } type pacemaker struct { - bus modules.Bus + base_modules.IntegratableModule + base_modules.InterruptableModule + pacemakerCfg *configs.PacemakerConfig stepCancelFunc context.CancelFunc @@ -56,20 +59,21 @@ type pacemaker struct { logPrefix string } -func CreatePacemaker(bus modules.Bus) (modules.Module, error) { - var m pacemaker - return m.Create(bus) +func CreatePacemaker(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(pacemaker).Create(bus, options...) } -func (*pacemaker) Create(bus modules.Bus) (modules.Module, error) { +func (*pacemaker) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { m := &pacemaker{ logPrefix: defaultLogPrefix, } - if err := bus.RegisterModule(m); err != nil { - return nil, err + for _, option := range options { + option(m) } + bus.RegisterModule(m) + runtimeMgr := bus.GetRuntimeMgr() cfg := runtimeMgr.GetConfig() @@ -88,25 +92,11 @@ func (m *pacemaker) Start() error { m.RestartTimer() return nil } -func (*pacemaker) Stop() error { - return nil -} func (*pacemaker) GetModuleName() string { return pacemakerModuleName } -func (m *pacemaker) SetBus(pocketBus modules.Bus) { - m.bus = pocketBus -} - -func (m *pacemaker) GetBus() modules.Bus { - if m.bus == nil { - log.Fatalf("PocketBus is not initialized") - } - return m.bus -} - func (m *pacemaker) SetLogPrefix(logPrefix string) { m.logPrefix = logPrefix } diff --git a/consensus/state_sync/module.go b/consensus/state_sync/module.go index 74bcf353b..4e45b06dd 100644 --- a/consensus/state_sync/module.go +++ b/consensus/state_sync/module.go @@ -55,20 +55,21 @@ type stateSync struct { logPrefix string } -func CreateStateSync(bus modules.Bus) (modules.Module, error) { - var m stateSync - return m.Create(bus) +func CreateStateSync(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(stateSync).Create(bus, options...) } -func (*stateSync) Create(bus modules.Bus) (modules.Module, error) { +func (*stateSync) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { m := &stateSync{ logPrefix: DefaultLogPrefix, } - if err := bus.RegisterModule(m); err != nil { - return nil, err + for _, option := range options { + option(m) } + bus.RegisterModule(m) + // when node is starting, it is in sync mode, as it might need to bootstrap to the latest state m.currentMode = Sync m.serverMode = false diff --git a/go.mod b/go.mod index a292ac7d7..35ec76c52 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/jackc/pgconn v1.13.0 github.com/jordanorelli/lexnum v0.0.0-20141216151731-460eeb125754 github.com/labstack/echo/v4 v4.9.1 + github.com/looplab/fsm v1.0.1 github.com/manifoldco/promptui v0.9.0 github.com/mitchellh/mapstructure v1.5.0 github.com/quasilyte/go-ruleguard/dsl v0.3.21 diff --git a/go.sum b/go.sum index ae3da1f69..c41006b56 100644 --- a/go.sum +++ b/go.sum @@ -341,6 +341,8 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/looplab/fsm v1.0.1 h1:OEW0ORrIx095N/6lgoGkFkotqH6s7vaFPsgjLAaF5QU= +github.com/looplab/fsm v1.0.1/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= diff --git a/logger/docs/CHANGELOG.md b/logger/docs/CHANGELOG.md index abba623a7..7e3a49617 100644 --- a/logger/docs/CHANGELOG.md +++ b/logger/docs/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.7] - 2023-02-17 + +- Module embeds `base_modules.IntegratableModule` and `base_modules.InterruptableModule` for DRYness + ## [0.0.0.6] - 2023-02-09 - `loggerModule` type-checking for `modules.Module` diff --git a/logger/module.go b/logger/module.go index 918b9ae75..2f7e26ebb 100644 --- a/logger/module.go +++ b/logger/module.go @@ -7,12 +7,16 @@ import ( "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" "github.com/rs/zerolog" ) var _ modules.Module = &loggerModule{} type loggerModule struct { + base_modules.IntegratableModule + base_modules.InterruptableModule + zerolog.Logger bus modules.Bus config *configs.LoggerConfig @@ -49,24 +53,27 @@ func init() { } } -func Create(bus modules.Bus) (modules.Module, error) { - return new(loggerModule).Create(bus) +func Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(loggerModule).Create(bus, options...) } func (*loggerModule) CreateLoggerForModule(moduleName string) modules.Logger { return Global.Logger.With().Str("module", moduleName).Logger() } -func (*loggerModule) Create(bus modules.Bus) (modules.Module, error) { +func (*loggerModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { runtimeMgr := bus.GetRuntimeMgr() cfg := runtimeMgr.GetConfig() m := &loggerModule{ config: cfg.Logger, } - if err := bus.RegisterModule(m); err != nil { - return nil, err + + for _, option := range options { + option(m) } + bus.RegisterModule(m) + Global.config = m.config Global.CreateLoggerForModule("global") @@ -95,25 +102,10 @@ func (m *loggerModule) Start() error { return nil } -func (m *loggerModule) Stop() error { - return nil -} - func (m *loggerModule) GetModuleName() string { return modules.LoggerModuleName } -func (m *loggerModule) SetBus(bus modules.Bus) { - m.bus = bus -} - -func (m *loggerModule) GetBus() modules.Bus { - if m.bus == nil { - m.Logger.Fatal().Msg("Bus is not initialized") - } - return m.bus -} - func (m *loggerModule) GetLogger() modules.Logger { return m.Logger } diff --git a/p2p/CHANGELOG.md b/p2p/CHANGELOG.md index ac5eeeb3f..7ac5febf4 100644 --- a/p2p/CHANGELOG.md +++ b/p2p/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.26] - 2023-02-17 + +- Modules embed `base_modules.IntegratableModule` and `base_modules.InterruptableModule` for DRYness +- Updated tests + ## [0.0.0.25] - 2023-02-09 - Updated logging initialization and passing to the network component instead of using the global logger diff --git a/p2p/event_handler.go b/p2p/event_handler.go new file mode 100644 index 000000000..4d920231f --- /dev/null +++ b/p2p/event_handler.go @@ -0,0 +1,10 @@ +package p2p + +import ( + "google.golang.org/protobuf/types/known/anypb" +) + +func (m *p2pModule) HandleEvent(event *anypb.Any) error { + // no-op (for now... PRs are already cooked) + return nil +} diff --git a/p2p/module.go b/p2p/module.go index c43a424b7..8a9985d9e 100644 --- a/p2p/module.go +++ b/p2p/module.go @@ -13,6 +13,7 @@ import ( cryptoPocket "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/base_modules" "github.com/pokt-network/pocket/telemetry" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" @@ -21,7 +22,7 @@ import ( var _ modules.P2PModule = &p2pModule{} type p2pModule struct { - bus modules.Bus + base_modules.IntegratableModule listener typesP2P.Transport address cryptoPocket.Address @@ -34,17 +35,15 @@ type p2pModule struct { injectedCurrentHeightProvider providers.CurrentHeightProvider } -func Create(bus modules.Bus) (modules.Module, error) { - return new(p2pModule).Create(bus) +func Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(p2pModule).Create(bus, options...) } // TODO(#429): need to define a better pattern for dependency injection. Currently we are probably limiting ourselves by having a common constructor `Create(bus modules.Bus) (modules.Module, error)` for all modules. func CreateWithProviders(bus modules.Bus, addrBookProvider providers.AddrBookProvider, currentHeightProvider providers.CurrentHeightProvider) (modules.Module, error) { log.Println("Creating network module") m := &p2pModule{} - if err := bus.RegisterModule(m); err != nil { - return nil, err - } + bus.RegisterModule(m) runtimeMgr := bus.GetRuntimeMgr() cfg := runtimeMgr.GetConfig() @@ -69,13 +68,16 @@ func CreateWithProviders(bus modules.Bus, addrBookProvider providers.AddrBookPro return m, nil } -func (*p2pModule) Create(bus modules.Bus) (modules.Module, error) { +func (*p2pModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { log.Println("Creating network module") m := &p2pModule{} - if err := bus.RegisterModule(m); err != nil { - return nil, err + + for _, option := range options { + option(m) } + bus.RegisterModule(m) + runtimeMgr := bus.GetRuntimeMgr() cfg := runtimeMgr.GetConfig() p2pCfg := cfg.P2P @@ -97,20 +99,6 @@ func (*p2pModule) Create(bus modules.Bus) (modules.Module, error) { return m, nil } -func (m *p2pModule) SetBus(bus modules.Bus) { - // INVESTIGATE: Can the code flow be modified to set the bus here? - // m.network.SetBus(m.GetBus()) - m.bus = bus -} - -func (m *p2pModule) GetBus() modules.Bus { - if m.bus == nil { - m.logger.Warn().Msg("PocketBus is not initialized") - return nil - } - return m.bus -} - func (m *p2pModule) GetModuleName() string { return modules.P2PModuleName } diff --git a/p2p/utils_test.go b/p2p/utils_test.go index 37fdc8dbb..dcab6c141 100644 --- a/p2p/utils_test.go +++ b/p2p/utils_test.go @@ -10,12 +10,11 @@ import ( "testing" "time" - types "github.com/pokt-network/pocket/runtime/configs/types" - "github.com/golang/mock/gomock" typesP2P "github.com/pokt-network/pocket/p2p/types" mocksP2P "github.com/pokt-network/pocket/p2p/types/mocks" "github.com/pokt-network/pocket/runtime/configs" + types "github.com/pokt-network/pocket/runtime/configs/types" "github.com/pokt-network/pocket/runtime/genesis" coreTypes "github.com/pokt-network/pocket/shared/core/types" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" @@ -150,10 +149,11 @@ func createMockBus(t *testing.T, runtimeMgr modules.RuntimeMgr) *mockModules.Moc ctrl := gomock.NewController(t) mockBus := mockModules.NewMockBus(ctrl) mockBus.EXPECT().GetRuntimeMgr().Return(runtimeMgr).AnyTimes() - mockBus.EXPECT().RegisterModule(gomock.Any()).DoAndReturn(func(m modules.Module) error { + mockBus.EXPECT().RegisterModule(gomock.Any()).DoAndReturn(func(m modules.Module) { m.SetBus(mockBus) - return nil }).AnyTimes() + mockModulesRegistry := mockModules.NewMockModulesRegistry(ctrl) + mockBus.EXPECT().GetModulesRegistry().Return(mockModulesRegistry).AnyTimes() mockBus.EXPECT().PublishEventToBus(gomock.Any()).AnyTimes() return mockBus } @@ -201,8 +201,7 @@ func prepareConsensusMock(t *testing.T, busMock *mockModules.MockBus) *mockModul consensusMock.EXPECT().GetBus().Return(busMock).AnyTimes() consensusMock.EXPECT().SetBus(busMock).AnyTimes() consensusMock.EXPECT().GetModuleName().Return(modules.ConsensusModuleName).AnyTimes() - err := busMock.RegisterModule(consensusMock) - require.NoError(t, err) + busMock.RegisterModule(consensusMock) return consensusMock } @@ -221,8 +220,7 @@ func preparePersistenceMock(t *testing.T, busMock *mockModules.MockBus, genesisS persistenceMock.EXPECT().GetBus().Return(busMock).AnyTimes() persistenceMock.EXPECT().SetBus(busMock).AnyTimes() persistenceMock.EXPECT().GetModuleName().Return(modules.PersistenceModuleName).AnyTimes() - err := busMock.RegisterModule(persistenceMock) - require.NoError(t, err) + busMock.RegisterModule(persistenceMock) return persistenceMock } @@ -241,8 +239,7 @@ func prepareTelemetryMock(t *testing.T, busMock *mockModules.MockBus, valId stri telemetryMock.EXPECT().GetModuleName().Return(modules.TelemetryModuleName).AnyTimes() telemetryMock.EXPECT().GetBus().Return(busMock).AnyTimes() telemetryMock.EXPECT().SetBus(busMock).AnyTimes() - err := busMock.RegisterModule(telemetryMock) - require.NoError(t, err) + busMock.RegisterModule(telemetryMock) return telemetryMock } diff --git a/persistence/docs/CHANGELOG.md b/persistence/docs/CHANGELOG.md index 8f790c2a6..378cf3b85 100644 --- a/persistence/docs/CHANGELOG.md +++ b/persistence/docs/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.36] - 2023-02-17 + +- Module now embeds `base_modules.IntegratableModule` for DRYness + ## [0.0.0.35] - 2023-02-15 - Add a few `nolint` comments to fix the code on main @@ -32,7 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.0.0.30] - 2023-02-04 -- Changed log lines to utilize new logger module. +- Changed log lines to utilize new logger module ## [0.0.0.29] - 2023-01-31 diff --git a/persistence/module.go b/persistence/module.go index 887fa8594..3f5f5faf2 100644 --- a/persistence/module.go +++ b/persistence/module.go @@ -11,6 +11,7 @@ import ( "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/runtime/genesis" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" ) var ( @@ -23,7 +24,8 @@ var ( // TODO: convert address and public key to string not bytes in all account and actor functions // TODO: remove address parameter from all pool operations type persistenceModule struct { - bus modules.Bus + base_modules.IntegratableModule + config *configs.PersistenceConfig genesisState *genesis.GenesisState @@ -37,18 +39,21 @@ type persistenceModule struct { writeContext *PostgresContext // only one write context is allowed at a time } -func Create(bus modules.Bus) (modules.Module, error) { - return new(persistenceModule).Create(bus) +func Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(persistenceModule).Create(bus, options...) } -func (*persistenceModule) Create(bus modules.Bus) (modules.Module, error) { +func (*persistenceModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { m := &persistenceModule{ writeContext: nil, } - if err := bus.RegisterModule(m); err != nil { - return nil, err + + for _, option := range options { + option(m) } + bus.RegisterModule(m) + runtimeMgr := bus.GetRuntimeMgr() persistenceCfg := runtimeMgr.GetConfig().Persistence @@ -117,17 +122,6 @@ func (m *persistenceModule) GetModuleName() string { return modules.PersistenceModuleName } -func (m *persistenceModule) SetBus(bus modules.Bus) { - m.bus = bus -} - -func (m *persistenceModule) GetBus() modules.Bus { - if m.bus == nil { - logger.Global.Fatal().Msg("PocketBus is not initialized") - } - return m.bus -} - func (m *persistenceModule) NewRWContext(height int64) (modules.PersistenceRWContext, error) { if m.writeContext != nil && m.writeContext.conn != nil && !m.writeContext.conn.IsClosed() { return nil, fmt.Errorf("write context already exists") diff --git a/rpc/doc/CHANGELOG.md b/rpc/doc/CHANGELOG.md index 869d12656..9c186294b 100644 --- a/rpc/doc/CHANGELOG.md +++ b/rpc/doc/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.11] - 2023-02-17 + +- Updated modules to embed `base_modules.IntegratableModule` and `base_modules.InterruptableModule` for DRYness + ## [0.0.0.10] - 2023-02-07 - Added GITHUB_WIKI tags where it was missing diff --git a/rpc/module.go b/rpc/module.go index c05b67b74..17537d70a 100644 --- a/rpc/module.go +++ b/rpc/module.go @@ -9,35 +9,41 @@ import ( "github.com/pokt-network/pocket/logger" "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" ) var _ modules.RPCModule = &rpcModule{} type rpcModule struct { - bus modules.Bus + base_modules.IntegratableModule + base_modules.InterruptableModule + logger modules.Logger config *configs.RPCConfig } -func Create(bus modules.Bus) (modules.Module, error) { - return new(rpcModule).Create(bus) +func Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(rpcModule).Create(bus, options...) } -func (*rpcModule) Create(bus modules.Bus) (modules.Module, error) { +func (*rpcModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { runtimeMgr := bus.GetRuntimeMgr() cfg := runtimeMgr.GetConfig() rpcCfg := cfg.RPC - rpcMod := modules.RPCModule(&rpcModule{ + m := modules.RPCModule(&rpcModule{ config: rpcCfg, }) if !rpcCfg.Enabled { - rpcMod = &noopRpcModule{} + m = &noopRpcModule{} } - if err := bus.RegisterModule(rpcMod); err != nil { - return nil, err + + for _, option := range options { + option(m) } - return rpcMod, nil + bus.RegisterModule(m) + + return m, nil } func (u *rpcModule) Start() error { @@ -46,21 +52,6 @@ func (u *rpcModule) Start() error { return nil } -func (u *rpcModule) Stop() error { - return nil -} - func (u *rpcModule) GetModuleName() string { return modules.RPCModuleName } - -func (u *rpcModule) SetBus(bus modules.Bus) { - u.bus = bus -} - -func (u *rpcModule) GetBus() modules.Bus { - if u.bus == nil { - u.logger.Fatal().Msg("Bus is not initialized") - } - return u.bus -} diff --git a/rpc/noop_module.go b/rpc/noop_module.go index daad5b40c..1c7401b80 100644 --- a/rpc/noop_module.go +++ b/rpc/noop_module.go @@ -4,31 +4,25 @@ import ( "log" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" ) var _ modules.RPCModule = &noopRpcModule{} -type noopRpcModule struct{} +type noopRpcModule struct { + base_modules.IntegratableModule + base_modules.InterruptableModule +} func (m *noopRpcModule) GetModuleName() string { return "noop_rpc_module" } -func (m *noopRpcModule) Create(bus modules.Bus) (modules.Module, error) { +func (m *noopRpcModule) Create(bus modules.Bus, _ ...modules.ModuleOption) (modules.Module, error) { return &rpcModule{}, nil } -func (m *noopRpcModule) SetBus(_ modules.Bus) {} - -func (m *noopRpcModule) GetBus() modules.Bus { - return nil -} - func (m *noopRpcModule) Start() error { log.Println("[WARN] RPC server: OFFLINE") return nil } - -func (m *noopRpcModule) Stop() error { - return nil -} diff --git a/rpc/server.go b/rpc/server.go index 087b3ea06..696c78a58 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -7,10 +7,11 @@ import ( "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" ) type rpcServer struct { - bus modules.Bus + base_modules.IntegratableModule logger modules.Logger } @@ -66,11 +67,3 @@ func (s *rpcServer) StartRPC(port string, timeout uint64, logger *modules.Logger s.logger.Fatal().Err(err).Msg("RPC server failed to start") } } - -func (s *rpcServer) SetBus(bus modules.Bus) { - s.bus = bus -} - -func (s *rpcServer) GetBus() modules.Bus { - return s.bus -} diff --git a/runtime/bus.go b/runtime/bus.go index 3289ba9f2..cea228733 100644 --- a/runtime/bus.go +++ b/runtime/bus.go @@ -1,9 +1,9 @@ package runtime import ( - "log" "sync" + "github.com/pokt-network/pocket/logger" "github.com/pokt-network/pocket/runtime/defaults" "github.com/pokt-network/pocket/shared/messaging" "github.com/pokt-network/pocket/shared/modules" @@ -21,7 +21,7 @@ type bus struct { // Node events channel modules.EventsChannel - modulesMap map[string]modules.Module + modulesRegistry modules.ModulesRegistry runtimeMgr modules.RuntimeMgr } @@ -34,17 +34,20 @@ func (b *bus) Create(runtimeMgr modules.RuntimeMgr) (modules.Bus, error) { bus := &bus{ channel: make(modules.EventsChannel, defaults.DefaultBusBufferSize), - runtimeMgr: runtimeMgr, - modulesMap: make(map[string]modules.Module), + runtimeMgr: runtimeMgr, + modulesRegistry: NewModulesRegistry(), } return bus, nil } -func (m *bus) RegisterModule(module modules.Module) error { +func (m *bus) GetModulesRegistry() modules.ModulesRegistry { + return m.modulesRegistry +} + +func (m *bus) RegisterModule(module modules.Module) { module.SetBus(m) - m.modulesMap[module.GetModuleName()] = module - return nil + m.modulesRegistry.RegisterModule(module) } func (m *bus) PublishEventToBus(e *messaging.PocketEnvelope) { @@ -60,75 +63,70 @@ func (m *bus) GetEventBus() modules.EventsChannel { return m.channel } +func (m *bus) GetRuntimeMgr() modules.RuntimeMgr { + return m.runtimeMgr +} + func (m *bus) GetPersistenceModule() modules.PersistenceModule { - if mod, ok := m.modulesMap[modules.PersistenceModuleName]; ok { - return mod.(modules.PersistenceModule) - } - log.Fatalf("%s", ErrModuleNotRegistered("persistence")) - return nil + return getModuleFromRegistry[modules.PersistenceModule](m, modules.PersistenceModuleName) } func (m *bus) GetP2PModule() modules.P2PModule { - if mod, ok := m.modulesMap[modules.P2PModuleName]; ok { - return mod.(modules.P2PModule) - } - log.Fatalf("%s", ErrModuleNotRegistered("P2P")) - return nil + return getModuleFromRegistry[modules.P2PModule](m, modules.P2PModuleName) } func (m *bus) GetUtilityModule() modules.UtilityModule { - if mod, ok := m.modulesMap[modules.UtilityModuleName]; ok { - return mod.(modules.UtilityModule) - } - log.Fatalf("%s", ErrModuleNotRegistered(modules.UtilityModuleName)) - return nil + return getModuleFromRegistry[modules.UtilityModule](m, modules.UtilityModuleName) } func (m *bus) GetConsensusModule() modules.ConsensusModule { - if mod, ok := m.modulesMap[modules.ConsensusModuleName]; ok { - return mod.(modules.ConsensusModule) - } - log.Fatalf("%s", ErrModuleNotRegistered(modules.ConsensusModuleName)) - return nil + return getModuleFromRegistry[modules.ConsensusModule](m, modules.ConsensusModuleName) } func (m *bus) GetTelemetryModule() modules.TelemetryModule { for _, moduleName := range telemetry.ImplementationNames { - telemetryMod, ok := m.modulesMap[moduleName] - if ok { + telemetryMod, err := m.modulesRegistry.GetModule(moduleName) + if err == nil { return telemetryMod.(modules.TelemetryModule) } } telemetryWarnOnce.Do(func() { - log.Printf("[WARNING] telemetry module not found, creating a default noop telemetry module instead") + logger.Global.Logger.Warn(). + Str("module", modules.TelemetryModuleName). + Msg("module not found, creating a default noop module instead") }) // this should happen only if called from the client noopModule, err := telemetry.CreateNoopTelemetryModule(m) if err != nil { - log.Fatalf("failed to create noop telemetry module: %v", err) - } - if err := m.RegisterModule(noopModule); err != nil { - log.Fatalf("[ERROR] Failed to register telemetry module: %v", err.Error()) + logger.Global.Logger.Fatal(). + Err(err). + Str("module", modules.TelemetryModuleName). + Msg("failed to create noop telemetry module") } + m.RegisterModule(noopModule) return noopModule.(modules.TelemetryModule) } func (m *bus) GetLoggerModule() modules.LoggerModule { - if mod, ok := m.modulesMap[modules.LoggerModuleName]; ok { - return mod.(modules.LoggerModule) - } - log.Fatalf("%s", ErrModuleNotRegistered(modules.LoggerModuleName)) - return nil + return getModuleFromRegistry[modules.LoggerModule](m, modules.LoggerModuleName) } func (m *bus) GetRPCModule() modules.RPCModule { - if mod, ok := m.modulesMap[modules.RPCModuleName]; ok { - return mod.(modules.RPCModule) - } - log.Fatalf("%s", ErrModuleNotRegistered(modules.RPCModuleName)) - return nil + return getModuleFromRegistry[modules.RPCModule](m, modules.RPCModuleName) } -func (m *bus) GetRuntimeMgr() modules.RuntimeMgr { - return m.runtimeMgr +func (m *bus) GetStateMachineModule() modules.StateMachineModule { + return getModuleFromRegistry[modules.StateMachineModule](m, modules.StateMachineModuleName) +} + +// getModuleFromRegistry is a helper function to get a module from the registry that handles errors and casting via generics +func getModuleFromRegistry[T modules.Module](m *bus, moduleName string) T { + mod, err := m.modulesRegistry.GetModule(moduleName) + if err != nil { + logger.Global.Logger.Fatal(). + Err(err). + Str("module", moduleName). + Msg("failed to get module from modulesRegistry") + } + return mod.(T) } diff --git a/runtime/docs/CHANGELOG.md b/runtime/docs/CHANGELOG.md index de6062661..8dbb058e7 100644 --- a/runtime/docs/CHANGELOG.md +++ b/runtime/docs/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.19] - 2023-02-17 + +- Introduced `modules.ModulesRegistry` for better separation of concerns +- Added `StateMachineModule` accessors +- `Manager` embeds `base_modules.IntegratableModule` for DRYness + ## [0.0.0.18] - 2023-02-16 - Added `IsProcessRunningInsideKubernetes` and centralized `GetEnv` so that it can be used across the board diff --git a/runtime/errors.go b/runtime/errors.go index 7ec9e1cc8..6750fd52a 100644 --- a/runtime/errors.go +++ b/runtime/errors.go @@ -4,7 +4,7 @@ import ( "fmt" ) -const ModuleNotRegisteredError = "module %s not found, did you call bus.RegisterModule() ?" +const ModuleNotRegisteredError = "module %s not found, did you call ModulesRegistry.RegisterModule() ?" func ErrModuleNotRegistered(moduleName string) error { return fmt.Errorf(ModuleNotRegisteredError, moduleName) diff --git a/runtime/manager.go b/runtime/manager.go index f08a52212..e1e41e4f7 100644 --- a/runtime/manager.go +++ b/runtime/manager.go @@ -16,17 +16,19 @@ import ( "github.com/pokt-network/pocket/runtime/genesis" cryptoPocket "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" "github.com/spf13/viper" ) var _ modules.RuntimeMgr = &Manager{} type Manager struct { + base_modules.IntegratableModule + config *configs.Config genesisState *genesis.GenesisState clock clock.Clock - bus modules.Bus } func NewManager(config *configs.Config, gen *genesis.GenesisState, options ...func(*Manager)) *Manager { @@ -39,7 +41,7 @@ func NewManager(config *configs.Config, gen *genesis.GenesisState, options ...fu mgr.config = config mgr.genesisState = gen mgr.clock = clock.New() - mgr.bus = bus + mgr.SetBus(bus) for _, o := range options { o(mgr) @@ -77,10 +79,6 @@ func (m *Manager) GetGenesis() *genesis.GenesisState { return m.genesisState } -func (b *Manager) GetBus() modules.Bus { - return b.bus -} - func (m *Manager) GetClock() clock.Clock { return m.clock } diff --git a/runtime/modules_registry.go b/runtime/modules_registry.go new file mode 100644 index 000000000..85b9c2c75 --- /dev/null +++ b/runtime/modules_registry.go @@ -0,0 +1,35 @@ +package runtime + +import ( + "sync" + + "github.com/pokt-network/pocket/shared/modules" +) + +var _ modules.ModulesRegistry = &modulesRegistry{} + +type modulesRegistry struct { + m sync.Mutex + registry map[string]modules.Module +} + +func NewModulesRegistry() *modulesRegistry { + return &modulesRegistry{ + registry: make(map[string]modules.Module), + } +} + +func (m *modulesRegistry) RegisterModule(module modules.Module) { + m.m.Lock() + defer m.m.Unlock() + m.registry[module.GetModuleName()] = module +} + +func (m *modulesRegistry) GetModule(moduleName string) (modules.Module, error) { + m.m.Lock() + defer m.m.Unlock() + if mod, ok := m.registry[moduleName]; ok { + return mod, nil + } + return nil, ErrModuleNotRegistered(moduleName) +} diff --git a/shared/CHANGELOG.md b/shared/CHANGELOG.md index 35c3b664f..0718222bc 100644 --- a/shared/CHANGELOG.md +++ b/shared/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.27] - 2023-02-17 + +- Added events `ConsensusNewHeightEvent` and `StateMachineTransitionEvent` +- Introduced `BaseInterruptableModule` and `IntegratableModule` to reduce repetition and boilerpate code (DRYness) +- Added `ModulesRegistry` and `StateMachineModule` accessors and interfaces +- Introduced generic `ModuleOption` pattern to fine tune modules behaviour +- Added `StateMachine` to the `node` initialization + ## [0.0.0.26] - 2023-02-16 - Added `FetchValidatorPrivateKeys` function since it is going to be used by the `debug-client` and also by the upcoming `cluster-manager` [#490](https://github.com/pokt-network/pocket/issues/490) diff --git a/shared/README.md b/shared/README.md index 58938f471..992ed5e50 100644 --- a/shared/README.md +++ b/shared/README.md @@ -18,11 +18,12 @@ ## Code Structure ```bash -shared # [to-be-refactored] All of this is bound to change -├── codec # App wide encoding (currently protobuf) -├── config # Utilities to load and verify Node configurations -├── crypto # Shared crypto utilities specific to Pocket -├── modules # Interfaces to the core Pocket modules +shared # [to-be-refactored] All of this is bound to change +├── codec # App wide encoding (currently protobuf) +├── config # Utilities to load and verify Node configurations +├── crypto # Shared crypto utilities specific to Pocket +├── modules # Interfaces to the core Pocket modules +| ├── base_modules # Base modules that are meant to be embed into the module structs in order to reduce boilerplate code | ├── module.go | ├── bus_module.go | ├── consensus_module.go @@ -30,10 +31,10 @@ shared # [to-be-refactored] All of this is bound to change | ├── utility_module.go | ├── persistence_module.go | ├── telemetry_module.go -| ├── types.go # Shared interfaces -├── tests # Cross-module and shared testing_artifacts (to be refactored to make testing more modular) -├── node.go # The main entrypoint to the Pocket Node -├── bus.go # Implementation of the Bus module +| ├── types.go # Shared interfaces +├── tests # Cross-module and shared testing_artifacts (to be refactored to make testing more modular) +├── node.go # The main entrypoint to the Pocket Node +├── bus.go # Implementation of the Bus module ``` ## High Level Architecture diff --git a/shared/core/types/fsm_events.go b/shared/core/types/fsm_events.go new file mode 100644 index 000000000..9321f1eb8 --- /dev/null +++ b/shared/core/types/fsm_events.go @@ -0,0 +1,13 @@ +package types + +type StateMachineEvent string + +const ( + StateMachineEvent_Start StateMachineEvent = "Start" + + StateMachineEvent_P2P_IsBootstrapped StateMachineEvent = "P2P_IsBootstrapped" + + StateMachineEvent_Consensus_IsUnsynched StateMachineEvent = "Consensus_IsUnsynched" + StateMachineEvent_Consensus_IsSyncing StateMachineEvent = "Consensus_IsSyncing" + StateMachineEvent_Consensus_IsCaughtUp StateMachineEvent = "Consensus_IsCaughtUp" +) diff --git a/shared/core/types/fsm_states.go b/shared/core/types/fsm_states.go new file mode 100644 index 000000000..adc8be077 --- /dev/null +++ b/shared/core/types/fsm_states.go @@ -0,0 +1,14 @@ +package types + +type StateMachineState string + +const ( + StateMachineState_Stopped StateMachineState = "Stopped" + + StateMachineState_P2P_Bootstrapping StateMachineState = "P2P_Bootstrapping" + StateMachineState_P2P_Bootstrapped StateMachineState = "P2P_Bootstrapped" + + StateMachineState_Consensus_Unsynched StateMachineState = "Consensus_Unsynched" + StateMachineState_Consensus_SyncMode StateMachineState = "Consensus_SyncMode" + StateMachineState_Consensus_Synced StateMachineState = "Consensus_Synced" +) diff --git a/shared/messaging/events.go b/shared/messaging/events.go index 1ccfdf0c0..ecaf6c0d1 100644 --- a/shared/messaging/events.go +++ b/shared/messaging/events.go @@ -1,5 +1,7 @@ package messaging const ( - NodeStartedEventType = "pocket.NodeStartedEvent" + NodeStartedEventType = "pocket.NodeStartedEvent" + ConsensusNewHeightEventType = "pocket.ConsensusNewHeightEvent" + StateMachineTransitionEventType = "pocket.StateMachineTransitionEvent" ) diff --git a/shared/messaging/proto/events.proto b/shared/messaging/proto/events.proto index 22356bcc7..13931be93 100644 --- a/shared/messaging/proto/events.proto +++ b/shared/messaging/proto/events.proto @@ -5,3 +5,13 @@ package pocket; option go_package = "github.com/pokt-network/pocket/shared/messaging"; message NodeStartedEvent {} + +message ConsensusNewHeightEvent { + uint64 height = 1; +} + +message StateMachineTransitionEvent { + string event = 1; + string previous_state = 2; + string new_state = 3; +} diff --git a/shared/modules/base_modules/integratable_module.go b/shared/modules/base_modules/integratable_module.go new file mode 100644 index 000000000..4b24254c4 --- /dev/null +++ b/shared/modules/base_modules/integratable_module.go @@ -0,0 +1,25 @@ +package base_modules + +import "github.com/pokt-network/pocket/shared/modules" + +var _ modules.IntegratableModule = &IntegratableModule{} + +// IntegratableModule is a base struct that is meant to be embedded in module structs that implement the interface `modules.IntegratableModule`. +// +// It provides the basic logic for the `SetBus` and `GetBus` methods and allows the implementer to reduce boilerplate code keeping the code +// DRY (Don't Repeat Yourself) while preserving the ability to override the methods if needed. +type IntegratableModule struct { + bus modules.Bus +} + +func NewIntegratableModule(bus modules.Bus) *IntegratableModule { + return &IntegratableModule{bus: bus} +} + +func (m *IntegratableModule) GetBus() modules.Bus { + return m.bus +} + +func (m *IntegratableModule) SetBus(bus modules.Bus) { + m.bus = bus +} diff --git a/shared/modules/base_modules/interruptable_module.go b/shared/modules/base_modules/interruptable_module.go new file mode 100644 index 000000000..9ad46f793 --- /dev/null +++ b/shared/modules/base_modules/interruptable_module.go @@ -0,0 +1,19 @@ +package base_modules + +import "github.com/pokt-network/pocket/shared/modules" + +var _ modules.InterruptableModule = &InterruptableModule{} + +// InterruptableModule is a noop implementation of the InterruptableModule interface. +// +// It is useful for modules that do not need any particular logic to be executed when started or stopped. +// In these situations, just embed this struct into the module struct. +type InterruptableModule struct{} + +func (*InterruptableModule) Start() error { + return nil +} + +func (*InterruptableModule) Stop() error { + return nil +} diff --git a/shared/modules/bus_module.go b/shared/modules/bus_module.go index d71e06e84..62f5f3b74 100644 --- a/shared/modules/bus_module.go +++ b/shared/modules/bus_module.go @@ -19,7 +19,9 @@ type Bus interface { GetBusEvent() *messaging.PocketEnvelope GetEventBus() EventsChannel - RegisterModule(module Module) error + // Dependency Injection / Service Discovery + GetModulesRegistry() ModulesRegistry + RegisterModule(module Module) // Pocket modules GetPersistenceModule() PersistenceModule @@ -29,6 +31,7 @@ type Bus interface { GetTelemetryModule() TelemetryModule GetLoggerModule() LoggerModule GetRPCModule() RPCModule + GetStateMachineModule() StateMachineModule // Runtime GetRuntimeMgr() RuntimeMgr diff --git a/shared/modules/doc/README.md b/shared/modules/doc/README.md index d7fdddea1..6483dfc8f 100644 --- a/shared/modules/doc/README.md +++ b/shared/modules/doc/README.md @@ -43,7 +43,12 @@ TODO(#235): Update once runtime configs are implemented #### Create the module -Module creation uses a typical constructor pattern signature `Create(bus modules.Bus) (modules.Module, error)` +TODO(#509): Add more detail and examples to this documentation. + +Module creation uses a typical constructor pattern signature `Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error)` + +Where `options ...modules.ModuleOption` is an optional variadic argument that allows for the passing of options to the module. +This is useful to configure the module at creation time and it's usually used during prototyping and in "sub-modules" that don't have a specific configuration file and where adding it would add unnecessary complexity and overhead. If a module has a lot of `ModuleOption`s, at that point a configuration file might be advisable. Currently, module creation is not embedded or enforced in the interface to prevent the initializer from having to use clunky creation syntax -> `modPackage.new(module).Create(bus modules.Bus)` rather `modPackage.Create(bus modules.Bus)` diff --git a/shared/modules/module.go b/shared/modules/module.go index 7e9ef989e..f390c5b66 100644 --- a/shared/modules/module.go +++ b/shared/modules/module.go @@ -20,9 +20,38 @@ type InterruptableModule interface { Stop() error } +// TODO(#509): improve the documentation for this and other interfaces/functions +// ModuleOption is a function that configures a module when it is created. +// It uses a widely used pattern in Go called functional options. +// See https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis +// for more information. +// +// It is used to provide optional parameters to the module constructor for all the cases +// where there is no configuration, which is often the case for sub-modules that are used +// and configured at runtime. +// +// It accepts an InitializableModule as a parameter, because in order to create a module with these options, +// at a minimum, the module must implement the InitializableModule interface. +// +// Example: +// +// func WithFoo(foo string) ModuleOption { +// return func(m InitializableModule) { +// m.(*MyModule).foo = foo +// } +// } +// +// func NewMyModule(options ...ModuleOption) (Module, error) { +// m := &MyModule{} +// for _, option := range options { +// option(m) +// } +// return m, nil +// } +type ModuleOption func(InitializableModule) type InitializableModule interface { GetModuleName() string - Create(bus Bus) (Module, error) + Create(bus Bus, options ...ModuleOption) (Module, error) } type KeyholderModule interface { diff --git a/shared/modules/modules_registry_module.go b/shared/modules/modules_registry_module.go new file mode 100644 index 000000000..67f2ef340 --- /dev/null +++ b/shared/modules/modules_registry_module.go @@ -0,0 +1,10 @@ +package modules + +//go:generate mockgen -source=$GOFILE -destination=./mocks/modules_registry_mock.go -aux_files=github.com/pokt-network/pocket/shared/modules=module.go + +type ModulesRegistry interface { + // RegisterModule registers a Module with the ModuleRegistry + RegisterModule(module Module) + // GetModule returns a Module by name or nil if not found in the ModuleRegistry + GetModule(moduleName string) (Module, error) +} diff --git a/shared/modules/p2p_module.go b/shared/modules/p2p_module.go index aea86dc2d..93ea6e63c 100644 --- a/shared/modules/p2p_module.go +++ b/shared/modules/p2p_module.go @@ -21,6 +21,9 @@ type P2PModule interface { // A direct asynchronous Send(addr cryptoPocket.Address, msg *anypb.Any) error + // HandleEvent is used to react to events that occur inside the application + HandleEvent(*anypb.Any) error + // CONSIDERATION: The P2P module currently does implement a synchronous "request-response" pattern // for core business logic between nodes. Rather, all communication is done // asynchronously via a "fire-and-forget" pattern using `Send` and `Broadcast`. diff --git a/shared/modules/state_machine_module.go b/shared/modules/state_machine_module.go new file mode 100644 index 000000000..5e7de8fc6 --- /dev/null +++ b/shared/modules/state_machine_module.go @@ -0,0 +1,15 @@ +package modules + +//go:generate mockgen -source=$GOFILE -destination=./mocks/state_machine_module_mock.go -aux_files=github.com/pokt-network/pocket/shared/modules=module.go + +import ( + coreTypes "github.com/pokt-network/pocket/shared/core/types" +) + +const StateMachineModuleName = "state_machine" + +type StateMachineModule interface { + Module + + SendEvent(event coreTypes.StateMachineEvent, args ...any) error +} diff --git a/shared/node.go b/shared/node.go index 4af232ea9..4f6a2a238 100644 --- a/shared/node.go +++ b/shared/node.go @@ -6,9 +6,11 @@ import ( "github.com/pokt-network/pocket/p2p" "github.com/pokt-network/pocket/persistence" "github.com/pokt-network/pocket/rpc" + coreTypes "github.com/pokt-network/pocket/shared/core/types" cryptoPocket "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/state_machine" "github.com/pokt-network/pocket/telemetry" "github.com/pokt-network/pocket/utility" ) @@ -26,12 +28,13 @@ func NewNodeWithP2PAddress(address cryptoPocket.Address) *Node { return &Node{p2pAddress: address} } -func CreateNode(bus modules.Bus) (modules.Module, error) { - return new(Node).Create(bus) +func CreateNode(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(Node).Create(bus, options...) } -func (m *Node) Create(bus modules.Bus) (modules.Module, error) { - for _, mod := range []func(modules.Bus) (modules.Module, error){ +func (m *Node) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + for _, mod := range []func(modules.Bus, ...modules.ModuleOption) (modules.Module, error){ + state_machine.Create, persistence.Create, utility.Create, consensus.Create, @@ -61,6 +64,10 @@ func (node *Node) Start() error { // IMPORTANT: Order of module startup here matters + if err := node.GetBus().GetStateMachineModule().Start(); err != nil { + return err + } + if err := node.GetBus().GetTelemetryModule().Start(); err != nil { return err } @@ -124,6 +131,9 @@ func (node *Node) handleEvent(message *messaging.PocketEnvelope) error { switch contentType { case messaging.NodeStartedEventType: logger.Global.Info().Msg("Received NodeStartedEvent") + if err := node.GetBus().GetStateMachineModule().SendEvent(coreTypes.StateMachineEvent_Start); err != nil { + return err + } case consensus.HotstuffMessageContentType: return node.GetBus().GetConsensusModule().HandleMessage(message.Content) case consensus.StateSyncMessageContentType: diff --git a/state_machine/docs/CHANGELOG.md b/state_machine/docs/CHANGELOG.md new file mode 100644 index 000000000..94b74f826 --- /dev/null +++ b/state_machine/docs/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +All notable changes to this module will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.0.0.1] - 2023-02-17 + +- Introduced this `CHANGELOG.md` and `README.md` +- Added `StateMachineModule` implementation with a POC of the finite state machine that will be used to manage the node lifecycle +- Added `StateMachine` diagram generator (linked in README.md) +- Integrated the `StateMachine` with the `bus` to propagate `StateMachineTransitionEvent` events whenever they occur + + diff --git a/state_machine/docs/README.md b/state_machine/docs/README.md new file mode 100644 index 000000000..200623949 --- /dev/null +++ b/state_machine/docs/README.md @@ -0,0 +1,64 @@ +# State Machine Module + +> ⚠️ Work in progress ⚠️ - At the time of writing this document, the architecture is still being defined. +> +> This is pretty much a POC at the moment. + +This document outlines the purpose of this module, its components and how they all interact with the other modules. + +## Contents +- [Overview](#overview) +- [Code Structure](#code-structure) +- [High Level Architecture](#high-level-architecture) + - [FSM primer](#fsm-primer) +- [Current State Machine Definition](#current-state-machine-definition) + +## Overview + +The `StateMachine` module implements a FSM (Finite State Machine) that is responsible for managing the node lifecycle since its internal behaviour can be different depending on certain conditions that are used to determine the current state. + +In a nutshell: The FSM guarantees that the node is always in one specific state and verifies state transition (i.e. edges) to/from valid states (i.e. vertices). + +## Code Structure + +```bash +├── docs +│ ├── CHANGELOG.md # Changelog +│ ├── README.md # You are here +│ └── state-machine.diagram.md # State machine diagram (generated by visualizer/main.go) +├── fsm.go # Finite State Machine definition (events, states, transitions) +├── module.go # Implementation of the StateMachine module +└── visualizer + └── main.go # State machine diagram generator +``` + +## High Level Architecture + +High-level implementation details: + +- The [github.com/looplab/fsm](https://github.com/looplab/fsm) library is used to implement the FSM +- Pocket builds a wrapper around `looplab/fsm` to integrate with the other modules +- The `StateMachineModule` can be accessed via the `bus` from any other `IntegratableModule` +- State machine transitions emit `StateMachineTransitionEvent` events that subscribed pocket modules can listen to +- The `node` has a central [event handler](../../shared/node.go) for events that fan-out event handling to the relevant modules during state transitions + +### FSM primer + +The FSM has a declarative definition of an initial state and a set of transitions that have an `Event`, `Source` states and a single `Destination` state. + +These are the main building blocks: + +- **Event**: An event is a string that represents an action that can trigger a transition. For example, the event `start` can be used to trigger a transition from the `stopped` state to the `starting` state. +- **State**: A state is a string that represents a state that the FSM can be in. For example, the state `stopped` can be used to represent a state where the node is not running. +- **Callback**: A callback is a function that is called when a transition occurs. For example, a callback can be used to log the transition or to perform some other action. Various types of callbacks essentially drive behaviour **WHEN** they are called and help build more complex behaviours like transition cancelling, etc. See the core FSM library documentation for more details. + +## Current State Machine Definition + +A diagram of the current state machine definition can be found [here](state-machine.diagram.md) +If you make any changes to it, you can re-generate it via: + +```bash +make generate_node_state_machine_diagram +``` + + diff --git a/state_machine/docs/state-machine.diagram.md b/state_machine/docs/state-machine.diagram.md new file mode 100644 index 000000000..651f4b20b --- /dev/null +++ b/state_machine/docs/state-machine.diagram.md @@ -0,0 +1,14 @@ +# Node Finite State Machine + +The following diagram displays the various states and events that govern the functionality of the node. + +```mermaid +stateDiagram-v2 + [*] --> Stopped + Consensus_SyncMode --> Consensus_Synced: Consensus_IsCaughtUp + Consensus_Unsynched --> Consensus_SyncMode: Consensus_IsSyncing + P2P_Bootstrapped --> Consensus_Synced: Consensus_IsCaughtUp + P2P_Bootstrapped --> Consensus_Unsynched: Consensus_IsUnsynched + P2P_Bootstrapping --> P2P_Bootstrapped: P2P_IsBootstrapped + Stopped --> P2P_Bootstrapping: Start +``` \ No newline at end of file diff --git a/state_machine/fsm.go b/state_machine/fsm.go new file mode 100644 index 000000000..df6051eb4 --- /dev/null +++ b/state_machine/fsm.go @@ -0,0 +1,67 @@ +package state_machine + +import ( + "github.com/looplab/fsm" + + coreTypes "github.com/pokt-network/pocket/shared/core/types" +) + +// NewNodeFSM returns a KISS Finite State Machine that is meant to mimick the various "states" of the node. +// +// The current set of states and events captures a limited subset of state sync and P2P bootstrapping-related events. +// More states & events in any of the modules supported should be added and documented here. +func NewNodeFSM(callbacks *fsm.Callbacks, options ...func(*fsm.FSM)) *fsm.FSM { + var cb = fsm.Callbacks{} + if callbacks != nil { + cb = *callbacks + } + + stateMachine := fsm.NewFSM( + string(coreTypes.StateMachineState_Stopped), + fsm.Events{ + { + Name: string(coreTypes.StateMachineEvent_Start), + Src: []string{ + string(coreTypes.StateMachineState_Stopped), + }, + Dst: string(coreTypes.StateMachineState_P2P_Bootstrapping), + }, + { + Name: string(coreTypes.StateMachineEvent_P2P_IsBootstrapped), + Src: []string{ + string(coreTypes.StateMachineState_P2P_Bootstrapping), + }, + Dst: string(coreTypes.StateMachineState_P2P_Bootstrapped), + }, + { + Name: string(coreTypes.StateMachineEvent_Consensus_IsUnsynched), + Src: []string{ + string(coreTypes.StateMachineState_P2P_Bootstrapped), + }, + Dst: string(coreTypes.StateMachineState_Consensus_Unsynched), + }, + { + Name: string(coreTypes.StateMachineEvent_Consensus_IsSyncing), + Src: []string{ + string(coreTypes.StateMachineState_Consensus_Unsynched), + }, + Dst: string(coreTypes.StateMachineState_Consensus_SyncMode), + }, + { + Name: string(coreTypes.StateMachineEvent_Consensus_IsCaughtUp), + Src: []string{ + string(coreTypes.StateMachineState_P2P_Bootstrapped), + string(coreTypes.StateMachineState_Consensus_SyncMode), + }, + Dst: string(coreTypes.StateMachineState_Consensus_Synced), + }, + }, + cb, + ) + + for _, option := range options { + option(stateMachine) + } + + return stateMachine +} diff --git a/state_machine/module.go b/state_machine/module.go new file mode 100644 index 000000000..d8410b3f0 --- /dev/null +++ b/state_machine/module.go @@ -0,0 +1,78 @@ +package state_machine + +import ( + "context" + + "github.com/looplab/fsm" + "github.com/pokt-network/pocket/logger" + coreTypes "github.com/pokt-network/pocket/shared/core/types" + "github.com/pokt-network/pocket/shared/messaging" + "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" +) + +var _ modules.StateMachineModule = &stateMachineModule{} + +type stateMachineModule struct { + base_modules.IntegratableModule + base_modules.InterruptableModule + + *fsm.FSM + logger modules.Logger +} + +func Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(stateMachineModule).Create(bus, options...) +} + +func (*stateMachineModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + m := &stateMachineModule{ + logger: logger.Global.CreateLoggerForModule(modules.StateMachineModuleName), + } + + m.FSM = NewNodeFSM(&fsm.Callbacks{ + "enter_state": func(_ context.Context, e *fsm.Event) { + m.logger.Info(). + Str("event", e.Event). + Str("sourceState", e.Src). + Msgf("entering state %s", e.Dst) + + newStateMachineTransitionEvent, err := messaging.PackMessage(&messaging.StateMachineTransitionEvent{ + Event: e.Event, + PreviousState: e.Src, + NewState: e.Dst, + }) + if err != nil { + m.logger.Fatal().Err(err).Msg("failed to pack state machine transition event") + } + + bus.PublishEventToBus(newStateMachineTransitionEvent) + }, + }) + + for _, option := range options { + option(m) + } + + bus.RegisterModule(m) + + return m, nil +} + +func (m *stateMachineModule) GetModuleName() string { + return modules.StateMachineModuleName +} + +func (m *stateMachineModule) SendEvent(event coreTypes.StateMachineEvent, args ...any) error { + return m.Event(context.TODO(), string(event), args) +} + +// options + +func WithCustomStateMachine(stateMachine *fsm.FSM) modules.ModuleOption { + return func(m modules.InitializableModule) { + if m, ok := m.(*stateMachineModule); ok { + m.FSM = stateMachine + } + } +} diff --git a/state_machine/visualizer/main.go b/state_machine/visualizer/main.go new file mode 100644 index 000000000..78824db40 --- /dev/null +++ b/state_machine/visualizer/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "os" + + "github.com/looplab/fsm" + "github.com/pokt-network/pocket/state_machine" +) + +func main() { + stateMachine := state_machine.NewNodeFSM(nil) + + mermaidStateDiagram, err := fsm.VisualizeForMermaidWithGraphType(stateMachine, fsm.StateDiagram) + if err != nil { + panic(err) + } + + header := "# Node Finite State Machine\n\nThe following diagram displays the various states and events that govern the functionality of the node.\n\n```mermaid\n" + footer := "```" + if err := os.WriteFile("state_machine/docs/state-machine.diagram.md", []byte(header+mermaidStateDiagram+footer), 0o600); err != nil { + panic(err) + } +} diff --git a/telemetry/CHANGELOG.md b/telemetry/CHANGELOG.md index 6b4dfe9b2..b3c005fc8 100644 --- a/telemetry/CHANGELOG.md +++ b/telemetry/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.8] - 2023-02-17 + +- Modules embed `base_modules.IntegratableModule` and `base_modules.InterruptableModule` for DRYness + ## [0.0.0.7] - 2023-02-07 - Added GITHUB_WIKI tags where it was missing diff --git a/telemetry/module.go b/telemetry/module.go index 6904ed40e..ba4f4f2aa 100644 --- a/telemetry/module.go +++ b/telemetry/module.go @@ -2,6 +2,7 @@ package telemetry import ( "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" ) var ( @@ -12,14 +13,17 @@ var ( } ) -type telemetryModule struct{} +type telemetryModule struct { + base_modules.IntegratableModule + base_modules.InterruptableModule +} -func Create(bus modules.Bus) (modules.Module, error) { - return new(telemetryModule).Create(bus) +func Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(telemetryModule).Create(bus, options...) } // TODO(pocket/issues/99): Add a switch statement and configuration variable when support for other telemetry modules is added. -func (*telemetryModule) Create(bus modules.Bus) (modules.Module, error) { +func (*telemetryModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { runtimeMgr := bus.GetRuntimeMgr() cfg := runtimeMgr.GetConfig() @@ -32,8 +36,4 @@ func (*telemetryModule) Create(bus modules.Bus) (modules.Module, error) { } } -func (t *telemetryModule) GetModuleName() string { return modules.TelemetryModuleName } -func (t *telemetryModule) SetBus(bus modules.Bus) {} -func (t *telemetryModule) GetBus() modules.Bus { return nil } -func (t *telemetryModule) Start() error { return nil } -func (t *telemetryModule) Stop() error { return nil } +func (t *telemetryModule) GetModuleName() string { return modules.TelemetryModuleName } diff --git a/telemetry/noop_module.go b/telemetry/noop_module.go index 6a1316935..e2fd75184 100644 --- a/telemetry/noop_module.go +++ b/telemetry/noop_module.go @@ -22,16 +22,18 @@ func NOOP(args ...any) { logger.Global.Debug().Msg("NOOP") } -func CreateNoopTelemetryModule(bus modules.Bus) (modules.Module, error) { - var m NoopTelemetryModule - return m.Create(bus) +func CreateNoopTelemetryModule(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(NoopTelemetryModule).Create(bus, options...) } -func (*NoopTelemetryModule) Create(bus modules.Bus) (modules.Module, error) { +func (*NoopTelemetryModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { m := &NoopTelemetryModule{} - if err := bus.RegisterModule(m); err != nil { - return nil, err + + for _, option := range options { + option(m) } + + bus.RegisterModule(m) return m, nil } diff --git a/telemetry/prometheus_module.go b/telemetry/prometheus_module.go index 1dd0191df..08dc017ae 100644 --- a/telemetry/prometheus_module.go +++ b/telemetry/prometheus_module.go @@ -8,6 +8,7 @@ import ( "github.com/pokt-network/pocket/logger" "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -23,7 +24,9 @@ var ( // DISCUSS(team): Should the warning logs in this module be handled differently? type PrometheusTelemetryModule struct { - bus modules.Bus + base_modules.IntegratableModule + base_modules.InterruptableModule + config *configs.TelemetryConfig logger modules.Logger @@ -33,17 +36,19 @@ type PrometheusTelemetryModule struct { gaugeVectors map[string]prometheus.GaugeVec } -func CreatePrometheusTelemetryModule(bus modules.Bus) (modules.Module, error) { - var m PrometheusTelemetryModule - return m.Create(bus) +func CreatePrometheusTelemetryModule(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(PrometheusTelemetryModule).Create(bus, options...) } -func (*PrometheusTelemetryModule) Create(bus modules.Bus) (modules.Module, error) { +func (*PrometheusTelemetryModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { m := &PrometheusTelemetryModule{} - if err := bus.RegisterModule(m); err != nil { - return nil, err + + for _, option := range options { + option(m) } + bus.RegisterModule(m) + runtimeMgr := bus.GetRuntimeMgr() cfg := runtimeMgr.GetConfig() telemetryCfg := cfg.Telemetry @@ -79,25 +84,10 @@ func (m *PrometheusTelemetryModule) Start() error { return nil } -func (m *PrometheusTelemetryModule) Stop() error { - return nil -} - -func (m *PrometheusTelemetryModule) SetBus(bus modules.Bus) { - m.bus = bus -} - func (m *PrometheusTelemetryModule) GetModuleName() string { return fmt.Sprintf("%s_prometheus", modules.TelemetryModuleName) } -func (m *PrometheusTelemetryModule) GetBus() modules.Bus { - if m.bus == nil { - m.logger.Fatal().Msg("PocketBus is not initialized") - } - return m.bus -} - // EventMetricsAgent interface implementation func (m *PrometheusTelemetryModule) GetEventMetricsAgent() modules.EventMetricsAgent { return modules.EventMetricsAgent(m) diff --git a/utility/block.go b/utility/block.go index c3d94cbaf..f5ff620af 100644 --- a/utility/block.go +++ b/utility/block.go @@ -57,6 +57,7 @@ func (u *utilityContext) CreateAndApplyProposalBlock(proposer []byte, maxTransac } txResult, err := u.applyTx(txIndex, tx) if err != nil { + u.logger.Err(err).Msg("Error in ApplyTransaction") // TODO(#327): Properly implement 'unhappy path' for save points if err := u.revertLastSavePoint(); err != nil { return "", nil, err diff --git a/utility/doc/CHANGELOG.md b/utility/doc/CHANGELOG.md index 036ea583e..4483fc792 100644 --- a/utility/doc/CHANGELOG.md +++ b/utility/doc/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.28] - 2023-02-17 + +- Module embeds `base_modules.IntegratableModule` and `base_modules.InterruptableModule` for DRYness +- Logging error if `ApplyTransaction` fails (it was completely ignored before and it was really hard to understand what was going on) + ## [0.0.0.27] - 2023-02-14 - Added a `Validatable` type for basic validation diff --git a/utility/message_handler.go b/utility/message_handler.go index 88387c34c..93d43c870 100644 --- a/utility/message_handler.go +++ b/utility/message_handler.go @@ -3,7 +3,6 @@ package utility import ( "encoding/hex" "fmt" - "log" "math/big" "github.com/pokt-network/pocket/shared/codec" @@ -32,9 +31,7 @@ func (u *utilityModule) HandleMessage(message *anypb.Any) error { } else if err := u.CheckTransaction(txGossipMsg.Tx); err != nil { return err } - - log.Println("MEMPOOL: Successfully added a new message to the mempool!") - + u.logger.Info().Str("source", "MEMPOOL").Msg("Successfully added a new message to the mempool!") default: return types.ErrUnknownMessageType(message.MessageName()) } diff --git a/utility/module.go b/utility/module.go index 24c7d64d5..0bc28b1f5 100644 --- a/utility/module.go +++ b/utility/module.go @@ -5,6 +5,7 @@ import ( "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/shared/mempool" "github.com/pokt-network/pocket/shared/modules" + "github.com/pokt-network/pocket/shared/modules/base_modules" "github.com/pokt-network/pocket/utility/types" ) @@ -14,23 +15,28 @@ var ( ) type utilityModule struct { - bus modules.Bus + base_modules.IntegratableModule + base_modules.InterruptableModule + config *configs.UtilityConfig logger modules.Logger mempool mempool.TXMempool } -func Create(bus modules.Bus) (modules.Module, error) { - return new(utilityModule).Create(bus) +func Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { + return new(utilityModule).Create(bus, options...) } -func (*utilityModule) Create(bus modules.Bus) (modules.Module, error) { +func (*utilityModule) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) { m := &utilityModule{} - if err := bus.RegisterModule(m); err != nil { - return nil, err + + for _, option := range options { + option(m) } + bus.RegisterModule(m) + runtimeMgr := bus.GetRuntimeMgr() cfg := runtimeMgr.GetConfig() @@ -47,25 +53,10 @@ func (u *utilityModule) Start() error { return nil } -func (u *utilityModule) Stop() error { - return nil -} - func (u *utilityModule) GetModuleName() string { return modules.UtilityModuleName } -func (u *utilityModule) SetBus(bus modules.Bus) { - u.bus = bus -} - -func (u *utilityModule) GetBus() modules.Bus { - if u.bus == nil { - u.logger.Fatal().Msg("Bus is not initialized") - } - return u.bus -} - func (u *utilityModule) GetMempool() mempool.TXMempool { return u.mempool }