From b0f784d8f92bb3e8999f13d0f8ad4b8bfe76e401 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 24 Mar 2020 15:55:52 -0400 Subject: [PATCH 01/28] Implement factory and remove tx generator from CLIContext --- client/context/context.go | 11 +-- client/tx/factory.go | 166 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 client/tx/factory.go diff --git a/client/context/context.go b/client/context/context.go index 80813b7655cd..b552bb149a42 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -7,14 +7,12 @@ import ( "github.com/pkg/errors" "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" - "github.com/tendermint/tendermint/libs/cli" tmlite "github.com/tendermint/tendermint/lite" rpcclient "github.com/tendermint/tendermint/rpc/client" + yaml "gopkg.in/yaml.v2" "github.com/cosmos/cosmos-sdk/client/flags" - clientx "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" @@ -26,7 +24,6 @@ type CLIContext struct { FromAddress sdk.AccAddress Client rpcclient.Client ChainID string - TxGenerator clientx.Generator Marshaler codec.Marshaler Keybase keys.Keybase Input io.Reader @@ -138,12 +135,6 @@ func (ctx CLIContext) WithInput(r io.Reader) CLIContext { return ctx } -// WithTxGenerator returns a copy of the CLIContext with an updated TxGenerator. -func (ctx CLIContext) WithTxGenerator(txg clientx.Generator) CLIContext { - ctx.TxGenerator = txg - return ctx -} - // WithMarshaler returns a copy of the CLIContext with an updated Marshaler. func (ctx CLIContext) WithMarshaler(m codec.Marshaler) CLIContext { ctx.Marshaler = m diff --git a/client/tx/factory.go b/client/tx/factory.go new file mode 100644 index 000000000000..be1b27e85341 --- /dev/null +++ b/client/tx/factory.go @@ -0,0 +1,166 @@ +package tx + +import ( + "io" + "strings" + + "githuf.com/spf13/viper" + "githuf.com/tendermint/tendermint/crypto" + + "githuf.com/cosmos/cosmos-sdk/client/flags" + "githuf.com/cosmos/cosmos-sdk/crypto/keys" + sdk "githuf.com/cosmos/cosmos-sdk/types" +) + +// AccountRetriever defines the interfaces required for use by the Factory to +// ensure an account exists and to be able to query for account fields necessary +// for signing. +type AccountRetriever interface { + EnsureExists(addr sdk.AccAddress) error + GetAccountNumberSequence(addr sdk.AccAddress) (uint64, uint64, error) +} + +// Factory defines a client transaction factory that facilitates generating and +// signing an application-specific transaction. +type Factory struct { + keybase keys.Keybase + txGenerator Generator + accountRetriever AccountRetriever + feeFn func(gas uint64, amount sdk.Coins) sdk.Fee + sigFn func(pk crypto.PubKey, sig []byte) sdk.Signature + accountNumber uint64 + sequence uint64 + gas uint64 + gasAdjustment float64 + simulateAndExecute bool + chainID string + memo string + fees sdk.Coins + gasPrices sdk.DecCoins +} + +func NewFactoryFromCLI(input io.Reader) Factory { + kb, err := keys.NewKeyring( + sdk.KeyringServiceName(), + viper.GetString(flags.FlagKeyringBackend), + viper.GetString(flags.FlagHome), + input, + ) + if err != nil { + panic(err) + } + + f := Factory{ + keybase: kb, + accountNumber: viper.GetUint64(flags.FlagAccountNumber), + sequence: viper.GetUint64(flags.FlagSequence), + gas: flags.GasFlagVar.Gas, + gasAdjustment: viper.GetFloat64(flags.FlagGasAdjustment), + simulateAndExecute: flags.GasFlagVar.Simulate, + chainID: viper.GetString(flags.FlagChainID), + memo: viper.GetString(flags.FlagMemo), + } + + f = f.WithFees(viper.GetString(flags.FlagFees)) + f = f.WithGasPrices(viper.GetString(flags.FlagGasPrices)) + + return f +} + +// nolint +func (f Factory) AccountNumber() uint64 { return f.accountNumber } +func (f Factory) Sequence() uint64 { return f.sequence } +func (f Factory) Gas() uint64 { return f.gas } +func (f Factory) GasAdjustment() float64 { return f.gasAdjustment } +func (f Factory) Keybase() keys.Keybase { return f.keybase } +func (f Factory) ChainID() string { return f.chainID } +func (f Factory) Memo() string { return f.memo } +func (f Factory) Fees() sdk.Coins { return f.fees } +func (f Factory) GasPrices() sdk.DecCoins { return f.gasPrices } +func (f Factory) AccountRetriever() AccountRetriever { return f.accountRetriever } + +// SimulateAndExecute returns the option to simulate and then execute the transaction +// using the gas from the simulation results +func (f Factory) SimulateAndExecute() bool { return f.simulateAndExecute } + +// WithTxGenerator returns a copy of the Builder with an updated Generator. +func (f Factory) WithTxGenerator(g Generator) Factory { + f.txGenerator = g + return f +} + +// WithAccountRetriever returns a copy of the Builder with an updated AccountRetriever. +func (f Factory) WithAccountRetriever(ar AccountRetriever) Factory { + f.accountRetriever = ar + return f +} + +// WithChainID returns a copy of the Builder with an updated chainID. +func (f Factory) WithChainID(chainID string) Factory { + f.chainID = chainID + return f +} + +// WithGas returns a copy of the Builder with an updated gas value. +func (f Factory) WithGas(gas uint64) Factory { + f.gas = gas + return f +} + +// WithSigFn returns a copy of the Builder with an updated tx signature constructor. +func (f Factory) WithSigFn(sigFn func(pk crypto.PubKey, sig []byte) sdk.Signature) Factory { + f.sigFn = sigFn + return f +} + +// WithFeeFn returns a copy of the Builder with an updated fee constructor. +func (f Factory) WithFeeFn(feeFn func(gas uint64, amount sdk.Coins) sdk.Fee) Factory { + f.feeFn = feeFn + return f +} + +// WithFees returns a copy of the Builder with an updated fee. +func (f Factory) WithFees(fees string) Factory { + parsedFees, err := sdk.ParseCoins(fees) + if err != nil { + panic(err) + } + + f.fees = parsedFees + return f +} + +// WithGasPrices returns a copy of the Builder with updated gas prices. +func (f Factory) WithGasPrices(gasPrices string) Factory { + parsedGasPrices, err := sdk.ParseDecCoins(gasPrices) + if err != nil { + panic(err) + } + + f.gasPrices = parsedGasPrices + return f +} + +// WithKeybase returns a copy of the Builder with updated Keybase. +func (f Factory) WithKeybase(keybase keys.Keybase) Factory { + f.keybase = keybase + return f +} + +// WithSequence returns a copy of the Builder with an updated sequence number. +func (f Factory) WithSequence(sequence uint64) Factory { + f.sequence = sequence + return f +} + +// WithMemo returns a copy of the Builder with an updated memo. +func (f Factory) WithMemo(memo string) Factory { + f.memo = strings.TrimSpace(memo) + return f +} + +// WithAccountNumber returns a copy of the Builder with an updated account number. +func (f Factory) WithAccountNumber(accnum uint64) Factory { + f.accountNumber = accnum + return f +} From d4d4da4a6ee762a22c0edef94b4c26d8980c3b88 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 24 Mar 2020 16:36:12 -0400 Subject: [PATCH 02/28] Implement client and factory --- client/context/context.go | 1 + client/tx/factory.go | 6 +- client/tx/tx.go | 268 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+), 3 deletions(-) diff --git a/client/context/context.go b/client/context/context.go index b552bb149a42..2970dab6ed3e 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -254,6 +254,7 @@ func (ctx CLIContext) PrintOutput(toPrint interface{}) error { out, err = yaml.Marshal(&toPrint) case "json": + // TODO: Use ctx.Marshaler. if ctx.Indent { out, err = ctx.Codec.MarshalJSONIndent(toPrint, "", " ") } else { diff --git a/client/tx/factory.go b/client/tx/factory.go index be1b27e85341..a4d3897692ab 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -4,12 +4,12 @@ import ( "io" "strings" - "githuf.com/spf13/viper" - "githuf.com/tendermint/tendermint/crypto" - "githuf.com/cosmos/cosmos-sdk/client/flags" "githuf.com/cosmos/cosmos-sdk/crypto/keys" sdk "githuf.com/cosmos/cosmos-sdk/types" + + "githuf.com/spf13/viper" + "githuf.com/tendermint/tendermint/crypto" ) // AccountRetriever defines the interfaces required for use by the Factory to diff --git a/client/tx/tx.go b/client/tx/tx.go index be25ec7541c7..49dfb1cddf90 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -1,7 +1,19 @@ package tx import ( + "bufio" + "errors" + "fmt" + "os" + + "githuf.com/cosmos/cosmos-sdk/client/flags" + "githuf.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/input" + clientkeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -36,3 +48,259 @@ type ( CanonicalSignBytes(cid string, num, seq uint64) ([]byte, error) } ) + +// GenerateOrBroadcastTx will either generate and print and unsigned transaction +// or sign it and broadcast it returning an error upon failure. +func GenerateOrBroadcastTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error { + if ctx.GenerateOnly { + return GenerateTx(ctx, txf, msgs...) + } + + return BroadcastTx(ctx, txf, msgs...) +} + +// GenerateTx will generate an unsigned transaction and print it to the writer +// specified by ctx.Output. If simulation was requested, the gas will be +// simulated and also printed to the same writer before the transaction is +// printed. +func GenerateTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error { + if txf.SimulateAndExecute() { + if ctx.Offline { + return errors.New("cannot estimate gas in offline mode") + } + + txBytes, err := BuildSimTx(txf, msgs...) + if err != nil { + return err + } + + _, adjusted, err := CalculateGas(ctx.QueryWithData, txBytes, txf.GasAdjustment()) + if err != nil { + return err + } + + txf = txf.WithGas(adjusted) + _, _ = fmt.Fprintf(os.Stderr, "%s\n", GasEstimateResponse{GasEstimate: txf.Gas()}) + } + + tx, err := BuildUnsignedTx(txf, msgs...) + if err != nil { + return err + } + + out, err := ctx.Marshaler.MarshalJSON(tx) + if err != nil { + return err + } + + _, _ = fmt.Fprintf(ctx.Output, "%s\n", out) + return nil +} + +// BroadcastTx attempts to generate, sign and broadcast a transaction with the +// given set of messages. It will also simulate gas requirements if necessary. +// It will return an error upon failure. +func BroadcastTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error { + txf, err := PrepareFactory(ctx, txf) + if err != nil { + return err + } + + if txf.SimulateAndExecute() || ctx.Simulate { + txBytes, err := BuildSimTx(txf, msgs...) + if err != nil { + return err + } + + _, adjusted, err := CalculateGas(ctx.QueryWithData, txBytes, txf.GasAdjustment()) + if err != nil { + return err + } + + txf = txf.WithGas(adjusted) + _, _ = fmt.Fprintf(os.Stderr, "%s\n", GasEstimateResponse{GasEstimate: txf.Gas()}) + } + + if ctx.Simulate { + return nil + } + + tx, err := BuildUnsignedTx(txf, msgs...) + if err != nil { + return err + } + + if !ctx.SkipConfirm { + out, err := ctx.Marshaler.MarshalJSON(tx) + if err != nil { + return err + } + + _, _ = fmt.Fprintf(os.Stderr, "%s\n\n", out) + + buf := bufio.NewReader(os.Stdin) + ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf) + if err != nil || !ok { + _, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction") + return err + } + } + + txBytes, err := Sign(txf, ctx.GetFromName(), clientkeys.DefaultKeyPass, tx) + if err != nil { + return err + } + + // broadcast to a Tendermint node + res, err := ctx.BroadcastTx(txBytes) + if err != nil { + return err + } + + return ctx.PrintOutput(res) +} + +// BuildUnsignedTx builds a transaction to be signed given a set of messages. The +// transaction is initially created via the provided factory's generator. Once +// created, the fee, memo, and messages are set. +func BuildUnsignedTx(txf Factory, msgs ...sdk.Msg) (ClientTx, error) { + if txf.chainID == "" { + return nil, fmt.Errorf("chain ID required but not specified") + } + + fees := txf.fees + if !txf.gasPrices.IsZero() { + if !fees.IsZero() { + return nil, errors.New("cannot provide both fees and gas prices") + } + + glDec := sdk.NewDec(int64(txf.gas)) + + // Derive the fees based on the provided gas prices, where + // fee = ceil(gasPrice * gasLimit). + fees = make(sdk.Coins, len(txf.gasPrices)) + for i, gp := range txf.gasPrices { + fee := gp.Amount.Mul(glDec) + fees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) + } + } + + tx := txf.txGenerator.NewTx() + tx.SetFee(txf.feeFn(txf.gas, fees)) + tx.SetMsgs(msgs...) + tx.SetMemo(txf.memo) + tx.SetSignatures(nil) + + return tx, nil +} + +// BuildSimTx creates an unsigned tx with an empty single signature and returns +// the encoded transaction or an error if the unsigned transaction cannot be +// built. +func BuildSimTx(txf Factory, msgs ...sdk.Msg) ([]byte, error) { + tx, err := BuildUnsignedTx(txf, msgs...) + if err != nil { + return nil, err + } + + // Create an empty signature literal as the ante handler will populate with a + // sentinel pubkey. + tx.SetSignatures(txf.sigFn(nil, nil)) + + return tx.Marshal() +} + +// CalculateGas simulates the execution of a transaction and returns the +// simulation response obtained by the query and the adjusted gas amount. +func CalculateGas( + queryFunc func(string, []byte) ([]byte, int64, error), txBytes []byte, adjustment float64, +) (sdk.SimulationResponse, uint64, error) { + + rawRes, _, err := queryFunc("/app/simulate", txBytes) + if err != nil { + return sdk.SimulationResponse{}, 0, err + } + + // TODO: Use JSON or proto instead of codec.cdc + var simRes sdk.SimulationResponse + if err := codec.Cdc.UnmarshalBinaryBare(rawRes, &simRes); err != nil { + return sdk.SimulationResponse{}, 0, err + } + + return simRes, uint64(adjustment * float64(simRes.GasUsed)), nil +} + +// PrepareFactory ensures the account defined by ctx.GetFromAddress() exists and +// if the account number and/or the account sequence number are zero (not set), +// they will be queried for and set on the provided Factory. A new Factory with +// the updated fields will be returned. +func PrepareFactory(ctx context.CLIContext, txf Factory) (Factory, error) { + from := ctx.GetFromAddress() + + if err := txf.accountRetriever.EnsureExists(from); err != nil { + return txf, err + } + + initNum, initSeq := txf.accountNumber, txf.sequence + if initNum == 0 || initSeq == 0 { + num, seq, err := txf.accountRetriever.GetAccountNumberSequence(from) + if err != nil { + return txf, err + } + + if initNum == 0 { + txf = txf.WithAccountNumber(num) + } + if initSeq == 0 { + txf = txf.WithSequence(seq) + } + } + + return txf, nil +} + +// Sign signs a given tx with the provided name and passphrase. If the Factory's +// Keybase is not set, a new one will be created based on the client's backend. +// The bytes signed over are canconical. The resulting signature will be set on +// the transaction. Finally, the marshaled transaction is returned. An error is +// returned upon failure. +// +// Note, It is assumed the Factory has the necessary fields set that are required +// by the CanonicalSignBytes call. +func Sign(txf Factory, name, passphrase string, tx ClientTx) ([]byte, error) { + if txf.keybase == nil { + keybase, err := keys.NewKeyring( + sdk.KeyringServiceName(), + viper.GetString(flags.FlagKeyringBackend), + viper.GetString(flags.FlagHome), + os.Stdin, + ) + if err != nil { + return nil, err + } + + txf = txf.WithKeybase(keybase) + } + + signBytes, err := tx.CanonicalSignBytes(txf.chainID, txf.accountNumber, txf.sequence) + if err != nil { + return nil, err + } + + sigBytes, pubkey, err := txf.keybase.Sign(name, passphrase, signBytes) + if err != nil { + return nil, err + } + + tx.SetSignatures(txf.sigFn(pubkey, sigBytes)) + return tx.Marshal() +} + +// GasEstimateResponse defines a response definition for tx gas estimation. +type GasEstimateResponse struct { + GasEstimate uint64 `json:"gas_estimate" yaml:"gas_estimate"` +} + +func (gr GasEstimateResponse) String() string { + return fmt.Sprintf("gas estimate: %d", gr.GasEstimate) +} From 8bb0cfdf6f5f6d6147edb8d42f4594b1b618bd84 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 24 Mar 2020 16:45:34 -0400 Subject: [PATCH 03/28] Use JSON for SimulationResponse --- baseapp/abci.go | 9 +++++++-- client/tx/tx.go | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index e984fe0edf24..3342f1aebc61 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -1,6 +1,7 @@ package baseapp import ( + "encoding/json" "fmt" "os" "sort" @@ -9,7 +10,6 @@ import ( abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -336,10 +336,15 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res Result: res, } + bz, err := json.Marshal(simRes) + if err != nil { + return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to JSON encode simulation response")) + } + return abci.ResponseQuery{ Codespace: sdkerrors.RootCodespace, Height: req.Height, - Value: codec.Cdc.MustMarshalBinaryBare(simRes), + Value: bz, } case "version": diff --git a/client/tx/tx.go b/client/tx/tx.go index 49dfb1cddf90..638c7eeec6e8 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -2,6 +2,7 @@ package tx import ( "bufio" + "encoding/json" "errors" "fmt" "os" @@ -216,14 +217,13 @@ func CalculateGas( queryFunc func(string, []byte) ([]byte, int64, error), txBytes []byte, adjustment float64, ) (sdk.SimulationResponse, uint64, error) { - rawRes, _, err := queryFunc("/app/simulate", txBytes) + bz, _, err := queryFunc("/app/simulate", txBytes) if err != nil { return sdk.SimulationResponse{}, 0, err } - // TODO: Use JSON or proto instead of codec.cdc var simRes sdk.SimulationResponse - if err := codec.Cdc.UnmarshalBinaryBare(rawRes, &simRes); err != nil { + if err := json.Unmarshal(bz, &simRes); err != nil { return sdk.SimulationResponse{}, 0, err } From 0f4473936dc0a19d46c8a648918c5286e9ae1950 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 24 Mar 2020 16:51:59 -0400 Subject: [PATCH 04/28] Fix imports --- client/tx/factory.go | 10 +++++----- client/tx/tx.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/tx/factory.go b/client/tx/factory.go index a4d3897692ab..1bd4fd975211 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -4,12 +4,12 @@ import ( "io" "strings" - "githuf.com/cosmos/cosmos-sdk/client/flags" - "githuf.com/cosmos/cosmos-sdk/crypto/keys" - sdk "githuf.com/cosmos/cosmos-sdk/types" + "github.com/spf13/viper" + "github.com/tendermint/tendermint/crypto" - "githuf.com/spf13/viper" - "githuf.com/tendermint/tendermint/crypto" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" ) // AccountRetriever defines the interfaces required for use by the Factory to diff --git a/client/tx/tx.go b/client/tx/tx.go index 638c7eeec6e8..cb90d3aef0e1 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -7,10 +7,10 @@ import ( "fmt" "os" - "githuf.com/cosmos/cosmos-sdk/client/flags" - "githuf.com/spf13/viper" + "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/input" clientkeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/codec" From ef29fef5142ba72a27947e84600939a774c8579e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 24 Mar 2020 18:00:22 -0400 Subject: [PATCH 05/28] Fix test and update ADR --- baseapp/baseapp_test.go | 6 ++++-- .../adr-020-protobuf-transaction-encoding.md | 9 +++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 2da5b95f6f00..11d3e346c484 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -3,6 +3,7 @@ package baseapp import ( "bytes" "encoding/binary" + "encoding/json" "fmt" "os" "sync" @@ -936,12 +937,13 @@ func TestSimulateTx(t *testing.T) { require.True(t, queryResult.IsOK(), queryResult.Log) var simRes sdk.SimulationResponse - err = codec.Cdc.UnmarshalBinaryBare(queryResult.Value, &simRes) - require.NoError(t, err) + require.NoError(t, json.Unmarshal(queryResult.Value, &simRes)) + require.Equal(t, gInfo, simRes.GasInfo) require.Equal(t, result.Log, simRes.Result.Log) require.Equal(t, result.Events, simRes.Result.Events) require.True(t, bytes.Equal(result.Data, simRes.Result.Data)) + app.EndBlock(abci.RequestEndBlock{}) app.Commit() } diff --git a/docs/architecture/adr-020-protobuf-transaction-encoding.md b/docs/architecture/adr-020-protobuf-transaction-encoding.md index 6689f29e2356..184ddf6af5f7 100644 --- a/docs/architecture/adr-020-protobuf-transaction-encoding.md +++ b/docs/architecture/adr-020-protobuf-transaction-encoding.md @@ -123,11 +123,12 @@ type ClientTx interface { } ``` -We then update `CLIContext` to have two new fields: `Generator` and `Marshler`. +We then update `CLIContext` to have a new field: `Marshler`. -Then, each module will at the minimum accept a `Marshaler` instead of a concrete -Amino codec. If the module needs to work with any interface types, it will use -the `Codec` interface defined by the module which also extends `Marshaler`. +Then, each module client handler will at the minimum accept a `Marshaler` instead +of a concrete Amino codec and a `Generator`. If the module needs to work with any +interface types, it will use the `Codec` interface defined by the module which also +extends `Marshaler`. ## Future Improvements From 486fa5b4b0163cdb3e6c0f0da4b034810a77d192 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 24 Mar 2020 18:55:53 -0400 Subject: [PATCH 06/28] Fix constructors --- codec/std/tx.go | 6 ++++-- x/auth/types/stdtx.go | 7 ++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/codec/std/tx.go b/codec/std/tx.go index eca3553f306d..8305c751775f 100644 --- a/codec/std/tx.go +++ b/codec/std/tx.go @@ -122,9 +122,11 @@ func (tx Transaction) GetSignatures() []sdk.Signature { // SetSignatures sets the transaction's signatures. It will overwrite any // existing signatures set. func (tx *Transaction) SetSignatures(sdkSigs ...sdk.Signature) { - sigs := make([]auth.StdSignature, len(tx.Signatures)) + sigs := make([]auth.StdSignature, len(sdkSigs)) for i, sig := range sdkSigs { - sigs[i] = auth.NewStdSignature(sig.GetPubKey(), sig.GetSignature()) + if sig != nil { + sigs[i] = auth.NewStdSignature(sig.GetPubKey(), sig.GetSignature()) + } } tx.Signatures = sigs diff --git a/x/auth/types/stdtx.go b/x/auth/types/stdtx.go index 12e4aecd0bea..04e9ec1fdd79 100644 --- a/x/auth/types/stdtx.go +++ b/x/auth/types/stdtx.go @@ -59,7 +59,12 @@ func (fee StdFee) GasPrices() sdk.DecCoins { } func NewStdSignature(pk crypto.PubKey, sig []byte) StdSignature { - return StdSignature{PubKey: pk.Bytes(), Signature: sig} + var pkBz []byte + if pk != nil { + pkBz = pk.Bytes() + } + + return StdSignature{PubKey: pkBz, Signature: sig} } // GetSignature returns the raw signature bytes. From 33c090ee179a0e3ce6f5c83b33bcd7cc9832f7b3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 24 Mar 2020 18:56:20 -0400 Subject: [PATCH 07/28] Use auth instead of func interface --- client/tx/factory.go | 15 --------------- client/tx/tx.go | 7 ++++--- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/client/tx/factory.go b/client/tx/factory.go index 1bd4fd975211..55a4982cb8f0 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/spf13/viper" - "github.com/tendermint/tendermint/crypto" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -26,8 +25,6 @@ type Factory struct { keybase keys.Keybase txGenerator Generator accountRetriever AccountRetriever - feeFn func(gas uint64, amount sdk.Coins) sdk.Fee - sigFn func(pk crypto.PubKey, sig []byte) sdk.Signature accountNumber uint64 sequence uint64 gas uint64 @@ -107,18 +104,6 @@ func (f Factory) WithGas(gas uint64) Factory { return f } -// WithSigFn returns a copy of the Builder with an updated tx signature constructor. -func (f Factory) WithSigFn(sigFn func(pk crypto.PubKey, sig []byte) sdk.Signature) Factory { - f.sigFn = sigFn - return f -} - -// WithFeeFn returns a copy of the Builder with an updated fee constructor. -func (f Factory) WithFeeFn(feeFn func(gas uint64, amount sdk.Coins) sdk.Fee) Factory { - f.feeFn = feeFn - return f -} - // WithFees returns a copy of the Builder with an updated fee. func (f Factory) WithFees(fees string) Factory { parsedFees, err := sdk.ParseCoins(fees) diff --git a/client/tx/tx.go b/client/tx/tx.go index cb90d3aef0e1..dca9787c2a02 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -16,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" ) type ( @@ -187,7 +188,7 @@ func BuildUnsignedTx(txf Factory, msgs ...sdk.Msg) (ClientTx, error) { } tx := txf.txGenerator.NewTx() - tx.SetFee(txf.feeFn(txf.gas, fees)) + tx.SetFee(auth.NewStdFee(txf.gas, fees)) tx.SetMsgs(msgs...) tx.SetMemo(txf.memo) tx.SetSignatures(nil) @@ -206,7 +207,7 @@ func BuildSimTx(txf Factory, msgs ...sdk.Msg) ([]byte, error) { // Create an empty signature literal as the ante handler will populate with a // sentinel pubkey. - tx.SetSignatures(txf.sigFn(nil, nil)) + tx.SetSignatures(auth.NewStdSignature(nil, nil)) return tx.Marshal() } @@ -292,7 +293,7 @@ func Sign(txf Factory, name, passphrase string, tx ClientTx) ([]byte, error) { return nil, err } - tx.SetSignatures(txf.sigFn(pubkey, sigBytes)) + tx.SetSignatures(auth.NewStdSignature(pubkey, sigBytes)) return tx.Marshal() } From 70d024137309fbfcb87b18087f9dddfbf166ca0a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 24 Mar 2020 19:07:27 -0400 Subject: [PATCH 08/28] tx tests --- client/tx/tx.go | 2 +- client/tx/tx_test.go | 106 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 client/tx/tx_test.go diff --git a/client/tx/tx.go b/client/tx/tx.go index dca9787c2a02..a3ead89d6679 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -191,7 +191,7 @@ func BuildUnsignedTx(txf Factory, msgs ...sdk.Msg) (ClientTx, error) { tx.SetFee(auth.NewStdFee(txf.gas, fees)) tx.SetMsgs(msgs...) tx.SetMemo(txf.memo) - tx.SetSignatures(nil) + tx.SetSignatures() return tx, nil } diff --git a/client/tx/tx_test.go b/client/tx/tx_test.go new file mode 100644 index 000000000000..55688dba7ef6 --- /dev/null +++ b/client/tx/tx_test.go @@ -0,0 +1,106 @@ +package tx_test + +import ( + "encoding/json" + "errors" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec/std" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" +) + +func TestCalculateGas(t *testing.T) { + makeQueryFunc := func(gasUsed uint64, wantErr bool) func(string, []byte) ([]byte, int64, error) { + return func(string, []byte) ([]byte, int64, error) { + if wantErr { + return nil, 0, errors.New("query failed") + } + simRes := sdk.SimulationResponse{ + GasInfo: sdk.GasInfo{GasUsed: gasUsed, GasWanted: gasUsed}, + Result: &sdk.Result{Data: []byte("tx data"), Log: "log"}, + } + + bz, err := json.Marshal(simRes) + if err != nil { + return nil, 0, err + } + + return bz, 0, nil + } + } + + type args struct { + queryFuncGasUsed uint64 + queryFuncWantErr bool + adjustment float64 + } + + testCases := []struct { + name string + args args + wantEstimate uint64 + wantAdjusted uint64 + expPass bool + }{ + {"error", args{0, true, 1.2}, 0, 0, false}, + {"adjusted gas", args{10, false, 1.2}, 10, 12, true}, + } + + for _, tc := range testCases { + stc := tc + + t.Run(stc.name, func(t *testing.T) { + queryFunc := makeQueryFunc(stc.args.queryFuncGasUsed, stc.args.queryFuncWantErr) + simRes, gotAdjusted, err := tx.CalculateGas(queryFunc, []byte(""), stc.args.adjustment) + if stc.expPass { + require.NoError(t, err) + require.Equal(t, simRes.GasInfo.GasUsed, stc.wantEstimate) + require.Equal(t, gotAdjusted, stc.wantAdjusted) + require.NotNil(t, simRes.Result) + } else { + require.Error(t, err) + require.Nil(t, simRes.Result) + } + }) + } +} + +func TestBuildSimTx(t *testing.T) { + txf := tx.Factory{}. + WithTxGenerator(std.TxGenerator{}). + WithAccountNumber(50). + WithSequence(23). + WithFees("50stake"). + WithMemo("memo"). + WithChainID("test-chain") + + msg := bank.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil) + bz, err := tx.BuildSimTx(txf, msg) + require.NoError(t, err) + require.NotNil(t, bz) + + tx := &std.Transaction{} + require.NoError(t, tx.Unmarshal(bz)) + require.Equal(t, []sdk.Signature{sdk.Signature(auth.StdSignature{})}, tx.GetSignatures()) +} + +func TestBuildUnsignedTx(t *testing.T) { + txf := tx.Factory{}. + WithTxGenerator(std.TxGenerator{}). + WithAccountNumber(50). + WithSequence(23). + WithFees("50stake"). + WithMemo("memo"). + WithChainID("test-chain") + + msg := bank.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil) + tx, err := tx.BuildUnsignedTx(txf, msg) + require.NoError(t, err) + require.NotNil(t, tx) + require.Equal(t, []sdk.Signature{}, tx.GetSignatures()) +} From c4a4047ed2c924253e7f2f6245b6aa25c80e9a04 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 10:55:28 -0400 Subject: [PATCH 09/28] Implement NewSendTxCmd --- x/bank/client/cli/tx.go | 63 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/x/bank/client/cli/tx.go b/x/bank/client/cli/tx.go index 0764045968a7..906db932b3a8 100644 --- a/x/bank/client/cli/tx.go +++ b/x/bank/client/cli/tx.go @@ -8,6 +8,8 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + clientx "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -15,7 +17,65 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank/types" ) +// NewTxCmd returns a root CLI command handler for all x/bank transaction commands. +func NewTxCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + txCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Bank transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + NewSendTxCmd(m, txg, ar), + ) + + return txCmd +} + +// NewSendTxCmd returns a CLI command handler for creating a MsgSend transaction. +func NewSendTxCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobra.Command { + cmd := &cobra.Command{ + Use: "send [from_key_or_address] [to_address] [amount]", + Short: "Create and/or sign and broadcast a MsgSend transaction", + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) + txf := tx.NewFactoryFromCLI(inBuf). + WithTxGenerator(txg). + WithAccountRetriever(ar) + + cliCtx := context.NewCLIContextWithInputAndFrom(inBuf, args[0]).WithMarshaler(m) + + toAddr, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + coins, err := sdk.ParseCoins(args[2]) + if err != nil { + return err + } + + msg := types.NewMsgSend(cliCtx.GetFromAddress(), toAddr, coins) + return clientx.GenerateOrBroadcastTx(cliCtx, txf, msg) + }, + } + + return flags.PostCommands(cmd)[0] +} + +// --------------------------------------------------------------------------- +// Deprecated +// +// TODO: Remove once client-side Protobuf migration has been completed. +// --------------------------------------------------------------------------- + // GetTxCmd returns the transaction commands for this module +// +// TODO: Remove once client-side Protobuf migration has been completed. +// ref: https://github.com/cosmos/cosmos-sdk/issues/5864 func GetTxCmd(cdc *codec.Codec) *cobra.Command { txCmd := &cobra.Command{ Use: types.ModuleName, @@ -31,6 +91,9 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command { } // SendTxCmd will create a send tx and sign it with the given key. +// +// TODO: Remove once client-side Protobuf migration has been completed. +// ref: https://github.com/cosmos/cosmos-sdk/issues/5864 func SendTxCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "send [from_key_or_address] [to_address] [amount]", From f20db1c835a2142ba79d0d7ebeab0a2be901a685 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 11:03:36 -0400 Subject: [PATCH 10/28] Update ADR --- .../adr-020-protobuf-transaction-encoding.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/architecture/adr-020-protobuf-transaction-encoding.md b/docs/architecture/adr-020-protobuf-transaction-encoding.md index 184ddf6af5f7..d58df6fb5de2 100644 --- a/docs/architecture/adr-020-protobuf-transaction-encoding.md +++ b/docs/architecture/adr-020-protobuf-transaction-encoding.md @@ -125,10 +125,11 @@ type ClientTx interface { We then update `CLIContext` to have a new field: `Marshler`. -Then, each module client handler will at the minimum accept a `Marshaler` instead -of a concrete Amino codec and a `Generator`. If the module needs to work with any -interface types, it will use the `Codec` interface defined by the module which also -extends `Marshaler`. +Then, each module's client handler will at the minimum accept a `Marshaler` instead +of a concrete Amino codec and a `Generator` along with an `AccountRetriever` so +that account fields can be retrieved for signing. If the module needs to work with +any interface types, it will use the `Codec` interface defined by the module which +also extends `Marshaler`. ## Future Improvements From e77d5cb67787d34319b66d38b7cb2882844ff800 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 11:09:26 -0400 Subject: [PATCH 11/28] Update ADR + changelog --- CHANGELOG.md | 1 + docs/architecture/adr-020-protobuf-transaction-encoding.md | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f855ade9fe6..b3aacd0de6e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ older clients. ### API Breaking Changes +* (baseapp) [\#5865](https://github.com/cosmos/cosmos-sdk/pull/5865) The `SimulationResponse` returned from tx simulation is now JSON encoded instead of Amino binary. * [\#5719](https://github.com/cosmos/cosmos-sdk/pull/5719) Bump Go requirement to 1.14+ * (x/params) [\#5619](https://github.com/cosmos/cosmos-sdk/pull/5619) The `x/params` keeper now accepts a `codec.Marshaller` instead of a reference to an amino codec. Amino is still used for JSON serialization. diff --git a/docs/architecture/adr-020-protobuf-transaction-encoding.md b/docs/architecture/adr-020-protobuf-transaction-encoding.md index d58df6fb5de2..d3afaa7be512 100644 --- a/docs/architecture/adr-020-protobuf-transaction-encoding.md +++ b/docs/architecture/adr-020-protobuf-transaction-encoding.md @@ -103,6 +103,11 @@ to handle all the types, but also knows how to generate transactions, signatures and messages. ```go +type AccountRetriever interface { + EnsureExists(addr sdk.AccAddress) error + GetAccountNumberSequence(addr sdk.AccAddress) (uint64, uint64, error) +} + type Generator interface { NewTx() ClientTx } From d962526b700de7e771381bc7291292efe19c48fb Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 12:24:13 -0400 Subject: [PATCH 12/28] Implement and use CLIContext.Print --- client/context/context.go | 44 +++++++++++++++++++++++++++++++++++++-- client/tx/tx.go | 10 ++------- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/client/context/context.go b/client/context/context.go index 2970dab6ed3e..8b26da6e1439 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -1,6 +1,7 @@ package context import ( + "encoding/json" "fmt" "io" "os" @@ -240,9 +241,49 @@ func (ctx CLIContext) WithBroadcastMode(mode string) CLIContext { return ctx } +// Print outputs toPrint to the ctx.Output based on ctx.OutputFormat which is +// either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint +// will be JSON encoded using ctx.Marshaler. An error is returned upon failure. +func (ctx CLIContext) Print(toPrint interface{}) error { + var ( + out []byte + err error + ) + + switch ctx.OutputFormat { + case "text": + out, err = yaml.Marshal(&toPrint) + + case "json": + out, err = ctx.Marshaler.MarshalJSON(toPrint) + + // To JSON indent, we re-encode the already encoded JSON given there is no + // error. The re-encoded JSON uses the standard library as the initial encoded + // JSON should have the correct output produced by ctx.Marshaler. + if ctx.Indent && err == nil { + var generic interface{} + + err = json.Unmarshal(out, &generic) + if err == nil { + out, err = json.MarshalIndent(generic, "", " ") + } + } + } + + if err != nil { + return err + } + + _, err = fmt.Fprintf(ctx.Output, "%s\n", out) + return err +} + // PrintOutput prints output while respecting output and indent flags // NOTE: pass in marshalled structs that have been unmarshaled -// because this function will panic on marshaling errors +// because this function will panic on marshaling errors. +// +// TODO: Remove once client-side Protobuf migration has been completed. +// ref: https://github.com/cosmos/cosmos-sdk/issues/5864 func (ctx CLIContext) PrintOutput(toPrint interface{}) error { var ( out []byte @@ -254,7 +295,6 @@ func (ctx CLIContext) PrintOutput(toPrint interface{}) error { out, err = yaml.Marshal(&toPrint) case "json": - // TODO: Use ctx.Marshaler. if ctx.Indent { out, err = ctx.Codec.MarshalJSONIndent(toPrint, "", " ") } else { diff --git a/client/tx/tx.go b/client/tx/tx.go index a3ead89d6679..906c2647ec97 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -90,13 +90,7 @@ func GenerateTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error { return err } - out, err := ctx.Marshaler.MarshalJSON(tx) - if err != nil { - return err - } - - _, _ = fmt.Fprintf(ctx.Output, "%s\n", out) - return nil + return ctx.Print(tx) } // BroadcastTx attempts to generate, sign and broadcast a transaction with the @@ -159,7 +153,7 @@ func BroadcastTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error { return err } - return ctx.PrintOutput(res) + return ctx.Print(res) } // BuildUnsignedTx builds a transaction to be signed given a set of messages. The From d21020e0f8a85002bcd827efbfbc01497a69975f Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 12:27:03 -0400 Subject: [PATCH 13/28] Update x/bank CLI query commands --- x/bank/client/cli/query.go | 91 ++++++++++++++++++++++++++++++++++++++ x/bank/client/cli/tx.go | 4 +- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/x/bank/client/cli/query.go b/x/bank/client/cli/query.go index e7327a091eb4..b63b9eabae3e 100644 --- a/x/bank/client/cli/query.go +++ b/x/bank/client/cli/query.go @@ -18,7 +18,95 @@ const ( flagDenom = "denom" ) +// NewQueryCmd returns a root CLI command handler for all x/bank query commands. +func NewQueryCmd(m codec.Marshaler) *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the bank module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand(NewBalancesCmd(m)) + + return cmd +} + +// NewBalancesCmd returns a CLI command handler for querying account balance(s). +func NewBalancesCmd(m codec.Marshaler) *cobra.Command { + cmd := &cobra.Command{ + Use: "balances [address]", + Short: "Query for account balance(s) by address", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithMarshaler(m) + + addr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + var ( + params interface{} + result interface{} + route string + ) + + denom := viper.GetString(flagDenom) + if denom == "" { + params = types.NewQueryAllBalancesParams(addr) + route = fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAllBalances) + } else { + params = types.NewQueryBalanceParams(addr, denom) + route = fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryBalance) + } + + bz, err := m.MarshalJSON(params) + if err != nil { + return fmt.Errorf("failed to marshal params: %w", err) + } + + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + if denom == "" { + var balances sdk.Coins + if err := m.UnmarshalJSON(res, &balances); err != nil { + return err + } + + result = balances + } else { + var balance sdk.Coin + if err := m.UnmarshalJSON(res, &balance); err != nil { + return err + } + + result = balance + } + + return cliCtx.Print(result) + }, + } + + cmd.Flags().String(flagDenom, "", "The specific balance denomination to query for") + + return flags.GetCommands(cmd)[0] +} + +// --------------------------------------------------------------------------- +// Deprecated +// +// TODO: Remove once client-side Protobuf migration has been completed. +// --------------------------------------------------------------------------- + // GetQueryCmd returns the parent querying command for the bank module. +// +// TODO: Remove once client-side Protobuf migration has been completed. +// ref: https://github.com/cosmos/cosmos-sdk/issues/5864 func GetQueryCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, @@ -35,6 +123,9 @@ func GetQueryCmd(cdc *codec.Codec) *cobra.Command { // GetAccountCmd returns a CLI command handler that facilitates querying for a // single or all account balances by address. +// +// TODO: Remove once client-side Protobuf migration has been completed. +// ref: https://github.com/cosmos/cosmos-sdk/issues/5864 func GetBalancesCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "balances [address]", diff --git a/x/bank/client/cli/tx.go b/x/bank/client/cli/tx.go index 906db932b3a8..6f59ec426dfe 100644 --- a/x/bank/client/cli/tx.go +++ b/x/bank/client/cli/tx.go @@ -27,9 +27,7 @@ func NewTxCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobr RunE: client.ValidateCmd, } - txCmd.AddCommand( - NewSendTxCmd(m, txg, ar), - ) + txCmd.AddCommand( NewSendTxCmd(m, txg, ar), ) return txCmd } From 7e67d84c0e95aa2189c748adcd492e8761617afa Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 12:30:10 -0400 Subject: [PATCH 14/28] Lint --- x/bank/client/cli/tx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/bank/client/cli/tx.go b/x/bank/client/cli/tx.go index 6f59ec426dfe..92341a371a5f 100644 --- a/x/bank/client/cli/tx.go +++ b/x/bank/client/cli/tx.go @@ -27,7 +27,7 @@ func NewTxCmd(m codec.Marshaler, txg tx.Generator, ar tx.AccountRetriever) *cobr RunE: client.ValidateCmd, } - txCmd.AddCommand( NewSendTxCmd(m, txg, ar), ) + txCmd.AddCommand(NewSendTxCmd(m, txg, ar)) return txCmd } From 3ecc4613850e4bb620a9b0c55235e4200cbb39d7 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 13:03:14 -0400 Subject: [PATCH 15/28] Implement and use MarshalIndentFromJSON --- client/context/context.go | 8 +------- types/utils.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/client/context/context.go b/client/context/context.go index 8b26da6e1439..f3746e31c0a8 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -1,7 +1,6 @@ package context import ( - "encoding/json" "fmt" "io" "os" @@ -261,12 +260,7 @@ func (ctx CLIContext) Print(toPrint interface{}) error { // error. The re-encoded JSON uses the standard library as the initial encoded // JSON should have the correct output produced by ctx.Marshaler. if ctx.Indent && err == nil { - var generic interface{} - - err = json.Unmarshal(out, &generic) - if err == nil { - out, err = json.MarshalIndent(generic, "", " ") - } + out, err = sdk.MarshalIndentFromJSON(out) } } diff --git a/types/utils.go b/types/utils.go index be33160261d5..56639e636f5c 100644 --- a/types/utils.go +++ b/types/utils.go @@ -14,6 +14,19 @@ var ( DBBackend = "" ) +// MarshalIndentFromJSON returns indented JSON-encoded bytes from already encoded +// JSON bytes. The output encoding will adhere to the original input's encoding +// (e.g. Proto3). +func MarshalIndentFromJSON(bz []byte) ([]byte, error) { + var generic interface{} + + if err := json.Unmarshal(bz, &generic); err == nil { + return nil, err + } + + return json.MarshalIndent(generic, "", " ") +} + // SortedJSON takes any JSON and returns it sorted by keys. Also, all white-spaces // are removed. // This method can be used to canonicalize JSON to be returned by GetSignBytes, From 17b68fb365253ebe76ceb3a7d08237b3a946587b Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 13:20:29 -0400 Subject: [PATCH 16/28] Prep PostProcessResponseBare and PostProcessResponse --- types/rest/rest.go | 68 ++++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/types/rest/rest.go b/types/rest/rest.go index 258917586248..ca79eb7175ce 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -229,21 +229,31 @@ func ParseQueryHeightOrReturnBadRequest(w http.ResponseWriter, cliCtx context.CL // PostProcessResponseBare post processes a body similar to PostProcessResponse // except it does not wrap the body and inject the height. -func PostProcessResponseBare(w http.ResponseWriter, cliCtx context.CLIContext, body interface{}) { +func PostProcessResponseBare(w http.ResponseWriter, ctx context.CLIContext, body interface{}) { var ( resp []byte err error ) + // TODO: Remove once client-side Protobuf migration has been completed. + // ref: https://github.com/cosmos/cosmos-sdk/issues/5864 + var marshaler codec.JSONMarshaler + + if ctx.Marshaler != nil { + marshaler = ctx.Marshaler + } else { + marshaler = ctx.Codec + } + switch b := body.(type) { case []byte: resp = b default: - if cliCtx.Indent { - resp, err = cliCtx.Codec.MarshalJSONIndent(body, "", " ") - } else { - resp, err = cliCtx.Codec.MarshalJSON(body) + resp, err = marshaler.MarshalJSON(body) + + if ctx.Indent && err == nil { + resp, err = sdk.MarshalIndentFromJSON(resp) } if err != nil { @@ -259,24 +269,36 @@ func PostProcessResponseBare(w http.ResponseWriter, cliCtx context.CLIContext, b // PostProcessResponse performs post processing for a REST response. The result // returned to clients will contain two fields, the height at which the resource // was queried at and the original result. -func PostProcessResponse(w http.ResponseWriter, cliCtx context.CLIContext, resp interface{}) { - var result []byte +func PostProcessResponse(w http.ResponseWriter, ctx context.CLIContext, resp interface{}) { + var ( + result []byte + err error + ) - if cliCtx.Height < 0 { + if ctx.Height < 0 { WriteErrorResponse(w, http.StatusInternalServerError, fmt.Errorf("negative height in response").Error()) return } + // TODO: Remove once client-side Protobuf migration has been completed. + // ref: https://github.com/cosmos/cosmos-sdk/issues/5864 + var marshaler codec.JSONMarshaler + + if ctx.Marshaler != nil { + marshaler = ctx.Marshaler + } else { + marshaler = ctx.Codec + } + switch res := resp.(type) { case []byte: result = res default: - var err error - if cliCtx.Indent { - result, err = cliCtx.Codec.MarshalJSONIndent(resp, "", " ") - } else { - result, err = cliCtx.Codec.MarshalJSON(resp) + result, err = marshaler.MarshalJSON(resp) + + if ctx.Indent && err == nil { + result, err = sdk.MarshalIndentFromJSON(result) } if err != nil { @@ -285,17 +307,11 @@ func PostProcessResponse(w http.ResponseWriter, cliCtx context.CLIContext, resp } } - wrappedResp := NewResponseWithHeight(cliCtx.Height, result) - - var ( - output []byte - err error - ) + wrappedResp := NewResponseWithHeight(ctx.Height, result) - if cliCtx.Indent { - output, err = cliCtx.Codec.MarshalJSONIndent(wrappedResp, "", " ") - } else { - output, err = cliCtx.Codec.MarshalJSON(wrappedResp) + output, err := marshaler.MarshalJSON(wrappedResp) + if ctx.Indent && err == nil { + output, err = sdk.MarshalIndentFromJSON(output) } if err != nil { @@ -312,10 +328,12 @@ func PostProcessResponse(w http.ResponseWriter, cliCtx context.CLIContext, resp // default limit can be provided. func ParseHTTPArgsWithLimit(r *http.Request, defaultLimit int) (tags []string, page, limit int, err error) { tags = make([]string, 0, len(r.Form)) + for key, values := range r.Form { if key == "page" || key == "limit" { continue } + var value string value, err = url.QueryUnescape(values[0]) if err != nil { @@ -326,13 +344,17 @@ func ParseHTTPArgsWithLimit(r *http.Request, defaultLimit int) (tags []string, p switch key { case types.TxHeightKey: tag = fmt.Sprintf("%s=%s", key, value) + case TxMinHeightKey: tag = fmt.Sprintf("%s>=%s", types.TxHeightKey, value) + case TxMaxHeightKey: tag = fmt.Sprintf("%s<=%s", types.TxHeightKey, value) + default: tag = fmt.Sprintf("%s='%s'", key, value) } + tags = append(tags, tag) } From 0e83e328d029b05ba1a958d26a18cc801ec1ce9f Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 13:22:29 -0400 Subject: [PATCH 17/28] Prep QueryBalancesRequestHandlerFn for proto --- x/bank/client/rest/query.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/x/bank/client/rest/query.go b/x/bank/client/rest/query.go index 99b3680bbb16..baf1cbe42922 100644 --- a/x/bank/client/rest/query.go +++ b/x/bank/client/rest/query.go @@ -7,6 +7,7 @@ import ( "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -14,7 +15,7 @@ import ( // QueryBalancesRequestHandlerFn returns a REST handler that queries for all // account balances or a specific balance by denomination. -func QueryBalancesRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func QueryBalancesRequestHandlerFn(ctx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") vars := mux.Vars(r) @@ -26,7 +27,7 @@ func QueryBalancesRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + ctx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, ctx, r) if !ok { return } @@ -36,6 +37,16 @@ func QueryBalancesRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { route string ) + // TODO: Remove once client-side Protobuf migration has been completed. + // ref: https://github.com/cosmos/cosmos-sdk/issues/5864 + var marshaler codec.JSONMarshaler + + if ctx.Marshaler != nil { + marshaler = ctx.Marshaler + } else { + marshaler = ctx.Codec + } + denom := r.FormValue("denom") if denom == "" { params = types.NewQueryAllBalancesParams(addr) @@ -45,19 +56,19 @@ func QueryBalancesRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { route = fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryBalance) } - bz, err := cliCtx.Codec.MarshalJSON(params) + bz, err := marshaler.MarshalJSON(params) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - res, height, err := cliCtx.QueryWithData(route, bz) + res, height, err := ctx.QueryWithData(route, bz) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + ctx = ctx.WithHeight(height) + rest.PostProcessResponse(w, ctx, res) } } From 3e0cf99e81870dadf1640c2115761051d9a61a6b Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 14:23:34 -0400 Subject: [PATCH 18/28] Finish remaining x/bank REST handlers --- client/tx/factory.go | 33 +++++++++----- client/tx/tx.go | 90 ++++++++++++++++++++++++++++++++------ client/tx/tx_test.go | 3 +- types/rest/rest.go | 11 ++--- x/bank/client/rest/rest.go | 15 +++++++ x/bank/client/rest/tx.go | 47 ++++++++++++++++++++ 6 files changed, 169 insertions(+), 30 deletions(-) diff --git a/client/tx/factory.go b/client/tx/factory.go index 55a4982cb8f0..00f2b135ed8a 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -80,31 +80,31 @@ func (f Factory) AccountRetriever() AccountRetriever { return f.accountRetriever // using the gas from the simulation results func (f Factory) SimulateAndExecute() bool { return f.simulateAndExecute } -// WithTxGenerator returns a copy of the Builder with an updated Generator. +// WithTxGenerator returns a copy of the Factory with an updated Generator. func (f Factory) WithTxGenerator(g Generator) Factory { f.txGenerator = g return f } -// WithAccountRetriever returns a copy of the Builder with an updated AccountRetriever. +// WithAccountRetriever returns a copy of the Factory with an updated AccountRetriever. func (f Factory) WithAccountRetriever(ar AccountRetriever) Factory { f.accountRetriever = ar return f } -// WithChainID returns a copy of the Builder with an updated chainID. +// WithChainID returns a copy of the Factory with an updated chainID. func (f Factory) WithChainID(chainID string) Factory { f.chainID = chainID return f } -// WithGas returns a copy of the Builder with an updated gas value. +// WithGas returns a copy of the Factory with an updated gas value. func (f Factory) WithGas(gas uint64) Factory { f.gas = gas return f } -// WithFees returns a copy of the Builder with an updated fee. +// WithFees returns a copy of the Factory with an updated fee. func (f Factory) WithFees(fees string) Factory { parsedFees, err := sdk.ParseCoins(fees) if err != nil { @@ -115,7 +115,7 @@ func (f Factory) WithFees(fees string) Factory { return f } -// WithGasPrices returns a copy of the Builder with updated gas prices. +// WithGasPrices returns a copy of the Factory with updated gas prices. func (f Factory) WithGasPrices(gasPrices string) Factory { parsedGasPrices, err := sdk.ParseDecCoins(gasPrices) if err != nil { @@ -126,26 +126,39 @@ func (f Factory) WithGasPrices(gasPrices string) Factory { return f } -// WithKeybase returns a copy of the Builder with updated Keybase. +// WithKeybase returns a copy of the Factory with updated Keybase. func (f Factory) WithKeybase(keybase keys.Keybase) Factory { f.keybase = keybase return f } -// WithSequence returns a copy of the Builder with an updated sequence number. +// WithSequence returns a copy of the Factory with an updated sequence number. func (f Factory) WithSequence(sequence uint64) Factory { f.sequence = sequence return f } -// WithMemo returns a copy of the Builder with an updated memo. +// WithMemo returns a copy of the Factory with an updated memo. func (f Factory) WithMemo(memo string) Factory { f.memo = strings.TrimSpace(memo) return f } -// WithAccountNumber returns a copy of the Builder with an updated account number. +// WithAccountNumber returns a copy of the Factory with an updated account number. func (f Factory) WithAccountNumber(accnum uint64) Factory { f.accountNumber = accnum return f } + +// WithGasAdjustment returns a copy of the Factory with an updated gas adjustment. +func (f Factory) WithGasAdjustment(gasAdj float64) Factory { + f.gasAdjustment = gasAdj + return f +} + +// WithSimulateAndExecute returns a copy of the Factory with an updated gas +// simulation value. +func (f Factory) WithSimulateAndExecute(sim bool) Factory { + f.simulateAndExecute = sim + return f +} diff --git a/client/tx/tx.go b/client/tx/tx.go index 906c2647ec97..71c57a3180d0 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "os" "github.com/spf13/viper" @@ -16,7 +17,9 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/types" ) type ( @@ -71,12 +74,7 @@ func GenerateTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error { return errors.New("cannot estimate gas in offline mode") } - txBytes, err := BuildSimTx(txf, msgs...) - if err != nil { - return err - } - - _, adjusted, err := CalculateGas(ctx.QueryWithData, txBytes, txf.GasAdjustment()) + _, adjusted, err := CalculateGas(ctx.QueryWithData, txf, msgs...) if err != nil { return err } @@ -103,12 +101,7 @@ func BroadcastTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error { } if txf.SimulateAndExecute() || ctx.Simulate { - txBytes, err := BuildSimTx(txf, msgs...) - if err != nil { - return err - } - - _, adjusted, err := CalculateGas(ctx.QueryWithData, txBytes, txf.GasAdjustment()) + _, adjusted, err := CalculateGas(ctx.QueryWithData, txf, msgs...) if err != nil { return err } @@ -156,6 +149,70 @@ func BroadcastTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error { return ctx.Print(res) } +// WriteGeneratedTxResponse writes a generated unsigned transaction to the +// provided http.ResponseWriter. It will simulate gas costs if requested by the +// BaseReq. Upon any error, the error will be written to the http.ResponseWriter. +func WriteGeneratedTxResponse( + ctx context.CLIContext, w http.ResponseWriter, txg Generator, br rest.BaseReq, msgs ...sdk.Msg, +) { + + gasAdj, ok := rest.ParseFloat64OrReturnBadRequest(w, br.GasAdjustment, flags.DefaultGasAdjustment) + if !ok { + return + } + + simAndExec, gas, err := flags.ParseGas(br.Gas) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txf := Factory{fees: br.Fees, gasPrices: br.GasPrices}. + WithAccountNumber(br.AccountNumber). + WithSequence(br.Sequence). + WithGas(gas). + WithGasAdjustment(gasAdj). + WithMemo(br.Memo). + WithChainID(br.ChainID). + WithSimulateAndExecute(br.Simulate) + + if br.Simulate || simAndExec { + if gasAdj < 0 { + rest.WriteErrorResponse(w, http.StatusBadRequest, types.ErrorInvalidGasAdjustment.Error()) + return + } + + _, adjusted, err := CalculateGas(ctx.QueryWithData, txf, msgs...) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + txf = txf.WithGas(adjusted) + + if br.Simulate { + rest.WriteSimulationResponse(w, ctx.Marshaler, txf.Gas()) + return + } + } + + tx, err := BuildUnsignedTx(txf, msgs...) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + output, err := ctx.Marshaler.MarshalJSON(tx) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write(output) +} + // BuildUnsignedTx builds a transaction to be signed given a set of messages. The // transaction is initially created via the provided factory's generator. Once // created, the fee, memo, and messages are set. @@ -209,9 +266,14 @@ func BuildSimTx(txf Factory, msgs ...sdk.Msg) ([]byte, error) { // CalculateGas simulates the execution of a transaction and returns the // simulation response obtained by the query and the adjusted gas amount. func CalculateGas( - queryFunc func(string, []byte) ([]byte, int64, error), txBytes []byte, adjustment float64, + queryFunc func(string, []byte) ([]byte, int64, error), txf Factory, msgs ...sdk.Msg, ) (sdk.SimulationResponse, uint64, error) { + txBytes, err := BuildSimTx(txf, msgs...) + if err != nil { + return sdk.SimulationResponse{}, 0, err + } + bz, _, err := queryFunc("/app/simulate", txBytes) if err != nil { return sdk.SimulationResponse{}, 0, err @@ -222,7 +284,7 @@ func CalculateGas( return sdk.SimulationResponse{}, 0, err } - return simRes, uint64(adjustment * float64(simRes.GasUsed)), nil + return simRes, uint64(txf.GasAdjustment() * float64(simRes.GasUsed)), nil } // PrepareFactory ensures the account defined by ctx.GetFromAddress() exists and diff --git a/client/tx/tx_test.go b/client/tx/tx_test.go index 55688dba7ef6..6fb3ceafb477 100644 --- a/client/tx/tx_test.go +++ b/client/tx/tx_test.go @@ -53,10 +53,11 @@ func TestCalculateGas(t *testing.T) { for _, tc := range testCases { stc := tc + txf := tx.Factory{}.WithChainID("test-chain").WithTxGenerator(std.TxGenerator{}) t.Run(stc.name, func(t *testing.T) { queryFunc := makeQueryFunc(stc.args.queryFuncGasUsed, stc.args.queryFuncWantErr) - simRes, gotAdjusted, err := tx.CalculateGas(queryFunc, []byte(""), stc.args.adjustment) + simRes, gotAdjusted, err := tx.CalculateGas(queryFunc, txf.WithGasAdjustment(stc.args.adjustment)) if stc.expPass { require.NoError(t, err) require.Equal(t, simRes.GasInfo.GasUsed, stc.wantEstimate) diff --git a/types/rest/rest.go b/types/rest/rest.go index ca79eb7175ce..8f90855f986a 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -119,16 +119,16 @@ func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { return true } -// ReadRESTReq reads and unmarshals a Request's body to the the BaseReq stuct. +// ReadRESTReq reads and unmarshals a Request's body to the the BaseReq struct. // Writes an error response to ResponseWriter and returns true if errors occurred. -func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) bool { +func ReadRESTReq(w http.ResponseWriter, r *http.Request, m codec.JSONMarshaler, req interface{}) bool { body, err := ioutil.ReadAll(r.Body) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return false } - err = cdc.UnmarshalJSON(body, req) + err = m.UnmarshalJSON(body, req) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to decode JSON payload: %s", err)) return false @@ -158,9 +158,10 @@ func WriteErrorResponse(w http.ResponseWriter, status int, err string) { // WriteSimulationResponse prepares and writes an HTTP // response for transactions simulations. -func WriteSimulationResponse(w http.ResponseWriter, cdc *codec.Codec, gas uint64) { +func WriteSimulationResponse(w http.ResponseWriter, m codec.JSONMarshaler, gas uint64) { gasEst := GasEstimateResponse{GasEstimate: gas} - resp, err := cdc.MarshalJSON(gasEst) + + resp, err := m.MarshalJSON(gasEst) if err != nil { WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return diff --git a/x/bank/client/rest/rest.go b/x/bank/client/rest/rest.go index 578e41c06712..48e2776c8765 100644 --- a/x/bank/client/rest/rest.go +++ b/x/bank/client/rest/rest.go @@ -4,8 +4,23 @@ import ( "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" ) +// RegisterHandlers registers all x/bank transaction and query HTTP REST handlers +// on the provided mux router. +func RegisterHandlers(ctx context.CLIContext, m codec.Marshaler, txg tx.Generator, r *mux.Router) { + r.HandleFunc("/bank/accounts/{address}/transfers", NewSendRequestHandlerFn(ctx, m, txg)).Methods("POST") + r.HandleFunc("/bank/balances/{address}", QueryBalancesRequestHandlerFn(ctx)).Methods("GET") +} + +// --------------------------------------------------------------------------- +// Deprecated +// +// TODO: Remove once client-side Protobuf migration has been completed. +// --------------------------------------------------------------------------- + // RegisterRoutes - Central function to define routes that get registered by the main application func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { r.HandleFunc("/bank/accounts/{address}/transfers", SendRequestHandlerFn(cliCtx)).Methods("POST") diff --git a/x/bank/client/rest/tx.go b/x/bank/client/rest/tx.go index 30178166bb5f..b9712c160acf 100644 --- a/x/bank/client/rest/tx.go +++ b/x/bank/client/rest/tx.go @@ -6,6 +6,8 @@ import ( "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" @@ -18,7 +20,52 @@ type SendReq struct { Amount sdk.Coins `json:"amount" yaml:"amount"` } +// NewSendRequestHandlerFn returns an HTTP REST handler for creating a MsgSend +// transaction. +func NewSendRequestHandlerFn(ctx context.CLIContext, m codec.Marshaler, txg tx.Generator) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx = ctx.WithMarshaler(m) + + vars := mux.Vars(r) + bech32Addr := vars["address"] + + toAddr, err := sdk.AccAddressFromBech32(bech32Addr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + var req SendReq + if !rest.ReadRESTReq(w, r, ctx.Marshaler, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + msg := types.NewMsgSend(fromAddr, toAddr, req.Amount) + tx.WriteGeneratedTxResponse(ctx, w, txg, req.BaseReq, msg) + } +} + +// --------------------------------------------------------------------------- +// Deprecated +// +// TODO: Remove once client-side Protobuf migration has been completed. +// --------------------------------------------------------------------------- + // SendRequestHandlerFn - http request handler to send coins to a address. +// +// TODO: Remove once client-side Protobuf migration has been completed. +// ref: https://github.com/cosmos/cosmos-sdk/issues/5864 func SendRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) From f4e6bd6ad7c1646f1fbd99c14c164817e9b9aefc Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 14:30:42 -0400 Subject: [PATCH 19/28] Fix build --- client/tx/factory.go | 10 +++++----- client/tx/tx.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/tx/factory.go b/client/tx/factory.go index 00f2b135ed8a..c4f08805e049 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -7,7 +7,7 @@ import ( "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -22,7 +22,7 @@ type AccountRetriever interface { // Factory defines a client transaction factory that facilitates generating and // signing an application-specific transaction. type Factory struct { - keybase keys.Keybase + keybase keyring.Keybase txGenerator Generator accountRetriever AccountRetriever accountNumber uint64 @@ -37,7 +37,7 @@ type Factory struct { } func NewFactoryFromCLI(input io.Reader) Factory { - kb, err := keys.NewKeyring( + kb, err := keyring.NewKeyring( sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), @@ -69,7 +69,7 @@ func (f Factory) AccountNumber() uint64 { return f.accountNumber } func (f Factory) Sequence() uint64 { return f.sequence } func (f Factory) Gas() uint64 { return f.gas } func (f Factory) GasAdjustment() float64 { return f.gasAdjustment } -func (f Factory) Keybase() keys.Keybase { return f.keybase } +func (f Factory) Keybase() keyring.Keybase { return f.keybase } func (f Factory) ChainID() string { return f.chainID } func (f Factory) Memo() string { return f.memo } func (f Factory) Fees() sdk.Coins { return f.fees } @@ -127,7 +127,7 @@ func (f Factory) WithGasPrices(gasPrices string) Factory { } // WithKeybase returns a copy of the Factory with updated Keybase. -func (f Factory) WithKeybase(keybase keys.Keybase) Factory { +func (f Factory) WithKeybase(keybase keyring.Keybase) Factory { f.keybase = keybase return f } diff --git a/client/tx/tx.go b/client/tx/tx.go index 71c57a3180d0..750f40b3f36a 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -15,7 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/input" clientkeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/auth" @@ -326,7 +326,7 @@ func PrepareFactory(ctx context.CLIContext, txf Factory) (Factory, error) { // by the CanonicalSignBytes call. func Sign(txf Factory, name, passphrase string, tx ClientTx) ([]byte, error) { if txf.keybase == nil { - keybase, err := keys.NewKeyring( + keybase, err := keyring.NewKeyring( sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), From e950dd34aa383c8b1aa3a7e1386aa5cd1a678f98 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 15:12:39 -0400 Subject: [PATCH 20/28] Error when keybase is nil during Sign --- client/tx/tx.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/client/tx/tx.go b/client/tx/tx.go index 750f40b3f36a..1d1d6a101697 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -326,17 +326,7 @@ func PrepareFactory(ctx context.CLIContext, txf Factory) (Factory, error) { // by the CanonicalSignBytes call. func Sign(txf Factory, name, passphrase string, tx ClientTx) ([]byte, error) { if txf.keybase == nil { - keybase, err := keyring.NewKeyring( - sdk.KeyringServiceName(), - viper.GetString(flags.FlagKeyringBackend), - viper.GetString(flags.FlagHome), - os.Stdin, - ) - if err != nil { - return nil, err - } - - txf = txf.WithKeybase(keybase) + return nil, errors.New("keybase must be set prior to signing a transaction") } signBytes, err := tx.CanonicalSignBytes(txf.chainID, txf.accountNumber, txf.sequence) From 6a33b424a6068fe70f434d8653bf263ad9e9f1e3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 15:15:43 -0400 Subject: [PATCH 21/28] Do not sanitize memo --- client/tx/factory.go | 3 +-- client/tx/tx.go | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/client/tx/factory.go b/client/tx/factory.go index c4f08805e049..4b05b2419a42 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -2,7 +2,6 @@ package tx import ( "io" - "strings" "github.com/spf13/viper" @@ -140,7 +139,7 @@ func (f Factory) WithSequence(sequence uint64) Factory { // WithMemo returns a copy of the Factory with an updated memo. func (f Factory) WithMemo(memo string) Factory { - f.memo = strings.TrimSpace(memo) + f.memo = memo return f } diff --git a/client/tx/tx.go b/client/tx/tx.go index 1d1d6a101697..593f13a2a0f5 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -8,14 +8,11 @@ import ( "net/http" "os" - "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/input" clientkeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/auth" From 75412d00f87400032384b7c2c90c8c58980034e3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 15:17:12 -0400 Subject: [PATCH 22/28] Rename to Println --- client/context/context.go | 4 ++-- client/tx/tx.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/context/context.go b/client/context/context.go index 4827f68e0f06..fb1708aed168 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -240,10 +240,10 @@ func (ctx CLIContext) WithBroadcastMode(mode string) CLIContext { return ctx } -// Print outputs toPrint to the ctx.Output based on ctx.OutputFormat which is +// Println outputs toPrint to the ctx.Output based on ctx.OutputFormat which is // either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint // will be JSON encoded using ctx.Marshaler. An error is returned upon failure. -func (ctx CLIContext) Print(toPrint interface{}) error { +func (ctx CLIContext) Println(toPrint interface{}) error { var ( out []byte err error diff --git a/client/tx/tx.go b/client/tx/tx.go index 593f13a2a0f5..7088a0bfda1a 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -85,7 +85,7 @@ func GenerateTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error { return err } - return ctx.Print(tx) + return ctx.Println(tx) } // BroadcastTx attempts to generate, sign and broadcast a transaction with the @@ -143,7 +143,7 @@ func BroadcastTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error { return err } - return ctx.Print(res) + return ctx.Println(res) } // WriteGeneratedTxResponse writes a generated unsigned transaction to the From 7310a0cca9cf2f89314af486aed4df218930a1ad Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 15:21:20 -0400 Subject: [PATCH 23/28] Fix build --- x/bank/client/cli/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/bank/client/cli/query.go b/x/bank/client/cli/query.go index b63b9eabae3e..ba81f44dfa8d 100644 --- a/x/bank/client/cli/query.go +++ b/x/bank/client/cli/query.go @@ -88,7 +88,7 @@ func NewBalancesCmd(m codec.Marshaler) *cobra.Command { result = balance } - return cliCtx.Print(result) + return cliCtx.Println(result) }, } From 2978de90e1c001434e8cf67cfe5be2b8aef0aaff Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 25 Mar 2020 15:59:29 -0400 Subject: [PATCH 24/28] Fix MarshalIndentFromJSON --- types/rest/rest_test.go | 47 ++++++++++++++++++++++++++++++----------- types/utils.go | 2 +- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/types/rest/rest_test.go b/types/rest/rest_test.go index db842696eeaf..ed3ca4173951 100644 --- a/types/rest/rest_test.go +++ b/types/rest/rest_test.go @@ -19,6 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestBaseReq_Sanitize(t *testing.T) { @@ -204,13 +205,12 @@ func TestProcessPostResponse(t *testing.T) { // setup expected results jsonNoIndent, err := ctx.Codec.MarshalJSON(acc) require.Nil(t, err) - jsonWithIndent, err := ctx.Codec.MarshalJSONIndent(acc, "", " ") - require.Nil(t, err) + respNoIndent := NewResponseWithHeight(height, jsonNoIndent) - respWithIndent := NewResponseWithHeight(height, jsonWithIndent) expectedNoIndent, err := ctx.Codec.MarshalJSON(respNoIndent) require.Nil(t, err) - expectedWithIndent, err := ctx.Codec.MarshalJSONIndent(respWithIndent, "", " ") + + expectedWithIndent, err := sdk.MarshalIndentFromJSON(expectedNoIndent) require.Nil(t, err) // check that negative height writes an error @@ -222,6 +222,7 @@ func TestProcessPostResponse(t *testing.T) { // check that height returns expected response ctx = ctx.WithHeight(height) runPostProcessResponse(t, ctx, acc, expectedNoIndent, false) + // check height with indent runPostProcessResponse(t, ctx, acc, expectedWithIndent, true) } @@ -313,11 +314,15 @@ func TestPostProcessResponseBare(t *testing.T) { ctx := context.CLIContext{} w := httptest.NewRecorder() bs := []byte("text string") + PostProcessResponseBare(w, ctx, bs) + res := w.Result() require.Equal(t, http.StatusOK, res.StatusCode) + got, err := ioutil.ReadAll(res.Body) require.NoError(t, err) + t.Cleanup(func() { res.Body.Close() }) require.Equal(t, "text string", string(got)) @@ -328,15 +333,19 @@ func TestPostProcessResponseBare(t *testing.T) { X int `json:"x"` S string `json:"s"` }{X: 10, S: "test"} + PostProcessResponseBare(w, ctx, data) + res = w.Result() require.Equal(t, http.StatusOK, res.StatusCode) + got, err = ioutil.ReadAll(res.Body) require.NoError(t, err) + t.Cleanup(func() { res.Body.Close() }) require.Equal(t, `{ - "x": "10", - "s": "test" + "s": "test", + "x": "10" }`, string(got)) // write struct, don't indent response @@ -346,11 +355,15 @@ func TestPostProcessResponseBare(t *testing.T) { X int `json:"x"` S string `json:"s"` }{X: 10, S: "test"} + PostProcessResponseBare(w, ctx, data) + res = w.Result() require.Equal(t, http.StatusOK, res.StatusCode) + got, err = ioutil.ReadAll(res.Body) require.NoError(t, err) + t.Cleanup(func() { res.Body.Close() }) require.Equal(t, `{"x":"10","s":"test"}`, string(got)) @@ -358,11 +371,15 @@ func TestPostProcessResponseBare(t *testing.T) { ctx = context.CLIContext{Indent: false}.WithCodec(codec.New()) w = httptest.NewRecorder() data2 := badJSONMarshaller{} + PostProcessResponseBare(w, ctx, data2) + res = w.Result() require.Equal(t, http.StatusInternalServerError, res.StatusCode) + got, err = ioutil.ReadAll(res.Body) require.NoError(t, err) + t.Cleanup(func() { res.Body.Close() }) require.Equal(t, []string{"application/json"}, res.Header["Content-Type"]) require.Equal(t, `{"error":"couldn't marshal"}`, string(got)) @@ -384,31 +401,37 @@ func runPostProcessResponse(t *testing.T, ctx context.CLIContext, obj interface{ // test using regular struct w := httptest.NewRecorder() + PostProcessResponse(w, ctx, obj) require.Equal(t, http.StatusOK, w.Code, w.Body) + resp := w.Result() t.Cleanup(func() { resp.Body.Close() }) + body, err := ioutil.ReadAll(resp.Body) require.Nil(t, err) require.Equal(t, expectedBody, body) - var marshalled []byte + marshalled, err := ctx.Codec.MarshalJSON(obj) + require.NoError(t, err) + if indent { - marshalled, err = ctx.Codec.MarshalJSONIndent(obj, "", " ") - } else { - marshalled, err = ctx.Codec.MarshalJSON(obj) + marshalled, err = sdk.MarshalIndentFromJSON(marshalled) + require.NoError(t, err) } - require.Nil(t, err) // test using marshalled struct w = httptest.NewRecorder() PostProcessResponse(w, ctx, marshalled) + require.Equal(t, http.StatusOK, w.Code, w.Body) resp = w.Result() + t.Cleanup(func() { resp.Body.Close() }) body, err = ioutil.ReadAll(resp.Body) + require.Nil(t, err) - require.Equal(t, expectedBody, body) + require.Equal(t, string(expectedBody), string(body)) } func mustNewRequest(t *testing.T, method, url string, body io.Reader) *http.Request { diff --git a/types/utils.go b/types/utils.go index 56639e636f5c..d927ff755917 100644 --- a/types/utils.go +++ b/types/utils.go @@ -20,7 +20,7 @@ var ( func MarshalIndentFromJSON(bz []byte) ([]byte, error) { var generic interface{} - if err := json.Unmarshal(bz, &generic); err == nil { + if err := json.Unmarshal(bz, &generic); err != nil { return nil, err } From c77b2b90bb77de962165913790559f9a2b1748f5 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Thu, 26 Mar 2020 11:11:03 -0400 Subject: [PATCH 25/28] Update docs/architecture/adr-020-protobuf-transaction-encoding.md Co-Authored-By: Aaron Craelius --- docs/architecture/adr-020-protobuf-transaction-encoding.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/adr-020-protobuf-transaction-encoding.md b/docs/architecture/adr-020-protobuf-transaction-encoding.md index d3afaa7be512..4d32267a7ad2 100644 --- a/docs/architecture/adr-020-protobuf-transaction-encoding.md +++ b/docs/architecture/adr-020-protobuf-transaction-encoding.md @@ -128,7 +128,7 @@ type ClientTx interface { } ``` -We then update `CLIContext` to have a new field: `Marshler`. +We then update `CLIContext` to have a new field: `Marshaler`. Then, each module's client handler will at the minimum accept a `Marshaler` instead of a concrete Amino codec and a `Generator` along with an `AccountRetriever` so From 6135912e5324aeddf6f2fe542dc40a9dc06dd444 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 26 Mar 2020 12:46:10 -0400 Subject: [PATCH 26/28] JSON Proto changes --- baseapp/abci.go | 10 +- baseapp/baseapp.go | 4 +- client/context/context.go | 2 +- codec/json.go | 48 +++ codec/proto_codec.go | 9 +- types/codec.go | 15 +- types/rest/rest.go | 6 +- types/rest/rest_test.go | 5 +- types/result.go | 39 +- types/types.pb.go | 752 ++++++++++++++++++++++++++++++++++- types/types.proto | 33 ++ types/utils.go | 13 - x/bank/handler.go | 4 +- x/bank/keeper/keeper_test.go | 10 +- x/crisis/handler.go | 2 +- x/distribution/handler.go | 8 +- x/evidence/handler.go | 2 +- x/gov/handler.go | 6 +- x/slashing/handler.go | 2 +- x/staking/handler.go | 10 +- 20 files changed, 868 insertions(+), 112 deletions(-) create mode 100644 codec/json.go diff --git a/baseapp/abci.go b/baseapp/abci.go index 3342f1aebc61..30273a12c839 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -1,7 +1,6 @@ package baseapp import ( - "encoding/json" "fmt" "os" "sort" @@ -10,6 +9,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -189,7 +189,7 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { GasUsed: int64(gInfo.GasUsed), // TODO: Should type accept unsigned ints? Log: result.Log, Data: result.Data, - Events: result.Events.ToABCIEvents(), + Events: result.Events, } } @@ -214,7 +214,7 @@ func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx GasUsed: int64(gInfo.GasUsed), // TODO: Should type accept unsigned ints? Log: result.Log, Data: result.Data, - Events: result.Events.ToABCIEvents(), + Events: result.Events, } } @@ -331,12 +331,12 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to simulate tx")) } - simRes := sdk.SimulationResponse{ + simRes := &sdk.SimulationResponse{ GasInfo: gInfo, Result: res, } - bz, err := json.Marshal(simRes) + bz, err := codec.ProtoMarshalJSON(simRes) if err != nil { return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to JSON encode simulation response")) } diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 0373a199cf07..f52aa0dd11e6 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -606,7 +606,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*s msgEvents := sdk.Events{ sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, msg.Type())), } - msgEvents = msgEvents.AppendEvents(msgResult.Events) + msgEvents = msgEvents.AppendEvents(msgResult.GetEvents()) // append message events, data and logs // @@ -620,6 +620,6 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*s return &sdk.Result{ Data: data, Log: strings.TrimSpace(msgLogs.String()), - Events: events, + Events: events.ToABCIEvents(), }, nil } diff --git a/client/context/context.go b/client/context/context.go index fb1708aed168..5e536251b0b6 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -260,7 +260,7 @@ func (ctx CLIContext) Println(toPrint interface{}) error { // error. The re-encoded JSON uses the standard library as the initial encoded // JSON should have the correct output produced by ctx.Marshaler. if ctx.Indent && err == nil { - out, err = sdk.MarshalIndentFromJSON(out) + out, err = codec.MarshalIndentFromJSON(out) } } diff --git a/codec/json.go b/codec/json.go new file mode 100644 index 000000000000..0bb44df31fd4 --- /dev/null +++ b/codec/json.go @@ -0,0 +1,48 @@ +package codec + +import ( + "bytes" + "encoding/json" + + "github.com/gogo/protobuf/jsonpb" + "github.com/gogo/protobuf/proto" +) + +// MarshalIndentFromJSON returns indented JSON-encoded bytes from already encoded +// JSON bytes. The output encoding will adhere to the original input's encoding +// (e.g. Proto3). +func MarshalIndentFromJSON(bz []byte) ([]byte, error) { + var generic interface{} + + if err := json.Unmarshal(bz, &generic); err != nil { + return nil, err + } + + return json.MarshalIndent(generic, "", " ") +} + +// ProtoMarshalJSON provides an auxiliary function to return Proto3 JSON encoded +// bytes of a message. +func ProtoMarshalJSON(msg proto.Message) ([]byte, error) { + jm := &jsonpb.Marshaler{EmitDefaults: false, OrigName: false} + buf := new(bytes.Buffer) + + if err := jm.Marshal(buf, msg); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// ProtoMarshalJSONIndent provides an auxiliary function to return Proto3 indented +// JSON encoded bytes of a message. +func ProtoMarshalJSONIndent(msg proto.Message) ([]byte, error) { + jm := &jsonpb.Marshaler{EmitDefaults: false, OrigName: false, Indent: " "} + buf := new(bytes.Buffer) + + if err := jm.Marshal(buf, msg); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/codec/proto_codec.go b/codec/proto_codec.go index f8a4169d4d8b..ff123cee23a3 100644 --- a/codec/proto_codec.go +++ b/codec/proto_codec.go @@ -95,14 +95,7 @@ func (pc *ProtoCodec) MarshalJSON(o interface{}) ([]byte, error) { // nolint: st return nil, fmt.Errorf("cannot protobuf JSON encode unsupported type: %T", o) } - buf := new(bytes.Buffer) - - marshaler := &jsonpb.Marshaler{} - if err := marshaler.Marshal(buf, m); err != nil { - return nil, err - } - - return buf.Bytes(), nil + return ProtoMarshalJSON(m) } func (pc *ProtoCodec) MustMarshalJSON(o interface{}) []byte { diff --git a/types/codec.go b/types/codec.go index 21cd269043a0..9cd1097c201b 100644 --- a/types/codec.go +++ b/types/codec.go @@ -1,10 +1,7 @@ package types import ( - "bytes" - jsonc "github.com/gibson042/canonicaljson-go" - "github.com/gogo/protobuf/jsonpb" "github.com/cosmos/cosmos-sdk/codec" ) @@ -19,19 +16,17 @@ func RegisterCodec(cdc *codec.Codec) { // can be signed over. The JSON encoding ensures all field names adhere to their // Proto definition, default values are omitted, and follows the JSON Canonical // Form. -func CanonicalSignBytes(m codec.ProtoMarshaler) ([]byte, error) { - jm := &jsonpb.Marshaler{EmitDefaults: false, OrigName: false} - buf := new(bytes.Buffer) - - // first, encode via canonical Protocol Buffer JSON - if err := jm.Marshal(buf, m); err != nil { +func CanonicalSignBytes(msg codec.ProtoMarshaler) ([]byte, error) { + // first, encode via canonical Proto3 JSON + bz, err := codec.ProtoMarshalJSON(msg) + if err != nil { return nil, err } genericJSON := make(map[string]interface{}) // decode canonical proto encoding into a generic map - if err := jsonc.Unmarshal(buf.Bytes(), &genericJSON); err != nil { + if err := jsonc.Unmarshal(bz, &genericJSON); err != nil { return nil, err } diff --git a/types/rest/rest.go b/types/rest/rest.go index 8f90855f986a..74bf5946d4c4 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -254,7 +254,7 @@ func PostProcessResponseBare(w http.ResponseWriter, ctx context.CLIContext, body resp, err = marshaler.MarshalJSON(body) if ctx.Indent && err == nil { - resp, err = sdk.MarshalIndentFromJSON(resp) + resp, err = codec.MarshalIndentFromJSON(resp) } if err != nil { @@ -299,7 +299,7 @@ func PostProcessResponse(w http.ResponseWriter, ctx context.CLIContext, resp int result, err = marshaler.MarshalJSON(resp) if ctx.Indent && err == nil { - result, err = sdk.MarshalIndentFromJSON(result) + result, err = codec.MarshalIndentFromJSON(result) } if err != nil { @@ -312,7 +312,7 @@ func PostProcessResponse(w http.ResponseWriter, ctx context.CLIContext, resp int output, err := marshaler.MarshalJSON(wrappedResp) if ctx.Indent && err == nil { - output, err = sdk.MarshalIndentFromJSON(output) + output, err = codec.MarshalIndentFromJSON(output) } if err != nil { diff --git a/types/rest/rest_test.go b/types/rest/rest_test.go index ed3ca4173951..0f259b7fc14c 100644 --- a/types/rest/rest_test.go +++ b/types/rest/rest_test.go @@ -19,7 +19,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) func TestBaseReq_Sanitize(t *testing.T) { @@ -210,7 +209,7 @@ func TestProcessPostResponse(t *testing.T) { expectedNoIndent, err := ctx.Codec.MarshalJSON(respNoIndent) require.Nil(t, err) - expectedWithIndent, err := sdk.MarshalIndentFromJSON(expectedNoIndent) + expectedWithIndent, err := codec.MarshalIndentFromJSON(expectedNoIndent) require.Nil(t, err) // check that negative height writes an error @@ -416,7 +415,7 @@ func runPostProcessResponse(t *testing.T, ctx context.CLIContext, obj interface{ require.NoError(t, err) if indent { - marshalled, err = sdk.MarshalIndentFromJSON(marshalled) + marshalled, err = codec.MarshalIndentFromJSON(marshalled) require.NoError(t, err) } diff --git a/types/result.go b/types/result.go index 54cc0e05330b..c488a027705b 100644 --- a/types/result.go +++ b/types/result.go @@ -7,39 +7,30 @@ import ( "math" "strings" + yaml "gopkg.in/yaml.v2" + "github.com/cosmos/cosmos-sdk/codec" ctypes "github.com/tendermint/tendermint/rpc/core/types" ) -// GasInfo defines tx execution gas context. -type GasInfo struct { - // GasWanted is the maximum units of work we allow this tx to perform. - GasWanted uint64 - - // GasUsed is the amount of gas actually consumed. - GasUsed uint64 +func (gi GasInfo) String() string { + bz, _ := yaml.Marshal(gi) + return string(bz) } -// Result is the union of ResponseFormat and ResponseCheckTx. -type Result struct { - // Data is any data returned from message or handler execution. It MUST be length - // prefixed in order to separate data from multiple message executions. - Data []byte - - // Log contains the log information from message or handler execution. - Log string - - // Events contains a slice of Event objects that were emitted during message or - // handler execution. - Events Events +func (r Result) String() string { + bz, _ := yaml.Marshal(r) + return string(bz) } -// SimulationResponse defines the response generated when a transaction is successfully -// simulated by the Baseapp. -type SimulationResponse struct { - GasInfo - Result *Result +func (r Result) GetEvents() Events { + events := make(Events, len(r.Events)) + for i, e := range r.Events { + events[i] = Event(e) + } + + return events } // ABCIMessageLogs represents a slice of ABCIMessageLog. diff --git a/types/types.pb.go b/types/types.pb.go index 845bf267ba4f..6b4d9813ab5f 100644 --- a/types/types.pb.go +++ b/types/types.pb.go @@ -7,6 +7,7 @@ import ( fmt "fmt" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" + types "github.com/tendermint/tendermint/abci/types" io "io" math "math" math_bits "math/bits" @@ -239,38 +240,199 @@ func (m *ValAddresses) GetAddresses() []ValAddress { return nil } +// GasInfo defines tx execution gas context. +type GasInfo struct { + // GasWanted is the maximum units of work we allow this tx to perform. + GasWanted uint64 `protobuf:"varint,1,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty" yaml:"gas_wanted"` + // GasUsed is the amount of gas actually consumed. + GasUsed uint64 `protobuf:"varint,2,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty" yaml:"gas_used"` +} + +func (m *GasInfo) Reset() { *m = GasInfo{} } +func (*GasInfo) ProtoMessage() {} +func (*GasInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_2c0f90c600ad7e2e, []int{5} +} +func (m *GasInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GasInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GasInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GasInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_GasInfo.Merge(m, src) +} +func (m *GasInfo) XXX_Size() int { + return m.Size() +} +func (m *GasInfo) XXX_DiscardUnknown() { + xxx_messageInfo_GasInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_GasInfo proto.InternalMessageInfo + +func (m *GasInfo) GetGasWanted() uint64 { + if m != nil { + return m.GasWanted + } + return 0 +} + +func (m *GasInfo) GetGasUsed() uint64 { + if m != nil { + return m.GasUsed + } + return 0 +} + +// Result is the union of ResponseFormat and ResponseCheckTx. +type Result struct { + // Data is any data returned from message or handler execution. It MUST be length + // prefixed in order to separate data from multiple message executions. + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + // Log contains the log information from message or handler execution. + Log string `protobuf:"bytes,2,opt,name=log,proto3" json:"log,omitempty"` + // Events contains a slice of Event objects that were emitted during message or + // handler execution. + Events []types.Event `protobuf:"bytes,3,rep,name=events,proto3" json:"events"` +} + +func (m *Result) Reset() { *m = Result{} } +func (*Result) ProtoMessage() {} +func (*Result) Descriptor() ([]byte, []int) { + return fileDescriptor_2c0f90c600ad7e2e, []int{6} +} +func (m *Result) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Result) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Result.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Result) XXX_Merge(src proto.Message) { + xxx_messageInfo_Result.Merge(m, src) +} +func (m *Result) XXX_Size() int { + return m.Size() +} +func (m *Result) XXX_DiscardUnknown() { + xxx_messageInfo_Result.DiscardUnknown(m) +} + +var xxx_messageInfo_Result proto.InternalMessageInfo + +// SimulationResponse defines the response generated when a transaction is +// successfully simulated. +type SimulationResponse struct { + GasInfo `protobuf:"bytes,1,opt,name=gas_info,json=gasInfo,proto3,embedded=gas_info" json:"gas_info"` + Result *Result `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` +} + +func (m *SimulationResponse) Reset() { *m = SimulationResponse{} } +func (*SimulationResponse) ProtoMessage() {} +func (*SimulationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_2c0f90c600ad7e2e, []int{7} +} +func (m *SimulationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SimulationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SimulationResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SimulationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SimulationResponse.Merge(m, src) +} +func (m *SimulationResponse) XXX_Size() int { + return m.Size() +} +func (m *SimulationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SimulationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SimulationResponse proto.InternalMessageInfo + +func (m *SimulationResponse) GetResult() *Result { + if m != nil { + return m.Result + } + return nil +} + func init() { proto.RegisterType((*Coin)(nil), "cosmos_sdk.v1.Coin") proto.RegisterType((*DecCoin)(nil), "cosmos_sdk.v1.DecCoin") proto.RegisterType((*IntProto)(nil), "cosmos_sdk.v1.IntProto") proto.RegisterType((*DecProto)(nil), "cosmos_sdk.v1.DecProto") proto.RegisterType((*ValAddresses)(nil), "cosmos_sdk.v1.ValAddresses") + proto.RegisterType((*GasInfo)(nil), "cosmos_sdk.v1.GasInfo") + proto.RegisterType((*Result)(nil), "cosmos_sdk.v1.Result") + proto.RegisterType((*SimulationResponse)(nil), "cosmos_sdk.v1.SimulationResponse") } func init() { proto.RegisterFile("types/types.proto", fileDescriptor_2c0f90c600ad7e2e) } var fileDescriptor_2c0f90c600ad7e2e = []byte{ - // 305 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2c, 0xa9, 0x2c, 0x48, - 0x2d, 0xd6, 0x07, 0x93, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xbc, 0xc9, 0xf9, 0xc5, 0xb9, - 0xf9, 0xc5, 0xf1, 0xc5, 0x29, 0xd9, 0x7a, 0x65, 0x86, 0x52, 0x6a, 0x25, 0x19, 0x99, 0x45, 0x29, - 0xf1, 0x05, 0x89, 0x45, 0x25, 0x95, 0xfa, 0x60, 0x15, 0xfa, 0xe9, 0xf9, 0xe9, 0xf9, 0x08, 0x16, - 0x44, 0x9b, 0x92, 0x3b, 0x17, 0x8b, 0x73, 0x7e, 0x66, 0x9e, 0x90, 0x08, 0x17, 0x6b, 0x4a, 0x6a, - 0x5e, 0x7e, 0xae, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x84, 0x23, 0xa4, 0xcc, 0xc5, 0x96, - 0x98, 0x9b, 0x5f, 0x9a, 0x57, 0x22, 0xc1, 0x04, 0x12, 0x76, 0xe2, 0x3e, 0x71, 0x4f, 0x9e, 0xe1, - 0xd6, 0x3d, 0x79, 0x66, 0xcf, 0xbc, 0x92, 0x20, 0xa8, 0x94, 0x15, 0xcb, 0x8b, 0x05, 0xf2, 0x8c, - 0x4a, 0x5e, 0x5c, 0xec, 0x2e, 0xa9, 0xc9, 0xe4, 0x98, 0xe5, 0x92, 0x9a, 0x8c, 0x66, 0x96, 0x26, - 0x17, 0x87, 0x67, 0x5e, 0x49, 0x00, 0xd8, 0x5f, 0xb2, 0x5c, 0xcc, 0x99, 0x79, 0x25, 0x10, 0xa3, - 0x50, 0xed, 0x07, 0x89, 0x83, 0x94, 0xba, 0xa4, 0x26, 0xc3, 0x95, 0xa6, 0xa4, 0x26, 0xa3, 0x2b, - 0x05, 0x19, 0x0f, 0x12, 0x57, 0x72, 0xe2, 0xe2, 0x09, 0x4b, 0xcc, 0x71, 0x4c, 0x49, 0x29, 0x4a, - 0x2d, 0x2e, 0x4e, 0x2d, 0x16, 0xd2, 0xe1, 0xe2, 0x4c, 0x84, 0x71, 0x24, 0x18, 0x15, 0x98, 0x35, - 0x78, 0x9c, 0xf8, 0x7e, 0xdd, 0x93, 0xe7, 0x42, 0x28, 0x0a, 0x42, 0x28, 0xb0, 0x62, 0x69, 0xb8, - 0xa3, 0xc0, 0xe8, 0xe4, 0x72, 0xe3, 0xa1, 0x1c, 0x43, 0xc3, 0x23, 0x39, 0x86, 0x13, 0x8f, 0xe4, - 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, - 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x52, 0x4a, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, - 0xce, 0xcf, 0xd5, 0x87, 0x44, 0x09, 0x94, 0xd2, 0x2d, 0x4e, 0xc9, 0x86, 0xc4, 0x58, 0x12, 0x1b, - 0x38, 0xec, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa7, 0x40, 0xd2, 0x04, 0xc7, 0x01, 0x00, - 0x00, + // 534 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0x4f, 0x6f, 0xd3, 0x4e, + 0x10, 0xb5, 0x7f, 0xf6, 0x2f, 0x7f, 0x36, 0xe1, 0x4f, 0x17, 0x8a, 0xa2, 0x0a, 0xec, 0xc8, 0x48, + 0x28, 0x48, 0xd4, 0x16, 0x29, 0xa7, 0x70, 0xc2, 0x04, 0x55, 0xe1, 0x84, 0x16, 0x01, 0x12, 0x97, + 0x68, 0xe3, 0xdd, 0xba, 0x56, 0xe3, 0xdd, 0xc8, 0xbb, 0x29, 0xca, 0x2d, 0x47, 0x8e, 0x7c, 0x84, + 0x7e, 0x9c, 0x1e, 0x73, 0xac, 0x10, 0xb2, 0x20, 0xb9, 0x70, 0xee, 0x91, 0x13, 0xda, 0xb5, 0xc1, + 0x6a, 0x7a, 0xe3, 0x92, 0xcc, 0xce, 0xbc, 0x79, 0x33, 0xf3, 0xfc, 0xc0, 0x8e, 0x5c, 0xcc, 0xa8, + 0x08, 0xf4, 0xaf, 0x3f, 0xcb, 0xb8, 0xe4, 0xf0, 0x46, 0xc4, 0x45, 0xca, 0xc5, 0x58, 0x90, 0x13, + 0xff, 0xf4, 0xe9, 0xde, 0x23, 0x79, 0x9c, 0x64, 0x64, 0x3c, 0xc3, 0x99, 0x5c, 0x04, 0x1a, 0x11, + 0xc4, 0x3c, 0xe6, 0x55, 0x54, 0xb4, 0xed, 0x1d, 0x5c, 0xc7, 0x49, 0xca, 0x08, 0xcd, 0xd2, 0x84, + 0xc9, 0x00, 0x4f, 0xa2, 0x24, 0xb8, 0x36, 0xcb, 0x3b, 0x04, 0xf6, 0x4b, 0x9e, 0x30, 0x78, 0x17, + 0xfc, 0x4f, 0x28, 0xe3, 0x69, 0xc7, 0xec, 0x9a, 0xbd, 0x26, 0x2a, 0x1e, 0xf0, 0x21, 0xa8, 0xe1, + 0x94, 0xcf, 0x99, 0xec, 0xfc, 0xa7, 0xd2, 0x61, 0xeb, 0x3c, 0x77, 0x8d, 0xaf, 0xb9, 0x6b, 0x8d, + 0x98, 0x44, 0x65, 0x69, 0x60, 0xff, 0x3c, 0x73, 0x4d, 0xef, 0x35, 0xa8, 0x0f, 0x69, 0xf4, 0x2f, + 0x5c, 0x43, 0x1a, 0x6d, 0x71, 0x3d, 0x06, 0x8d, 0x11, 0x93, 0x6f, 0xb4, 0x18, 0x0f, 0x80, 0x95, + 0x30, 0x59, 0x50, 0x5d, 0x9d, 0xaf, 0xf2, 0x0a, 0x3a, 0xa4, 0xd1, 0x5f, 0x28, 0xa1, 0xd1, 0x36, + 0x54, 0xd1, 0xab, 0xbc, 0x17, 0x82, 0xf6, 0x7b, 0x3c, 0x7d, 0x41, 0x48, 0x46, 0x85, 0xa0, 0x02, + 0x3e, 0x01, 0x4d, 0xfc, 0xe7, 0xd1, 0x31, 0xbb, 0x56, 0xaf, 0x1d, 0xde, 0xfc, 0x95, 0xbb, 0xa0, + 0x02, 0xa1, 0x0a, 0x30, 0xb0, 0x97, 0xdf, 0xba, 0xa6, 0xc7, 0x41, 0xfd, 0x10, 0x8b, 0x11, 0x3b, + 0xe2, 0xf0, 0x19, 0x00, 0x31, 0x16, 0xe3, 0x4f, 0x98, 0x49, 0x4a, 0xf4, 0x50, 0x3b, 0xdc, 0xbd, + 0xcc, 0xdd, 0x9d, 0x05, 0x4e, 0xa7, 0x03, 0xaf, 0xaa, 0x79, 0xa8, 0x19, 0x63, 0xf1, 0x41, 0xc7, + 0xd0, 0x07, 0x0d, 0x55, 0x99, 0x0b, 0x4a, 0xb4, 0x0e, 0x76, 0x78, 0xe7, 0x32, 0x77, 0x6f, 0x55, + 0x3d, 0xaa, 0xe2, 0xa1, 0x7a, 0x8c, 0xc5, 0x3b, 0x15, 0xcd, 0x40, 0x0d, 0x51, 0x31, 0x9f, 0x4a, + 0x08, 0x81, 0x4d, 0xb0, 0xc4, 0x7a, 0x52, 0x1b, 0xe9, 0x18, 0xde, 0x06, 0xd6, 0x94, 0xc7, 0x85, + 0xa0, 0x48, 0x85, 0x70, 0x00, 0x6a, 0xf4, 0x94, 0x32, 0x29, 0x3a, 0x56, 0xd7, 0xea, 0xb5, 0xfa, + 0xf7, 0xfd, 0xca, 0x03, 0xbe, 0xf2, 0x80, 0x5f, 0x7c, 0xfd, 0x57, 0x0a, 0x14, 0xda, 0x4a, 0x24, + 0x54, 0x76, 0x0c, 0xec, 0xcf, 0x67, 0xae, 0xe1, 0x2d, 0x4d, 0x00, 0xdf, 0x26, 0xe9, 0x7c, 0x8a, + 0x65, 0xc2, 0x19, 0xa2, 0x62, 0xc6, 0x99, 0xa0, 0xf0, 0x79, 0xb1, 0x78, 0xc2, 0x8e, 0xb8, 0x5e, + 0xa1, 0xd5, 0xbf, 0xe7, 0x5f, 0xf1, 0xa9, 0x5f, 0x0a, 0x13, 0x36, 0x14, 0xe9, 0x2a, 0x77, 0x4d, + 0x7d, 0x85, 0xd6, 0x6a, 0x1f, 0xd4, 0x32, 0x7d, 0x85, 0x5e, 0xb5, 0xd5, 0xdf, 0xdd, 0x6a, 0x2d, + 0x4e, 0x44, 0x25, 0x28, 0x1c, 0x5e, 0xfc, 0x70, 0x8c, 0xe5, 0xda, 0x31, 0xce, 0xd7, 0x8e, 0xb9, + 0x5a, 0x3b, 0xe6, 0xf7, 0xb5, 0x63, 0x7e, 0xd9, 0x38, 0xc6, 0x6a, 0xe3, 0x18, 0x17, 0x1b, 0xc7, + 0xf8, 0xe8, 0xc5, 0x89, 0x3c, 0x9e, 0x4f, 0xfc, 0x88, 0xa7, 0x41, 0x41, 0x55, 0xfe, 0xed, 0x0b, + 0x72, 0x52, 0x18, 0x7c, 0x52, 0xd3, 0x0e, 0x3f, 0xf8, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x0e, 0xe8, + 0xc3, 0x0c, 0x62, 0x03, 0x00, 0x00, } func (this *Coin) Equal(that interface{}) bool { @@ -505,6 +667,135 @@ func (m *ValAddresses) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *GasInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GasInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GasInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.GasUsed != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.GasUsed)) + i-- + dAtA[i] = 0x10 + } + if m.GasWanted != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.GasWanted)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Result) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Result) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Result) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Events) > 0 { + for iNdEx := len(m.Events) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Events[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Log) > 0 { + i -= len(m.Log) + copy(dAtA[i:], m.Log) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Log))) + i-- + dAtA[i] = 0x12 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SimulationResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SimulationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SimulationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Result != nil { + { + size, err := m.Result.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + { + size, err := m.GasInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -583,6 +874,59 @@ func (m *ValAddresses) Size() (n int) { return n } +func (m *GasInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.GasWanted != 0 { + n += 1 + sovTypes(uint64(m.GasWanted)) + } + if m.GasUsed != 0 { + n += 1 + sovTypes(uint64(m.GasUsed)) + } + return n +} + +func (m *Result) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.Log) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if len(m.Events) > 0 { + for _, e := range m.Events { + l = e.Size() + n += 1 + l + sovTypes(uint64(l)) + } + } + return n +} + +func (m *SimulationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.GasInfo.Size() + n += 1 + l + sovTypes(uint64(l)) + if m.Result != nil { + l = m.Result.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1104,6 +1448,372 @@ func (m *ValAddresses) Unmarshal(dAtA []byte) error { } return nil } +func (m *GasInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GasInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GasInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasWanted", wireType) + } + m.GasWanted = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasWanted |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasUsed", wireType) + } + m.GasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Result) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Result: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Result: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Log", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Log = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Events = append(m.Events, types.Event{}) + if err := m.Events[len(m.Events)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SimulationResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SimulationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SimulationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GasInfo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.GasInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Result == nil { + m.Result = &Result{} + } + if err := m.Result.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTypes(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/types/types.proto b/types/types.proto index 7fff608b9c9d..c26219caa789 100644 --- a/types/types.proto +++ b/types/types.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package cosmos_sdk.v1; import "third_party/proto/gogoproto/gogo.proto"; +import "third_party/proto/tendermint/abci/types/types.proto"; option go_package = "github.com/cosmos/cosmos-sdk/types"; option (gogoproto.goproto_stringer_all) = false; @@ -45,3 +46,35 @@ message ValAddresses { repeated bytes addresses = 1 [(gogoproto.casttype) = "ValAddress"]; } + +// GasInfo defines tx execution gas context. +message GasInfo { + // GasWanted is the maximum units of work we allow this tx to perform. + uint64 gas_wanted = 1 [(gogoproto.moretags) = "yaml:\"gas_wanted\""]; + + // GasUsed is the amount of gas actually consumed. + uint64 gas_used = 2 [(gogoproto.moretags) = "yaml:\"gas_used\""]; +} + +// Result is the union of ResponseFormat and ResponseCheckTx. +message Result { + option (gogoproto.goproto_getters) = false; + + // Data is any data returned from message or handler execution. It MUST be length + // prefixed in order to separate data from multiple message executions. + bytes data = 1; + + // Log contains the log information from message or handler execution. + string log = 2; + + // Events contains a slice of Event objects that were emitted during message or + // handler execution. + repeated tendermint.abci.types.Event events = 3 [(gogoproto.nullable) = false]; +} + +// SimulationResponse defines the response generated when a transaction is +// successfully simulated. +message SimulationResponse { + GasInfo gas_info = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; + Result result = 2; +} diff --git a/types/utils.go b/types/utils.go index d927ff755917..be33160261d5 100644 --- a/types/utils.go +++ b/types/utils.go @@ -14,19 +14,6 @@ var ( DBBackend = "" ) -// MarshalIndentFromJSON returns indented JSON-encoded bytes from already encoded -// JSON bytes. The output encoding will adhere to the original input's encoding -// (e.g. Proto3). -func MarshalIndentFromJSON(bz []byte) ([]byte, error) { - var generic interface{} - - if err := json.Unmarshal(bz, &generic); err != nil { - return nil, err - } - - return json.MarshalIndent(generic, "", " ") -} - // SortedJSON takes any JSON and returns it sorted by keys. Also, all white-spaces // are removed. // This method can be used to canonicalize JSON to be returned by GetSignBytes, diff --git a/x/bank/handler.go b/x/bank/handler.go index 30341ab47b57..5ec0033bce82 100644 --- a/x/bank/handler.go +++ b/x/bank/handler.go @@ -47,7 +47,7 @@ func handleMsgSend(ctx sdk.Context, k keeper.Keeper, msg types.MsgSend) (*sdk.Re ), ) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } // Handle MsgMultiSend. @@ -75,5 +75,5 @@ func handleMsgMultiSend(ctx sdk.Context, k keeper.Keeper, msg types.MsgMultiSend ), ) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } diff --git a/x/bank/keeper/keeper_test.go b/x/bank/keeper/keeper_test.go index 472fe39972d5..6135ad5d723c 100644 --- a/x/bank/keeper/keeper_test.go +++ b/x/bank/keeper/keeper_test.go @@ -240,7 +240,7 @@ func (suite *IntegrationTestSuite) TestMsgSendEvents() { suite.Require().Error(app.BankKeeper.SendCoins(ctx, addr, addr2, newCoins)) - events := ctx.EventManager().Events() + events := ctx.EventManager().ABCIEvents() suite.Require().Equal(2, len(events)) event1 := sdk.Event{ @@ -272,7 +272,7 @@ func (suite *IntegrationTestSuite) TestMsgSendEvents() { suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr, addr2, newCoins)) - events = ctx.EventManager().Events() + events = ctx.EventManager().ABCIEvents() suite.Require().Equal(4, len(events)) suite.Require().Equal(event1, events[2]) suite.Require().Equal(event2, events[3]) @@ -306,7 +306,7 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { suite.Require().Error(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) - events := ctx.EventManager().Events() + events := ctx.EventManager().ABCIEvents() suite.Require().Equal(0, len(events)) // Set addr's coins but not addr2's coins @@ -314,7 +314,7 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { suite.Require().Error(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) - events = ctx.EventManager().Events() + events = ctx.EventManager().ABCIEvents() suite.Require().Equal(1, len(events)) event1 := sdk.Event{ @@ -336,7 +336,7 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { suite.Require().NoError(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) - events = ctx.EventManager().Events() + events = ctx.EventManager().ABCIEvents() suite.Require().Equal(5, len(events)) event2 := sdk.Event{ diff --git a/x/crisis/handler.go b/x/crisis/handler.go index cd94db85a5b1..3abf3fa83158 100644 --- a/x/crisis/handler.go +++ b/x/crisis/handler.go @@ -83,5 +83,5 @@ func handleMsgVerifyInvariant(ctx sdk.Context, msg types.MsgVerifyInvariant, k k ), }) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } diff --git a/x/distribution/handler.go b/x/distribution/handler.go index 642b9e0d89a1..5216cdad29f6 100644 --- a/x/distribution/handler.go +++ b/x/distribution/handler.go @@ -47,7 +47,7 @@ func handleMsgModifyWithdrawAddress(ctx sdk.Context, msg types.MsgSetWithdrawAdd ), ) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } func handleMsgWithdrawDelegatorReward(ctx sdk.Context, msg types.MsgWithdrawDelegatorReward, k keeper.Keeper) (*sdk.Result, error) { @@ -64,7 +64,7 @@ func handleMsgWithdrawDelegatorReward(ctx sdk.Context, msg types.MsgWithdrawDele ), ) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } func handleMsgWithdrawValidatorCommission(ctx sdk.Context, msg types.MsgWithdrawValidatorCommission, k keeper.Keeper) (*sdk.Result, error) { @@ -81,7 +81,7 @@ func handleMsgWithdrawValidatorCommission(ctx sdk.Context, msg types.MsgWithdraw ), ) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } func handleMsgFundCommunityPool(ctx sdk.Context, msg types.MsgFundCommunityPool, k keeper.Keeper) (*sdk.Result, error) { @@ -97,7 +97,7 @@ func handleMsgFundCommunityPool(ctx sdk.Context, msg types.MsgFundCommunityPool, ), ) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } func NewCommunityPoolSpendProposalHandler(k Keeper) govtypes.Handler { diff --git a/x/evidence/handler.go b/x/evidence/handler.go index e7b83eb1900d..1d3c82f93726 100644 --- a/x/evidence/handler.go +++ b/x/evidence/handler.go @@ -41,6 +41,6 @@ func handleMsgSubmitEvidence(ctx sdk.Context, k Keeper, msg exported.MsgSubmitEv return &sdk.Result{ Data: evidence.Hash(), - Events: ctx.EventManager().Events(), + Events: ctx.EventManager().ABCIEvents(), }, nil } diff --git a/x/gov/handler.go b/x/gov/handler.go index b50644a519ad..02edefcdbd80 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -59,7 +59,7 @@ func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitPropos return &sdk.Result{ Data: GetProposalIDBytes(proposal.ProposalID), - Events: ctx.EventManager().Events(), + Events: ctx.EventManager().ABCIEvents(), }, nil } @@ -86,7 +86,7 @@ func handleMsgDeposit(ctx sdk.Context, keeper Keeper, msg MsgDeposit) (*sdk.Resu ) } - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } func handleMsgVote(ctx sdk.Context, keeper Keeper, msg MsgVote) (*sdk.Result, error) { @@ -103,5 +103,5 @@ func handleMsgVote(ctx sdk.Context, keeper Keeper, msg MsgVote) (*sdk.Result, er ), ) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } diff --git a/x/slashing/handler.go b/x/slashing/handler.go index 629a92dde29e..91d3c49d1e47 100644 --- a/x/slashing/handler.go +++ b/x/slashing/handler.go @@ -37,5 +37,5 @@ func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) (*sdk.Result, err ), ) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } diff --git a/x/staking/handler.go b/x/staking/handler.go index e6ed8e3b57c3..d140dc92bfb2 100644 --- a/x/staking/handler.go +++ b/x/staking/handler.go @@ -116,7 +116,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k ), }) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keeper.Keeper) (*sdk.Result, error) { @@ -172,7 +172,7 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe ), }) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) (*sdk.Result, error) { @@ -204,7 +204,7 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) ), }) - return &sdk.Result{Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil } func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keeper) (*sdk.Result, error) { @@ -244,7 +244,7 @@ func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keep ), }) - return &sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().ABCIEvents()}, nil } func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) (*sdk.Result, error) { @@ -287,5 +287,5 @@ func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k k ), }) - return &sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().Events()}, nil + return &sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().ABCIEvents()}, nil } From da400156c31f673d7c1bab5d78e8c1d4975bc9bd Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 26 Mar 2020 12:50:39 -0400 Subject: [PATCH 27/28] Fix tests --- baseapp/baseapp_test.go | 5 +++-- client/tx/tx.go | 6 ++++-- client/tx/tx_test.go | 6 +++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 11d3e346c484..9de25e701049 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -3,12 +3,13 @@ package baseapp import ( "bytes" "encoding/binary" - "encoding/json" "fmt" "os" + "strings" "sync" "testing" + "github.com/gogo/protobuf/jsonpb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -937,7 +938,7 @@ func TestSimulateTx(t *testing.T) { require.True(t, queryResult.IsOK(), queryResult.Log) var simRes sdk.SimulationResponse - require.NoError(t, json.Unmarshal(queryResult.Value, &simRes)) + require.NoError(t, jsonpb.Unmarshal(strings.NewReader(string(queryResult.Value)), &simRes)) require.Equal(t, gInfo, simRes.GasInfo) require.Equal(t, result.Log, simRes.Result.Log) diff --git a/client/tx/tx.go b/client/tx/tx.go index 7088a0bfda1a..6d1c1cd08157 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -2,11 +2,13 @@ package tx import ( "bufio" - "encoding/json" "errors" "fmt" "net/http" "os" + "strings" + + "github.com/gogo/protobuf/jsonpb" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" @@ -277,7 +279,7 @@ func CalculateGas( } var simRes sdk.SimulationResponse - if err := json.Unmarshal(bz, &simRes); err != nil { + if err := jsonpb.Unmarshal(strings.NewReader(string(bz)), &simRes); err != nil { return sdk.SimulationResponse{}, 0, err } diff --git a/client/tx/tx_test.go b/client/tx/tx_test.go index 6fb3ceafb477..13e94f819bfa 100644 --- a/client/tx/tx_test.go +++ b/client/tx/tx_test.go @@ -1,13 +1,13 @@ package tx_test import ( - "encoding/json" "errors" "testing" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/std" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -20,12 +20,12 @@ func TestCalculateGas(t *testing.T) { if wantErr { return nil, 0, errors.New("query failed") } - simRes := sdk.SimulationResponse{ + simRes := &sdk.SimulationResponse{ GasInfo: sdk.GasInfo{GasUsed: gasUsed, GasWanted: gasUsed}, Result: &sdk.Result{Data: []byte("tx data"), Log: "log"}, } - bz, err := json.Marshal(simRes) + bz, err := codec.ProtoMarshalJSON(simRes) if err != nil { return nil, 0, err } From 78fd300127c3f1f382ce91c0563fb70e5a124aed Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 26 Mar 2020 13:50:34 -0400 Subject: [PATCH 28/28] Fix bank tests --- x/bank/keeper/keeper_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/x/bank/keeper/keeper_test.go b/x/bank/keeper/keeper_test.go index 6135ad5d723c..c41e41be09d1 100644 --- a/x/bank/keeper/keeper_test.go +++ b/x/bank/keeper/keeper_test.go @@ -264,8 +264,8 @@ func (suite *IntegrationTestSuite) TestMsgSendEvents() { tmkv.Pair{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())}, ) - suite.Require().Equal(event1, events[0]) - suite.Require().Equal(event2, events[1]) + suite.Require().Equal(abci.Event(event1), events[0]) + suite.Require().Equal(abci.Event(event2), events[1]) app.BankKeeper.SetBalances(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50))) newCoins = sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)) @@ -274,8 +274,8 @@ func (suite *IntegrationTestSuite) TestMsgSendEvents() { events = ctx.EventManager().ABCIEvents() suite.Require().Equal(4, len(events)) - suite.Require().Equal(event1, events[2]) - suite.Require().Equal(event2, events[3]) + suite.Require().Equal(abci.Event(event1), events[2]) + suite.Require().Equal(abci.Event(event2), events[3]) } func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { @@ -325,7 +325,7 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { event1.Attributes, tmkv.Pair{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())}, ) - suite.Require().Equal(event1, events[0]) + suite.Require().Equal(abci.Event(event1), events[0]) // Set addr's coins and addr2's coins app.BankKeeper.SetBalances(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50))) @@ -371,10 +371,10 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { tmkv.Pair{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins2.String())}, ) - suite.Require().Equal(event1, events[1]) - suite.Require().Equal(event2, events[2]) - suite.Require().Equal(event3, events[3]) - suite.Require().Equal(event4, events[4]) + suite.Require().Equal(abci.Event(event1), events[1]) + suite.Require().Equal(abci.Event(event2), events[2]) + suite.Require().Equal(abci.Event(event3), events[3]) + suite.Require().Equal(abci.Event(event4), events[4]) } func (suite *IntegrationTestSuite) TestSpendableCoins() {