Skip to content

Commit

Permalink
add mint/epoch/distribution tests/helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
codchen committed Jun 12, 2023
1 parent 262ae7e commit 286eff4
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 9 deletions.
34 changes: 34 additions & 0 deletions tests/distribution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package tests

import (
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/sei-protocol/sei-chain/testutil/processblock"
"github.com/sei-protocol/sei-chain/testutil/processblock/msgs"
"github.com/sei-protocol/sei-chain/testutil/processblock/verify"
"github.com/stretchr/testify/require"
)

func TestDistribution(t *testing.T) {
app := processblock.NewTestApp()
processblock.CommonPreset(app)
signer1 := app.NewSignableAccount("signer1")
app.FundAccount(signer1, 100000000)
alice := app.NewAccount()

sendAliceMsg := msgs.Send(signer1, alice, 1000)
tx1 := app.Sign(signer1, []sdk.Msg{sendAliceMsg}, 20000)

// block T (no distribution yet since this is the first block)
block := []signing.Tx{tx1}
blockRunner := func() []uint32 { return app.RunBlock(block) }
require.Equal(t, []uint32{0}, blockRunner())

// block T+1 (distribution of fees from T)
blockRunner = func() []uint32 { return app.RunBlock([]signing.Tx{}) }
blockRunner = verify.Allocation(t, app, blockRunner)

require.Equal(t, []uint32{}, blockRunner())
}
29 changes: 29 additions & 0 deletions tests/epoch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package tests

import (
"testing"
"time"

"github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/sei-protocol/sei-chain/testutil/processblock"
"github.com/sei-protocol/sei-chain/testutil/processblock/verify"
"github.com/stretchr/testify/require"
)

func TestEpoch(t *testing.T) {
app := processblock.NewTestApp()
processblock.CommonPreset(app)
app.FastEpoch()

blockRunner := func() []uint32 { return app.RunBlock([]signing.Tx{}) }
blockRunner = verify.Epoch(t, app, blockRunner)

require.Equal(t, []uint32{}, blockRunner())

time.Sleep(6 * time.Second)

blockRunner = func() []uint32 { return app.RunBlock([]signing.Tx{}) }
blockRunner = verify.Epoch(t, app, blockRunner)

require.Equal(t, []uint32{}, blockRunner())
}
30 changes: 30 additions & 0 deletions tests/mint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package tests

import (
"testing"
"time"

"github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/sei-protocol/sei-chain/testutil/processblock"
"github.com/sei-protocol/sei-chain/testutil/processblock/verify"
"github.com/stretchr/testify/require"
)

func TestMint(t *testing.T) {
app := processblock.NewTestApp()
processblock.CommonPreset(app)
app.NewMinter(1000000)
app.FastEpoch()

blockRunner := func() []uint32 { return app.RunBlock([]signing.Tx{}) }
blockRunner = verify.MintRelease(t, app, blockRunner)

require.Equal(t, []uint32{}, blockRunner())

time.Sleep(6 * time.Second)

blockRunner = func() []uint32 { return app.RunBlock([]signing.Tx{}) }
blockRunner = verify.MintRelease(t, app, blockRunner)

require.Equal(t, []uint32{}, blockRunner())
}
22 changes: 13 additions & 9 deletions testutil/processblock/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,7 @@ func (a *App) RunBlock(txs []signing.Tx) (resultCodes []uint32) {
}),
DecidedLastCommit: types.CommitInfo{
Round: 0,
Votes: utils.Map(a.GetAllValidators(), func(v stakingtypes.Validator) types.VoteInfo {
return types.VoteInfo{
Validator: types.Validator{
Address: getValAddress(v),
Power: 1,
},
SignedLastBlock: true,
}
}),
Votes: a.GetVotes(),
},
ByzantineValidators: []types.Misbehavior{},
Hash: []byte("abc"), // no needed for application logic
Expand All @@ -112,6 +104,18 @@ func (a *App) RunBlock(txs []signing.Tx) (resultCodes []uint32) {
return utils.Map(res.TxResults, func(r *types.ExecTxResult) uint32 { return r.Code })
}

