diff --git a/x/auth/ante/ante_test.go b/x/auth/ante/ante_test.go index b3d22061b668..60fe2149cd62 100644 --- a/x/auth/ante/ante_test.go +++ b/x/auth/ante/ante_test.go @@ -35,6 +35,58 @@ func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, require.Equal(t, sdk.CodespaceRoot, result.Codespace) } +// Test that simulate transaction accurately estimates gas cost +func TestSimulateGasCost(t *testing.T) { + // setup + app, ctx := createTestApp(true) + ctx = ctx.WithBlockHeight(1) + anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + priv2, _, addr2 := types.KeyTestPubAddr() + priv3, _, addr3 := types.KeyTestPubAddr() + + // set the accounts + acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins(types.NewTestCoins()) + require.NoError(t, acc1.SetAccountNumber(0)) + app.AccountKeeper.SetAccount(ctx, acc1) + acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + acc2.SetCoins(types.NewTestCoins()) + require.NoError(t, acc2.SetAccountNumber(1)) + app.AccountKeeper.SetAccount(ctx, acc2) + acc3 := app.AccountKeeper.NewAccountWithAddress(ctx, addr3) + acc3.SetCoins(types.NewTestCoins()) + require.NoError(t, acc3.SetAccountNumber(2)) + app.AccountKeeper.SetAccount(ctx, acc3) + + // set up msgs and fee + var tx sdk.Tx + msg1 := types.NewTestMsg(addr1, addr2) + msg2 := types.NewTestMsg(addr3, addr1) + msg3 := types.NewTestMsg(addr2, addr3) + msgs := []sdk.Msg{msg1, msg2, msg3} + fee := types.NewTestStdFee() + + // signers in order. accnums are all 0 because it is in genesis block + privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + + cc, _ := ctx.CacheContext() + newCtx, err := anteHandler(cc, tx, true) + require.Nil(t, err, "transaction failed on simulate mode") + + simulatedGas := newCtx.GasMeter().GasConsumed() + fee.Gas = simulatedGas + + // update tx with simulated gas estimate + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + _, err = anteHandler(ctx, tx, false) + + require.Nil(t, err, "transaction failed with gas estimate") +} + // Test various error cases in the AnteHandler control flow. func TestAnteHandlerSigErrors(t *testing.T) { // setup diff --git a/x/auth/ante/fee.go b/x/auth/ante/fee.go index d428cb713593..3d81808b2d0c 100644 --- a/x/auth/ante/fee.go +++ b/x/auth/ante/fee.go @@ -97,6 +97,10 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo feePayer := feeTx.FeePayer() feePayerAcc := dfd.ak.GetAccount(ctx, feePayer) + if feePayerAcc == nil { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", feePayer) + } + // deduct the fees if !feeTx.GetFee().IsZero() { err = DeductFees(dfd.supplyKeeper, ctx, feePayerAcc, feeTx.GetFee()) diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index 4b340907e1e8..347015232100 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -59,9 +59,6 @@ func NewSetPubKeyDecorator(ak keeper.AccountKeeper) SetPubKeyDecorator { } func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - if simulate { - return next(ctx, tx, simulate) - } sigTx, ok := tx.(SigVerifiableTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid tx type") @@ -73,9 +70,13 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b for i, pk := range pubkeys { // PublicKey was omitted from slice since it has already been set in context if pk == nil { - continue + if !simulate { + continue + } + pk = simSecp256k1Pubkey } - if !bytes.Equal(pk.Address(), signers[i]) { + // Only make check if simulate=false + if !simulate && !bytes.Equal(pk.Address(), signers[i]) { return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "pubKey does not match signer address %s with signer index: %d", signers[i], i) } @@ -84,6 +85,10 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b if err != nil { return ctx, err } + // account already has pubkey set,no need to reset + if acc.GetPubKey() != nil { + continue + } err = acc.SetPubKey(pk) if err != nil { return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, err.Error()) @@ -131,16 +136,22 @@ func (sgcd SigGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula pubKey := signerAcc.GetPubKey() if simulate { + // 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 pubKey == nil { + pubKey = simSecp256k1Pubkey + } // Simulated txs should not contain a signature and are not required to // contain a pubkey, so we must account for tx size of including a // StdSignature (Amino encoding) and simulate gas consumption // (assuming a SECP256k1 simulation key). consumeSimSigGas(ctx.GasMeter(), pubKey, sig, params) - } else { - err = sgcd.sigGasConsumer(ctx.GasMeter(), sig, pubKey, params) - if err != nil { - return ctx, err - } + } + err = sgcd.sigGasConsumer(ctx.GasMeter(), sig, pubKey, params) + if err != nil { + return ctx, err } }