diff --git a/app/client/cli/debug.go b/app/client/cli/debug.go index 5ff6d521b..99d5b83de 100644 --- a/app/client/cli/debug.go +++ b/app/client/cli/debug.go @@ -1,8 +1,6 @@ package cli import ( - "errors" - "fmt" "os" "github.com/manifoldco/promptui" @@ -11,10 +9,7 @@ import ( "github.com/pokt-network/pocket/app/client/cli/helpers" "github.com/pokt-network/pocket/logger" - "github.com/pokt-network/pocket/p2p/providers/peerstore_provider" - typesP2P "github.com/pokt-network/pocket/p2p/types" "github.com/pokt-network/pocket/shared/messaging" - "github.com/pokt-network/pocket/shared/modules" ) // TECHDEBT: Lowercase variables / constants that do not need to be exported. @@ -28,26 +23,20 @@ const ( PromptSendBlockRequest string = "BlockRequest (broadcast)" ) -var ( - items = []string{ - PromptPrintNodeState, - PromptTriggerNextView, - PromptTogglePacemakerMode, - PromptResetToGenesis, - PromptShowLatestBlockInStore, - PromptSendMetadataRequest, - PromptSendBlockRequest, - } -) +var items = []string{ + PromptPrintNodeState, + PromptTriggerNextView, + PromptTogglePacemakerMode, + PromptResetToGenesis, + PromptShowLatestBlockInStore, + PromptSendMetadataRequest, + PromptSendBlockRequest, +} func init() { dbgUI := newDebugUICommand() dbgUI.AddCommand(newDebugUISubCommands()...) rootCmd.AddCommand(dbgUI) - - dbg := newDebugCommand() - dbg.AddCommand(debugCommands()...) - rootCmd.AddCommand(dbg) } // newDebugUISubCommands builds out the list of debug subcommands by matching the @@ -60,7 +49,7 @@ func newDebugUISubCommands() []*cobra.Command { commands[idx] = &cobra.Command{ Use: promptItem, PersistentPreRunE: helpers.P2PDependenciesPreRunE, - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, _ []string) { handleSelect(cmd, cmd.Use) }, ValidArgs: items, @@ -81,56 +70,7 @@ func newDebugUICommand() *cobra.Command { } } -// newDebugCommand returns the cobra CLI for the Debug command. -func newDebugCommand() *cobra.Command { - return &cobra.Command{ - Use: "Debug", - Aliases: []string{"d"}, - Short: "Debug utility for rapid development", - Args: cobra.MaximumNArgs(1), - PersistentPreRunE: helpers.P2PDependenciesPreRunE, - } -} - -func debugCommands() []*cobra.Command { - cmds := []*cobra.Command{ - { - Use: "TriggerView", - Aliases: []string{"next", "trigger", "view"}, - Short: "Trigger the next view in consensus", - Long: "Sends a message to all visible nodes on the network to start the next view (height/step/round) in consensus", - Args: cobra.ExactArgs(0), - RunE: func(cmd *cobra.Command, args []string) error { - m := &messaging.DebugMessage{ - Action: messaging.DebugMessageAction_DEBUG_CONSENSUS_TRIGGER_NEXT_VIEW, - Type: messaging.DebugMessageRoutingType_DEBUG_MESSAGE_TYPE_BROADCAST, - Message: nil, - } - broadcastDebugMessage(cmd, m) - return nil - }, - }, - { - Use: "TogglePacemakerMode", - Short: "Toggle the pacemaker", - Long: "Toggle the consensus pacemaker either on or off so the chain progresses on its own or loses liveness", - Aliases: []string{"togglePaceMaker"}, - Args: cobra.ExactArgs(0), - RunE: func(cmd *cobra.Command, args []string) error { - m := &messaging.DebugMessage{ - Action: messaging.DebugMessageAction_DEBUG_CONSENSUS_TOGGLE_PACE_MAKER_MODE, - Type: messaging.DebugMessageRoutingType_DEBUG_MESSAGE_TYPE_BROADCAST, - Message: nil, - } - broadcastDebugMessage(cmd, m) - return nil - }, - }, - } - return cmds -} - -func runDebug(cmd *cobra.Command, args []string) (err error) { +func runDebug(cmd *cobra.Command, _ []string) (err error) { for { if selection, err := promptGetInput(); err == nil { handleSelect(cmd, selection) @@ -218,32 +158,20 @@ func handleSelect(cmd *cobra.Command, selection string) { } } -// Broadcast to the entire validator set +// Broadcast to the entire network. func broadcastDebugMessage(cmd *cobra.Command, debugMsg *messaging.DebugMessage) { anyProto, err := anypb.New(debugMsg) if err != nil { logger.Global.Fatal().Err(err).Msg("Failed to create Any proto") } - // TODO(olshansky): Once we implement the cleanup layer in RainTree, we'll be able to use - // broadcast. The reason it cannot be done right now is because this client is not in the - // address book of the actual validator nodes, so `validator1` never receives the message. - // p2pMod.Broadcast(anyProto) - - pstore, err := fetchPeerstore(cmd) + bus, err := helpers.GetBusFromCmd(cmd) if err != nil { - logger.Global.Fatal().Err(err).Msg("Unable to retrieve the pstore") + logger.Global.Fatal().Err(err).Msg("Failed to retrieve bus from command") } - for _, val := range pstore.GetPeerList() { - addr := val.GetAddress() - if err != nil { - logger.Global.Fatal().Err(err).Msg("Failed to convert validator address into pocketCrypto.Address") - } - if err := helpers.P2PMod.Send(addr, anyProto); err != nil { - logger.Global.Error().Err(err).Msg("Failed to send debug message") - } + if err := bus.GetP2PModule().Broadcast(anyProto); err != nil { + logger.Global.Error().Err(err).Msg("Failed to broadcast debug message") } - } // Send to just a single (i.e. first) validator in the set @@ -253,62 +181,29 @@ func sendDebugMessage(cmd *cobra.Command, debugMsg *messaging.DebugMessage) { logger.Global.Error().Err(err).Msg("Failed to create Any proto") } - pstore, err := fetchPeerstore(cmd) + pstore, err := helpers.FetchPeerstore(cmd) if err != nil { logger.Global.Fatal().Err(err).Msg("Unable to retrieve the pstore") } - var validatorAddress []byte if pstore.Size() == 0 { logger.Global.Fatal().Msg("No validators found") } // if the message needs to be broadcast, it'll be handled by the business logic of the message handler - validatorAddress = pstore.GetPeerList()[0].GetAddress() + // + // TODO(#936): The statement above is false. Using `#Send()` will only + // be unicast with no opportunity for further propagation. + firstStakedActorAddress := pstore.GetPeerList()[0].GetAddress() if err != nil { logger.Global.Fatal().Err(err).Msg("Failed to convert validator address into pocketCrypto.Address") } - if err := helpers.P2PMod.Send(validatorAddress, anyProto); err != nil { - logger.Global.Error().Err(err).Msg("Failed to send debug message") - } -} - -// fetchPeerstore retrieves the providers from the CLI context and uses them to retrieve the address book for the current height -func fetchPeerstore(cmd *cobra.Command) (typesP2P.Peerstore, error) { - bus, ok := helpers.GetValueFromCLIContext[modules.Bus](cmd, helpers.BusCLICtxKey) - if !ok || bus == nil { - return nil, errors.New("retrieving bus from CLI context") - } - // TECHDEBT(#810, #811): use `bus.GetPeerstoreProvider()` after peerstore provider - // is retrievable as a proper submodule - pstoreProvider, err := bus.GetModulesRegistry().GetModule(peerstore_provider.PeerstoreProviderSubmoduleName) - if err != nil { - return nil, errors.New("retrieving peerstore provider") - } - currentHeightProvider := bus.GetCurrentHeightProvider() - - height := currentHeightProvider.CurrentHeight() - pstore, err := pstoreProvider.(peerstore_provider.PeerstoreProvider).GetStakedPeerstoreAtHeight(height) - if err != nil { - return nil, fmt.Errorf("retrieving peerstore at height %d", height) - } - // Inform the client's main P2P that a the blockchain is at a new height so it can, if needed, update its view of the validator set - err = sendConsensusNewHeightEventToP2PModule(height, bus) + bus, err := helpers.GetBusFromCmd(cmd) if err != nil { - return nil, errors.New("sending consensus new height event") + logger.Global.Fatal().Err(err).Msg("Failed to retrieve bus from command") } - return pstore, nil -} - -// sendConsensusNewHeightEventToP2PModule mimicks the consensus module sending a ConsensusNewHeightEvent to the p2p module -// This is necessary because the debug client is not a validator and has no consensus module but it has to update the peerstore -// depending on the changes in the validator set. -// TODO(#613): Make the debug client mimic a full node. -func sendConsensusNewHeightEventToP2PModule(height uint64, bus modules.Bus) error { - newHeightEvent, err := messaging.PackMessage(&messaging.ConsensusNewHeightEvent{Height: height}) - if err != nil { - logger.Global.Fatal().Err(err).Msg("Failed to pack consensus new height event") + if err := bus.GetP2PModule().Send(firstStakedActorAddress, anyProto); err != nil { + logger.Global.Error().Err(err).Msg("Failed to send debug message") } - return bus.GetP2PModule().HandleEvent(newHeightEvent.Content) } diff --git a/app/client/cli/helpers/common.go b/app/client/cli/helpers/common.go index b9f6d547b..647d4241d 100644 --- a/app/client/cli/helpers/common.go +++ b/app/client/cli/helpers/common.go @@ -1,14 +1,56 @@ package helpers import ( + "errors" + "fmt" + + "github.com/spf13/cobra" + + "github.com/pokt-network/pocket/logger" + "github.com/pokt-network/pocket/p2p/providers/peerstore_provider" + "github.com/pokt-network/pocket/p2p/types" "github.com/pokt-network/pocket/runtime" + "github.com/pokt-network/pocket/shared/messaging" "github.com/pokt-network/pocket/shared/modules" ) -var ( - // TECHDEBT: Accept reading this from `Datadir` and/or as a flag. - genesisPath = runtime.GetEnv("GENESIS_PATH", "build/config/genesis.json") +// TECHDEBT: Accept reading this from `Datadir` and/or as a flag. +var genesisPath = runtime.GetEnv("GENESIS_PATH", "build/config/genesis.json") - // P2PMod is initialized in order to broadcast a message to the local network - P2PMod modules.P2PModule -) +// FetchPeerstore retrieves the providers from the CLI context and uses them to retrieve the address book for the current height +func FetchPeerstore(cmd *cobra.Command) (types.Peerstore, error) { + bus, err := GetBusFromCmd(cmd) + if err != nil { + return nil, err + } + // TECHDEBT(#811): use `bus.GetPeerstoreProvider()` after peerstore provider + // is retrievable as a proper submodule + pstoreProvider, err := bus.GetModulesRegistry().GetModule(peerstore_provider.PeerstoreProviderSubmoduleName) + if err != nil { + return nil, errors.New("retrieving peerstore provider") + } + currentHeightProvider := bus.GetCurrentHeightProvider() + height := currentHeightProvider.CurrentHeight() + pstore, err := pstoreProvider.(peerstore_provider.PeerstoreProvider).GetStakedPeerstoreAtHeight(height) + if err != nil { + return nil, fmt.Errorf("retrieving peerstore at height %d", height) + } + // Inform the client's main P2P that a the blockchain is at a new height so it can, if needed, update its view of the validator set + if err := sendConsensusNewHeightEventToP2PModule(height, bus); err != nil { + return nil, errors.New("sending consensus new height event") + } + return pstore, nil +} + +// sendConsensusNewHeightEventToP2PModule mimicks the consensus module sending a ConsensusNewHeightEvent to the p2p module +// This is necessary because the debug client is not a validator and has no consensus module but it has to update the peerstore +// depending on the changes in the validator set. +// TODO(#613): Make the debug client mimic a full node. +// TECHDEBT: This may no longer be required (https://github.com/pokt-network/pocket/pull/891/files#r1262710098) +func sendConsensusNewHeightEventToP2PModule(height uint64, bus modules.Bus) error { + newHeightEvent, err := messaging.PackMessage(&messaging.ConsensusNewHeightEvent{Height: height}) + if err != nil { + logger.Global.Fatal().Err(err).Msg("Failed to pack consensus new height event") + } + return bus.GetP2PModule().HandleEvent(newHeightEvent.Content) +} diff --git a/app/client/cli/helpers/context.go b/app/client/cli/helpers/context.go index f9f3f4549..f1494c5ac 100644 --- a/app/client/cli/helpers/context.go +++ b/app/client/cli/helpers/context.go @@ -2,12 +2,17 @@ package helpers import ( "context" + "fmt" "github.com/spf13/cobra" + + "github.com/pokt-network/pocket/shared/modules" ) const BusCLICtxKey cliContextKey = "bus" +var ErrCxtFromBus = fmt.Errorf("could not get context from bus") + // NOTE: this is required by the linter, otherwise a simple string constant would have been enough type cliContextKey string @@ -19,3 +24,12 @@ func GetValueFromCLIContext[T any](cmd *cobra.Command, key cliContextKey) (T, bo value, ok := cmd.Context().Value(key).(T) return value, ok } + +func GetBusFromCmd(cmd *cobra.Command) (modules.Bus, error) { + bus, ok := GetValueFromCLIContext[modules.Bus](cmd, BusCLICtxKey) + if !ok { + return nil, ErrCxtFromBus + } + + return bus, nil +} diff --git a/app/client/cli/helpers/setup.go b/app/client/cli/helpers/setup.go index 34605bd1e..3766b6ae9 100644 --- a/app/client/cli/helpers/setup.go +++ b/app/client/cli/helpers/setup.go @@ -16,11 +16,27 @@ import ( "github.com/pokt-network/pocket/shared/modules" ) +// debugPrivKey is used in the generation of a runtime config to provide a private key to the P2P and Consensus modules +// this is not a private key used for sending transactions, but is used for the purposes of broadcasting messages etc. +// this must be done as the CLI does not take a node configuration file and still requires a Private Key for modules +const debugPrivKey = "09fc8ee114e678e665d09179acb9a30060f680df44ba06b51434ee47940a8613be19b2b886e743eb1ff7880968d6ce1a46350315e569243e747a227ee8faec3d" + // P2PDependenciesPreRunE initializes peerstore & current height providers, and a // p2p module which consumes them. Everything is registered to the bus. func P2PDependenciesPreRunE(cmd *cobra.Command, _ []string) error { // TECHDEBT: this was being used for backwards compatibility with LocalNet and need to re-evaluate if its still necessary flags.ConfigPath = runtime.GetEnv("CONFIG_PATH", "build/config/config.validator1.json") + configs.ParseConfig(flags.ConfigPath) + + // set final `remote_cli_url` value; order of precedence: flag > env var > config > default + flags.RemoteCLIURL = viper.GetString("remote_cli_url") + + // By this time, the config path should be set. + // This is only being called for viper related side effects + // TECHDEBT(#907): refactor and improve how viper is used to parse configs throughout the codebase + _ = configs.ParseConfig(flags.ConfigPath) + // set final `remote_cli_url` value; order of precedence: flag > env var > config > default + flags.RemoteCLIURL = viper.GetString("remote_cli_url") // By this time, the config path should be set. // This is only being called for viper related side effects @@ -32,7 +48,7 @@ func P2PDependenciesPreRunE(cmd *cobra.Command, _ []string) error { runtimeMgr := runtime.NewManagerFromFiles( flags.ConfigPath, genesisPath, runtime.WithClientDebugMode(), - runtime.WithRandomPK(), + runtime.WithPK(debugPrivKey), ) bus := runtimeMgr.GetBus() @@ -79,7 +95,7 @@ func setupAndStartP2PModule(rm runtime.Manager) { } var ok bool - P2PMod, ok = mod.(modules.P2PModule) + P2PMod, ok := mod.(modules.P2PModule) if !ok { logger.Global.Fatal().Msgf("unexpected P2P module type: %T", mod) } diff --git a/runtime/manager.go b/runtime/manager.go index 151f2c198..da6209957 100644 --- a/runtime/manager.go +++ b/runtime/manager.go @@ -104,6 +104,7 @@ func WithRandomPK() func(*Manager) { return WithPK(privateKey.String()) } +// TECHDEBT(#750): separate consensus and P2P (identity vs communication) keys. func WithPK(pk string) func(*Manager) { return func(b *Manager) { if b.config.Consensus == nil {