func (a *App) GetVotes() []types.VoteInfo {
return utils.Map(a.GetAllValidators(), func(v stakingtypes.Validator) types.VoteInfo {
return types.VoteInfo{
Validator: types.Validator{
Address: getValAddress(v),
Power: 1,
},
SignedLastBlock: true,
}
})
}

func (a *App) GetAllValidators() []stakingtypes.Validator {
return a.StakingKeeper.GetAllValidators(a.Ctx())
}
Expand Down
9 changes: 9 additions & 0 deletions testutil/processblock/genesisepoch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package processblock

import "time"

func (a *App) FastEpoch() {
epoch := a.EpochKeeper.GetEpoch(a.Ctx())
epoch.EpochDuration = 5 * time.Second
a.EpochKeeper.SetEpoch(a.Ctx(), epoch)
}
19 changes: 19 additions & 0 deletions testutil/processblock/genesismint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package processblock

import (
"time"

minttypes "github.com/sei-protocol/sei-chain/x/mint/types"
)

func (a *App) NewMinter(amount uint64) {
today := time.Now()
dayAfterTomorrow := today.Add(48 * time.Hour)
a.MintKeeper.SetMinter(a.Ctx(), minttypes.Minter{
StartDate: today.Format(minttypes.TokenReleaseDateFormat),
EndDate: dayAfterTomorrow.Format(minttypes.TokenReleaseDateFormat),
Denom: "usei",
TotalMintAmount: amount,
RemainingMintAmount: amount,
})
}
88 changes: 88 additions & 0 deletions testutil/processblock/verify/distribution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package verify

import (
"fmt"
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/sei-protocol/sei-chain/testutil/processblock"
"github.com/sei-protocol/sei-chain/utils"
"github.com/stretchr/testify/require"
)

// assuming only `usei` will get distributed
func Allocation(t *testing.T, app *processblock.App, f BlockRunnable) BlockRunnable {
return func() []uint32 {
// fees collected in T-1 are allocated in T's BeginBlock, so we can simply
// query fee collector's balance since this function is called between T-1
// and T.
feeCollector := app.AccountKeeper.GetModuleAccount(app.Ctx(), authtypes.FeeCollectorName)
feesCollectedInt := app.BankKeeper.GetBalance(app.Ctx(), feeCollector.GetAddress(), "usei")
feesCollected := sdk.NewDecCoinFromCoin(feesCollectedInt)

prevProposer := sdk.ValAddress(app.DistrKeeper.GetPreviousProposerConsAddr(app.Ctx())).String()
votedValidators := utils.Map(app.GetAllValidators(), func(v stakingtypes.Validator) string {
return v.GetOperator().String()
})
expectedOutstandingRewards := getOutstandingRewards(app)

baseProposerReward := app.DistrKeeper.GetBaseProposerReward(app.Ctx())
bonusProposerReward := app.DistrKeeper.GetBonusProposerReward(app.Ctx())
proposerMultiplier := baseProposerReward.Add(bonusProposerReward.MulTruncate(sdk.OneDec())) // in test, every val always signs
proposerReward := sdk.DecCoin{
Denom: "usei",
Amount: feesCollected.Amount.MulTruncate(proposerMultiplier),
}
expectedOutstandingRewards[prevProposer] = expectedOutstandingRewards[prevProposer].Add(proposerReward)

communityTax := app.DistrKeeper.GetCommunityTax(app.Ctx())
voteMultiplier := sdk.OneDec().Sub(proposerMultiplier).Sub(communityTax).QuoInt(sdk.NewInt(int64(len(votedValidators))))
voterReward := sdk.DecCoin{
Denom: "usei",
Amount: feesCollected.Amount.MulTruncate(voteMultiplier),
}

for _, validator := range votedValidators {
expectedOutstandingRewards[validator] = expectedOutstandingRewards[validator].Add(voterReward)
}

res := f()

actualOutstandingRewards := getOutstandingRewards(app)

require.Equal(t, len(expectedOutstandingRewards), len(actualOutstandingRewards))

for val, reward := range expectedOutstandingRewards {
require.True(t, reward.Equal(actualOutstandingRewards[val]))
}

return res
}
}

