package ante import ( "bytes" "encoding/base64" "encoding/hex" "fmt" "time" "google.golang.org/protobuf/types/known/anypb" errorsmod "cosmossdk.io/errors" storetypes "cosmossdk.io/store/types" txsigning "cosmossdk.io/x/tx/signing" codectypes "git.cw.tr/mukan-network/mukan-sdk/codec/types" "git.cw.tr/mukan-network/mukan-sdk/crypto/keys/ed25519" kmultisig "git.cw.tr/mukan-network/mukan-sdk/crypto/keys/multisig" "git.cw.tr/mukan-network/mukan-sdk/crypto/keys/secp256k1" "git.cw.tr/mukan-network/mukan-sdk/crypto/keys/secp256r1" cryptotypes "git.cw.tr/mukan-network/mukan-sdk/crypto/types" "git.cw.tr/mukan-network/mukan-sdk/crypto/types/multisig" sdk "git.cw.tr/mukan-network/mukan-sdk/types" sdkerrors "git.cw.tr/mukan-network/mukan-sdk/types/errors" "git.cw.tr/mukan-network/mukan-sdk/types/tx/signing" authsigning "git.cw.tr/mukan-network/mukan-sdk/x/auth/signing" "git.cw.tr/mukan-network/mukan-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 ) 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 storetypes.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, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid tx type") } pubkeys, err := sigTx.GetPubKeys() if err != nil { return ctx, err } signers, err := sigTx.GetSigners() if err != nil { return sdk.Context{}, err } signerStrs := make([]string, len(signers)) for i, pk := range pubkeys { var err error signerStrs[i], err = spkd.ak.AddressCodec().BytesToString(signers[i]) if err != nil { return sdk.Context{}, err } // 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]) && ctx.IsSigverifyTx() { return ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidPubKey, "pubKey does not match signer address %s with signer index: %d", signerStrs[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, errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, err.Error()) } spkd.ak.SetAccount(ctx, acc) } // Also emit the following events, so that txs can be indexed by these // indices: // - signature (via `tx.signature=''`), // - concat(address,"/",sequence) (via `tx.acc_seq='cosmos1abc...def/42'`). sigs, err := sigTx.GetSignaturesV2() if err != nil { return ctx, err } isUnordered := false utx, ok := tx.(sdk.TxWithUnordered) if ok && utx.GetUnordered() { isUnordered = true } var events sdk.Events for i, sig := range sigs { // this shouldn't happen, but if we somehow got a tx with both a sequence set, and is unordered, // we shouldn't emit the event, as this is a false sequence, and won't actually be used. if !isUnordered { events = append(events, sdk.NewEvent(sdk.EventTypeTx, sdk.NewAttribute(sdk.AttributeKeyAccountSequence, fmt.Sprintf("%s/%d", signerStrs[i], sig.Sequence)), )) } sigBzs, err := signatureDataToBz(sig.Data) if err != nil { return ctx, err } for _, sigBz := range sigBzs { events = append(events, sdk.NewEvent(sdk.EventTypeTx, sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz)), )) } } ctx.EventManager().EmitEvents(events) 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 { if sigGasConsumer == nil { sigGasConsumer = DefaultSigVerificationGasConsumer } 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, errorsmod.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. signers, err := sigTx.GetSigners() if err != nil { return ctx, err } for i, sig := range sigs { signerAcc, err := GetSignerAcc(ctx, sgcd.ak, signers[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) } // SigVerificationDecorator verifies all signatures for a tx and returns an error if any are invalid. // Note, the SigVerificationDecorator will not check signatures on ReCheck. // // As of Cosmos SDK v0.53.0, the SigVerificationDecorator will also verify the validity of unordered transactions. // This involves ensuring the TTL is valid, and that the unordered nonce has not been used previously. // // 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 *txsigning.HandlerMap maxTxTimeoutDuration time.Duration unorderedTxGasCost uint64 } type SigVerificationDecoratorOption func(svd *SigVerificationDecorator) // WithMaxUnorderedTxTimeoutDuration sets the maximum TTL a transaction can define for unordered transactions. func WithMaxUnorderedTxTimeoutDuration(duration time.Duration) SigVerificationDecoratorOption { return func(svd *SigVerificationDecorator) { svd.maxTxTimeoutDuration = duration } } // WithUnorderedTxGasCost sets the gas cost for unordered transactions. // We must charge extra gas for unordered transactions // as they incur extra processing time for cleaning up the expired txs in x/auth PreBlocker. // Note: this value was chosen by 2x-ing the cost of fetching and removing an unordered nonce entry. func WithUnorderedTxGasCost(gasCost uint64) SigVerificationDecoratorOption { return func(svd *SigVerificationDecorator) { svd.unorderedTxGasCost = gasCost } } const ( // DefaultMaxTimeoutDuration defines a default maximum TTL a transaction can define. DefaultMaxTimeoutDuration = 10 * time.Minute // DefaultUnorderedTxGasCost defines a default gas cost for unordered transactions. // We must charge extra gas for unordered transactions // as they incur extra processing time for cleaning up the expired txs in x/auth PreBlocker. // Note: this value was chosen by 2x-ing the cost of fetching and removing an unordered nonce entry. DefaultUnorderedTxGasCost = uint64(2240) ) func NewSigVerificationDecorator(ak AccountKeeper, signModeHandler *txsigning.HandlerMap, opts ...SigVerificationDecoratorOption) SigVerificationDecorator { svd := SigVerificationDecorator{ ak: ak, signModeHandler: signModeHandler, maxTxTimeoutDuration: DefaultMaxTimeoutDuration, unorderedTxGasCost: DefaultUnorderedTxGasCost, } for _, opt := range opts { opt(&svd) } return svd } // 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) { sigTx, ok := tx.(authsigning.Tx) if !ok { return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") } utx, ok := tx.(sdk.TxWithUnordered) isUnordered := ok && utx.GetUnordered() unorderedEnabled := svd.ak.UnorderedTransactionsEnabled() if isUnordered && !unorderedEnabled { return ctx, errorsmod.Wrap(sdkerrors.ErrNotSupported, "unordered transactions are not enabled") } // 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 } signers, err := sigTx.GetSigners() if err != nil { return ctx, err } // check that signer length and signature length are the same if len(sigs) != len(signers) { return ctx, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signer; expected: %d, got %d", len(signers), len(sigs)) } // In normal transactions, each signer has a sequence value. In unordered transactions, the nonce value is at the tx body level, // so we get one nonce value for all the signers, rather than a sequence value for each signer. // Because of this, we verify the unordered nonce outside the sigs loop, to avoid verifying the same nonce multiple times. if isUnordered { if err := svd.verifyUnorderedNonce(ctx, utx); err != nil { return ctx, err } } for i, sig := range sigs { if sig.Sequence > 0 && isUnordered { return ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "sequence is not allowed for unordered transactions") } acc, err := GetSignerAcc(ctx, svd.ak, signers[i]) if err != nil { return ctx, err } // retrieve pubkey pubKey := acc.GetPubKey() if !simulate && pubKey == nil { return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "pubkey on account is not set") } // Check account sequence number. if !isUnordered { if sig.Sequence != acc.GetSequence() { return ctx, errorsmod.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() } // no need to verify signatures on recheck tx if !simulate && !ctx.IsReCheckTx() && ctx.IsSigverifyTx() { anyPk, _ := codectypes.NewAnyWithValue(pubKey) signerData := txsigning.SignerData{ Address: acc.GetAddress().String(), ChainID: chainID, AccountNumber: accNum, Sequence: sig.Sequence, PubKey: &anypb.Any{ TypeUrl: anyPk.TypeUrl, Value: anyPk.Value, }, } adaptableTx, ok := tx.(authsigning.V2AdaptableTx) if !ok { return ctx, fmt.Errorf("expected tx to implement V2AdaptableTx, got %T", tx) } txData := adaptableTx.GetSigningTxData() err = authsigning.VerifySignature(ctx, pubKey, signerData, sig.Data, svd.signModeHandler, txData) if err != nil { var errMsg string if OnlyLegacyAminoSigners(sig.Data) { // 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): (%s)", accNum, chainID, err.Error()) } return ctx, errorsmod.Wrap(sdkerrors.ErrUnauthorized, errMsg) } } } return next(ctx, tx, simulate) } // verifyUnorderedNonce verifies the unordered nonce of an unordered transaction. // This checks that: // 1. The unordered transaction's timeout timestamp is set. // 2. The unordered transaction's timeout timestamp is not in the past. // 3. The unordered transaction's timeout timestamp is not more than the max TTL. // 4. The unordered transaction's nonce has not been used previously. // // If all the checks above pass, the nonce is marked as used for each signer of the transaction. func (svd SigVerificationDecorator) verifyUnorderedNonce(ctx sdk.Context, unorderedTx sdk.TxWithUnordered) error { blockTime := ctx.BlockTime() timeoutTimestamp := unorderedTx.GetTimeoutTimeStamp() if timeoutTimestamp.IsZero() || timeoutTimestamp.Unix() == 0 { return errorsmod.Wrap( sdkerrors.ErrInvalidRequest, "unordered transaction must have timeout_timestamp set", ) } if timeoutTimestamp.Before(blockTime) { return errorsmod.Wrap( sdkerrors.ErrInvalidRequest, "unordered transaction has a timeout_timestamp that has already passed", ) } if timeoutTimestamp.After(blockTime.Add(svd.maxTxTimeoutDuration)) { return errorsmod.Wrapf( sdkerrors.ErrInvalidRequest, "unordered tx ttl exceeds %s", svd.maxTxTimeoutDuration.String(), ) } ctx.GasMeter().ConsumeGas(svd.unorderedTxGasCost, "unordered tx") execMode := ctx.ExecMode() if execMode == sdk.ExecModeSimulate { return nil } signerAddrs, err := extractSignersBytes(unorderedTx) if err != nil { return err } for _, signerAddr := range signerAddrs { if err := svd.ak.TryAddUnorderedNonce(ctx, signerAddr, unorderedTx.GetTimeoutTimeStamp()); err != nil { return errorsmod.Wrapf( sdkerrors.ErrInvalidRequest, "failed to add unordered nonce: %s", err, ) } } return nil } func extractSignersBytes(tx sdk.Tx) ([][]byte, error) { sigTx, ok := tx.(authsigning.SigVerifiableTx) if !ok { return nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid tx type") } return sigTx.GetSigners() } // IncrementSequenceDecorator handles incrementing sequences of all signers. // Use the IncrementSequenceDecorator decorator to prevent replay attacks. Note, // there is need to execute IncrementSequenceDecorator on RecheckTx since // BaseApp.Commit() will set the check state based on the latest header. // // 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) { if utx, ok := tx.(sdk.TxWithUnordered); ok && utx.GetUnordered() { if !isd.ak.UnorderedTransactionsEnabled() { return ctx, errorsmod.Wrap(sdkerrors.ErrNotSupported, "unordered transactions are disabled") } return next(ctx, tx, simulate) } sigTx, ok := tx.(authsigning.SigVerifiableTx) if !ok { return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") } // increment sequence of all signers signers, err := sigTx.GetSigners() if err != nil { return sdk.Context{}, err } for _, signer := range signers { acc := isd.ak.GetAccount(ctx, signer) 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, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a sigTx") } params := vscd.ak.GetParams(ctx) pubKeys, err := sigTx.GetPubKeys() if err != nil { return ctx, err } sigCount := 0 for _, pk := range pubKeys { sigCount += CountSubKeys(pk) if uint64(sigCount) > params.TxSigLimit { return ctx, errorsmod.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 storetypes.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 nil case *secp256k1.PubKey: meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: secp256k1") return nil case *secp256r1.PubKey: meter.ConsumeGas(params.SigVerifyCostSecp256r1(), "ante verify: secp256r1") 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 errorsmod.Wrapf(sdkerrors.ErrInvalidPubKey, "unrecognized public key type: %T", pubkey) } } // ConsumeMultisignatureVerificationGas consumes gas from a GasMeter for verifying a multisig pubkey signature func ConsumeMultisignatureVerificationGas( meter storetypes.GasMeter, sig *signing.MultiSignatureData, pubkey multisig.PubKey, params types.Params, accSeq uint64, ) error { size := sig.BitArray.Count() sigIndex := 0 for i := range size { 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) (sdk.AccountI, error) { if acc := ak.GetAccount(ctx, addr); acc != nil { return acc, nil } return nil, errorsmod.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr) } // CountSubKeys counts the total number of keys for a multi-sig public key. // A non-multisig, i.e. a regular signature, it naturally a count of 1. If it is a multisig, // then it recursively calls it on its pubkeys. func CountSubKeys(pub cryptotypes.PubKey) int { if pub == nil { return 0 } v, ok := pub.(*kmultisig.LegacyAminoPubKey) if !ok { return 1 } numKeys := 0 for _, subkey := range v.GetPubKeys() { numKeys += CountSubKeys(subkey) } return numKeys } // signatureDataToBz converts a SignatureData into raw bytes signature. // For SingleSignatureData, it returns the signature raw bytes. // For MultiSignatureData, it returns an array of all individual signatures, // as well as the aggregated signature. func signatureDataToBz(data signing.SignatureData) ([][]byte, error) { if data == nil { return nil, fmt.Errorf("got empty SignatureData") } switch data := data.(type) { case *signing.SingleSignatureData: return [][]byte{data.Signature}, nil case *signing.MultiSignatureData: sigs := [][]byte{} var err error for _, d := range data.Signatures { nestedSigs, err := signatureDataToBz(d) if err != nil { return nil, err } sigs = append(sigs, nestedSigs...) } multiSignature := cryptotypes.MultiSignature{ Signatures: sigs, } aggregatedSig, err := multiSignature.Marshal() if err != nil { return nil, err } sigs = append(sigs, aggregatedSig) return sigs, nil default: return nil, sdkerrors.ErrInvalidType.Wrapf("unexpected signature data type %T", data) } }