Skip to content

Commit

Permalink
v1.x backport: add burn tax split logic (#103)
Browse files Browse the repository at this point in the history
* add burn tax split logic
* add unit test for split tax ante
  • Loading branch information
nghuyenthevinh2000 authored Feb 12, 2023
1 parent f0ad7d8 commit f1c1b2a
Show file tree
Hide file tree
Showing 20 changed files with 472 additions and 58 deletions.
9 changes: 9 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ import (
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"

terraappparams "github.com/classic-terra/core/app/params"
v2 "github.com/classic-terra/core/app/upgrades/v2"

customauth "github.com/classic-terra/core/custom/auth"
customante "github.com/classic-terra/core/custom/auth/ante"
Expand Down Expand Up @@ -514,6 +515,7 @@ func NewTerraApp(
app.mm.RegisterRoutes(app.Router(), app.QueryRouter(), encodingConfig.Amino)
app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter())
app.mm.RegisterServices(app.configurator)
app.setupUpgradeHandlers()

// create the simulation manager and define the order of the modules for deterministic simulations
//
Expand Down Expand Up @@ -806,3 +808,10 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino

return paramsKeeper
}

func (app *TerraApp) setupUpgradeHandlers() {
app.UpgradeKeeper.SetUpgradeHandler(
v2.UpgradeName,
v2.CreateV2UpgradeHandler(app.mm, app.configurator),
)
}
3 changes: 3 additions & 0 deletions app/upgrades/v2/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package v2

const UpgradeName = "v2"
16 changes: 16 additions & 0 deletions app/upgrades/v2/upgrades.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package v2

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
)

func CreateV2UpgradeHandler(
mm *module.Manager,
cfg module.Configurator,
) upgradetypes.UpgradeHandler {
return func(ctx sdk.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
return mm.RunMigrations(ctx, cfg, fromVM)
}
}
22 changes: 12 additions & 10 deletions custom/auth/ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
cosmosante "github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
)

// HandlerOptions are the options required for constructing a default SDK AnteHandler.
type HandlerOptions struct {
AccountKeeper cosmosante.AccountKeeper
BankKeeper BankKeeper
FeegrantKeeper cosmosante.FeegrantKeeper
OracleKeeper OracleKeeper
TreasuryKeeper TreasuryKeeper
SignModeHandler signing.SignModeHandler
SigGasConsumer cosmosante.SignatureVerificationGasConsumer
IBCChannelKeeper channelkeeper.Keeper
AccountKeeper cosmosante.AccountKeeper
BankKeeper BankKeeper
FeegrantKeeper cosmosante.FeegrantKeeper
OracleKeeper OracleKeeper
TreasuryKeeper TreasuryKeeper
SignModeHandler signing.SignModeHandler
SigGasConsumer cosmosante.SignatureVerificationGasConsumer
IBCChannelKeeper channelkeeper.Keeper
DistributionKeeper distributionkeeper.Keeper
}

// NewAnteHandler returns an AnteHandler that checks and increments sequence
Expand Down Expand Up @@ -61,8 +63,8 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
cosmosante.NewValidateMemoDecorator(options.AccountKeeper),
cosmosante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
cosmosante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper),
NewBurnTaxFeeDecorator(options.TreasuryKeeper, options.BankKeeper), // burn tax proceeds
cosmosante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
NewBurnTaxFeeDecorator(options.TreasuryKeeper, options.BankKeeper, options.DistributionKeeper), // burn tax proceeds
cosmosante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
cosmosante.NewValidateSigCountDecorator(options.AccountKeeper),
cosmosante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer),
NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
Expand Down
3 changes: 3 additions & 0 deletions custom/auth/ante/ante_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"

terraapp "github.com/classic-terra/core/app"
treasurytypes "github.com/classic-terra/core/x/treasury/types"
Expand Down Expand Up @@ -49,6 +50,8 @@ func createTestApp(isCheckTx bool, tempDir string) (*terraapp.TerraApp, sdk.Cont
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())

