This PR begins improving the godocs for the auth module, and begins cleaning up the Ante handler. Additionally we previously calculated if the fee was sufficient for the tx on every single signer. This is now refactored to be more efficient, and have a better logical flow. No changelog entry as this is new to this release.
161 lines
4.6 KiB
Go
161 lines
4.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/tendermint/tendermint/crypto"
|
|
)
|
|
|
|
var _ sdk.Tx = (*StdTx)(nil)
|
|
|
|
// StdTx is a standard way to wrap a Msg with Fee and Signatures.
|
|
// NOTE: the first signature is the fee payer (Signatures must not be nil).
|
|
type StdTx struct {
|
|
Msgs []sdk.Msg `json:"msg"`
|
|
Fee StdFee `json:"fee"`
|
|
Signatures []StdSignature `json:"signatures"`
|
|
Memo string `json:"memo"`
|
|
}
|
|
|
|
func NewStdTx(msgs []sdk.Msg, fee StdFee, sigs []StdSignature, memo string) StdTx {
|
|
return StdTx{
|
|
Msgs: msgs,
|
|
Fee: fee,
|
|
Signatures: sigs,
|
|
Memo: memo,
|
|
}
|
|
}
|
|
|
|
//nolint
|
|
func (tx StdTx) GetMsgs() []sdk.Msg { return tx.Msgs }
|
|
|
|
// GetSigners returns the addresses that must sign the transaction.
|
|
// Addresses are returned in a determistic 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 StdTx) GetSigners() []sdk.AccAddress {
|
|
seen := map[string]bool{}
|
|
var signers []sdk.AccAddress
|
|
for _, msg := range tx.GetMsgs() {
|
|
for _, addr := range msg.GetSigners() {
|
|
if !seen[addr.String()] {
|
|
signers = append(signers, addr)
|
|
seen[addr.String()] = true
|
|
}
|
|
}
|
|
}
|
|
return signers
|
|
}
|
|
|
|
//nolint
|
|
func (tx StdTx) GetMemo() string { return tx.Memo }
|
|
|
|
// Signatures returns the signature of signers who signed the Msg.
|
|
// GetSignatures returns the signature of signers who signed the Msg.
|
|
// CONTRACT: Length returned is same as length of
|
|
// pubkeys returned from MsgKeySigners, and the order
|
|
// matches.
|
|
// CONTRACT: If the signature is missing (ie the Msg is
|
|
// invalid), then the corresponding signature is
|
|
// .Empty().
|
|
func (tx StdTx) GetSignatures() []StdSignature { return tx.Signatures }
|
|
|
|
//__________________________________________________________
|
|
|
|
// StdFee includes the amount of coins paid in fees and the maximum
|
|
// gas to be used by the transaction. The ratio yields an effective "gasprice",
|
|
// which must be above some miminum to be accepted into the mempool.
|
|
type StdFee struct {
|
|
Amount sdk.Coins `json:"amount"`
|
|
Gas int64 `json:"gas"`
|
|
}
|
|
|
|
func NewStdFee(gas int64, amount ...sdk.Coin) StdFee {
|
|
return StdFee{
|
|
Amount: amount,
|
|
Gas: gas,
|
|
}
|
|
}
|
|
|
|
// fee bytes for signing later
|
|
func (fee StdFee) Bytes() []byte {
|
|
// normalize. XXX
|
|
// this is a sign of something ugly
|
|
// (in the lcd_test, client side its null,
|
|
// server side its [])
|
|
if len(fee.Amount) == 0 {
|
|
fee.Amount = sdk.Coins{}
|
|
}
|
|
bz, err := msgCdc.MarshalJSON(fee) // TODO
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return bz
|
|
}
|
|
|
|
//__________________________________________________________
|
|
|
|
// StdSignDoc is replay-prevention structure.
|
|
// It includes the result of msg.GetSignBytes(),
|
|
// as well as the ChainID (prevent cross chain replay)
|
|
// and the Sequence numbers for each signature (prevent
|
|
// inchain replay and enforce tx ordering per account).
|
|
type StdSignDoc struct {
|
|
AccountNumber int64 `json:"account_number"`
|
|
ChainID string `json:"chain_id"`
|
|
Fee json.RawMessage `json:"fee"`
|
|
Memo string `json:"memo"`
|
|
Msgs []json.RawMessage `json:"msgs"`
|
|
Sequence int64 `json:"sequence"`
|
|
}
|
|
|
|
// StdSignBytes returns the bytes to sign for a transaction.
|
|
func StdSignBytes(chainID string, accnum int64, sequence int64, fee StdFee, msgs []sdk.Msg, memo string) []byte {
|
|
var msgsBytes []json.RawMessage
|
|
for _, msg := range msgs {
|
|
msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes()))
|
|
}
|
|
bz, err := msgCdc.MarshalJSON(StdSignDoc{
|
|
AccountNumber: accnum,
|
|
ChainID: chainID,
|
|
Fee: json.RawMessage(fee.Bytes()),
|
|
Memo: memo,
|
|
Msgs: msgsBytes,
|
|
Sequence: sequence,
|
|
})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return sdk.MustSortJSON(bz)
|
|
}
|
|
|
|
// Standard Signature
|
|
type StdSignature struct {
|
|
crypto.PubKey `json:"pub_key"` // optional
|
|
Signature []byte `json:"signature"`
|
|
AccountNumber int64 `json:"account_number"`
|
|
Sequence int64 `json:"sequence"`
|
|
}
|
|
|
|
// logic for standard transaction decoding
|
|
func DefaultTxDecoder(cdc *codec.Codec) sdk.TxDecoder {
|
|
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
|
var tx = StdTx{}
|
|
|
|
if len(txBytes) == 0 {
|
|
return nil, sdk.ErrTxDecode("txBytes are empty")
|
|
}
|
|
|
|
// StdTx.Msg is an interface. The concrete types
|
|
// are registered by MakeTxCodec
|
|
err := cdc.UnmarshalBinary(txBytes, &tx)
|
|
if err != nil {
|
|
return nil, sdk.ErrTxDecode("").TraceSDK(err.Error())
|
|
}
|
|
return tx, nil
|
|
}
|
|
}
|