[issue-#1389] add multisig tx support (#1390)
* [issue-#1389] add multisig tx support * [issue-#1389] add changes in CHANGELOG improvements * [issue-#1389] fix style issue * [issue-#1389] fix style issue * [issue-#1389] add tests
This commit is contained in:
parent
83e509bc57
commit
3ab761b8c4
@ -63,6 +63,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||||||
* (rpc) [#1352](https://github.com/evmos/ethermint/pull/1352) Make the grpc queries run concurrently, don't block the consensus state machine.
|
* (rpc) [#1352](https://github.com/evmos/ethermint/pull/1352) Make the grpc queries run concurrently, don't block the consensus state machine.
|
||||||
* (cli) [#1360](https://github.com/evmos/ethermint/pull/1360) Introduce a new `grpc-only` flag, such that when enabled, will start the node in a query-only mode. Note, gRPC MUST be enabled with this flag.
|
* (cli) [#1360](https://github.com/evmos/ethermint/pull/1360) Introduce a new `grpc-only` flag, such that when enabled, will start the node in a query-only mode. Note, gRPC MUST be enabled with this flag.
|
||||||
* (rpc) [#1378](https://github.com/evmos/ethermint/pull/1378) Add support for EVM RPC metrics
|
* (rpc) [#1378](https://github.com/evmos/ethermint/pull/1378) Add support for EVM RPC metrics
|
||||||
|
* (ante) [#1390](https://github.com/evmos/ethermint/pull/1390) Added multisig tx support.
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||||
@ -99,12 +100,48 @@ var _ authante.SignatureVerificationGasConsumer = DefaultSigVerificationGasConsu
|
|||||||
func DefaultSigVerificationGasConsumer(
|
func DefaultSigVerificationGasConsumer(
|
||||||
meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params,
|
meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params,
|
||||||
) error {
|
) error {
|
||||||
// support for ethereum ECDSA secp256k1 keys
|
pubkey := sig.PubKey
|
||||||
_, ok := sig.PubKey.(*ethsecp256k1.PubKey)
|
switch pubkey := pubkey.(type) {
|
||||||
if ok {
|
case *ethsecp256k1.PubKey:
|
||||||
meter.ConsumeGas(secp256k1VerifyCost, "ante verify: eth_secp256k1")
|
meter.ConsumeGas(secp256k1VerifyCost, "ante verify: eth_secp256k1")
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
case multisig.PubKey:
|
||||||
|
// Multisig keys
|
||||||
|
multisignature, ok := sig.Data.(*signing.MultiSignatureData)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected %T, got, %T", &signing.MultiSignatureData{}, sig.Data)
|
||||||
|
}
|
||||||
|
return ConsumeMultisignatureVerificationGas(meter, multisignature, pubkey, params, sig.Sequence)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return authante.DefaultSigVerificationGasConsumer(meter, sig, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsumeMultisignatureVerificationGas consumes gas from a GasMeter for verifying a multisig pubkey signature
|
||||||
|
func ConsumeMultisignatureVerificationGas(
|
||||||
|
meter sdk.GasMeter, sig *signing.MultiSignatureData, pubkey multisig.PubKey,
|
||||||
|
params authtypes.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 authante.DefaultSigVerificationGasConsumer(meter, sig, params)
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,22 @@ package ante_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sdkmath "cosmossdk.io/math"
|
sdkmath "cosmossdk.io/math"
|
||||||
|
kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1"
|
||||||
|
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
||||||
|
"github.com/cosmos/cosmos-sdk/simapp"
|
||||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
|
||||||
|
|
||||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/authz"
|
"github.com/cosmos/cosmos-sdk/x/authz"
|
||||||
|
|
||||||
@ -16,6 +25,8 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
ethparams "github.com/ethereum/go-ethereum/params"
|
ethparams "github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/evmos/ethermint/app/ante"
|
||||||
|
"github.com/evmos/ethermint/crypto/ethsecp256k1"
|
||||||
"github.com/evmos/ethermint/tests"
|
"github.com/evmos/ethermint/tests"
|
||||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||||||
|
|
||||||
@ -914,3 +925,93 @@ func (suite AnteTestSuite) TestAnteHandlerWithParams() {
|
|||||||
}
|
}
|
||||||
suite.evmParamsOption = nil
|
suite.evmParamsOption = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *AnteTestSuite) TestConsumeSignatureVerificationGas() {
|
||||||
|
params := authtypes.DefaultParams()
|
||||||
|
msg := []byte{1, 2, 3, 4}
|
||||||
|
cdc := simapp.MakeTestEncodingConfig().Amino
|
||||||
|
|
||||||
|
p := authtypes.DefaultParams()
|
||||||
|
skR1, _ := secp256r1.GenPrivKey()
|
||||||
|
pkSet1, sigSet1, err := generatePubKeysAndSignatures(5, msg, false)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
multisigKey1 := kmultisig.NewLegacyAminoPubKey(2, pkSet1)
|
||||||
|
multisignature1 := multisig.NewMultisig(len(pkSet1))
|
||||||
|
expectedCost1 := expectedGasCostByKeys(pkSet1)
|
||||||
|
|
||||||
|
for i := 0; i < len(pkSet1); i++ {
|
||||||
|
stdSig := legacytx.StdSignature{PubKey: pkSet1[i], Signature: sigSet1[i]}
|
||||||
|
sigV2, err := legacytx.StdSignatureToSignatureV2(cdc, stdSig)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
err = multisig.AddSignatureV2(multisignature1, sigV2, pkSet1)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
meter sdk.GasMeter
|
||||||
|
sig signing.SignatureData
|
||||||
|
pubkey cryptotypes.PubKey
|
||||||
|
params authtypes.Params
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
gasConsumed uint64
|
||||||
|
shouldErr bool
|
||||||
|
}{
|
||||||
|
{"PubKeyEd25519", args{sdk.NewInfiniteGasMeter(), nil, ed25519.GenPrivKey().PubKey(), params}, p.SigVerifyCostED25519, true},
|
||||||
|
{"PubKeyEthSecp256k1", args{sdk.NewInfiniteGasMeter(), nil, pkSet1[0], params}, 21_000, false},
|
||||||
|
{"PubKeySecp256r1", args{sdk.NewInfiniteGasMeter(), nil, skR1.PubKey(), params}, p.SigVerifyCostSecp256r1(), false},
|
||||||
|
{"Multisig", args{sdk.NewInfiniteGasMeter(), multisignature1, multisigKey1, params}, expectedCost1, false},
|
||||||
|
{"unknown key", args{sdk.NewInfiniteGasMeter(), nil, nil, params}, 0, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
sigV2 := signing.SignatureV2{
|
||||||
|
PubKey: tt.args.pubkey,
|
||||||
|
Data: tt.args.sig,
|
||||||
|
Sequence: 0, // Arbitrary account sequence
|
||||||
|
}
|
||||||
|
err := ante.DefaultSigVerificationGasConsumer(tt.args.meter, sigV2, tt.args.params)
|
||||||
|
|
||||||
|
if tt.shouldErr {
|
||||||
|
suite.Require().NotNil(err)
|
||||||
|
} else {
|
||||||
|
suite.Require().Nil(err)
|
||||||
|
suite.Require().Equal(tt.gasConsumed, tt.args.meter.GasConsumed(), fmt.Sprintf("%d != %d", tt.gasConsumed, tt.args.meter.GasConsumed()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePubKeysAndSignatures(n int, msg []byte, _ bool) (pubkeys []cryptotypes.PubKey, signatures [][]byte, err error) {
|
||||||
|
pubkeys = make([]cryptotypes.PubKey, n)
|
||||||
|
signatures = make([][]byte, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
privkey, err := ethsecp256k1.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubkeys[i] = privkey.PubKey()
|
||||||
|
signatures[i], _ = privkey.Sign(msg)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectedGasCostByKeys(pubkeys []cryptotypes.PubKey) uint64 {
|
||||||
|
cost := uint64(0)
|
||||||
|
for _, pubkey := range pubkeys {
|
||||||
|
pubkeyType := strings.ToLower(fmt.Sprintf("%T", pubkey))
|
||||||
|
switch {
|
||||||
|
case strings.Contains(pubkeyType, "ed25519"):
|
||||||
|
cost += authtypes.DefaultParams().SigVerifyCostED25519
|
||||||
|
case strings.Contains(pubkeyType, "ethsecp256k1"):
|
||||||
|
cost += 21_000
|
||||||
|
case strings.Contains(pubkeyType, "secp256k1"):
|
||||||
|
cost += authtypes.DefaultParams().SigVerifyCostSecp256k1
|
||||||
|
default:
|
||||||
|
panic("unexpected key type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cost
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user