diff --git a/core/testing/gas/service_mocks.go b/core/testing/gas/service_mocks.go new file mode 100644 index 000000000000..a38e7d5c074d --- /dev/null +++ b/core/testing/gas/service_mocks.go @@ -0,0 +1,143 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: core/gas/service.go + +// Package gas is a generated GoMock package. +package gas + +import ( + context "context" + reflect "reflect" + + gas "cosmossdk.io/core/gas" + gomock "github.com/golang/mock/gomock" +) + +// MockService is a mock of Service interface. +type MockService struct { + ctrl *gomock.Controller + recorder *MockServiceMockRecorder +} + +// MockServiceMockRecorder is the mock recorder for MockService. +type MockServiceMockRecorder struct { + mock *MockService +} + +// NewMockService creates a new mock instance. +func NewMockService(ctrl *gomock.Controller) *MockService { + mock := &MockService{ctrl: ctrl} + mock.recorder = &MockServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockService) EXPECT() *MockServiceMockRecorder { + return m.recorder +} + +// GasConfig mocks base method. +func (m *MockService) GasConfig(ctx context.Context) gas.GasConfig { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GasConfig", ctx) + ret0, _ := ret[0].(gas.GasConfig) + return ret0 +} + +// GasConfig indicates an expected call of GasConfig. +func (mr *MockServiceMockRecorder) GasConfig(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GasConfig", reflect.TypeOf((*MockService)(nil).GasConfig), ctx) +} + +// GasMeter mocks base method. +func (m *MockService) GasMeter(arg0 context.Context) gas.Meter { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GasMeter", arg0) + ret0, _ := ret[0].(gas.Meter) + return ret0 +} + +// GasMeter indicates an expected call of GasMeter. +func (mr *MockServiceMockRecorder) GasMeter(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GasMeter", reflect.TypeOf((*MockService)(nil).GasMeter), arg0) +} + +// MockMeter is a mock of Meter interface. +type MockMeter struct { + ctrl *gomock.Controller + recorder *MockMeterMockRecorder +} + +// MockMeterMockRecorder is the mock recorder for MockMeter. +type MockMeterMockRecorder struct { + mock *MockMeter +} + +// NewMockMeter creates a new mock instance. +func NewMockMeter(ctrl *gomock.Controller) *MockMeter { + mock := &MockMeter{ctrl: ctrl} + mock.recorder = &MockMeterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockMeter) EXPECT() *MockMeterMockRecorder { + return m.recorder +} + +// Consume mocks base method. +func (m *MockMeter) Consume(amount gas.Gas, descriptor string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Consume", amount, descriptor) + ret0, _ := ret[0].(error) + return ret0 +} + +// Consume indicates an expected call of Consume. +func (mr *MockMeterMockRecorder) Consume(amount, descriptor interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Consume", reflect.TypeOf((*MockMeter)(nil).Consume), amount, descriptor) +} + +// Limit mocks base method. +func (m *MockMeter) Limit() gas.Gas { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Limit") + ret0, _ := ret[0].(gas.Gas) + return ret0 +} + +// Limit indicates an expected call of Limit. +func (mr *MockMeterMockRecorder) Limit() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Limit", reflect.TypeOf((*MockMeter)(nil).Limit)) +} + +// Refund mocks base method. +func (m *MockMeter) Refund(amount gas.Gas, descriptor string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Refund", amount, descriptor) + ret0, _ := ret[0].(error) + return ret0 +} + +// Refund indicates an expected call of Refund. +func (mr *MockMeterMockRecorder) Refund(amount, descriptor interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Refund", reflect.TypeOf((*MockMeter)(nil).Refund), amount, descriptor) +} + +// Remaining mocks base method. +func (m *MockMeter) Remaining() gas.Gas { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Remaining") + ret0, _ := ret[0].(gas.Gas) + return ret0 +} + +// Remaining indicates an expected call of Remaining. +func (mr *MockMeterMockRecorder) Remaining() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remaining", reflect.TypeOf((*MockMeter)(nil).Remaining)) +} diff --git a/core/testing/go.mod b/core/testing/go.mod index 0952d63b869a..311b0c16ac02 100644 --- a/core/testing/go.mod +++ b/core/testing/go.mod @@ -6,5 +6,6 @@ replace cosmossdk.io/core => ../ require ( cosmossdk.io/core v1.0.0 + github.com/golang/mock v1.6.0 github.com/tidwall/btree v1.7.0 ) diff --git a/core/testing/go.sum b/core/testing/go.sum index 25d624e85744..86d7a5ef2ad5 100644 --- a/core/testing/go.sum +++ b/core/testing/go.sum @@ -1,2 +1,27 @@ +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/scripts/init-simapp-v2.sh b/scripts/init-simapp-v2.sh index a4788dfee838..ca25b4f706c0 100755 --- a/scripts/init-simapp-v2.sh +++ b/scripts/init-simapp-v2.sh @@ -19,5 +19,5 @@ jq '.app_state.gov.params.expedited_voting_period = "300s"' $SIMD_HOME/config/ge jq '.app_state.mint.minter.inflation = "0.300000000000000000"' $SIMD_HOME/config/genesis.json > temp.json && mv temp.json $SIMD_HOME/config/genesis.json # to change the inflation $SIMD_BIN genesis add-genesis-account alice 5000000000stake --keyring-backend test $SIMD_BIN genesis add-genesis-account bob 5000000000stake --keyring-backend test -$SIMD_BIN genesis gentx alice 1000000stake --chain-id demo +$SIMD_BIN genesis gentx alice 1000000stake --chain-id simapp-v2-chain $SIMD_BIN genesis collect-gentxs \ No newline at end of file diff --git a/scripts/mockgen.sh b/scripts/mockgen.sh index eef0730c881a..c5eac358b1d5 100755 --- a/scripts/mockgen.sh +++ b/scripts/mockgen.sh @@ -26,3 +26,4 @@ $mockgen_cmd -source=x/gov/testutil/expected_keepers.go -package testutil -desti $mockgen_cmd -source=x/staking/types/expected_keepers.go -package testutil -destination x/staking/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/auth/vesting/types/expected_keepers.go -package testutil -destination x/auth/vesting/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/protocolpool/types/expected_keepers.go -package testutil -destination x/protocolpool/testutil/expected_keepers_mocks.go +$mockgen_cmd -source=core/gas/service.go -package gas -destination core/testing/gas/service_mocks.go diff --git a/simapp/simd/cmd/testnet_test.go b/simapp/simd/cmd/testnet_test.go index e15a0b5db2b7..d79e60b42b4b 100644 --- a/simapp/simd/cmd/testnet_test.go +++ b/simapp/simd/cmd/testnet_test.go @@ -45,7 +45,7 @@ func Test_TestnetCmd(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, moduleManager) - require.Len(t, moduleManager.Modules, 8) + require.Len(t, moduleManager.Modules, 9) // the registered above + runtime home := t.TempDir() cdcOpts := codectestutil.CodecOptions{} diff --git a/tests/systemtests/cli.go b/tests/systemtests/cli.go index 697cc6b85225..871d810ea4a3 100644 --- a/tests/systemtests/cli.go +++ b/tests/systemtests/cli.go @@ -223,7 +223,7 @@ func (c CLIWrapper) runWithInput(args []string, input io.Reader) (output string, cmd := exec.Command(locateExecutable(c.execBinary), args...) //nolint:gosec // test code only cmd.Dir = WorkDir cmd.Stdin = input - return cmd.Output() + return cmd.CombinedOutput() }() gotOut = filterProtoNoise(gotOut) ok = c.assertErrorFn(c.t, gotErr, string(gotOut)) diff --git a/tests/systemtests/staking_test.go b/tests/systemtests/staking_test.go index f197bff1a0ac..0b18408410df 100644 --- a/tests/systemtests/staking_test.go +++ b/tests/systemtests/staking_test.go @@ -10,6 +10,7 @@ import ( ) func TestStakeUnstake(t *testing.T) { + t.Skip("The fee deduction is not yet implemented in v2") // Scenario: // delegate tokens to validator // undelegate some tokens diff --git a/tests/systemtests/unordered_tx_test.go b/tests/systemtests/unordered_tx_test.go index 4b22df92b0a5..6b1a9c743e7d 100644 --- a/tests/systemtests/unordered_tx_test.go +++ b/tests/systemtests/unordered_tx_test.go @@ -12,6 +12,7 @@ import ( ) func TestUnorderedTXDuplicate(t *testing.T) { + t.Skip("The unordered tx antehanlder is missing in v2") // scenario: test unordered tx duplicate // given a running chain with a tx in the unordered tx pool // when a new tx with the same hash is broadcasted diff --git a/x/auth/ante/ante.go b/x/auth/ante/ante.go index fb4eb6c7ad44..c81070242ed5 100644 --- a/x/auth/ante/ante.go +++ b/x/auth/ante/ante.go @@ -2,8 +2,8 @@ package ante import ( "cosmossdk.io/core/appmodule" + "cosmossdk.io/core/gas" errorsmod "cosmossdk.io/errors" - storetypes "cosmossdk.io/store/types" "cosmossdk.io/x/auth/types" txsigning "cosmossdk.io/x/tx/signing" @@ -21,7 +21,7 @@ type HandlerOptions struct { ExtensionOptionChecker ExtensionOptionChecker FeegrantKeeper FeegrantKeeper SignModeHandler *txsigning.HandlerMap - SigGasConsumer func(meter storetypes.GasMeter, sig signing.SignatureV2, params types.Params) error + SigGasConsumer func(meter gas.Meter, sig signing.SignatureV2, params types.Params) error TxFeeChecker TxFeeChecker } diff --git a/x/auth/ante/ante_test.go b/x/auth/ante/ante_test.go index 7377638550a6..88592fe34170 100644 --- a/x/auth/ante/ante_test.go +++ b/x/auth/ante/ante_test.go @@ -10,9 +10,10 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "cosmossdk.io/core/gas" + "cosmossdk.io/core/header" errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" - storetypes "cosmossdk.io/store/types" "cosmossdk.io/x/auth/ante" authtypes "cosmossdk.io/x/auth/types" @@ -392,7 +393,7 @@ func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("Case %s", tc.desc), func(t *testing.T) { suite := SetupTestSuite(t, false) - suite.ctx = suite.ctx.WithBlockHeight(0) + suite.ctx = suite.ctx.WithBlockHeight(0).WithHeaderInfo(header.Info{Height: 0, ChainID: suite.ctx.ChainID()}) suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() args := tc.malleate(suite) @@ -1158,22 +1159,6 @@ func generatePubKeysAndSignatures(n int, msg []byte, _ bool) (pubkeys []cryptoty 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, "secp256k1"): - cost += authtypes.DefaultParams().SigVerifyCostSecp256k1 - default: - panic("unexpected key type") - } - } - return cost -} - func TestCountSubkeys(t *testing.T) { genPubKeys := func(n int) []cryptotypes.PubKey { var ret []cryptotypes.PubKey @@ -1266,11 +1251,10 @@ func TestCustomSignatureVerificationGasConsumer(t *testing.T) { BankKeeper: suite.bankKeeper, FeegrantKeeper: suite.feeGrantKeeper, SignModeHandler: suite.clientCtx.TxConfig.SignModeHandler(), - SigGasConsumer: func(meter storetypes.GasMeter, sig signing.SignatureV2, params authtypes.Params) error { + SigGasConsumer: func(meter gas.Meter, sig signing.SignatureV2, params authtypes.Params) error { switch pubkey := sig.PubKey.(type) { case *ed25519.PubKey: - meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519") - return nil + return meter.Consume(params.SigVerifyCostED25519, "ante verify: ed25519") default: return errorsmod.Wrapf(sdkerrors.ErrInvalidPubKey, "unrecognized public key type: %T", pubkey) } diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index a435ba03bdfa..5ec01498e1f5 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -10,9 +10,10 @@ import ( secp256k1dcrd "github.com/decred/dcrd/dcrec/secp256k1/v4" "google.golang.org/protobuf/types/known/anypb" + "cosmossdk.io/core/event" + "cosmossdk.io/core/gas" "cosmossdk.io/core/transaction" errorsmod "cosmossdk.io/errors" - storetypes "cosmossdk.io/store/types" authsigning "cosmossdk.io/x/auth/signing" "cosmossdk.io/x/auth/types" txsigning "cosmossdk.io/x/tx/signing" @@ -47,7 +48,7 @@ func init() { // 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 +type SignatureVerificationGasConsumer = func(meter gas.Meter, sig signing.SignatureV2, params types.Params) error type AccountAbstractionKeeper interface { IsAbstractedAccount(ctx context.Context, addr []byte) (bool, error) @@ -150,31 +151,38 @@ func verifyIsOnCurve(pubKey cryptotypes.PubKey) (err error) { } 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 ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") + 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 ctx, err + return err } signers, err := sigTx.GetSigners() if err != nil { - return ctx, err + return err } // check that signer length and signature length are the same if len(signatures) != len(signers) { - return ctx, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signer; expected: %d, got %d", len(signers), len(signatures)) + 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 ctx, err + return err } // NOTE: the tx_wrapper implementation returns nil, in case the pubkey is not populated. @@ -182,44 +190,46 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, _ boo // 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 ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid number of pubkeys; expected %d, got %d", len(signers), len(pubKeys)) + 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 ctx, err + return err } } - var events sdk.Events + 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 ctx, err + return err } - events = append(events, sdk.NewEvent(sdk.EventTypeTx, - sdk.NewAttribute(sdk.AttributeKeyAccountSequence, fmt.Sprintf("%s/%d", signerStr, sig.Sequence)), - )) + + 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 ctx, err + return err } for _, sigBz := range sigBzs { - events = append(events, sdk.NewEvent(sdk.EventTypeTx, - sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz)), - )) + events = append(events, []event.Attribute{event.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz))}) } } - ctx.EventManager().EmitEvents(events) + for _, v := range events { + if err := eventMgr.EmitKV(sdk.EventTypeTx, v...); err != nil { + return err + } + } - return next(ctx, tx, false) + return nil } // authenticate the authentication of the TX for a specific tx signer. -func (svd SigVerificationDecorator) authenticate(ctx sdk.Context, tx authsigning.Tx, signer []byte, sig signing.SignatureV2, txPubKey cryptotypes.PubKey, signerIndex int) error { +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) @@ -276,7 +286,7 @@ func (svd SigVerificationDecorator) authenticate(ctx sdk.Context, tx authsigning // consumeSignatureGas will consume gas according to the pub-key being verified. func (svd SigVerificationDecorator) consumeSignatureGas( - ctx sdk.Context, + ctx context.Context, pubKey cryptotypes.PubKey, signature signing.SignatureV2, ) error { @@ -291,15 +301,11 @@ func (svd SigVerificationDecorator) consumeSignatureGas( Sequence: signature.Sequence, } - err := svd.sigGasConsumer(ctx.GasMeter(), signature, svd.ak.GetParams(ctx)) - if err != nil { - return err - } - return nil + 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 sdk.Context, tx sdk.Tx, acc sdk.AccountI, sig signing.SignatureV2, newlyCreated bool) error { +func (svd SigVerificationDecorator) verifySig(ctx context.Context, tx sdk.Tx, acc sdk.AccountI, sig signing.SignatureV2, newlyCreated bool) error { if sig.Sequence != acc.GetSequence() { return errorsmod.Wrapf( sdkerrors.ErrWrongSequence, @@ -310,7 +316,9 @@ func (svd SigVerificationDecorator) verifySig(ctx sdk.Context, tx sdk.Tx, acc sd // 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 svd.ak.GetEnvironment().TransactionService.ExecMode(ctx) == transaction.ExecModeSimulate || ctx.IsReCheckTx() || !ctx.IsSigverifyTx() { + if svd.ak.GetEnvironment().TransactionService.ExecMode(ctx) == transaction.ExecModeSimulate || + isRecheckTx(ctx, svd.ak.GetEnvironment().TransactionService) || + !isSigverifyTx(ctx) { return nil } @@ -321,8 +329,9 @@ func (svd SigVerificationDecorator) verifySig(ctx sdk.Context, tx sdk.Tx, acc sd } // retrieve signer data - genesis := ctx.BlockHeight() == 0 - chainID := ctx.ChainID() + 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 { @@ -372,9 +381,9 @@ func (svd SigVerificationDecorator) verifySig(ctx sdk.Context, tx sdk.Tx, acc sd // 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 sdk.Context, acc sdk.AccountI, txPubKey cryptotypes.PubKey) error { +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 !ctx.IsSigverifyTx() { + if !isSigverifyTx(ctx) { return nil } @@ -425,7 +434,7 @@ func (svd SigVerificationDecorator) increaseSequence(tx authsigning.Tx, acc sdk. } // authenticateAbstractedAccount computes an AA authentication instruction and invokes the auth flow on the AA. -func (svd SigVerificationDecorator) authenticateAbstractedAccount(ctx sdk.Context, authTx authsigning.Tx, signer []byte, index int) error { +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 { @@ -500,21 +509,21 @@ func (vscd ValidateSigCountDecorator) ValidateTx(ctx context.Context, tx sdk.Tx) // 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 { +func DefaultSigVerificationGasConsumer(meter gas.Meter, sig signing.SignatureV2, params types.Params) error { pubkey := sig.PubKey switch pubkey := pubkey.(type) { case *ed25519.PubKey: - meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519") + if err := meter.Consume(params.SigVerifyCostED25519, "ante verify: ed25519"); err != nil { + return err + } return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "ED25519 public keys are unsupported") case *secp256k1.PubKey: - meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: secp256k1") - return nil + return meter.Consume(params.SigVerifyCostSecp256k1, "ante verify: secp256k1") case *secp256r1.PubKey: - meter.ConsumeGas(params.SigVerifyCostSecp256r1(), "ante verify: secp256r1") - return nil + return meter.Consume(params.SigVerifyCostSecp256r1(), "ante verify: secp256r1") case multisig.PubKey: multisignature, ok := sig.Data.(*signing.MultiSignatureData) @@ -536,7 +545,7 @@ func DefaultSigVerificationGasConsumer(meter storetypes.GasMeter, sig signing.Si // ConsumeMultisignatureVerificationGas consumes gas from a GasMeter for verifying a multisig pubKey signature. func ConsumeMultisignatureVerificationGas( - meter storetypes.GasMeter, sig *signing.MultiSignatureData, pubKey multisig.PubKey, + 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. @@ -571,7 +580,7 @@ func ConsumeMultisignatureVerificationGas( // 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 storetypes.GasMeter, sig *signing.MultiSignatureData, pubKey multisig.PubKey, + meter gas.Meter, sig *signing.MultiSignatureData, pubKey multisig.PubKey, params types.Params, accSeq uint64, ) error { for i := 0; i < len(sig.Signatures); i++ { @@ -592,7 +601,7 @@ func multisignatureSimulationVerificationGas( // 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 { +func GetSignerAcc(ctx context.Context, ak AccountKeeper, addr sdk.AccAddress) sdk.AccountI { return ak.GetAccount(ctx, addr) } @@ -659,3 +668,19 @@ func signatureDataToBz(data signing.SignatureData) ([][]byte, error) { 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 +} diff --git a/x/auth/ante/sigverify_test.go b/x/auth/ante/sigverify_test.go index ab21482b48c8..1c71e4ae0f79 100644 --- a/x/auth/ante/sigverify_test.go +++ b/x/auth/ante/sigverify_test.go @@ -8,6 +8,9 @@ import ( "github.com/stretchr/testify/require" bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1" + "cosmossdk.io/core/gas" + "cosmossdk.io/core/header" + gastestutil "cosmossdk.io/core/testing/gas" storetypes "cosmossdk.io/store/types" "cosmossdk.io/x/auth/ante" "cosmossdk.io/x/auth/migrations/legacytx" @@ -39,7 +42,6 @@ func TestConsumeSignatureVerificationGas(t *testing.T) { pkSet1, sigSet1 := generatePubKeysAndSignatures(5, msg, false) 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]} //nolint:staticcheck // SA1019: legacytx.StdSignature is deprecated sigV2, err := legacytx.StdSignatureToSignatureV2(suite.clientCtx.LegacyAmino, stdSig) @@ -48,7 +50,6 @@ func TestConsumeSignatureVerificationGas(t *testing.T) { require.NoError(t, err) } - simulationExpectedCost := expectedGasCostByKeys(pkSet1[:multisigKey1.Threshold]) simulationMultiSignatureData := make([]signing.SignatureData, 0, multisigKey1.Threshold) for i := uint32(0); i < multisigKey1.Threshold; i++ { simulationMultiSignatureData = append(simulationMultiSignatureData, &signing.SingleSignatureData{}) @@ -58,38 +59,77 @@ func TestConsumeSignatureVerificationGas(t *testing.T) { } type args struct { - meter storetypes.GasMeter - sig signing.SignatureData - pubkey cryptotypes.PubKey - params types.Params + sig signing.SignatureData + pubkey cryptotypes.PubKey + params types.Params + malleate func(*gastestutil.MockMeter) } tests := []struct { - name string - args args - gasConsumed uint64 - shouldErr bool + name string + args args + shouldErr bool }{ - {"PubKeyEd25519", args{storetypes.NewInfiniteGasMeter(), nil, ed25519.GenPrivKey().PubKey(), params}, p.SigVerifyCostED25519, true}, - {"PubKeySecp256k1", args{storetypes.NewInfiniteGasMeter(), nil, secp256k1.GenPrivKey().PubKey(), params}, p.SigVerifyCostSecp256k1, false}, - {"PubKeySecp256r1", args{storetypes.NewInfiniteGasMeter(), nil, skR1.PubKey(), params}, p.SigVerifyCostSecp256r1(), false}, - {"Multisig", args{storetypes.NewInfiniteGasMeter(), multisignature1, multisigKey1, params}, expectedCost1, false}, - {"Multisig simulation", args{storetypes.NewInfiniteGasMeter(), multisigSimulationSignature, multisigKey1, params}, simulationExpectedCost, false}, - {"unknown key", args{storetypes.NewInfiniteGasMeter(), nil, nil, params}, 0, true}, + { + "PubKeyEd25519", + args{nil, ed25519.GenPrivKey().PubKey(), params, func(mm *gastestutil.MockMeter) { + mm.EXPECT().Consume(p.SigVerifyCostED25519, "ante verify: ed25519").Times(1) + }}, + true, + }, + { + "PubKeySecp256k1", + args{nil, secp256k1.GenPrivKey().PubKey(), params, func(mm *gastestutil.MockMeter) { + mm.EXPECT().Consume(p.SigVerifyCostSecp256k1, "ante verify: secp256k1").Times(1) + }}, + false, + }, + { + "PubKeySecp256r1", + args{nil, skR1.PubKey(), params, func(mm *gastestutil.MockMeter) { + mm.EXPECT().Consume(p.SigVerifyCostSecp256r1(), "ante verify: secp256r1").Times(1) + }}, + false, + }, + { + "Multisig", + args{multisignature1, multisigKey1, params, func(mm *gastestutil.MockMeter) { + // 5 signatures + mm.EXPECT().Consume(p.SigVerifyCostSecp256k1, "ante verify: secp256k1").Times(5) + }}, + false, + }, + { + "Multisig simulation", + args{multisigSimulationSignature, multisigKey1, params, func(mm *gastestutil.MockMeter) { + mm.EXPECT().Consume(p.SigVerifyCostSecp256k1, "ante verify: secp256k1").Times(int(multisigKey1.Threshold)) + }}, + false, + }, + { + "unknown key", + args{nil, nil, params, func(mm *gastestutil.MockMeter) {}}, + 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) + t.Run(tt.name, func(t *testing.T) { + sigV2 := signing.SignatureV2{ + PubKey: tt.args.pubkey, + Data: tt.args.sig, + Sequence: 0, // Arbitrary account sequence + } - if tt.shouldErr { - require.NotNil(t, err) - } else { - require.Nil(t, err) - require.Equal(t, tt.gasConsumed, tt.args.meter.GasConsumed(), fmt.Sprintf("%d != %d", tt.gasConsumed, tt.args.meter.GasConsumed())) - } + ctrl := gomock.NewController(t) + mockMeter := gastestutil.NewMockMeter(ctrl) + tt.args.malleate(mockMeter) + err := ante.DefaultSigVerificationGasConsumer(mockMeter, sigV2, tt.args.params) + + if tt.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) } } @@ -118,7 +158,7 @@ func TestSigVerification(t *testing.T) { suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // make block height non-zero to ensure account numbers part of signBytes - suite.ctx = suite.ctx.WithBlockHeight(1) + suite.ctx = suite.ctx.WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1, ChainID: suite.ctx.ChainID()}) // keys and addresses priv1, _, addr1 := testdata.KeyTestPubAddr() @@ -154,7 +194,7 @@ func TestSigVerification(t *testing.T) { txConfigOpts, ) require.NoError(t, err) - noOpGasConsume := func(_ storetypes.GasMeter, _ signing.SignatureV2, _ types.Params) error { return nil } + noOpGasConsume := func(_ gas.Meter, _ signing.SignatureV2, _ types.Params) error { return nil } svd := ante.NewSigVerificationDecorator(suite.accountKeeper, anteTxConfig.SignModeHandler(), noOpGasConsume, nil) antehandler := sdk.ChainAnteDecorators(svd) defaultSignMode, err := authsign.APISignModeToInternal(anteTxConfig.SignModeHandler().DefaultMode()) @@ -188,6 +228,12 @@ func TestSigVerification(t *testing.T) { t.Run(fmt.Sprintf("%s with %s", tc.name, signMode), func(t *testing.T) { ctx, _ := suite.ctx.CacheContext() ctx = ctx.WithIsReCheckTx(tc.recheck).WithIsSigverifyTx(tc.sigverify) + if tc.recheck { + ctx = ctx.WithExecMode(sdk.ExecModeReCheck) + } else { + ctx = ctx.WithExecMode(sdk.ExecModeCheck) + } + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // Create new txBuilder for each test require.NoError(t, suite.txBuilder.SetMsgs(msgs...)) @@ -246,23 +292,23 @@ func TestSigIntegration(t *testing.T) { params := types.DefaultParams() initialSigCost := params.SigVerifyCostSecp256k1 - initialCost, err := runSigDecorators(t, params, false, privs...) + initialCost, err := runSigDecorators(t, params, privs...) require.Nil(t, err) params.SigVerifyCostSecp256k1 *= 2 - doubleCost, err := runSigDecorators(t, params, false, privs...) + doubleCost, err := runSigDecorators(t, params, privs...) require.Nil(t, err) require.Equal(t, initialSigCost*uint64(len(privs)), doubleCost-initialCost) } -func runSigDecorators(t *testing.T, params types.Params, _ bool, privs ...cryptotypes.PrivKey) (storetypes.Gas, error) { +func runSigDecorators(t *testing.T, params types.Params, privs ...cryptotypes.PrivKey) (storetypes.Gas, error) { t.Helper() suite := SetupTestSuite(t, true) suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // Make block-height non-zero to include accNum in SignBytes - suite.ctx = suite.ctx.WithBlockHeight(1) + suite.ctx = suite.ctx.WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1}) err := suite.accountKeeper.Params.Set(suite.ctx, params) require.NoError(t, err) diff --git a/x/auth/ante/testutil_test.go b/x/auth/ante/testutil_test.go index f9ea3b397d70..04b8f63f8555 100644 --- a/x/auth/ante/testutil_test.go +++ b/x/auth/ante/testutil_test.go @@ -13,6 +13,7 @@ import ( _ "cosmossdk.io/api/cosmos/bank/v1beta1" _ "cosmossdk.io/api/cosmos/crypto/secp256k1" "cosmossdk.io/core/appmodule" + "cosmossdk.io/core/header" coretesting "cosmossdk.io/core/testing" storetypes "cosmossdk.io/store/types" "cosmossdk.io/x/auth" @@ -77,7 +78,7 @@ func SetupTestSuite(t *testing.T, isCheckTx bool) *AnteTestSuite { key := storetypes.NewKVStoreKey(types.StoreKey) testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test")) - suite.ctx = testCtx.Ctx.WithIsCheckTx(isCheckTx).WithBlockHeight(1) + suite.ctx = testCtx.Ctx.WithIsCheckTx(isCheckTx).WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1, ChainID: testCtx.Ctx.ChainID()}) suite.encCfg = moduletestutil.MakeTestEncodingConfig(codectestutil.CodecOptions{}, auth.AppModule{}) accNum := uint64(0) diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index d98cae0ca1e8..497cc50d8850 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -29,7 +29,7 @@ import ( // GetMultiSignCommand returns the multi-sign command func GetMultiSignCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "multi-sign [...]", + Use: "multi-sign [...]", Aliases: []string{"multisign"}, Short: "Generate multisig signatures for transactions generated offline", Long: strings.TrimSpace( diff --git a/x/auth/tx/config/depinject.go b/x/auth/tx/config/depinject.go index d85c2921fc8c..bdf3879efbc0 100644 --- a/x/auth/tx/config/depinject.go +++ b/x/auth/tx/config/depinject.go @@ -15,6 +15,8 @@ import ( txconfigv1 "cosmossdk.io/api/cosmos/tx/config/v1" "cosmossdk.io/core/address" "cosmossdk.io/core/appmodule" + appmodulev2 "cosmossdk.io/core/appmodule/v2" + "cosmossdk.io/core/transaction" "cosmossdk.io/depinject" "cosmossdk.io/depinject/appconfig" "cosmossdk.io/x/auth/ante" @@ -49,12 +51,13 @@ type ModuleInputs struct { ProtoFileResolver txsigning.ProtoFileResolver Environment appmodule.Environment // BankKeeper is the expected bank keeper to be passed to AnteHandlers - BankKeeper authtypes.BankKeeper `optional:"true"` - MetadataBankKeeper BankKeeper `optional:"true"` - AccountKeeper ante.AccountKeeper `optional:"true"` - FeeGrantKeeper ante.FeegrantKeeper `optional:"true"` - CustomSignModeHandlers func() []txsigning.SignModeHandler `optional:"true"` - CustomGetSigners []txsigning.CustomGetSigner `optional:"true"` + BankKeeper authtypes.BankKeeper `optional:"true"` + MetadataBankKeeper BankKeeper `optional:"true"` + AccountKeeper ante.AccountKeeper `optional:"true"` + FeeGrantKeeper ante.FeegrantKeeper `optional:"true"` + AccountAbstractionKeeper ante.AccountAbstractionKeeper `optional:"true"` + CustomSignModeHandlers func() []txsigning.SignModeHandler `optional:"true"` + CustomGetSigners []txsigning.CustomGetSigner `optional:"true"` } type ModuleOutputs struct { @@ -63,6 +66,7 @@ type ModuleOutputs struct { TxConfig client.TxConfig TxConfigOptions tx.ConfigOptions BaseAppOption runtime.BaseAppOption // This is only useful for chains using baseapp. Server/v2 chains use TxValidator. + Module appmodule.AppModule } func ProvideProtoRegistry() txsigning.ProtoFileResolver { @@ -140,7 +144,15 @@ func ProvideModule(in ModuleInputs) ModuleOutputs { app.SetTxEncoder(txConfig.TxEncoder()) } - return ModuleOutputs{TxConfig: txConfig, TxConfigOptions: txConfigOptions, BaseAppOption: baseAppOption} + svd := ante.NewSigVerificationDecorator( + in.AccountKeeper, + txConfig.SignModeHandler(), + ante.DefaultSigVerificationGasConsumer, + in.AccountAbstractionKeeper, + ) + appModule := AppModule{svd} + + return ModuleOutputs{TxConfig: txConfig, TxConfigOptions: txConfigOptions, BaseAppOption: baseAppOption, Module: appModule} } func newAnteHandler(txConfig client.TxConfig, in ModuleInputs) (sdk.AnteHandler, error) { @@ -220,3 +232,23 @@ func metadataExists(err error) error { return err } + +var ( + _ appmodulev2.AppModule = AppModule{} + _ appmodulev2.HasTxValidator[transaction.Tx] = AppModule{} +) + +type AppModule struct { + sigVerification ante.SigVerificationDecorator +} + +// TxValidator implements appmodule.HasTxValidator. +func (a AppModule) TxValidator(ctx context.Context, tx transaction.Tx) error { + return a.sigVerification.ValidateTx(ctx, tx) +} + +// IsAppModule implements appmodule.AppModule. +func (a AppModule) IsAppModule() {} + +// IsOnePerModuleType implements appmodule.AppModule. +func (a AppModule) IsOnePerModuleType() {} diff --git a/x/auth/types/account_retriever.go b/x/auth/types/account_retriever.go index 020252aa939e..c46d4330653f 100644 --- a/x/auth/types/account_retriever.go +++ b/x/auth/types/account_retriever.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strconv" + "strings" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -75,7 +76,8 @@ func (ar AccountRetriever) EnsureExists(clientCtx client.Context, addr sdk.AccAd func (ar AccountRetriever) GetAccountNumberSequence(clientCtx client.Context, addr sdk.AccAddress) (uint64, uint64, error) { acc, err := ar.GetAccount(clientCtx, addr) if err != nil { - if status.Code(err) == codes.NotFound { + // the error might come wrapped from CometBFT, so we check with the string too + if status.Code(err) == codes.NotFound || strings.Contains(err.Error(), "code = NotFound") { return 0, 0, nil } return 0, 0, err