cosmos-sdk/types/tx/types.go

196 lines
4.8 KiB
Go

package tx
import (
"fmt"
"strings"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// MaxGasWanted defines the max gas allowed.
const MaxGasWanted = uint64((1 << 63) - 1)
// Interface implementation checks.
var _, _, _, _ codectypes.UnpackInterfacesMessage = &Tx{}, &TxBody{}, &AuthInfo{}, &SignerInfo{}
var _ sdk.Tx = &Tx{}
// GetMsgs implements the GetMsgs method on sdk.Tx.
func (t *Tx) GetMsgs() []sdk.Msg {
if t == nil || t.Body == nil {
return nil
}
anys := t.Body.Messages
res := make([]sdk.Msg, len(anys))
for i, any := range anys {
var msg sdk.Msg
if isServiceMsg(any.TypeUrl) {
msg = sdk.ServiceMsg{
MethodName: any.TypeUrl,
Request: any.GetCachedValue().(sdk.MsgRequest),
}
} else {
msg = any.GetCachedValue().(sdk.Msg)
}
res[i] = msg
}
return res
}
// ValidateBasic implements the ValidateBasic method on sdk.Tx.
func (t *Tx) ValidateBasic() error {
if t == nil {
return fmt.Errorf("bad Tx")
}
body := t.Body
if body == nil {
return fmt.Errorf("missing TxBody")
}
authInfo := t.AuthInfo
if authInfo == nil {
return fmt.Errorf("missing AuthInfo")
}
fee := authInfo.Fee
if fee == nil {
return fmt.Errorf("missing fee")
}
if fee.GasLimit > MaxGasWanted {
return sdkerrors.Wrapf(
sdkerrors.ErrInvalidRequest,
"invalid gas supplied; %d > %d", fee.GasLimit, MaxGasWanted,
)
}
if fee.Amount.IsAnyNegative() {
return sdkerrors.Wrapf(
sdkerrors.ErrInsufficientFee,
"invalid fee provided: %s", fee.Amount,
)
}
if fee.Payer != "" {
_, err := sdk.AccAddressFromBech32(fee.Payer)
if err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid fee payer address (%s)", err)
}
}
sigs := t.Signatures
if len(sigs) == 0 {
return sdkerrors.ErrNoSignatures
}
if len(sigs) != len(t.GetSigners()) {
return sdkerrors.Wrapf(
sdkerrors.ErrUnauthorized,
"wrong number of signers; expected %d, got %d", len(t.GetSigners()), len(sigs),
)
}
return nil
}
// GetSigners retrieves all the signers of a tx.
// This includes all unique signers of the messages (in order),
// as well as the FeePayer (if specified and not already included).
func (t *Tx) GetSigners() []sdk.AccAddress {
var signers []sdk.AccAddress
seen := map[string]bool{}
for _, msg := range t.GetMsgs() {
for _, addr := range msg.GetSigners() {
if !seen[addr.String()] {
signers = append(signers, addr)
seen[addr.String()] = true
}
}
}
// ensure any specified fee payer is included in the required signers (at the end)
feePayer := t.AuthInfo.Fee.Payer
if feePayer != "" && !seen[feePayer] {
payerAddr, err := sdk.AccAddressFromBech32(feePayer)
if err != nil {
panic(err)
}
signers = append(signers, payerAddr)
seen[feePayer] = true
}
return signers
}
// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method
func (t *Tx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
if t.Body != nil {
if err := t.Body.UnpackInterfaces(unpacker); err != nil {
return err
}
}
if t.AuthInfo != nil {
return t.AuthInfo.UnpackInterfaces(unpacker)
}
return nil
}
// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method
func (m *TxBody) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
for _, any := range m.Messages {
// If the any's typeUrl contains 2 slashes, then we unpack the any into
// a ServiceMsg struct as per ADR-031.
if isServiceMsg(any.TypeUrl) {
var req sdk.MsgRequest
err := unpacker.UnpackAny(any, &req)
if err != nil {
return err
}
} else {
var msg sdk.Msg
err := unpacker.UnpackAny(any, &msg)
if err != nil {
return err
}
}
}
return nil
}
// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method
func (m *AuthInfo) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
for _, signerInfo := range m.SignerInfos {
err := signerInfo.UnpackInterfaces(unpacker)
if err != nil {
return err
}
}
return nil
}
// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method
func (m *SignerInfo) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
return unpacker.UnpackAny(m.PublicKey, new(cryptotypes.PubKey))
}
// RegisterInterfaces registers the sdk.Tx interface.
func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
registry.RegisterInterface("cosmos.tx.v1beta1.Tx", (*sdk.Tx)(nil))
registry.RegisterImplementations((*sdk.Tx)(nil), &Tx{})
}
// isServiceMsg checks if a type URL corresponds to a service method name,
// i.e. /cosmos.bank.Msg/Send vs /cosmos.bank.MsgSend
func isServiceMsg(typeURL string) bool {
return strings.Count(typeURL, "/") >= 2
}