cosmos-sdk/x/auth/client/context/context.go
Alessio Treglia f432c0c383
Simulate transactions before actual execution
* Change --gas=0 semantic in order to enable gas auto estimate.
* REST clients have been modified to simulate the execution of
  the tx first to then populate the context with the estimated
  gas amount returned by the simulation.
* The simulation returns both an unadjusted gas estimate and an
  adjusted one. The adjustment is required to ensure that the
  ensuing execution doesn't fail due to state changes that might
  have occurred. Gas adjustment can be controlled via the CLI's
  --gas-adjustment flag.
* Tiny refactorig of REST endpoints error handling.

Closes: #1246
2018-08-24 10:16:51 +01:00

151 lines
4.0 KiB
Go

package context
import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/pkg/errors"
"github.com/spf13/viper"
)
// TxContext implements a transaction context created in SDK modules.
type TxContext struct {
Codec *wire.Codec
AccountNumber int64
Sequence int64
Gas int64
ChainID string
Memo string
Fee string
}
// NewTxContextFromCLI returns a new initialized TxContext with parameters from
// the command line using Viper.
func NewTxContextFromCLI() TxContext {
// if chain ID is not specified manually, read default chain ID
chainID := viper.GetString(client.FlagChainID)
if chainID == "" {
defaultChainID, err := defaultChainID()
if err != nil {
chainID = defaultChainID
}
}
return TxContext{
ChainID: chainID,
Gas: viper.GetInt64(client.FlagGas),
AccountNumber: viper.GetInt64(client.FlagAccountNumber),
Sequence: viper.GetInt64(client.FlagSequence),
Fee: viper.GetString(client.FlagFee),
Memo: viper.GetString(client.FlagMemo),
}
}
// WithCodec returns a copy of the context with an updated codec.
func (ctx TxContext) WithCodec(cdc *wire.Codec) TxContext {
ctx.Codec = cdc
return ctx
}
// WithChainID returns a copy of the context with an updated chainID.
func (ctx TxContext) WithChainID(chainID string) TxContext {
ctx.ChainID = chainID
return ctx
}
// WithGas returns a copy of the context with an updated gas.
func (ctx TxContext) WithGas(gas int64) TxContext {
ctx.Gas = gas
return ctx
}
// WithFee returns a copy of the context with an updated fee.
func (ctx TxContext) WithFee(fee string) TxContext {
ctx.Fee = fee
return ctx
}
// WithSequence returns a copy of the context with an updated sequence number.
func (ctx TxContext) WithSequence(sequence int64) TxContext {
ctx.Sequence = sequence
return ctx
}
// WithMemo returns a copy of the context with an updated memo.
func (ctx TxContext) WithMemo(memo string) TxContext {
ctx.Memo = memo
return ctx
}
// WithAccountNumber returns a copy of the context with an account number.
func (ctx TxContext) WithAccountNumber(accnum int64) TxContext {
ctx.AccountNumber = accnum
return ctx
}
// Build builds a single message to be signed from a TxContext given a set of
// messages. It returns an error if a fee is supplied but cannot be parsed.
func (ctx TxContext) Build(msgs []sdk.Msg) (auth.StdSignMsg, error) {
chainID := ctx.ChainID
if chainID == "" {
return auth.StdSignMsg{}, errors.Errorf("chain ID required but not specified")
}
fee := sdk.Coin{}
if ctx.Fee != "" {
parsedFee, err := sdk.ParseCoin(ctx.Fee)
if err != nil {
return auth.StdSignMsg{}, err
}
fee = parsedFee
}
return auth.StdSignMsg{
ChainID: ctx.ChainID,
AccountNumber: ctx.AccountNumber,
Sequence: ctx.Sequence,
Memo: ctx.Memo,
Msgs: msgs,
Fee: auth.NewStdFee(ctx.Gas, fee),
}, nil
}
// Sign signs a transaction given a name, passphrase, and a single message to
// signed. An error is returned if signing fails.
func (ctx TxContext) Sign(name, passphrase string, msg auth.StdSignMsg) ([]byte, error) {
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
sig, pubkey, err := keybase.Sign(name, passphrase, msg.Bytes())
if err != nil {
return nil, err
}
sigs := []auth.StdSignature{{
AccountNumber: msg.AccountNumber,
Sequence: msg.Sequence,
PubKey: pubkey,
Signature: sig,
}}
return ctx.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo))
}
// BuildAndSign builds a single message to be signed, and signs a transaction
// with the built message given a name, passphrase, and a set of
// messages.
func (ctx TxContext) BuildAndSign(name, passphrase string, msgs []sdk.Msg) ([]byte, error) {
msg, err := ctx.Build(msgs)
if err != nil {
return nil, err
}
return ctx.Sign(name, passphrase, msg)
}