cosmos-sdk/std/tx.go

266 lines
6.6 KiB
Go

package std
import (
"github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto"
clientx "github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth"
)
var (
_ sdk.Tx = (*Transaction)(nil)
_ clientx.ClientTx = (*Transaction)(nil)
_ clientx.Generator = TxGenerator{}
_ clientx.ClientFee = &StdFee{}
_ clientx.ClientSignature = &StdSignature{}
)
// TxGenerator defines a transaction generator that allows clients to construct
// transactions.
type TxGenerator struct{}
func (g TxGenerator) NewFee() clientx.ClientFee {
return &StdFee{}
}
func (g TxGenerator) NewSignature() clientx.ClientSignature {
return &StdSignature{}
}
// NewTx returns a reference to an empty Transaction type.
func (TxGenerator) NewTx() clientx.ClientTx {
return &Transaction{}
}
func NewTransaction(fee StdFee, memo string, sdkMsgs []sdk.Msg) (*Transaction, error) {
tx := &Transaction{
StdTxBase: NewStdTxBase(fee, nil, memo),
}
if err := tx.SetMsgs(sdkMsgs...); err != nil {
return nil, err
}
return tx, nil
}
// GetMsgs returns all the messages in a Transaction as a slice of sdk.Msg.
func (tx Transaction) GetMsgs() []sdk.Msg {
msgs := make([]sdk.Msg, len(tx.Msgs))
for i, m := range tx.Msgs {
msgs[i] = m.GetMsg()
}
return msgs
}
// GetSigners returns the addresses that must sign the transaction. Addresses are
// returned in a deterministic order. They are accumulated from the GetSigners
// method for each Msg in the order they appear in tx.GetMsgs(). Duplicate addresses
// will be omitted.
func (tx Transaction) GetSigners() []sdk.AccAddress {
var signers []sdk.AccAddress
seen := map[string]bool{}
for _, msg := range tx.GetMsgs() {
for _, addr := range msg.GetSigners() {
if !seen[addr.String()] {
signers = append(signers, addr)
seen[addr.String()] = true
}
}
}
return signers
}
// ValidateBasic does a simple and lightweight validation check that doesn't
// require access to any other information.
func (tx Transaction) ValidateBasic() error {
stdSigs := tx.GetSignatures()
if tx.Fee.Gas > auth.MaxGasWanted {
return sdkerrors.Wrapf(
sdkerrors.ErrInvalidRequest, "invalid gas supplied; %d > %d", tx.Fee.Gas, auth.MaxGasWanted,
)
}
if tx.Fee.Amount.IsAnyNegative() {
return sdkerrors.Wrapf(
sdkerrors.ErrInsufficientFee, "invalid fee provided: %s", tx.Fee.Amount,
)
}
if len(stdSigs) == 0 {
return sdkerrors.ErrNoSignatures
}
if len(stdSigs) != len(tx.GetSigners()) {
return sdkerrors.Wrapf(
sdkerrors.ErrUnauthorized, "wrong number of signers; expected %d, got %d", tx.GetSigners(), len(stdSigs),
)
}
return nil
}
// SetMsgs sets the messages for a Transaction. It will overwrite any existing
// messages set.
func (tx *Transaction) SetMsgs(sdkMsgs ...sdk.Msg) error {
msgs := make([]Message, len(sdkMsgs))
for i, msg := range sdkMsgs {
m := &Message{}
if err := m.SetMsg(msg); err != nil {
return err
}
msgs[i] = *m
}
tx.Msgs = msgs
return nil
}
// GetSignatures returns all the transaction's signatures.
func (tx Transaction) GetSignatures() []sdk.Signature {
sdkSigs := make([]sdk.Signature, len(tx.Signatures))
for i, sig := range tx.Signatures {
sdkSigs[i] = sig
}
return sdkSigs
}
// SetSignatures sets the transaction's signatures. It will overwrite any
// existing signatures set.
func (tx *Transaction) SetSignatures(sdkSigs ...clientx.ClientSignature) error {
sigs := make([]StdSignature, len(sdkSigs))
for i, sig := range sdkSigs {
if sig != nil {
sigs[i] = NewStdSignature(sig.GetPubKey(), sig.GetSignature())
}
}
tx.Signatures = sigs
return nil
}
// GetFee returns the transaction's fee.
func (tx Transaction) GetFee() sdk.Fee {
return tx.Fee
}
// SetFee sets the transaction's fee. It will overwrite any existing fee set.
func (tx *Transaction) SetFee(fee clientx.ClientFee) error {
tx.Fee = NewStdFee(fee.GetGas(), fee.GetAmount())
return nil
}
// GetMemo returns the transaction's memo.
func (tx Transaction) GetMemo() string {
return tx.Memo
}
// SetMemo sets the transaction's memo. It will overwrite any existing memo set.
func (tx *Transaction) SetMemo(memo string) {
tx.Memo = memo
}
// CanonicalSignBytes returns the canonical JSON bytes to sign over for the
// Transaction given a chain ID, account sequence and account number. The JSON
// encoding ensures all field names adhere to their proto definition, default
// values are omitted, and follows the JSON Canonical Form.
func (tx Transaction) CanonicalSignBytes(cid string, num, seq uint64) ([]byte, error) {
return NewSignDoc(num, seq, cid, tx.Memo, tx.Fee, tx.Msgs...).CanonicalSignBytes()
}
func NewSignDoc(num, seq uint64, cid, memo string, fee StdFee, msgs ...Message) *SignDoc {
return &SignDoc{
StdSignDocBase: NewStdSignDocBase(num, seq, cid, memo, fee),
Msgs: msgs,
}
}
// CanonicalSignBytes returns the canonical JSON bytes to sign over, where the
// SignDoc is derived from a Transaction. The JSON encoding ensures all field
// names adhere to their proto definition, default values are omitted, and follows
// the JSON Canonical Form.
func (sd *SignDoc) CanonicalSignBytes() ([]byte, error) {
return sdk.CanonicalSignBytes(sd)
}
// NewStdFee returns a new instance of StdFee
func NewStdFee(gas uint64, amount sdk.Coins) StdFee {
return StdFee{
Amount: amount,
Gas: gas,
}
}
func NewStdSignature(pk crypto.PubKey, sig []byte) StdSignature {
var pkBz []byte
if pk != nil {
pkBz = pk.Bytes()
}
return StdSignature{PubKey: pkBz, Signature: sig}
}
func NewStdTxBase(fee StdFee, sigs []StdSignature, memo string) StdTxBase {
return StdTxBase{
Fee: fee,
Signatures: sigs,
Memo: memo,
}
}
func NewStdSignDocBase(num, seq uint64, cid, memo string, fee StdFee) StdSignDocBase {
return StdSignDocBase{
ChainID: cid,
AccountNumber: num,
Sequence: seq,
Memo: memo,
Fee: fee,
}
}
func (m StdFee) GetGas() uint64 {
return m.Gas
}
func (m StdFee) GetAmount() sdk.Coins {
return m.Amount
}
func (m *StdFee) SetGas(gas uint64) {
m.Gas = gas
}
func (m *StdFee) SetAmount(amount sdk.Coins) {
m.Amount = amount
}
func (m StdSignature) GetPubKey() crypto.PubKey {
var pk crypto.PubKey
if len(m.PubKey) == 0 {
return nil
}
amino.MustUnmarshalBinaryBare(m.PubKey, &pk)
return pk
}
func (m StdSignature) GetSignature() []byte {
return m.Signature
}
func (m *StdSignature) SetPubKey(pk crypto.PubKey) error {
m.PubKey = pk.Bytes()
return nil
}
func (m *StdSignature) SetSignature(signature []byte) {
m.Signature = signature
}