cosmos-sdk/x/auth/client/context/context.go
Alessio Treglia 599923fb99
Introduce simulate mode
Add a simulate only flag '--dry-run' to both CLI tx commands
and RESTful endpoints to trigger the simulation of unsigned
transactions.

* Turning --dry-run on causes the --gas flag to be ignored.
  The simulation will return the estimate of the gas required
  to actually run the transaction.
* Adjustment is no longer required. It now defaults to 1.0.
* In some test cases accounts retrieved from the state do not
  come with a PubKey. In such cases, a fake secp256k1 key is
  generated and gas consumption calculated accordingly.

Closes: #2110
2018-08-31 19:04:11 +02:00

180 lines
4.8 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)
}
// BuildWithPubKey builds a single message to be signed from a TxContext given a set of
// messages and attach the public key associated to the given name.
// It returns an error if a fee is supplied but cannot be parsed or the key cannot be
// retrieved.
func (ctx TxContext) BuildWithPubKey(name string, msgs []sdk.Msg) ([]byte, error) {
msg, err := ctx.Build(msgs)
if err != nil {
return nil, err
}
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
info, err := keybase.Get(name)
if err != nil {
return nil, err
}
sigs := []auth.StdSignature{{
AccountNumber: msg.AccountNumber,
Sequence: msg.Sequence,
PubKey: info.GetPubKey(),
}}
return ctx.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo))
}