return app, ctx
}
Expand Down
23 changes: 22 additions & 1 deletion custom/auth/ante/burntax.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@ import (
// This will still need a parameter change proposal, but can be activated
// anytime after this height
const TaxPowerUpgradeHeight = 9346889
const TaxPowerSplitHeight = 123456789

// BurnTaxFeeDecorator will immediately burn the collected Tax
type BurnTaxFeeDecorator struct {
treasuryKeeper TreasuryKeeper
bankKeeper BankKeeper
distrKeeper DistrKeeper
}

// NewBurnTaxFeeDecorator returns new tax fee decorator instance
func NewBurnTaxFeeDecorator(treasuryKeeper TreasuryKeeper, bankKeeper BankKeeper) BurnTaxFeeDecorator {
func NewBurnTaxFeeDecorator(treasuryKeeper TreasuryKeeper, bankKeeper BankKeeper, distrKeeper DistrKeeper) BurnTaxFeeDecorator {
return BurnTaxFeeDecorator{
treasuryKeeper: treasuryKeeper,
bankKeeper: bankKeeper,
distrKeeper: distrKeeper,
}
}

Expand Down Expand Up @@ -50,6 +53,24 @@ func (btfd BurnTaxFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate

// Record tax proceeds
if !taxes.IsZero() {
if currHeight >= TaxPowerSplitHeight {
feePool := btfd.distrKeeper.GetFeePool(ctx)

for i, taxCoin := range taxes {
splitTaxRate := btfd.treasuryKeeper.GetBurnSplitRate(ctx)
splitcoinAmount := splitTaxRate.MulInt(taxCoin.Amount).RoundInt()

splitCoin := sdk.NewCoin(taxCoin.Denom, splitcoinAmount)
taxes[i] = taxCoin.Sub(splitCoin)

if splitcoinAmount.IsPositive() {
feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinFromCoin(splitCoin))
}
}

btfd.distrKeeper.SetFeePool(ctx, feePool)
}

err = btfd.bankKeeper.SendCoinsFromModuleToModule(ctx, types.FeeCollectorName, treasury.BurnModuleName, taxes)
if err != nil {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
Expand Down
88 changes: 87 additions & 1 deletion custom/auth/ante/burntax_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/classic-terra/core/custom/auth/ante"
core "github.com/classic-terra/core/types"
treasury "github.com/classic-terra/core/x/treasury/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/auth/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
Expand All @@ -17,7 +18,7 @@ func (suite *AnteTestSuite) TestEnsureBurnTaxModule() {
suite.SetupTest(true) // setup
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()

mfd := ante.NewBurnTaxFeeDecorator(suite.app.TreasuryKeeper, suite.app.BankKeeper)
mfd := ante.NewBurnTaxFeeDecorator(suite.app.TreasuryKeeper, suite.app.BankKeeper, suite.app.DistrKeeper)
antehandler := sdk.ChainAnteDecorators(mfd)

// keys and addresses
Expand Down Expand Up @@ -87,3 +88,88 @@ func (suite *AnteTestSuite) TestEnsureBurnTaxModule() {
suite.Require().Equal(taxes, totalSupply.Sub(supplyAfterBurn))

}

// go test -v -run ^TestAnteTestSuite/TestSplitTax$ github.com/classic-terra/core/custom/auth/ante
func (suite *AnteTestSuite) TestSplitTax() {
suite.SetupTest(true) // setup
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()

mfd := ante.NewBurnTaxFeeDecorator(suite.app.TreasuryKeeper, suite.app.BankKeeper, suite.app.DistrKeeper)
antehandler := sdk.ChainAnteDecorators(mfd)

// keys and addresses
priv1, _, addr1 := testdata.KeyTestPubAddr()

// msg and signatures
sendAmount := int64(1000000)
sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount))
msg := banktypes.NewMsgSend(addr1, addr1, sendCoins)

feeAmount := testdata.NewTestFeeAmount()
gasLimit := testdata.NewTestGasLimit()
suite.Require().NoError(suite.txBuilder.SetMsgs(msg))
suite.txBuilder.SetFeeAmount(feeAmount)
suite.txBuilder.SetGasLimit(gasLimit)

privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0}
tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID())
suite.Require().NoError(err)

// ===tax should not be split before TaxPowerSplitHeight===
suite.ctx = suite.ctx.WithBlockHeight(ante.TaxPowerUpgradeHeight)
burnModule := suite.app.AccountKeeper.GetModuleAddress(treasury.BurnModuleName)

feePoolBefore := suite.app.DistrKeeper.GetFeePool(suite.ctx).CommunityPool
burnBefore := suite.app.BankKeeper.GetAllBalances(suite.ctx, burnModule)

// send taxes to fee collector to simulate DeductFeeDecorator antehandler
taxes := suite.DeductFees(sendAmount)

// send tx to BurnTaxFeeDecorator antehandler
_, err = antehandler(suite.ctx, tx, false)

feePoolAfter := suite.app.DistrKeeper.GetFeePool(suite.ctx).CommunityPool
burnAfter := suite.app.BankKeeper.GetAllBalances(suite.ctx, burnModule)

// expected: 1000 tax goes to burn
suite.Require().Equal(feePoolAfter, feePoolBefore)
suite.Require().Equal(burnBefore.Add(taxes...), burnAfter)

// ===tax should be split after TaxPowerSplitHeight===
suite.ctx = suite.ctx.WithBlockHeight(ante.TaxPowerSplitHeight)

feePoolBefore = suite.app.DistrKeeper.GetFeePool(suite.ctx).CommunityPool
burnBefore = suite.app.BankKeeper.GetAllBalances(suite.ctx, burnModule)

// send taxes to fee collector to simulate DeductFeeDecorator antehandler
taxes = suite.DeductFees(sendAmount)
splitTaxRate := suite.app.TreasuryKeeper.GetBurnSplitRate(suite.ctx)
splitTaxesDec := splitTaxRate.MulInt(taxes.AmountOf(core.MicroSDRDenom))
splitTaxesCoin := sdk.NewCoin(core.MicroSDRDenom, splitTaxesDec.RoundInt())

// send tx to BurnTaxFeeDecorator antehandler
_, err = antehandler(suite.ctx, tx, false)

feePoolAfter = suite.app.DistrKeeper.GetFeePool(suite.ctx).CommunityPool
burnAfter = suite.app.BankKeeper.GetAllBalances(suite.ctx, burnModule)
taxesAfter := taxes[0].Sub(splitTaxesCoin)

// expected: 500 tax goes to fee pool, 500 tax goes to burn
suite.Require().Equal(splitTaxesDec, feePoolAfter[0].Amount)
suite.Require().Equal(burnBefore.Add(taxesAfter), burnAfter)
}

func (suite *AnteTestSuite) DeductFees(sendAmount int64) sdk.Coins {
tk := suite.app.TreasuryKeeper
expectedTax := tk.GetTaxRate(suite.ctx).MulInt64(sendAmount).TruncateInt()
if taxCap := tk.GetTaxCap(suite.ctx, core.MicroSDRDenom); expectedTax.GT(taxCap) {
expectedTax = taxCap
}
taxes := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, expectedTax.Int64()))
bk := suite.app.BankKeeper
bk.MintCoins(suite.ctx, minttypes.ModuleName, taxes)
// populate the FeeCollector module with taxes
bk.SendCoinsFromModuleToModule(suite.ctx, minttypes.ModuleName, types.FeeCollectorName, taxes)

