440 lines
14 KiB
Go
440 lines
14 KiB
Go
package ante
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"fmt"
|
|
|
|
"github.com/tendermint/tendermint/crypto"
|
|
|
|
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
|
kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
|
|
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
|
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
|
"github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
|
|
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
|
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
)
|
|
|
|
var (
|
|
// simulation signature values used to estimate gas consumption
|
|
key = make([]byte, secp256k1.PubKeySize)
|
|
simSecp256k1Pubkey = &secp256k1.PubKey{Key: key}
|
|
simSecp256k1Sig [64]byte
|
|
|
|
_ authsigning.SigVerifiableTx = (*legacytx.StdTx)(nil) // assert StdTx implements SigVerifiableTx
|
|
)
|
|
|
|
func init() {
|
|
// This decodes a valid hex string into a sepc256k1Pubkey for use in transaction simulation
|
|
bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A")
|
|
copy(key, bz)
|
|
simSecp256k1Pubkey.Key = key
|
|
}
|
|
|
|
// SignatureVerificationGasConsumer is the type of function that is used to both
|
|
// consume gas when verifying signatures and also to accept or reject different types of pubkeys
|
|
// This is where apps can define their own PubKey
|
|
type SignatureVerificationGasConsumer = func(meter sdk.GasMeter, sig signing.SignatureV2, params types.Params) error
|
|
|
|
// SetPubKeyDecorator sets PubKeys in context for any signer which does not already have pubkey set
|
|
// PubKeys must be set in context for all signers before any other sigverify decorators run
|
|
// CONTRACT: Tx must implement SigVerifiableTx interface
|
|
type SetPubKeyDecorator struct {
|
|
ak AccountKeeper
|
|
}
|
|
|
|
func NewSetPubKeyDecorator(ak AccountKeeper) SetPubKeyDecorator {
|
|
return SetPubKeyDecorator{
|
|
ak: ak,
|
|
}
|
|
}
|
|
|
|
func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
|
sigTx, ok := tx.(authsigning.SigVerifiableTx)
|
|
if !ok {
|
|
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid tx type")
|
|
}
|
|
|
|
pubkeys := sigTx.GetPubKeys()
|
|
signers := sigTx.GetSigners()
|
|
|
|
for i, pk := range pubkeys {
|
|
// PublicKey was omitted from slice since it has already been set in context
|
|
if pk == nil {
|
|
if !simulate {
|
|
continue
|
|
}
|
|
pk = simSecp256k1Pubkey
|
|
}
|
|
// Only make check if simulate=false
|
|
if !simulate && !bytes.Equal(pk.Address(), signers[i]) {
|
|
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey,
|
|
"pubKey does not match signer address %s with signer index: %d", signers[i], i)
|
|
}
|
|
|
|
acc, err := GetSignerAcc(ctx, spkd.ak, signers[i])
|
|
if err != nil {
|
|
return ctx, err
|
|
}
|
|
// account already has pubkey set,no need to reset
|
|
if acc.GetPubKey() != nil {
|
|
continue
|
|
}
|
|
err = acc.SetPubKey(pk)
|
|
if err != nil {
|
|
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, err.Error())
|
|
}
|
|
spkd.ak.SetAccount(ctx, acc)
|
|
}
|
|
|
|
return next(ctx, tx, simulate)
|
|
}
|
|
|
|
// Consume parameter-defined amount of gas for each signature according to the passed-in SignatureVerificationGasConsumer function
|
|
// before calling the next AnteHandler
|
|
// CONTRACT: Pubkeys are set in context for all signers before this decorator runs
|
|
// CONTRACT: Tx must implement SigVerifiableTx interface
|
|
type SigGasConsumeDecorator struct {
|
|
ak AccountKeeper
|
|
sigGasConsumer SignatureVerificationGasConsumer
|
|
}
|
|
|
|
func NewSigGasConsumeDecorator(ak AccountKeeper, sigGasConsumer SignatureVerificationGasConsumer) SigGasConsumeDecorator {
|
|
return SigGasConsumeDecorator{
|
|
ak: ak,
|
|
sigGasConsumer: sigGasConsumer,
|
|
}
|
|
}
|
|
|
|
func (sgcd SigGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
|
sigTx, ok := tx.(authsigning.SigVerifiableTx)
|
|
if !ok {
|
|
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type")
|
|
}
|
|
|
|
params := sgcd.ak.GetParams(ctx)
|
|
sigs, err := sigTx.GetSignaturesV2()
|
|
if err != nil {
|
|
return ctx, err
|
|
}
|
|
|
|
// stdSigs contains the sequence number, account number, and signatures.
|
|
// When simulating, this would just be a 0-length slice.
|
|
signerAddrs := sigTx.GetSigners()
|
|
|
|
for i, sig := range sigs {
|
|
signerAcc, err := GetSignerAcc(ctx, sgcd.ak, signerAddrs[i])
|
|
if err != nil {
|
|
return ctx, err
|
|
}
|
|
|
|
pubKey := signerAcc.GetPubKey()
|
|
|
|
// In simulate mode the transaction comes with no signatures, thus if the
|
|
// account's pubkey is nil, both signature verification and gasKVStore.Set()
|
|
// shall consume the largest amount, i.e. it takes more gas to verify
|
|
// secp256k1 keys than ed25519 ones.
|
|
if simulate && pubKey == nil {
|
|
pubKey = simSecp256k1Pubkey
|
|
}
|
|
|
|
// make a SignatureV2 with PubKey filled in from above
|
|
sig = signing.SignatureV2{
|
|
PubKey: pubKey,
|
|
Data: sig.Data,
|
|
Sequence: sig.Sequence,
|
|
}
|
|
|
|
err = sgcd.sigGasConsumer(ctx.GasMeter(), sig, params)
|
|
if err != nil {
|
|
return ctx, err
|
|
}
|
|
}
|
|
|
|
return next(ctx, tx, simulate)
|
|
}
|
|
|
|
// Verify all signatures for a tx and return an error if any are invalid. Note,
|
|
// the SigVerificationDecorator decorator will not get executed on ReCheck.
|
|
//
|
|
// CONTRACT: Pubkeys are set in context for all signers before this decorator runs
|
|
// CONTRACT: Tx must implement SigVerifiableTx interface
|
|
type SigVerificationDecorator struct {
|
|
ak AccountKeeper
|
|
signModeHandler authsigning.SignModeHandler
|
|
}
|
|
|
|
func NewSigVerificationDecorator(ak AccountKeeper, signModeHandler authsigning.SignModeHandler) SigVerificationDecorator {
|
|
return SigVerificationDecorator{
|
|
ak: ak,
|
|
signModeHandler: signModeHandler,
|
|
}
|
|
}
|
|
|
|
// OnlyLegacyAminoSigners checks SignatureData to see if all
|
|
// signers are using SIGN_MODE_LEGACY_AMINO_JSON. If this is the case
|
|
// then the corresponding SignatureV2 struct will not have account sequence
|
|
// explicitly set, and we should skip the explicit verification of sig.Sequence
|
|
// in the SigVerificationDecorator's AnteHandler function.
|
|
func OnlyLegacyAminoSigners(sigData signing.SignatureData) bool {
|
|
switch v := sigData.(type) {
|
|
case *signing.SingleSignatureData:
|
|
return v.SignMode == signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON
|
|
case *signing.MultiSignatureData:
|
|
for _, s := range v.Signatures {
|
|
if !OnlyLegacyAminoSigners(s) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
|
// no need to verify signatures on recheck tx
|
|
if ctx.IsReCheckTx() {
|
|
return next(ctx, tx, simulate)
|
|
}
|
|
sigTx, ok := tx.(authsigning.SigVerifiableTx)
|
|
if !ok {
|
|
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type")
|
|
}
|
|
|
|
// stdSigs contains the sequence number, account number, and signatures.
|
|
// When simulating, this would just be a 0-length slice.
|
|
sigs, err := sigTx.GetSignaturesV2()
|
|
if err != nil {
|
|
return ctx, err
|
|
}
|
|
|
|
signerAddrs := sigTx.GetSigners()
|
|
|
|
// check that signer length and signature length are the same
|
|
if len(sigs) != len(signerAddrs) {
|
|
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signer; expected: %d, got %d", len(signerAddrs), len(sigs))
|
|
}
|
|
|
|
for i, sig := range sigs {
|
|
acc, err := GetSignerAcc(ctx, svd.ak, signerAddrs[i])
|
|
if err != nil {
|
|
return ctx, err
|
|
}
|
|
|
|
// retrieve pubkey
|
|
pubKey := acc.GetPubKey()
|
|
if !simulate && pubKey == nil {
|
|
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, "pubkey on account is not set")
|
|
}
|
|
|
|
// Check account sequence number.
|
|
// When using Amino StdSignatures, we actually don't have the Sequence in
|
|
// the SignatureV2 struct (it's only in the SignDoc). In this case, we
|
|
// cannot check sequence directly, and must do it via signature
|
|
// verification (in the VerifySignature call below).
|
|
onlyAminoSigners := OnlyLegacyAminoSigners(sig.Data)
|
|
if !onlyAminoSigners {
|
|
if sig.Sequence != acc.GetSequence() {
|
|
return ctx, sdkerrors.Wrapf(
|
|
sdkerrors.ErrWrongSequence,
|
|
"account sequence mismatch, expected %d, got %d", acc.GetSequence(), sig.Sequence,
|
|
)
|
|
}
|
|
}
|
|
|
|
// retrieve signer data
|
|
genesis := ctx.BlockHeight() == 0
|
|
chainID := ctx.ChainID()
|
|
var accNum uint64
|
|
if !genesis {
|
|
accNum = acc.GetAccountNumber()
|
|
}
|
|
signerData := authsigning.SignerData{
|
|
ChainID: chainID,
|
|
AccountNumber: accNum,
|
|
Sequence: acc.GetSequence(),
|
|
}
|
|
|
|
if !simulate {
|
|
err := authsigning.VerifySignature(pubKey, signerData, sig.Data, svd.signModeHandler, tx)
|
|
if err != nil {
|
|
var errMsg string
|
|
if onlyAminoSigners {
|
|
// If all signers are using SIGN_MODE_LEGACY_AMINO, we rely on VerifySignature to check account sequence number,
|
|
// and therefore communicate sequence number as a potential cause of error.
|
|
errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d), sequence (%d) and chain-id (%s)", accNum, acc.GetSequence(), chainID)
|
|
} else {
|
|
errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d) and chain-id (%s)", accNum, chainID)
|
|
}
|
|
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, errMsg)
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return next(ctx, tx, simulate)
|
|
}
|
|
|
|
// IncrementSequenceDecorator handles incrementing sequences of all signers.
|
|
// Use the IncrementSequenceDecorator decorator to prevent replay attacks. Note,
|
|
// there is no need to execute IncrementSequenceDecorator on RecheckTX since
|
|
// CheckTx would already bump the sequence number.
|
|
//
|
|
// NOTE: Since CheckTx and DeliverTx state are managed separately, subsequent and
|
|
// sequential txs orginating from the same account cannot be handled correctly in
|
|
// a reliable way unless sequence numbers are managed and tracked manually by a
|
|
// client. It is recommended to instead use multiple messages in a tx.
|
|
type IncrementSequenceDecorator struct {
|
|
ak AccountKeeper
|
|
}
|
|
|
|
func NewIncrementSequenceDecorator(ak AccountKeeper) IncrementSequenceDecorator {
|
|
return IncrementSequenceDecorator{
|
|
ak: ak,
|
|
}
|
|
}
|
|
|
|
func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
|
sigTx, ok := tx.(authsigning.SigVerifiableTx)
|
|
if !ok {
|
|
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type")
|
|
}
|
|
|
|
// increment sequence of all signers
|
|
for _, addr := range sigTx.GetSigners() {
|
|
acc := isd.ak.GetAccount(ctx, addr)
|
|
if err := acc.SetSequence(acc.GetSequence() + 1); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
isd.ak.SetAccount(ctx, acc)
|
|
}
|
|
|
|
return next(ctx, tx, simulate)
|
|
}
|
|
|
|
// ValidateSigCountDecorator takes in Params and returns errors if there are too many signatures in the tx for the given params
|
|
// otherwise it calls next AnteHandler
|
|
// Use this decorator to set parameterized limit on number of signatures in tx
|
|
// CONTRACT: Tx must implement SigVerifiableTx interface
|
|
type ValidateSigCountDecorator struct {
|
|
ak AccountKeeper
|
|
}
|
|
|
|
func NewValidateSigCountDecorator(ak AccountKeeper) ValidateSigCountDecorator {
|
|
return ValidateSigCountDecorator{
|
|
ak: ak,
|
|
}
|
|
}
|
|
|
|
func (vscd ValidateSigCountDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
|
sigTx, ok := tx.(authsigning.SigVerifiableTx)
|
|
if !ok {
|
|
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a sigTx")
|
|
}
|
|
|
|
params := vscd.ak.GetParams(ctx)
|
|
pubKeys := sigTx.GetPubKeys()
|
|
|
|
sigCount := 0
|
|
for _, pk := range pubKeys {
|
|
sigCount += CountSubKeys(pk)
|
|
if uint64(sigCount) > params.TxSigLimit {
|
|
return ctx, sdkerrors.Wrapf(sdkerrors.ErrTooManySignatures,
|
|
"signatures: %d, limit: %d", sigCount, params.TxSigLimit)
|
|
}
|
|
}
|
|
|
|
return next(ctx, tx, simulate)
|
|
}
|
|
|
|
// DefaultSigVerificationGasConsumer is the default implementation of SignatureVerificationGasConsumer. It consumes gas
|
|
// for signature verification based upon the public key type. The cost is fetched from the given params and is matched
|
|
// by the concrete type.
|
|
func DefaultSigVerificationGasConsumer(
|
|
meter sdk.GasMeter, sig signing.SignatureV2, params types.Params,
|
|
) error {
|
|
pubkey := sig.PubKey
|
|
switch pubkey := pubkey.(type) {
|
|
case *ed25519.PubKey:
|
|
meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519")
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, "ED25519 public keys are unsupported")
|
|
|
|
case *secp256k1.PubKey:
|
|
meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: secp256k1")
|
|
return nil
|
|
|
|
case multisig.PubKey:
|
|
multisignature, ok := sig.Data.(*signing.MultiSignatureData)
|
|
if !ok {
|
|
return fmt.Errorf("expected %T, got, %T", &signing.MultiSignatureData{}, sig.Data)
|
|
}
|
|
err := ConsumeMultisignatureVerificationGas(meter, multisignature, pubkey, params, sig.Sequence)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
|
|
default:
|
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "unrecognized public key type: %T", pubkey)
|
|
}
|
|
}
|
|
|
|
// ConsumeMultisignatureVerificationGas consumes gas from a GasMeter for verifying a multisig pubkey signature
|
|
func ConsumeMultisignatureVerificationGas(
|
|
meter sdk.GasMeter, sig *signing.MultiSignatureData, pubkey multisig.PubKey,
|
|
params types.Params, accSeq uint64,
|
|
) error {
|
|
|
|
size := sig.BitArray.Count()
|
|
sigIndex := 0
|
|
|
|
for i := 0; i < size; i++ {
|
|
if !sig.BitArray.GetIndex(i) {
|
|
continue
|
|
}
|
|
sigV2 := signing.SignatureV2{
|
|
PubKey: pubkey.GetPubKeys()[i],
|
|
Data: sig.Signatures[sigIndex],
|
|
Sequence: accSeq,
|
|
}
|
|
err := DefaultSigVerificationGasConsumer(meter, sigV2, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sigIndex++
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetSignerAcc returns an account for a given address that is expected to sign
|
|
// a transaction.
|
|
func GetSignerAcc(ctx sdk.Context, ak AccountKeeper, addr sdk.AccAddress) (types.AccountI, error) {
|
|
if acc := ak.GetAccount(ctx, addr); acc != nil {
|
|
return acc, nil
|
|
}
|
|
|
|
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr)
|
|
}
|
|
|
|
// CountSubKeys counts the total number of keys for a multi-sig public key.
|
|
func CountSubKeys(pub crypto.PubKey) int {
|
|
v, ok := pub.(*kmultisig.LegacyAminoPubKey)
|
|
if !ok {
|
|
return 1
|
|
}
|
|
|
|
numKeys := 0
|
|
for _, subkey := range v.GetPubKeys() {
|
|
numKeys += CountSubKeys(subkey)
|
|
}
|
|
|
|
return numKeys
|
|
}
|