func getOutstandingRewards(app *processblock.App) map[string]sdk.DecCoin {
outstandingRewards := map[string]sdk.DecCoin{}
for _, val := range app.GetAllValidators() {
outstandingRewards[val.GetOperator().String()] = sdk.NewDecCoin("usei", sdk.NewInt(0))
}
app.DistrKeeper.IterateValidatorOutstandingRewards(
app.Ctx(),
func(val sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
if len(rewards.Rewards) == 0 {
return false
}
if len(rewards.Rewards) > 1 {
panic("expecting only usei as validator reward denom but found multiple")
}
if rewards.Rewards[0].Denom != "usei" {
panic(fmt.Sprintf("expecting only usei as validator reward denom but found %s", rewards.Rewards[0].Denom))
}
outstandingRewards[val.String()] = rewards.Rewards[0]
return false
},
)
return outstandingRewards
}
20 changes: 20 additions & 0 deletions testutil/processblock/verify/epoch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package verify

import (
"testing"

"github.com/sei-protocol/sei-chain/testutil/processblock"
"github.com/stretchr/testify/require"
)

func Epoch(t *testing.T, app *processblock.App, f BlockRunnable) BlockRunnable {
return func() []uint32 {
oldEpoch := app.EpochKeeper.GetEpoch(app.Ctx())
res := f()
if app.Ctx().BlockTime().Sub(oldEpoch.CurrentEpochStartTime) > oldEpoch.EpochDuration {
newPoch := app.EpochKeeper.GetEpoch(app.Ctx())
require.Equal(t, oldEpoch.CurrentEpoch+1, newPoch.CurrentEpoch)
}
return res
}
}
39 changes: 39 additions & 0 deletions testutil/processblock/verify/mint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package verify

import (
"testing"
"time"

"github.com/sei-protocol/sei-chain/testutil/processblock"
minttypes "github.com/sei-protocol/sei-chain/x/mint/types"
"github.com/stretchr/testify/require"
)

func MintRelease(t *testing.T, app *processblock.App, f BlockRunnable) BlockRunnable {
return func() []uint32 {
oldMinter := app.MintKeeper.GetMinter(app.Ctx())
oldEpoch := app.EpochKeeper.GetEpoch(app.Ctx())
oldSupply := app.BankKeeper.GetSupply(app.Ctx(), "usei")
res := f()
// if minter minted, it must be a new epoch, but not the other way around
newMinter := app.MintKeeper.GetMinter(app.Ctx())
if newMinter.RemainingMintAmount == oldMinter.RemainingMintAmount {
return res
}
newPoch := app.EpochKeeper.GetEpoch(app.Ctx())
require.Equal(t, oldEpoch.CurrentEpoch+1, newPoch.CurrentEpoch)
startDate, err := time.Parse(minttypes.TokenReleaseDateFormat, oldMinter.StartDate)
if err != nil {
panic(err)
}
endDate, err := time.Parse(minttypes.TokenReleaseDateFormat, oldMinter.EndDate)
if err != nil {
panic(err)
}
expectedMintedAmount := oldMinter.TotalMintAmount / uint64(endDate.Sub(startDate)/(24*time.Hour))
require.Equal(t, expectedMintedAmount, oldMinter.RemainingMintAmount-newMinter.RemainingMintAmount)
newSupply := app.BankKeeper.GetSupply(app.Ctx(), "usei")
require.Equal(t, expectedMintedAmount, uint64(newSupply.Amount.Int64()-oldSupply.Amount.Int64()))
return res
}
}

0 comments on commit 286eff4

Please sign in to comment.