Skip to content

Commit

Permalink
test(tax2gas): add test for post handler (#523)
Browse files Browse the repository at this point in the history
  • Loading branch information
TropicalDog17 authored Oct 8, 2024
1 parent 78c8818 commit c2797fb
Show file tree
Hide file tree
Showing 3 changed files with 306 additions and 2 deletions.
168 changes: 168 additions & 0 deletions x/tax2gas/post/fee_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package post_test

import (
oracle "github.com/classic-terra/core/v3/x/oracle/types"
"github.com/classic-terra/core/v3/x/tax2gas/ante"
"github.com/classic-terra/core/v3/x/tax2gas/post"
tax2gastypes "github.com/classic-terra/core/v3/x/tax2gas/types"
"github.com/classic-terra/core/v3/x/tax2gas/utils"
"github.com/classic-terra/core/v3/x/treasury/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)

func (s *PostTestSuite) TestDeductFeeDecorator() {
anteConsumedFee := int64(207566)
postConsumedFee := int64(1368041)

testCases := []struct {
name string
simulation bool
checkTx bool
setupFunc func(addr sdk.AccAddress, tx signing.Tx)
expectedOracle sdk.Coins
expectedCp sdk.Coins
expectedBurn sdk.Coins
expFail bool
expErrMsg string
}{
{
name: "happy case",
setupFunc: func(addr sdk.AccAddress, tx signing.Tx) {
s.setupTestCase(addr, 100000000, 100000, anteConsumedFee+500022)
},
// amount: 100000000
// tax(0.5%): 500022
// use default value
// burn: tax * 0.9
// distributtion: tax * 0.1 = 500022
// cp: 2% of distribution: 499994 * 0.1 * 0.02 ~ 1000
// oracle: distribution - oracle = 49002
expectedBurn: sdk.NewCoins(sdk.NewInt64Coin("uluna", 450020)),
expectedCp: sdk.NewCoins(sdk.NewInt64Coin("uluna", 1000)),
expectedOracle: sdk.NewCoins(sdk.NewInt64Coin("uluna", 49002)),
},
{
name: "not enough fee",
setupFunc: func(addr sdk.AccAddress, tx signing.Tx) {
s.setupTestCase(addr, 100000000, 100000, 600000)
},
expFail: true,
},
{
name: "combine ante + post, not enough fee",
setupFunc: func(addr sdk.AccAddress, tx signing.Tx) {
s.setupCombineAnteAndPost(addr, tx, 100000, 7328, 707559)
},
expFail: true,
},
{
name: "combine ante + post, enough fee",
setupFunc: func(addr sdk.AccAddress, tx signing.Tx) {
s.setupCombineAnteAndPost(addr, tx, 100000000, 100000, anteConsumedFee+postConsumedFee+500022)
},
expFail: false,
expectedOracle: sdk.NewCoins(sdk.NewInt64Coin("uluna", 49002)),
expectedCp: sdk.NewCoins(sdk.NewInt64Coin("uluna", 1000)),
expectedBurn: sdk.NewCoins(sdk.NewInt64Coin("uluna", 450020)),
},
}

for _, tc := range testCases {
s.Run(tc.name, func() {
// Reset the entire app and context for each test case
s.SetupTest(true)
s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder()
deco := post.NewTax2GasPostDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper, s.app.Tax2gasKeeper)
posthandler := sdk.ChainPostDecorators(deco)

// Generate a new address for each test case
priv1, _, addr1 := testdata.KeyTestPubAddr()

// Fund the account
coins := sdk.NewCoins(sdk.NewCoin("uluna", sdk.NewInt(10000000)))
testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins)

// Create a new test transaction
privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0}
tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID())
s.Require().NoError(err)

tc.setupFunc(addr1, tx)

_, err = posthandler(s.ctx, tx, tc.simulation, true)
s.assertTestCase(tc, err)
})
}
}

func (s *PostTestSuite) setupTestCase(addr sdk.AccAddress, sendAmount, gasLimit, feeAmount int64) {
msg := banktypes.NewMsgSend(addr, addr, sdk.NewCoins(sdk.NewCoin("uluna", sdk.NewInt(sendAmount))))
gm := tax2gastypes.NewTax2GasMeter(s.ctx.GasMeter().Limit(), false)
s.setupTax2GasMeter(gm, msg)
s.ctx = s.ctx.WithGasMeter(gm)
s.ctx = s.ctx.WithValue(tax2gastypes.AnteConsumedGas, uint64(7328))
s.Require().NoError(s.txBuilder.SetMsgs(msg))
s.txBuilder.SetGasLimit(uint64(gasLimit))
s.ctx = s.ctx.WithValue(tax2gastypes.PaidDenom, "uluna")
s.txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin("uluna", feeAmount)))
}

