Skip to content

Commit

Permalink
Merge branch 'main' into eze/bump_gogoproto
Browse files Browse the repository at this point in the history
  • Loading branch information
raynaudoe authored Mar 28, 2024
2 parents 3eb9bc3 + 705fad6 commit 50271e5
Show file tree
Hide file tree
Showing 42 changed files with 662 additions and 223 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
name: Add issue to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@v0.6.1
- uses: actions/add-to-project@v1.0.0
with:
project-url: https://github.com/orgs/cosmos/projects/26
# add all issues opened to the issue board for triage and assignment
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,13 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i
* (gRPC) [#19049](https://github.com/cosmos/cosmos-sdk/pull/19049) Add debug log prints for each gRPC request.
* (x/consensus) [#19483](https://github.com/cosmos/cosmos-sdk/pull/19483) Add consensus messages registration to consensus module.
* (types) [#19759](https://github.com/cosmos/cosmos-sdk/pull/19759) Align SignerExtractionAdapter in PriorityNonceMempool Remove.
* (client) [#19870](https://github.com/cosmos/cosmos-sdk/pull/19870) Add new query command `wait-tx`. Alias `event-query-tx-for` to `wait-tx` for backward compatibility.

### Improvements

* (server) [#19854](https://github.com/cosmos/cosmos-sdk/pull/19854) Add customizability to start command.
* Add `StartCmdOptions` in `server.AddCommands` instead of `servertypes.ModuleInitFlags`. To set custom flags set them in the `StartCmdOptions` struct on the `AddFlags` field.
* Add `StartCommandHandler` to `StartCmdOptions` to allow custom start command handlers. Users now have total control over how the app starts.
* (types) [#19672](https://github.com/cosmos/cosmos-sdk/pull/19672) `PreBlock` now returns only an error for consistency with server/v2. The SDK has upgraded x/upgrade accordingly. `ResponsePreBlock` hence has been removed.
* (server) [#19455](https://github.com/cosmos/cosmos-sdk/pull/19455) Allow calling back into the application struct in PostSetup.
* (types) [#19512](https://github.com/cosmos/cosmos-sdk/pull/19512) The notion of basic manager does not exist anymore (and all related helpers).
Expand Down Expand Up @@ -103,6 +107,7 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i

### API Breaking Changes

* (server) [#19854](https://github.com/cosmos/cosmos-sdk/pull/19854) Remove `servertypes.ModuleInitFlags` types and from `server.AddCommands` as `StartCmdOptions` already achieves the same goal.
* (types) [#19792](https://github.com/cosmos/cosmos-sdk/pull/19792) In `MsgSimulatorFn` `sdk.Context` argument is replaced for an `address.Codec`. It also returns an error.
* (types) [#19742](https://github.com/cosmos/cosmos-sdk/pull/19742) Removes the use of `Accounts.String`
* `SimulationState` now has address and validator codecs as fields.
Expand Down
7 changes: 7 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ clientCtx = clientCtx.

Refer to SimApp `root_v2.go` and `root.go` for an example with an app v2 and a legacy app.

Additionally, a simplification of the start command leads to the following change:

```diff
- server.AddCommands(rootCmd, newApp, func(startCmd *cobra.Command) {})
+ server.AddCommands(rootCmd, newApp, server.StartCmdOptions[servertypes.Application]{})
```

#### Server (`app.go`)

##### Module Manager
Expand Down
98 changes: 88 additions & 10 deletions client/rpc/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package rpc
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"strings"
"time"

Expand All @@ -16,8 +18,11 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/version"
)

const TimeoutFlag = "timeout"

func newTxResponseCheckTx(res *coretypes.ResultBroadcastTxCommit) *sdk.TxResponse {
if res == nil {
return nil
Expand Down Expand Up @@ -84,18 +89,36 @@ func newResponseFormatBroadcastTxCommit(res *coretypes.ResultBroadcastTxCommit)
return newTxResponseDeliverTx(res)
}

// QueryEventForTxCmd returns a CLI command that subscribes to a WebSocket connection and waits for a transaction event with the given hash.
// QueryEventForTxCmd is an alias for WaitTxCmd, kept for backwards compatibility.
func QueryEventForTxCmd() *cobra.Command {
return WaitTxCmd()
}

// WaitTx returns a CLI command that waits for a transaction with the given hash to be included in a block.
func WaitTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "event-query-tx-for [hash]",
Short: "Query for a transaction by hash",
Long: `Subscribes to a CometBFT WebSocket connection and waits for a transaction event with the given hash.`,
Args: cobra.ExactArgs(1),
Use: "wait-tx [hash]",
Aliases: []string{"event-query-tx-for"},
Short: "Wait for a transaction to be included in a block",
Long: `Subscribes to a CometBFT WebSocket connection and waits for a transaction event with the given hash.`,
Example: fmt.Sprintf(`By providing the transaction hash:
$ %[1]sd q wait-tx [hash]
Or, by piping a "tx" command:
$ %[1]sd tx [flags] | %[1]sd q wait-tx
`, version.AppName),
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

timeout, err := cmd.Flags().GetDuration(TimeoutFlag)
if err != nil {
return err
}

c, err := rpchttp.New(clientCtx.NodeURI, "/websocket")
if err != nil {
return err
Expand All @@ -105,18 +128,54 @@ func QueryEventForTxCmd() *cobra.Command {
}
defer c.Stop() //nolint:errcheck // ignore stop error

ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

hash := args[0]
query := fmt.Sprintf("%s='%s' AND %s='%s'", tmtypes.EventTypeKey, tmtypes.EventTx, tmtypes.TxHashKey, hash)
var hash []byte
if len(args) == 0 {
// read hash from stdin
in, err := io.ReadAll(cmd.InOrStdin())
if err != nil {
return err
}
hashByt, err := parseHashFromInput(in)
if err != nil {
return err
}

hash = hashByt
} else {
// read hash from args
hashByt, err := hex.DecodeString(args[0])
if err != nil {
return err
}

hash = hashByt
}

// subscribe to websocket events
query := fmt.Sprintf("%s='%s' AND %s='%X'", tmtypes.EventTypeKey, tmtypes.EventTx, tmtypes.TxHashKey, hash)
const subscriber = "subscriber"
eventCh, err := c.Subscribe(ctx, subscriber, query)
if err != nil {
return fmt.Errorf("failed to subscribe to tx: %w", err)
}
defer c.UnsubscribeAll(context.Background(), subscriber) //nolint:errcheck // ignore unsubscribe error

// return immediately if tx is already included in a block
res, err := c.Tx(ctx, hash, false)
if err == nil {
// tx already included in a block
res := &coretypes.ResultBroadcastTxCommit{
TxResult: res.TxResult,
Hash: res.Hash,
Height: res.Height,
}
return clientCtx.PrintProto(newResponseFormatBroadcastTxCommit(res))
}

// tx not yet included in a block, wait for event on websocket
select {
case evt := <-eventCh:
if txe, ok := evt.Data.(tmtypes.EventDataTx); ok {
Expand All @@ -128,13 +187,32 @@ func QueryEventForTxCmd() *cobra.Command {
return clientCtx.PrintProto(newResponseFormatBroadcastTxCommit(res))
}
case <-ctx.Done():
return errors.ErrLogic.Wrapf("timed out waiting for event, the transaction could have already been included or wasn't yet included")
return errors.ErrLogic.Wrapf("timed out waiting for transaction %X to be included in a block", hash)
}
return nil
},
}

flags.AddTxFlagsToCmd(cmd)
cmd.Flags().Duration(TimeoutFlag, 15*time.Second, "The maximum time to wait for the transaction to be included in a block")
flags.AddQueryFlagsToCmd(cmd)

return cmd
}

func parseHashFromInput(in []byte) ([]byte, error) {
var resultTx coretypes.ResultTx
if err := json.Unmarshal(in, &resultTx); err == nil {
// input was JSON, return the hash
return resultTx.Hash, nil
}

// try to parse the hash from the output of a tx command
lines := strings.Split(string(in), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "txhash:") {
hash := strings.TrimSpace(line[len("txhash:"):])
return hex.DecodeString(hash)
}
}
return nil, fmt.Errorf("txhash not found")
}
4 changes: 2 additions & 2 deletions docs/build/building-apps/05-app-testnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,8 @@ Before we can run the testnet we must plug everything together.
in `root.go`, in the `initRootCmd` function we add:

```diff
server.AddCommands(rootCmd, simapp.DefaultNodeHome, newApp, createMerlinAppAndExport, addModuleInitFlags)
++ server.AddTestnetCreatorCommand(rootCmd, simapp.DefaultNodeHome, newTestnetApp, addModuleInitFlags)
server.AddCommands(rootCmd, simapp.DefaultNodeHome, newApp, createMerlinAppAndExport)
+server.AddTestnetCreatorCommand(rootCmd, simapp.DefaultNodeHome, newTestnetApp)
```

Next we will add a newTestnetApp helper function:
Expand Down
6 changes: 6 additions & 0 deletions docs/build/building-modules/05-protobuf-annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,9 @@ Encoding instructs the amino json marshaler how to encode certain fields that ma
```proto reference
https://github.com/cosmos/cosmos-sdk/blob/e8f28bf5db18b8d6b7e0d94b542ce4cf48fed9d6/proto/cosmos/bank/v1beta1/genesis.proto#L23
```

Another example is how `bytes` is encoded when using the amino json encoding format. The `bytes_as_string` option tells the json marshaler [how to encode bytes as string](https://github.com/pinosu/cosmos-sdk/blob/9879ece09c58068402782fa2096199dc89a23d13/x/tx/signing/aminojson/json_marshal.go#L75).

```proto
(amino.encoding) = "bytes_as_string",
```
31 changes: 16 additions & 15 deletions server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ type StartCmdOptions[T types.Application] struct {
PostSetupStandalone func(app T, svrCtx *Context, clientCtx client.Context, ctx context.Context, g *errgroup.Group) error
// AddFlags add custom flags to start cmd
AddFlags func(cmd *cobra.Command)
// StartCommandHanlder can be used to customize the start command handler
StartCommandHandler func(svrCtx *Context, clientCtx client.Context, appCreator types.AppCreator[T], inProcessConsensus bool, opts StartCmdOptions[T]) error
}

// StartCmd runs the service passed in, either stand-alone or in-process with
Expand All @@ -139,6 +141,10 @@ func StartCmdWithOptions[T types.Application](appCreator types.AppCreator[T], op
opts.DBOpener = OpenDB
}

if opts.StartCommandHandler == nil {
opts.StartCommandHandler = start
}

cmd := &cobra.Command{
Use: "start",
Short: "Run the full node",
Expand Down Expand Up @@ -187,7 +193,7 @@ is performed. Note, when enabled, gRPC will also be automatically enabled.
}

err = wrapCPUProfile(serverCtx, func() error {
return start(serverCtx, clientCtx, appCreator, withCMT, opts)
return opts.StartCommandHandler(serverCtx, clientCtx, appCreator, withCMT, opts)
})

serverCtx.Logger.Debug("received quit signal")
Expand Down Expand Up @@ -270,10 +276,7 @@ func startStandAlone[T types.Application](svrCtx *Context, svrCfg serverconfig.C
return err
}

cmtCfg := svrCtx.Config
home := cmtCfg.RootDir

err = startAPIServer(ctx, g, cmtCfg, svrCfg, clientCtx, svrCtx, app, home, grpcSrv, metrics)
err = startAPIServer(ctx, g, svrCfg, clientCtx, svrCtx, app, svrCtx.Config.RootDir, grpcSrv, metrics)
if err != nil {
return err
}
Expand Down Expand Up @@ -304,8 +307,6 @@ func startInProcess[T types.Application](svrCtx *Context, svrCfg serverconfig.Co
metrics *telemetry.Metrics, opts StartCmdOptions[T],
) error {
cmtCfg := svrCtx.Config
home := cmtCfg.RootDir

gRPCOnly := svrCtx.Viper.GetBool(flagGRPCOnly)

g, ctx := getCtx(svrCtx, true)
Expand Down Expand Up @@ -341,7 +342,7 @@ func startInProcess[T types.Application](svrCtx *Context, svrCfg serverconfig.Co
return err
}

err = startAPIServer(ctx, g, cmtCfg, svrCfg, clientCtx, svrCtx, app, home, grpcSrv, metrics)
err = startAPIServer(ctx, g, svrCfg, clientCtx, svrCtx, app, cmtCfg.RootDir, grpcSrv, metrics)
if err != nil {
return err
}
Expand Down Expand Up @@ -504,7 +505,6 @@ func startGrpcServer(
func startAPIServer(
ctx context.Context,
g *errgroup.Group,
cmtCfg *cmtcfg.Config,
svrCfg serverconfig.Config,
clientCtx client.Context,
svrCtx *Context,
Expand Down Expand Up @@ -612,7 +612,7 @@ func startApp[T types.Application](svrCtx *Context, appCreator types.AppCreator[

if isTestnet, ok := svrCtx.Viper.Get(KeyIsTestnet).(bool); ok && isTestnet {
var appPtr *T
appPtr, err = testnetify[T](svrCtx, home, appCreator, db, traceWriter)
appPtr, err = testnetify[T](svrCtx, appCreator, db, traceWriter)
if err != nil {
return app, traceCleanupFn, err
}
Expand All @@ -639,6 +639,10 @@ func InPlaceTestnetCreator[T types.Application](testnetAppCreator types.AppCreat
opts.DBOpener = OpenDB
}

if opts.StartCommandHandler == nil {
opts.StartCommandHandler = start
}

cmd := &cobra.Command{
Use: "in-place-testnet [newChainID] [newOperatorAddress]",
Short: "Create and start a testnet from current local state",
Expand Down Expand Up @@ -703,7 +707,7 @@ you want to test the upgrade handler itself.
serverCtx.Viper.Set(KeyNewOpAddr, newOperatorAddress)

err = wrapCPUProfile(serverCtx, func() error {
return start(serverCtx, clientCtx, testnetAppCreator, withCMT, opts)
return opts.StartCommandHandler(serverCtx, clientCtx, testnetAppCreator, withCMT, opts)
})

serverCtx.Logger.Debug("received quit signal")
Expand All @@ -726,7 +730,7 @@ you want to test the upgrade handler itself.

// testnetify modifies both state and blockStore, allowing the provided operator address and local validator key to control the network
// that the state in the data folder represents. The chainID of the local genesis file is modified to match the provided chainID.
func testnetify[T types.Application](ctx *Context, home string, testnetAppCreator types.AppCreator[T], db dbm.DB, traceWriter io.WriteCloser) (*T, error) {
func testnetify[T types.Application](ctx *Context, testnetAppCreator types.AppCreator[T], db dbm.DB, traceWriter io.WriteCloser) (*T, error) {
config := ctx.Config

newChainID, ok := ctx.Viper.Get(KeyNewChainID).(string)
Expand Down Expand Up @@ -772,9 +776,6 @@ func testnetify[T types.Application](ctx *Context, home string, testnetAppCreato
return nil, err
}
validatorAddress := userPubKey.Address()
if err != nil {
return nil, err
}

stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: config.Storage.DiscardABCIResponses,
Expand Down
4 changes: 0 additions & 4 deletions server/types/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
cmttypes "github.com/cometbft/cometbft/types"
dbm "github.com/cosmos/cosmos-db"
"github.com/cosmos/gogoproto/grpc"
"github.com/spf13/cobra"

"cosmossdk.io/log"
"cosmossdk.io/store/snapshots"
Expand Down Expand Up @@ -68,9 +67,6 @@ type (
// application using various configurations.
AppCreator[T Application] func(log.Logger, dbm.DB, io.Writer, AppOptions) T

// ModuleInitFlags takes a start command and adds modules specific init flags.
ModuleInitFlags func(startCmd *cobra.Command)

// ExportedApp represents an exported app state, along with
// validators, consensus params and latest app height.
ExportedApp struct {
Expand Down
17 changes: 9 additions & 8 deletions server/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ func interceptConfigs(rootViper *viper.Viper, customAppTemplate string, customCo
}

// AddCommands add server commands
func AddCommands[T types.Application](rootCmd *cobra.Command, appCreator types.AppCreator[T], addStartFlags types.ModuleInitFlags) {
func AddCommands[T types.Application](rootCmd *cobra.Command, appCreator types.AppCreator[T], opts StartCmdOptions[T]) {
cometCmd := &cobra.Command{
Use: "comet",
Aliases: []string{"cometbft", "tendermint"},
Expand All @@ -341,11 +341,7 @@ func AddCommands[T types.Application](rootCmd *cobra.Command, appCreator types.A
BootstrapStateCmd(appCreator),
)

startCmd := StartCmd(appCreator)
if addStartFlags != nil {
addStartFlags(startCmd)
}

startCmd := StartCmdWithOptions(appCreator, opts)
rootCmd.AddCommand(
startCmd,
cometCmd,
Expand All @@ -354,10 +350,15 @@ func AddCommands[T types.Application](rootCmd *cobra.Command, appCreator types.A
)
}

// AddCommandsWithStartCmdOptions adds server commands with the provided StartCmdOptions.
// Deprecated: Use AddCommands directly instead.
func AddCommandsWithStartCmdOptions[T types.Application](rootCmd *cobra.Command, appCreator types.AppCreator[T], opts StartCmdOptions[T]) {
AddCommands(rootCmd, appCreator, opts)
}

// AddTestnetCreatorCommand allows chains to create a testnet from the state existing in their node's data directory.
func AddTestnetCreatorCommand[T types.Application](rootCmd *cobra.Command, appCreator types.AppCreator[T], addStartFlags types.ModuleInitFlags) {
func AddTestnetCreatorCommand[T types.Application](rootCmd *cobra.Command, appCreator types.AppCreator[T]) {
testnetCreateCmd := InPlaceTestnetCreator(appCreator)
addStartFlags(testnetCreateCmd)
rootCmd.AddCommand(testnetCreateCmd)
}

Expand Down
Loading

0 comments on commit 50271e5

Please sign in to comment.