210 lines
5.9 KiB
Go
210 lines
5.9 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) {
|
|
sig, err := MakeSignature(name, passphrase, msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ctx.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, []auth.StdSignature{sig}, 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))
|
|
}
|
|
|
|
// SignStdTx appends a signature to a StdTx and returns a copy of a it. If append
|
|
// is false, it replaces the signatures already attached with the new signature.
|
|
func (ctx TxContext) SignStdTx(name, passphrase string, stdTx auth.StdTx, appendSig bool) (signedStdTx auth.StdTx, err error) {
|
|
stdSignature, err := MakeSignature(name, passphrase, auth.StdSignMsg{
|
|
ChainID: ctx.ChainID,
|
|
AccountNumber: ctx.AccountNumber,
|
|
Sequence: ctx.Sequence,
|
|
Fee: stdTx.Fee,
|
|
Msgs: stdTx.GetMsgs(),
|
|
Memo: stdTx.GetMemo(),
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
sigs := stdTx.GetSignatures()
|
|
if len(sigs) == 0 || !appendSig {
|
|
sigs = []auth.StdSignature{stdSignature}
|
|
} else {
|
|
sigs = append(sigs, stdSignature)
|
|
}
|
|
signedStdTx = auth.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, sigs, stdTx.GetMemo())
|
|
return
|
|
}
|
|
|
|
// MakeSignature builds a StdSignature given key name, passphrase, and a StdSignMsg.
|
|
func MakeSignature(name, passphrase string, msg auth.StdSignMsg) (sig auth.StdSignature, err error) {
|
|
keybase, err := keys.GetKeyBase()
|
|
if err != nil {
|
|
return
|
|
}
|
|
sigBytes, pubkey, err := keybase.Sign(name, passphrase, msg.Bytes())
|
|
if err != nil {
|
|
return
|
|
}
|
|
return auth.StdSignature{
|
|
AccountNumber: msg.AccountNumber,
|
|
Sequence: msg.Sequence,
|
|
PubKey: pubkey,
|
|
Signature: sigBytes,
|
|
}, nil
|
|
}
|