func (s *PostTestSuite) setupCombineAnteAndPost(addr sdk.AccAddress, tx signing.Tx, sendAmount, gasLimit, feeAmount int64) {
msg := banktypes.NewMsgSend(addr, addr, sdk.NewCoins(sdk.NewCoin("uluna", sdk.NewInt(sendAmount))))
gm := tax2gastypes.NewTax2GasMeter(s.ctx.GasMeter().Limit(), false)
// s.setupTax2GasMeter(gm, msg)
mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.Tax2gasKeeper)
s.txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin("uluna", feeAmount)))
s.txBuilder.SetGasLimit(uint64(gasLimit))
s.Require().NoError(s.txBuilder.SetMsgs(msg))
s.ctx = s.ctx.WithGasMeter(gm)

antehandler := sdk.ChainAnteDecorators(mfd)
newCtx, _ := antehandler(s.ctx, tx, false)
distrBalance := s.app.BankKeeper.GetBalance(s.ctx, s.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName), "uluna")

// msg send consume 7328 gas, uluna gas price: 28.325
// expect only this consumed gas is transferred to fee collector account after ante.
s.Require().Equal(sdk.NewDecWithPrec(7328, 0).Mul(sdk.NewDecWithPrec(28325, 3)).RoundInt64(), distrBalance.Amount.Int64())
s.ctx = newCtx
}

func (s *PostTestSuite) setupTax2GasMeter(gm sdk.GasMeter, msg sdk.Msg) {
burnTaxRate := s.app.Tax2gasKeeper.GetBurnTaxRate(s.ctx)
lunaGasPrice := sdk.NewDecCoinFromDec("uluna", sdk.NewDecWithPrec(28325, 3))
taxes := utils.FilterMsgAndComputeTax(s.ctx, s.app.TreasuryKeeper, burnTaxRate, msg)
taxGas, _ := utils.ComputeGas(sdk.DecCoins{lunaGasPrice}, taxes)
gm.(*tax2gastypes.Tax2GasMeter).ConsumeTax(taxGas, "tax")
}

func (s *PostTestSuite) assertTestCase(tc struct {
name string
simulation bool
checkTx bool
setupFunc func(addr sdk.AccAddress, tx signing.Tx)
expectedOracle sdk.Coins
expectedCp sdk.Coins
expectedBurn sdk.Coins
expFail bool
expErrMsg string
}, err error,
) {
currentOracle := s.app.BankKeeper.GetBalance(s.ctx, s.app.AccountKeeper.GetModuleAddress(oracle.ModuleName), "uluna")
currentCp := s.app.DistrKeeper.GetFeePoolCommunityCoins(s.ctx).AmountOf("uluna")
currentBurn := s.app.BankKeeper.GetBalance(s.ctx, s.app.AccountKeeper.GetModuleAddress(types.BurnModuleName), "uluna")
if tc.expFail {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expErrMsg)
} else {
s.Require().NoError(err)
}
s.Require().Equal(tc.expectedOracle.AmountOf("uluna").Int64(), currentOracle.Amount.Int64())
s.Require().Equal(tc.expectedCp.AmountOf("uluna").Int64(), currentCp.RoundInt64())
s.Require().Equal(tc.expectedBurn.AmountOf("uluna").Int64(), currentBurn.Amount.Int64())
}
6 changes: 4 additions & 2 deletions x/tax2gas/post/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ func (tgd Tax2gasPostDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate

if !simulate {
// Deduct feeCoins with paid amount
if paidAmount.Ceil().RoundInt().Int64() > feeCoins.AmountOf(paidDenom).Int64() {
return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fee: %s", feeCoins)
}
feeCoins = feeCoins.Sub(sdk.NewCoin(paidDenom, paidAmount.Ceil().RoundInt()))
}

Expand Down Expand Up @@ -155,12 +158,11 @@ func (tgd Tax2gasPostDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate

// First, we will deduct the fees covered taxGas and handle BurnTaxSplit
taxes, payableFees, gasRemaining := tax2gasutils.CalculateTaxesAndPayableFee(gasPrices, feeCoins, taxGas, totalGasRemaining)
if !simulate && !ctx.IsCheckTx() && gasRemaining.IsPositive() {
if !simulate && gasRemaining.IsPositive() {
gasRemainingFees, err := tax2gasutils.ComputeFeesOnGasConsumed(tx, gasPrices, gasRemaining)
if err != nil {
return ctx, err
}

return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "fees are not enough to pay for gas, need to cover %s gas more, which equal to %q ", gasRemaining.String(), gasRemainingFees)
}
feePayerAccount := tgd.accountKeeper.GetAccount(ctx, feePayer)
Expand Down
134 changes: 134 additions & 0 deletions x/tax2gas/post/post_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package post_test

import (
"testing"

"github.com/stretchr/testify/suite"

dbm "github.com/cometbft/cometbft-db"
"github.com/cometbft/cometbft/libs/log"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"

wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"

terraapp "github.com/classic-terra/core/v3/app"
tax2gastypes "github.com/classic-terra/core/v3/x/tax2gas/types"
treasurytypes "github.com/classic-terra/core/v3/x/treasury/types"
)

type PostTestSuite struct {
suite.Suite

app *terraapp.TerraApp
// anteHandler sdk.AnteHandler
ctx sdk.Context
clientCtx client.Context
txBuilder client.TxBuilder
}

// returns context and app with params set on account keeper
func createTestApp(isCheckTx bool, tempDir string) (*terraapp.TerraApp, sdk.Context) {
// TODO: we need to feed in custom binding here?
var wasmOpts []wasmkeeper.Option
app := terraapp.NewTerraApp(
log.NewNopLogger(), dbm.NewMemDB(), nil, true, map[int64]bool{},
tempDir, terraapp.MakeEncodingConfig(),
simtestutil.EmptyAppOptions{}, wasmOpts,
)
ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{})
app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams())
app.TreasuryKeeper.SetParams(ctx, treasurytypes.DefaultParams())
app.DistrKeeper.SetParams(ctx, distributiontypes.DefaultParams())
app.DistrKeeper.SetFeePool(ctx, distributiontypes.InitialFeePool())
tax2gasParams := tax2gastypes.DefaultParams()
tax2gasParams.Enabled = true
app.Tax2gasKeeper.SetParams(ctx, tax2gasParams)

return app, ctx
}