return taxes
}
7 changes: 7 additions & 0 deletions custom/auth/ante/expected_keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package ante

import (
sdk "github.com/cosmos/cosmos-sdk/types"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
)

// TreasuryKeeper for tax charging & recording
type TreasuryKeeper interface {
RecordEpochTaxProceeds(ctx sdk.Context, delta sdk.Coins)
GetTaxRate(ctx sdk.Context) (taxRate sdk.Dec)
GetTaxCap(ctx sdk.Context, denom string) (taxCap sdk.Int)
GetBurnSplitRate(ctx sdk.Context) sdk.Dec
}

// OracleKeeper for feeder validation
Expand All @@ -21,3 +23,8 @@ type BankKeeper interface {
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) error
}

type DistrKeeper interface {
SetFeePool(ctx sdk.Context, feePool distributiontypes.FeePool)
GetFeePool(ctx sdk.Context) distributiontypes.FeePool
}
1 change: 1 addition & 0 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -13022,6 +13022,7 @@ Params defines the parameters for the oracle module.
| `window_short` | [uint64](#uint64) | | |
| `window_long` | [uint64](#uint64) | | |
| `window_probation` | [uint64](#uint64) | | |
| `burn_tax_split` | [string](#string) | | |



Expand Down
5 changes: 5 additions & 0 deletions proto/terra/treasury/v1beta1/treasury.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ message Params {
uint64 window_short = 5 [(gogoproto.moretags) = "yaml:\"window_short\""];
uint64 window_long = 6 [(gogoproto.moretags) = "yaml:\"window_long\""];
uint64 window_probation = 7 [(gogoproto.moretags) = "yaml:\"window_probation\""];
string burn_tax_split = 8 [
(gogoproto.moretags) = "yaml:\"burn_tax_split\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}

// PolicyConstraints - defines policy constraints can be applied in tax & reward policies
Expand Down
76 changes: 76 additions & 0 deletions scripts/run-node.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/bin/bash

rm -rf mytestnet
pkill terrad

BINARY=$1
DENOM=$2

SED_BINARY=sed
# check if this is OS X
if [[ "$OSTYPE" == "darwin"* ]]; then
# check if gsed is installed
if ! command -v gsed &> /dev/null
then
echo "gsed could not be found. Please install it with 'brew install gnu-sed'"
exit
else
SED_BINARY=gsed
fi
fi

# check BINARY is set. If not, build terrad and set BINARY
if [ -z "$BINARY" ]; then
make build
BINARY=build/terrad
fi

# check DENOM is set. If not, set to uluna
if [ -z "$DENOM" ]; then
DENOM=uluna
fi

HOME=mytestnet
CHAIN_ID="test"
KEYRING="test"
KEY="test"
KEY1="test1"
KEY2="test2"

# Function updates the config based on a jq argument as a string
update_test_genesis () {
# EX: update_test_genesis '.consensus_params["block"]["max_gas"]="100000000"'
cat $HOME/config/genesis.json | jq --arg DENOM "$2" "$1" > $HOME/config/tmp_genesis.json && mv $HOME/config/tmp_genesis.json $HOME/config/genesis.json
}

$BINARY init --chain-id $CHAIN_ID moniker --home $HOME

$BINARY keys add $KEY --keyring-backend $KEYRING --home $HOME
$BINARY keys add $KEY1 --keyring-backend $KEYRING --home $HOME
$BINARY keys add $KEY2 --keyring-backend $KEYRING --home $HOME

# Allocate genesis accounts (cosmos formatted addresses)
$BINARY add-genesis-account $KEY "1000000000000${DENOM}" --keyring-backend $KEYRING --home $HOME
$BINARY add-genesis-account $KEY1 "1000000000000${DENOM}" --keyring-backend $KEYRING --home $HOME
$BINARY add-genesis-account $KEY2 "1000000000000${DENOM}" --keyring-backend $KEYRING --home $HOME

update_test_genesis '.app_state["gov"]["voting_params"]["voting_period"] = "50s"'
update_test_genesis '.app_state["mint"]["params"]["mint_denom"]=$DENOM' $DENOM
update_test_genesis '.app_state["gov"]["deposit_params"]["min_deposit"]=[{"denom": $DENOM,"amount": "1000000"}]' $DENOM
update_test_genesis '.app_state["crisis"]["constant_fee"]={"denom": $DENOM,"amount": "1000"}' $DENOM
update_test_genesis '.app_state["staking"]["params"]["bond_denom"]=$DENOM' $DENOM

# enable rest server and swagger
$SED_BINARY -i '0,/enable = false/s//enable = true/' $HOME/config/app.toml
$SED_BINARY -i 's/swagger = false/swagger = true/' $HOME/config/app.toml

# Sign genesis transaction
$BINARY gentx $KEY "1000000${DENOM}" --keyring-backend $KEYRING --chain-id $CHAIN_ID --home $HOME

# Collect genesis tx
$BINARY collect-gentxs --home $HOME

# Run this to ensure everything worked and that the genesis file is setup correctly
$BINARY validate-genesis --home $HOME

$BINARY start --home $HOME
Loading

0 comments on commit f1c1b2a

Please sign in to comment.