cosmos-sdk/x/auth/ante/sigverify.go
Miloš Živković d3e059dd2a
feat: add support for ed25519 tx signature verification (#23283)
Co-authored-by: Alex | Interchain Labs <alex@skip.money>
Co-authored-by: Alex | Interchain Labs <alex@interchainlabs.io>
Co-authored-by: Marko <marko@baricevic.me>
2025-02-04 07:57:07 +00:00

715 lines
23 KiB
Go

package ante
import (
"context"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
secp256k1dcrd "github.com/decred/dcrd/dcrec/secp256k1/v4"
"google.golang.org/protobuf/types/known/anypb"
apisigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
"cosmossdk.io/core/event"
"cosmossdk.io/core/gas"
"cosmossdk.io/core/transaction"
errorsmod "cosmossdk.io/errors"
txsigning "cosmossdk.io/x/tx/signing"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"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/keys/secp256r1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"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"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
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
)
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 gas.Meter, sig signing.SignatureV2, params types.Params) error
type AccountAbstractionKeeper interface {
IsAbstractedAccount(ctx context.Context, addr []byte) (bool, error)
AuthenticateAccount(ctx context.Context, signer []byte, bundler string, rawTx *tx.TxRaw, protoTx *tx.Tx, signIndex uint32) error
}
// SigVerificationDecorator verifies all signatures for a tx and returns an
// error if any are invalid.
// It will populate an account's public key if that is not present only if
// PubKey.Address() == Account.Address().
// Note, the SigVerificationDecorator will not check
// signatures on ReCheckTx. It will also increase the sequence number, and consume
// gas for signature verification.
//
// In cases where unordered or parallel transactions are desired, it is recommended
// to set unordered=true with a reasonable timeout_height value, in which case
// this nonce verification and increment will be skipped.
//
// CONTRACT: Tx must implement SigVerifiableTx interface
type SigVerificationDecorator struct {
ak AccountKeeper
aaKeeper AccountAbstractionKeeper
signModeHandler *txsigning.HandlerMap
sigGasConsumer SignatureVerificationGasConsumer
extraVerifyIsOnCurve func(pubKey cryptotypes.PubKey) (bool, error)
}
func NewSigVerificationDecorator(ak AccountKeeper, signModeHandler *txsigning.HandlerMap, sigGasConsumer SignatureVerificationGasConsumer, aaKeeper AccountAbstractionKeeper) SigVerificationDecorator {
return NewSigVerificationDecoratorWithVerifyOnCurve(ak, signModeHandler, sigGasConsumer, aaKeeper, nil)
}
func NewSigVerificationDecoratorWithVerifyOnCurve(ak AccountKeeper, signModeHandler *txsigning.HandlerMap, sigGasConsumer SignatureVerificationGasConsumer, aaKeeper AccountAbstractionKeeper, verifyFn func(pubKey cryptotypes.PubKey) (bool, error)) SigVerificationDecorator {
return SigVerificationDecorator{
aaKeeper: aaKeeper,
ak: ak,
signModeHandler: signModeHandler,
sigGasConsumer: sigGasConsumer,
extraVerifyIsOnCurve: verifyFn,
}
}
// 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 == apisigning.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) VerifyIsOnCurve(pubKey cryptotypes.PubKey) error {
if svd.extraVerifyIsOnCurve != nil {
handled, err := svd.extraVerifyIsOnCurve(pubKey)
if handled {
return err
}
}
// when simulating pubKey.Key will always be nil
if pubKey.Bytes() == nil {
return nil
}
switch typedPubKey := pubKey.(type) {
case *ed25519.PubKey:
if !typedPubKey.IsOnCurve() {
return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "ed25519 key is not on curve")
}
case *secp256k1.PubKey:
pubKeyObject, err := secp256k1dcrd.ParsePubKey(typedPubKey.Bytes())
if err != nil {
if errors.Is(err, secp256k1dcrd.ErrPubKeyNotOnCurve) {
return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "secp256k1 key is not on curve")
}
return err
}
if !pubKeyObject.IsOnCurve() {
return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "secp256k1 key is not on curve")
}
case *secp256r1.PubKey:
pubKeyObject := typedPubKey.Key.PublicKey
if !pubKeyObject.IsOnCurve(pubKeyObject.X, pubKeyObject.Y) {
return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "secp256r1 key is not on curve")
}
case multisig.PubKey:
pubKeysObjects := typedPubKey.GetPubKeys()
ok := true
for _, pubKeyObject := range pubKeysObjects {
if err := svd.VerifyIsOnCurve(pubKeyObject); err != nil {
ok = false
break
}
}
if !ok {
return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "some keys are not on curve")
}
default:
return errorsmod.Wrapf(sdkerrors.ErrInvalidPubKey, "unsupported key type: %T", typedPubKey)
}
return nil
}
func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, _ bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
if err := svd.ValidateTx(ctx, tx); err != nil {
return ctx, err
}
return next(ctx, tx, false)
}
func (svd SigVerificationDecorator) ValidateTx(ctx context.Context, tx transaction.Tx) error {
sigTx, ok := tx.(authsigning.Tx)
if !ok {
return errorsmod.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.
signatures, err := sigTx.GetSignaturesV2()
if err != nil {
return err
}
signers, err := sigTx.GetSigners()
if err != nil {
return err
}
// check that signer length and signature length are the same
if len(signatures) != len(signers) {
return errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signer; expected: %d, got %d", len(signers), len(signatures))
}
pubKeys, err := sigTx.GetPubKeys()
if err != nil {
return err
}
// NOTE: the tx_wrapper implementation returns nil, in case the pubkey is not populated.
// so we can always expect the pubkey of the signer to be at the same index as the signer
// itself. If this does not work, it's a failure in the implementation of the interface.
// we're erroring, but most likely we should be panicking.
if len(pubKeys) != len(signers) {
return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid number of pubkeys; expected %d, got %d", len(signers), len(pubKeys))
}
for i := range signers {
err = svd.authenticate(ctx, sigTx, signers[i], signatures[i], pubKeys[i], i)
if err != nil {
return err
}
}
eventMgr := svd.ak.GetEnvironment().EventService.EventManager(ctx)
events := [][]event.Attribute{}
for i, sig := range signatures {
signerStr, err := svd.ak.AddressCodec().BytesToString(signers[i])
if err != nil {
return err
}
events = append(events, []event.Attribute{event.NewAttribute(sdk.AttributeKeyAccountSequence, fmt.Sprintf("%s/%d", signerStr, sig.Sequence))})
sigBzs, err := signatureDataToBz(sig.Data)
if err != nil {
return err
}
for _, sigBz := range sigBzs {
events = append(events, []event.Attribute{event.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz))})
}
}
for _, v := range events {
if err := eventMgr.EmitKV(sdk.EventTypeTx, v...); err != nil {
return err
}
}
return nil
}
// authenticate the authentication of the TX for a specific tx signer.
func (svd SigVerificationDecorator) authenticate(ctx context.Context, tx authsigning.Tx, signer []byte, sig signing.SignatureV2, txPubKey cryptotypes.PubKey, signerIndex int) error {
// first we check if it's an AA
if svd.aaKeeper != nil {
isAa, err := svd.aaKeeper.IsAbstractedAccount(ctx, signer)
if err != nil {
return err
}
if isAa {
return svd.authenticateAbstractedAccount(ctx, tx, signer, signerIndex)
}
}
// not an AA, proceed with standard auth flow.
// newlyCreated is a flag that indicates if the account was newly created.
// This is only the case when the user is sending their first tx.
newlyCreated := false
acc := GetSignerAcc(ctx, svd.ak, signer)
if acc == nil {
// If the account is nil, we assume this is the account's first tx. In this case, the account needs to be
// created, but the sign doc should use account number 0. This is because the account number is
// not known until the account is created when the tx was signed, the account number was unknown
// and 0 was set.
acc = svd.ak.NewAccountWithAddress(ctx, txPubKey.Address().Bytes())
newlyCreated = true
}
// the account is without a pubkey, let's attempt to check if in the
// tx we were correctly provided a valid pubkey.
if acc.GetPubKey() == nil {
err := svd.setPubKey(ctx, acc, txPubKey)
if err != nil {
return err
}
}
err := svd.consumeSignatureGas(ctx, acc.GetPubKey(), sig)
if err != nil {
return err
}
err = svd.verifySig(ctx, tx, acc, sig, newlyCreated)
if err != nil {
return err
}
err = svd.increaseSequence(tx, acc)
if err != nil {
return err
}
// update account changes in state.
svd.ak.SetAccount(ctx, acc)
return nil
}
// consumeSignatureGas will consume gas according to the pub-key being verified.
func (svd SigVerificationDecorator) consumeSignatureGas(
ctx context.Context,
pubKey cryptotypes.PubKey,
signature signing.SignatureV2,
) error {
if svd.ak.GetEnvironment().TransactionService.ExecMode(ctx) == transaction.ExecModeSimulate && pubKey == nil {
pubKey = simSecp256k1Pubkey
}
// make a SignatureV2 with PubKey filled in from above
signature = signing.SignatureV2{
PubKey: pubKey,
Data: signature.Data,
Sequence: signature.Sequence,
}
return svd.sigGasConsumer(svd.ak.GetEnvironment().GasService.GasMeter(ctx), signature, svd.ak.GetParams(ctx))
}
// verifySig will verify the signature of the provided signer account.
func (svd SigVerificationDecorator) verifySig(ctx context.Context, tx sdk.Tx, acc sdk.AccountI, sig signing.SignatureV2, newlyCreated bool) error {
execMode := svd.ak.GetEnvironment().TransactionService.ExecMode(ctx)
unorderedTx, ok := tx.(sdk.TxWithUnordered)
isUnordered := ok && unorderedTx.GetUnordered()
// only check sequence if the tx is not unordered
if !isUnordered {
if execMode == transaction.ExecModeCheck {
if sig.Sequence < acc.GetSequence() {
return errorsmod.Wrapf(
sdkerrors.ErrWrongSequence,
"account sequence mismatch: expected higher than or equal to %d, got %d", acc.GetSequence(), sig.Sequence,
)
}
} else if sig.Sequence != acc.GetSequence() {
return errorsmod.Wrapf(
sdkerrors.ErrWrongSequence,
"account sequence mismatch: expected %d, got %d", acc.GetSequence(), sig.Sequence,
)
}
}
// we're in simulation mode, or in ReCheckTx, or context is not
// on sig verify tx, then we do not need to verify the signatures
// in the tx.
if execMode == transaction.ExecModeSimulate ||
isRecheckTx(ctx, svd.ak.GetEnvironment().TransactionService) ||
!isSigverifyTx(ctx) {
return nil
}
// retrieve pubkey
pubKey := acc.GetPubKey()
if pubKey == nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "pubkey on account is not set")
}
// retrieve signer data
hinfo := svd.ak.GetEnvironment().HeaderService.HeaderInfo(ctx)
genesis := hinfo.Height == 0
chainID := hinfo.ChainID
var accNum uint64
// if we are not in genesis use the account number from the account
if !genesis {
accNum = acc.GetAccountNumber()
}
// if the account number is 0 and the account is signing, the sign doc will not have an account number
if acc.GetSequence() == 0 && newlyCreated {
// If the account sequence is 0, and we're in genesis, then we're
// dealing with an account that has been generated but never used.
// in this case, we should not verify signatures.
accNum = 0
}
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 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): (%s)", accNum, acc.GetSequence(), chainID, err.Error())
} else {
errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d) and chain-id (%s): (%s)", accNum, chainID, err.Error())
}
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, errMsg)
}
return nil
}
// setPubKey will attempt to set the pubkey for the account given the list of available public keys.
// This must be called only in case the account has not a pubkey set yet.
func (svd SigVerificationDecorator) setPubKey(ctx context.Context, acc sdk.AccountI, txPubKey cryptotypes.PubKey) error {
// if we're not in sig verify then we can just skip.
if !isSigverifyTx(ctx) {
return nil
}
// if the pubkey is nil then we don't have any pubkey to set
// for this account, which also means we cannot do signature
// verification.
if txPubKey == nil {
// if we're not in simulation mode, and we do not have a valid pubkey
// for this signer, then we simply error.
if svd.ak.GetEnvironment().TransactionService.ExecMode(ctx) != transaction.ExecModeSimulate {
return fmt.Errorf("the account %s is without a pubkey and did not provide a pubkey in the tx to set it", acc.GetAddress().String())
}
// if we're in simulation mode, then we can populate the pubkey with the
// sim one and simply return.
txPubKey = simSecp256k1Pubkey
return acc.SetPubKey(txPubKey)
}
// this code path is taken when a user has received tokens but not submitted their first transaction
// if the address does not match the pubkey, then we error.
// TODO: in the future the relationship between address and pubkey should be more flexible.
if !acc.GetAddress().Equals(sdk.AccAddress(txPubKey.Address().Bytes())) {
return sdkerrors.ErrInvalidPubKey.Wrapf("the account %s cannot be claimed by public key with address %x", acc.GetAddress(), txPubKey.Address())
}
err := svd.VerifyIsOnCurve(txPubKey)
if err != nil {
return err
}
// we set the pubkey in the account, without setting it in state.
// this will be done by the increaseSequenceAndUpdateAccount method.
return acc.SetPubKey(txPubKey)
}
// increaseSequence will increase the provided account interface sequence, unless
// the tx is unordered.
func (svd SigVerificationDecorator) increaseSequence(tx authsigning.Tx, acc sdk.AccountI) error {
// Bypass incrementing sequence for transactions with unordered set to true.
// The actual parameters of the un-ordered tx will be checked in a separate
// decorator.
unorderedTx, ok := tx.(sdk.TxWithUnordered)
if ok && unorderedTx.GetUnordered() {
return nil
}
return acc.SetSequence(acc.GetSequence() + 1)
}
// authenticateAbstractedAccount computes an AA authentication instruction and invokes the auth flow on the AA.
func (svd SigVerificationDecorator) authenticateAbstractedAccount(ctx context.Context, authTx authsigning.Tx, signer []byte, index int) error {
// the bundler is the AA itself.
selfBundler, err := svd.ak.AddressCodec().BytesToString(signer)
if err != nil {
return err
}
infoTx := authTx.(interface {
AsTxRaw() (*tx.TxRaw, error)
AsTx() (*tx.Tx, error)
})
txRaw, err := infoTx.AsTxRaw()
if err != nil {
return fmt.Errorf("unable to get raw tx: %w", err)
}
protoTx, err := infoTx.AsTx()
if err != nil {
return fmt.Errorf("unable to get proto tx: %w", err)
}
return svd.aaKeeper.AuthenticateAccount(ctx, signer, selfBundler, txRaw, protoTx, uint32(index))
}
// 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,
}
}
// AnteHandle implements an ante decorator for ValidateSigCountDecorator
func (vscd ValidateSigCountDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, _ bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
if err := vscd.ValidateTx(ctx, tx); err != nil {
return ctx, err
}
return next(ctx, tx, false)
}
// ValidateTx implements an TxValidator for ValidateSigCountDecorator
func (vscd ValidateSigCountDecorator) ValidateTx(ctx context.Context, tx sdk.Tx) error {
sigTx, ok := tx.(authsigning.SigVerifiableTx)
if !ok {
return errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a sigTx")
}
params := vscd.ak.GetParams(ctx)
pubKeys, err := sigTx.GetPubKeys()
if err != nil {
return err
}
sigCount := 0
for _, pk := range pubKeys {
sigCount += CountSubKeys(pk)
if uint64(sigCount) > params.TxSigLimit {
return errorsmod.Wrapf(sdkerrors.ErrTooManySignatures, "signatures: %d, limit: %d", sigCount, params.TxSigLimit)
}
}
return nil
}
// 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 gas.Meter, sig signing.SignatureV2, params types.Params) error {
pubkey := sig.PubKey
switch pubkey := pubkey.(type) {
case *ed25519.PubKey:
return meter.Consume(params.SigVerifyCostED25519, "ante verify: ed25519")
case *secp256k1.PubKey:
return meter.Consume(params.SigVerifyCostSecp256k1, "ante verify: secp256k1")
case *secp256r1.PubKey:
return meter.Consume(params.SigVerifyCostSecp256r1(), "ante verify: secp256r1")
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 gas.Meter, sig *signing.MultiSignatureData, pubKey multisig.PubKey,
params types.Params, accSeq uint64,
) error {
// if BitArray is nil, it means tx has been built for simulation.
if sig.BitArray == nil {
return multisignatureSimulationVerificationGas(meter, sig, pubKey, params, accSeq)
}
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
}
// multisignatureSimulationVerificationGas consume gas for verifying a simulation multisig pubKey signature. As it's
// a simulation tx the number of signatures its equal to the multisig threshold.
func multisignatureSimulationVerificationGas(
meter gas.Meter, sig *signing.MultiSignatureData, pubKey multisig.PubKey,
params types.Params, accSeq uint64,
) error {
for i := 0; i < len(sig.Signatures); i++ {
sigV2 := signing.SignatureV2{
PubKey: pubKey.GetPubKeys()[i],
Data: sig.Signatures[i],
Sequence: accSeq,
}
err := DefaultSigVerificationGasConsumer(meter, sigV2, params)
if err != nil {
return err
}
}
return nil
}
// GetSignerAcc returns an account for a given address that is expected to sign
// a transaction.
func GetSignerAcc(ctx context.Context, ak AccountKeeper, addr sdk.AccAddress) sdk.AccountI {
return ak.GetAccount(ctx, 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, errors.New("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)
}
}
// isSigverifyTx will always return true, unless the context is a sdk.Context, in which case we will return the
// value of IsSigverifyTx.
func isSigverifyTx(ctx context.Context) bool {
if sdkCtx, ok := sdk.TryUnwrapSDKContext(ctx); ok {
return sdkCtx.IsSigverifyTx()
}
return true
}
func isRecheckTx(ctx context.Context, txSvc transaction.Service) bool {
if sdkCtx, ok := sdk.TryUnwrapSDKContext(ctx); ok {
return sdkCtx.IsReCheckTx()
}
return txSvc.ExecMode(ctx) == transaction.ExecModeReCheck
}