// SetupTest setups a new test, with new app, context, and anteHandler.
func (suite *PostTestSuite) SetupTest(isCheckTx bool) {
tempDir := suite.T().TempDir()
suite.app, suite.ctx = createTestApp(isCheckTx, tempDir)
suite.ctx = suite.ctx.WithBlockHeight(1)
suite.ctx = suite.ctx.WithGasMeter(tax2gastypes.NewTax2GasMeter(suite.ctx.GasMeter().Limit(), false))

// Set up TxConfig.
encodingConfig := suite.SetupEncoding()

suite.clientCtx = client.Context{}.
WithTxConfig(encodingConfig.TxConfig)
}

func (suite *PostTestSuite) SetupEncoding() testutil.TestEncodingConfig {
encodingConfig := testutil.MakeTestEncodingConfig()
// We're using TestMsg encoding in some tests, so register it here.
encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil)
testdata.RegisterInterfaces(encodingConfig.InterfaceRegistry)

return encodingConfig
}

// CreateTestTx is a helper function to create a tx given multiple inputs.
func (suite *PostTestSuite) CreateTestTx(privs []cryptotypes.PrivKey, accNums []uint64, accSeqs []uint64, chainID string) (xauthsigning.Tx, error) {
// First round: we gather all the signer infos. We use the "set empty
// signature" hack to do that.
var sigsV2 []signing.SignatureV2
for i, priv := range privs {
sigV2 := signing.SignatureV2{
PubKey: priv.PubKey(),
Data: &signing.SingleSignatureData{
SignMode: suite.clientCtx.TxConfig.SignModeHandler().DefaultMode(),
Signature: nil,
},
Sequence: accSeqs[i],
}

sigsV2 = append(sigsV2, sigV2)
}
err := suite.txBuilder.SetSignatures(sigsV2...)
if err != nil {
return nil, err
}

// Second round: all signer infos are set, so each signer can sign.
sigsV2 = []signing.SignatureV2{}
for i, priv := range privs {
signerData := xauthsigning.SignerData{
ChainID: chainID,
AccountNumber: accNums[i],
Sequence: accSeqs[i],
}
sigV2, err := tx.SignWithPrivKey(
suite.clientCtx.TxConfig.SignModeHandler().DefaultMode(), signerData,
suite.txBuilder, priv, suite.clientCtx.TxConfig, accSeqs[i])
if err != nil {
return nil, err
}

sigsV2 = append(sigsV2, sigV2)
}
err = suite.txBuilder.SetSignatures(sigsV2...)
if err != nil {
return nil, err
}

return suite.txBuilder.GetTx(), nil
}

func TestPostTestSuite(t *testing.T) {
suite.Run(t, new(PostTestSuite))
}

0 comments on commit c2797fb

Please sign in to comment.