From b6bf26195f31069289911aec57a390cdd42b4fc6 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Thu, 24 Oct 2024 08:48:12 +0700 Subject: [PATCH] add more code for grpc broadcast --- broadcast/broadcast.go | 146 ------------------------ broadcast/grpc.go | 250 +++++++++++++++++++++++++++++++++++++++++ broadcast/rpc.go | 155 +++++++++++++++++++++++++ lib/lib.go | 2 +- main.go | 7 ++ nodes.toml | 6 +- types/types.go | 7 +- 7 files changed, 422 insertions(+), 151 deletions(-) create mode 100644 broadcast/grpc.go create mode 100644 broadcast/rpc.go diff --git a/broadcast/broadcast.go b/broadcast/broadcast.go index 02a9b73..e87f2e9 100644 --- a/broadcast/broadcast.go +++ b/broadcast/broadcast.go @@ -8,29 +8,11 @@ import ( cometrpc "github.com/cometbft/cometbft/rpc/client/http" coretypes "github.com/cometbft/cometbft/rpc/core/types" tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/ibc-go/modules/apps/callbacks/testing/simapp/params" - "github.com/cosmos/ibc-go/v8/modules/apps/transfer" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - ibc "github.com/cosmos/ibc-go/v8/modules/core" - meteoritebank "github.com/somatic-labs/meteorite/modules/bank" - meteoriteibc "github.com/somatic-labs/meteorite/modules/ibc" - wasm "github.com/somatic-labs/meteorite/modules/wasm" - types "github.com/somatic-labs/meteorite/types" - sdkmath "cosmossdk.io/math" - - "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/std" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" - "github.com/cosmos/cosmos-sdk/x/bank" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/gov" - - wasmd "github.com/CosmWasm/wasmd/x/wasm" ) var cdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry()) @@ -40,134 +22,6 @@ func init() { banktypes.RegisterInterfaces(cdc.InterfaceRegistry()) } -// SendTransactionViaRPC sends a transaction using the provided TransactionParams and sequence number. -func SendTransactionViaRPC(txParams types.TransactionParams, sequence uint64) (response *coretypes.ResultBroadcastTx, txbody string, err error) { - encodingConfig := params.MakeTestEncodingConfig() - encodingConfig.Codec = cdc - - // Register IBC and other necessary types - transferModule := transfer.AppModuleBasic{} - ibcModule := ibc.AppModuleBasic{} - bankModule := bank.AppModuleBasic{} - wasmModule := wasmd.AppModuleBasic{} - govModule := gov.AppModuleBasic{} - - ibcModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) - transferModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) - bankModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) - wasmModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) - govModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) - std.RegisterInterfaces(encodingConfig.InterfaceRegistry) - - // Create a new TxBuilder. - txBuilder := encodingConfig.TxConfig.NewTxBuilder() - - var msg sdk.Msg - var memo string // Declare a variable to hold the memo - - switch txParams.MsgType { - case "ibc_transfer": - msg, memo, err = meteoriteibc.CreateIBCTransferMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) - if err != nil { - return nil, "", err - } - case "bank_send": - msg, memo, err = meteoritebank.CreateBankSendMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) - if err != nil { - return nil, "", err - } - case "store_code": - msg, memo, err = wasm.CreateStoreCodeMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) - if err != nil { - return nil, "", err - } - case "instantiate_contract": - msg, memo, err = wasm.CreateInstantiateContractMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) - if err != nil { - return nil, "", err - } - default: - return nil, "", fmt.Errorf("unsupported message type: %s", txParams.MsgType) - } - - // Set messages - err = txBuilder.SetMsgs(msg) - if err != nil { - return nil, "", err - } - - // Estimate gas limit based on transaction size - txSize := len(msg.String()) - gasLimit := uint64((int64(txSize) * txParams.Config.Bytes) + txParams.Config.BaseGas) - txBuilder.SetGasLimit(gasLimit) - - // Calculate fee based on gas limit and a fixed gas price - gasPrice := sdk.NewDecCoinFromDec(txParams.Config.Denom, sdkmath.LegacyNewDecWithPrec(txParams.Config.Gas.Low, txParams.Config.Gas.Precision)) - feeAmount := gasPrice.Amount.MulInt64(int64(gasLimit)).RoundInt() - feecoin := sdk.NewCoin(txParams.Config.Denom, feeAmount) - txBuilder.SetFeeAmount(sdk.NewCoins(feecoin)) - - // Set the memo (either random for bank_send or as per IBC transfer) - txBuilder.SetMemo(memo) - txBuilder.SetTimeoutHeight(0) - - // First round: gather all the signer infos using the "set empty signature" hack - sigV2 := signing.SignatureV2{ - PubKey: txParams.PubKey, - Sequence: sequence, - Data: &signing.SingleSignatureData{ - SignMode: signing.SignMode(encodingConfig.TxConfig.SignModeHandler().DefaultMode()), - }, - } - - err = txBuilder.SetSignatures(sigV2) - if err != nil { - fmt.Println("Error setting signatures") - return nil, "", err - } - - signerData := authsigning.SignerData{ - ChainID: txParams.ChainID, - AccountNumber: txParams.AccNum, - Sequence: sequence, - } - - ctx := context.Background() - - signed, err := tx.SignWithPrivKey( - ctx, - signing.SignMode(encodingConfig.TxConfig.SignModeHandler().DefaultMode()), - signerData, - txBuilder, - txParams.PrivKey, - encodingConfig.TxConfig, - sequence, - ) - if err != nil { - fmt.Println("Couldn't sign") - return nil, "", err - } - - err = txBuilder.SetSignatures(signed) - if err != nil { - return nil, "", err - } - - // Generate the encoded transaction bytes - txBytes, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx()) - if err != nil { - fmt.Println(err) - return nil, "", err - } - - resp, err := Transaction(txBytes, txParams.NodeURL) - if err != nil { - return resp, string(txBytes), fmt.Errorf("failed to broadcast transaction: %w", err) - } - - return resp, string(txBytes), nil -} - // Transaction broadcasts the transaction bytes to the given RPC endpoint. func Transaction(txBytes []byte, rpcEndpoint string) (*coretypes.ResultBroadcastTx, error) { cmtCli, err := cometrpc.New(rpcEndpoint, "/websocket") diff --git a/broadcast/grpc.go b/broadcast/grpc.go new file mode 100644 index 0000000..81698cb --- /dev/null +++ b/broadcast/grpc.go @@ -0,0 +1,250 @@ +package broadcast + +import ( + "context" + "fmt" + + "github.com/cosmos/ibc-go/modules/apps/callbacks/testing/simapp/params" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer" + ibc "github.com/cosmos/ibc-go/v8/modules/core" + client "github.com/somatic-labs/meteorite/client" + meteoritebank "github.com/somatic-labs/meteorite/modules/bank" + meteoriteibc "github.com/somatic-labs/meteorite/modules/ibc" + wasm "github.com/somatic-labs/meteorite/modules/wasm" + types "github.com/somatic-labs/meteorite/types" + + sdkmath "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/std" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/gov" + + wasmd "github.com/CosmWasm/wasmd/x/wasm" +) + +func SendTransactionViaGRPC( + ctx context.Context, + txParams types.TransactionParams, + sequence uint64, + grpcClient *client.GRPCClient, +) (*sdk.TxResponse, string, error) { + encodingConfig := params.MakeTestEncodingConfig() + encodingConfig.Codec = cdc + + // Register necessary interfaces + transferModule := transfer.AppModuleBasic{} + ibcModule := ibc.AppModuleBasic{} + bankModule := bank.AppModuleBasic{} + wasmModule := wasmd.AppModuleBasic{} + govModule := gov.AppModuleBasic{} + + ibcModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) + transferModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) + bankModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) + wasmModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) + govModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) + std.RegisterInterfaces(encodingConfig.InterfaceRegistry) + + // Create a new TxBuilder. + txBuilder := encodingConfig.TxConfig.NewTxBuilder() + + var msg sdk.Msg + var memo string + + // Construct the message based on the message type + switch txParams.MsgType { + case "ibc_transfer": + var err error + msg, memo, err = meteoriteibc.CreateIBCTransferMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + if err != nil { + return nil, "", err + } + case "bank_send": + var err error + msg, memo, err = meteoritebank.CreateBankSendMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + if err != nil { + return nil, "", err + } + case "store_code": + var err error + msg, memo, err = wasm.CreateStoreCodeMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + if err != nil { + return nil, "", err + } + case "instantiate_contract": + var err error + msg, memo, err = wasm.CreateInstantiateContractMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + if err != nil { + return nil, "", err + } + default: + return nil, "", fmt.Errorf("unsupported message type: %s", txParams.MsgType) + } + + // Set the message and other transaction parameters + if err := txBuilder.SetMsgs(msg); err != nil { + return nil, "", err + } + + // Estimate gas limit + txSize := len(msg.String()) + gasLimit := uint64((int64(txSize) * txParams.Config.Bytes) + txParams.Config.BaseGas) + txBuilder.SetGasLimit(gasLimit) + + // Calculate fee + gasPrice := sdk.NewDecCoinFromDec(txParams.Config.Denom, sdkmath.LegacyNewDecWithPrec(txParams.Config.Gas.Low, txParams.Config.Gas.Precision)) + feeAmount := gasPrice.Amount.MulInt64(int64(gasLimit)).RoundInt() + feeCoin := sdk.NewCoin(txParams.Config.Denom, feeAmount) + txBuilder.SetFeeAmount(sdk.NewCoins(feeCoin)) + + // Set memo and timeout height + txBuilder.SetMemo(memo) + txBuilder.SetTimeoutHeight(0) + + // Set up signature + sigV2 := signing.SignatureV2{ + PubKey: txParams.PubKey, + Sequence: sequence, + Data: &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_DIRECT, + }, + } + + if err := txBuilder.SetSignatures(sigV2); err != nil { + return nil, "", err + } + + signerData := authsigning.SignerData{ + ChainID: txParams.ChainID, + AccountNumber: txParams.AccNum, + Sequence: sequence, + } + + // Sign the transaction + if _, err := tx.SignWithPrivKey( + ctx, + signing.SignMode_SIGN_MODE_DIRECT, + signerData, + txBuilder, + txParams.PrivKey, + encodingConfig.TxConfig, + sequence, + ); err != nil { + return nil, "", err + } + + // Encode the transaction + txBytes, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return nil, "", err + } + + // Broadcast the transaction via gRPC + grpcRes, err := grpcClient.SendTx(ctx, txBytes) + if err != nil { + return nil, "", fmt.Errorf("failed to broadcast transaction via gRPC: %w", err) + } + + // Check for errors in the response + if grpcRes.Code != 0 { + return grpcRes, string(txBytes), fmt.Errorf("broadcast error code %d: %s", grpcRes.Code, grpcRes.RawLog) + } + + return grpcRes, string(txBytes), nil +} + +func BuildAndSignTx( + txParams types.TransactionParams, + sequence uint64, +) ([]byte, string, error) { + encodingConfig := params.MakeTestEncodingConfig() + encodingConfig.Codec = cdc + + // Create a new TxBuilder. + txBuilder := encodingConfig.TxConfig.NewTxBuilder() + + var msg sdk.Msg + var memo string + var err error + + // Construct the message based on the message type + switch txParams.MsgType { + case "ibc_transfer": + msg, memo, err = meteoriteibc.CreateIBCTransferMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + case "bank_send": + msg, memo, err = meteoritebank.CreateBankSendMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + case "store_code": + msg, memo, err = wasm.CreateStoreCodeMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + case "instantiate_contract": + msg, memo, err = wasm.CreateInstantiateContractMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + default: + return nil, "", fmt.Errorf("unsupported message type: %s", txParams.MsgType) + } + + if err != nil { + return nil, "", err + } + + // Set the message and other transaction parameters + if err := txBuilder.SetMsgs(msg); err != nil { + return nil, "", err + } + + // Estimate gas limit + txSize := len(msg.String()) + gasLimit := uint64((int64(txSize) * txParams.Config.Bytes) + txParams.Config.BaseGas) + txBuilder.SetGasLimit(gasLimit) + + // Calculate fee + gasPrice := sdk.NewDecCoinFromDec(txParams.Config.Denom, sdkmath.LegacyNewDecWithPrec(txParams.Config.Gas.Low, txParams.Config.Gas.Precision)) + feeAmount := gasPrice.Amount.MulInt64(int64(gasLimit)).RoundInt() + feeCoin := sdk.NewCoin(txParams.Config.Denom, feeAmount) + txBuilder.SetFeeAmount(sdk.NewCoins(feeCoin)) + + // Set memo and timeout height + txBuilder.SetMemo(memo) + txBuilder.SetTimeoutHeight(0) + + // Set up signature + sigV2 := signing.SignatureV2{ + PubKey: txParams.PubKey, + Sequence: sequence, + Data: &signing.SingleSignatureData{ + SignMode: encodingConfig.TxConfig.SignModeHandler().DefaultMode(), + }, + } + + if err := txBuilder.SetSignatures(sigV2); err != nil { + return nil, "", err + } + + signerData := authsigning.SignerData{ + ChainID: txParams.ChainID, + AccountNumber: txParams.AccNum, + Sequence: sequence, + } + + // Sign the transaction + if err := tx.SignWithPrivKey( + encodingConfig.TxConfig.SignModeHandler().DefaultMode(), + signerData, + txBuilder, + txParams.PrivKey, + encodingConfig.TxConfig, + sequence, + ); err != nil { + return nil, "", err + } + + // Encode the transaction + txBytes, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return nil, "", err + } + + return txBytes, memo, nil +} diff --git a/broadcast/rpc.go b/broadcast/rpc.go new file mode 100644 index 0000000..0d04dd6 --- /dev/null +++ b/broadcast/rpc.go @@ -0,0 +1,155 @@ +package broadcast + +import ( + "context" + "fmt" + + coretypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/cosmos/ibc-go/modules/apps/callbacks/testing/simapp/params" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer" + ibc "github.com/cosmos/ibc-go/v8/modules/core" + meteoritebank "github.com/somatic-labs/meteorite/modules/bank" + meteoriteibc "github.com/somatic-labs/meteorite/modules/ibc" + wasm "github.com/somatic-labs/meteorite/modules/wasm" + types "github.com/somatic-labs/meteorite/types" + + sdkmath "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/std" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/gov" + + wasmd "github.com/CosmWasm/wasmd/x/wasm" +) + +// SendTransactionViaRPC sends a transaction using the provided TransactionParams and sequence number. +func SendTransactionViaRPC(txParams types.TransactionParams, sequence uint64) (response *coretypes.ResultBroadcastTx, txbody string, err error) { + encodingConfig := params.MakeTestEncodingConfig() + encodingConfig.Codec = cdc + + // Register IBC and other necessary types + transferModule := transfer.AppModuleBasic{} + ibcModule := ibc.AppModuleBasic{} + bankModule := bank.AppModuleBasic{} + wasmModule := wasmd.AppModuleBasic{} + govModule := gov.AppModuleBasic{} + + ibcModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) + transferModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) + bankModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) + wasmModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) + govModule.RegisterInterfaces(encodingConfig.InterfaceRegistry) + std.RegisterInterfaces(encodingConfig.InterfaceRegistry) + + // Create a new TxBuilder. + txBuilder := encodingConfig.TxConfig.NewTxBuilder() + + var msg sdk.Msg + var memo string // Declare a variable to hold the memo + + switch txParams.MsgType { + case "ibc_transfer": + msg, memo, err = meteoriteibc.CreateIBCTransferMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + if err != nil { + return nil, "", err + } + case "bank_send": + msg, memo, err = meteoritebank.CreateBankSendMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + if err != nil { + return nil, "", err + } + case "store_code": + msg, memo, err = wasm.CreateStoreCodeMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + if err != nil { + return nil, "", err + } + case "instantiate_contract": + msg, memo, err = wasm.CreateInstantiateContractMsg(txParams.Config, txParams.AcctAddress, txParams.MsgParams) + if err != nil { + return nil, "", err + } + default: + return nil, "", fmt.Errorf("unsupported message type: %s", txParams.MsgType) + } + + // Set messages + err = txBuilder.SetMsgs(msg) + if err != nil { + return nil, "", err + } + + // Estimate gas limit based on transaction size + txSize := len(msg.String()) + gasLimit := uint64((int64(txSize) * txParams.Config.Bytes) + txParams.Config.BaseGas) + txBuilder.SetGasLimit(gasLimit) + + // Calculate fee based on gas limit and a fixed gas price + gasPrice := sdk.NewDecCoinFromDec(txParams.Config.Denom, sdkmath.LegacyNewDecWithPrec(txParams.Config.Gas.Low, txParams.Config.Gas.Precision)) + feeAmount := gasPrice.Amount.MulInt64(int64(gasLimit)).RoundInt() + feecoin := sdk.NewCoin(txParams.Config.Denom, feeAmount) + txBuilder.SetFeeAmount(sdk.NewCoins(feecoin)) + + // Set the memo (either random for bank_send or as per IBC transfer) + txBuilder.SetMemo(memo) + txBuilder.SetTimeoutHeight(0) + + // First round: gather all the signer infos using the "set empty signature" hack + sigV2 := signing.SignatureV2{ + PubKey: txParams.PubKey, + Sequence: sequence, + Data: &signing.SingleSignatureData{ + SignMode: signing.SignMode(encodingConfig.TxConfig.SignModeHandler().DefaultMode()), + }, + } + + err = txBuilder.SetSignatures(sigV2) + if err != nil { + fmt.Println("Error setting signatures") + return nil, "", err + } + + signerData := authsigning.SignerData{ + ChainID: txParams.ChainID, + AccountNumber: txParams.AccNum, + Sequence: sequence, + } + + ctx := context.Background() + + signed, err := tx.SignWithPrivKey( + ctx, + signing.SignMode(encodingConfig.TxConfig.SignModeHandler().DefaultMode()), + signerData, + txBuilder, + txParams.PrivKey, + encodingConfig.TxConfig, + sequence, + ) + if err != nil { + fmt.Println("Couldn't sign") + return nil, "", err + } + + err = txBuilder.SetSignatures(signed) + if err != nil { + return nil, "", err + } + + // Generate the encoded transaction bytes + txBytes, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + fmt.Println(err) + return nil, "", err + } + + resp, err := Transaction(txBytes, txParams.NodeURL) + if err != nil { + return resp, string(txBytes), fmt.Errorf("failed to broadcast transaction: %w", err) + } + + return resp, string(txBytes), nil +} diff --git a/lib/lib.go b/lib/lib.go index 886ad90..c23b1e5 100644 --- a/lib/lib.go +++ b/lib/lib.go @@ -20,7 +20,7 @@ import ( ) var client = &http.Client{ - Timeout: 10 * time.Second, // Adjusted timeout to 10 seconds + Timeout: 1 * time.Second, // Adjusted timeout to 10 seconds Transport: &http.Transport{ MaxIdleConns: 100, // Increased maximum idle connections MaxIdleConnsPerHost: 10, // Increased maximum idle connections per host diff --git a/main.go b/main.go index 0d502b8..367ad58 100644 --- a/main.go +++ b/main.go @@ -70,6 +70,13 @@ func main() { MsgParams: msgParams, } + // ctx := context.Background() + + // _, err := client.NewGRPCClient(config.Nodes.GRPC) + // if err != nil { + // log.Fatalf("Failed to create gRPC client: %v", err) + // } + // Call the broadcast loop successfulTxns, failedTxns, responseCodes, _ := broadcastLoop(txParams, BatchSize) diff --git a/nodes.toml b/nodes.toml index 2b7b84d..bc34fc7 100644 --- a/nodes.toml +++ b/nodes.toml @@ -5,7 +5,7 @@ channel = "channel-1" denom = "uom" prefix = "mantra" gas_per_byte = 100 -base_gas = 41772713 +base_gas = 160000 ibc_memo = "Contract store spam test" memo = "Storing compiled contract with randomized memo" ibc_memo_repeat = 10 @@ -15,6 +15,9 @@ revision_number = 4 timeout_height = 21720608 slip44 = 118 +broadcast_mode = "grpc" # or "rpc" + + msg_type = "bank_send" [msg_params] @@ -42,3 +45,4 @@ precision = 3 [nodes] rpc = ["http://127.0.0.1:26657"] api = "https://api.canary.mantrachain.dev:443" +grpc = "localhost:9090" diff --git a/types/types.go b/types/types.go index 3689ff5..b3e752f 100644 --- a/types/types.go +++ b/types/types.go @@ -134,7 +134,7 @@ type Config struct { MsgParams MsgParams `toml:"msg_params"` Gas GasConfig `toml:"gas"` Nodes NodesConfig `toml:"nodes"` - API string `toml:"api"` + BroadcastMode string `toml:"broadcast_mode"` } type MsgParams struct { @@ -160,8 +160,9 @@ type GasConfig struct { } type NodesConfig struct { - RPC []string `toml:"rpc"` - API string `toml:"api"` + RPC []string `toml:"rpc"` + API string `toml:"api"` + GRPC string `toml:"grpc"` } type NodeInfo struct {