Merge PR #2527: Minting

This commit is contained in:
Rigel 2018-10-19 14:36:00 -04:00 committed by Christopher Goes
parent 593921d04d
commit b48d0d5623
38 changed files with 510 additions and 391 deletions

View File

@ -82,6 +82,8 @@ BREAKING CHANGES
* [x/stake] \#2500 Block conflicting redelegations until we add an index
* [x/params] Global Paramstore refactored
* [x/stake] \#2508 Utilize Tendermint power for validator power key
* [x/stake] \#2531 Remove all inflation logic
* [x/mint] \#2531 Add minting module and inflation logic
* Tendermint
* Update tendermint version from v0.23.0 to v0.25.0, notable changes

View File

@ -480,11 +480,7 @@ func TestPoolParamsQuery(t *testing.T) {
var pool stake.Pool
err = cdc.UnmarshalJSON([]byte(body), &pool)
require.Nil(t, err)
require.Equal(t, initialPool.DateLastCommissionReset, pool.DateLastCommissionReset)
require.Equal(t, initialPool.PrevBondedShares, pool.PrevBondedShares)
require.Equal(t, initialPool.BondedTokens, pool.BondedTokens)
require.Equal(t, initialPool.NextInflation(params), pool.Inflation)
initialPool = initialPool.ProcessProvisions(params) // provisions are added to the pool every hour
require.Equal(t, initialPool.LooseTokens, pool.LooseTokens)
}

View File

@ -10,6 +10,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
@ -46,6 +47,7 @@ type GaiaApp struct {
keyStake *sdk.KVStoreKey
tkeyStake *sdk.TransientStoreKey
keySlashing *sdk.KVStoreKey
keyMint *sdk.KVStoreKey
keyDistr *sdk.KVStoreKey
tkeyDistr *sdk.TransientStoreKey
keyGov *sdk.KVStoreKey
@ -59,6 +61,7 @@ type GaiaApp struct {
bankKeeper bank.Keeper
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
mintKeeper mint.Keeper
distrKeeper distr.Keeper
govKeeper gov.Keeper
paramsKeeper params.Keeper
@ -78,6 +81,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
keyAccount: sdk.NewKVStoreKey("acc"),
keyStake: sdk.NewKVStoreKey("stake"),
tkeyStake: sdk.NewTransientStoreKey("transient_stake"),
keyMint: sdk.NewKVStoreKey("mint"),
keyDistr: sdk.NewKVStoreKey("distr"),
tkeyDistr: sdk.NewTransientStoreKey("transient_distr"),
keySlashing: sdk.NewKVStoreKey("slashing"),
@ -110,6 +114,10 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace),
app.RegisterCodespace(stake.DefaultCodespace),
)
app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint,
app.paramsKeeper.Subspace(mint.DefaultParamspace),
app.stakeKeeper, app.feeCollectionKeeper,
)
app.distrKeeper = distr.NewKeeper(
app.cdc,
app.keyDistr,
@ -147,11 +155,11 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
AddRoute("stake", stake.NewQuerier(app.stakeKeeper, app.cdc))
// initialize BaseApp
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keyMint, app.keyDistr,
app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams)
app.SetInitChainer(app.initChainer)
app.SetBeginBlocker(app.BeginBlocker)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keyDistr,
app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams)
app.MountStoresTransient(app.tkeyParams, app.tkeyStake, app.tkeyDistr)
app.SetEndBlocker(app.EndBlocker)
@ -184,6 +192,9 @@ func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) ab
// distribute rewards from previous block
distr.BeginBlocker(ctx, req, app.distrKeeper)
// mint new tokens for this new block
mint.BeginBlocker(ctx, app.mintKeeper)
return abci.ResponseBeginBlock{
Tags: tags.ToKVPairs(),
}
@ -232,8 +243,8 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
// load the address to pubkey map
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData)
gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData)
mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData)
distr.InitGenesis(ctx, app.distrKeeper, genesisState.DistrData)
err = GaiaValidateGenesisState(genesisState)
if err != nil {
@ -289,13 +300,14 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val
return false
}
app.accountMapper.IterateAccounts(ctx, appendAccount)
genState := GenesisState{
Accounts: accounts,
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
DistrData: distr.WriteGenesis(ctx, app.distrKeeper),
GovData: gov.WriteGenesis(ctx, app.govKeeper),
}
genState := NewGenesisState(
accounts,
stake.WriteGenesis(ctx, app.stakeKeeper),
mint.WriteGenesis(ctx, app.mintKeeper),
distr.WriteGenesis(ctx, app.distrKeeper),
gov.WriteGenesis(ctx, app.govKeeper),
slashing.GenesisState{}, // TODO create write methods
)
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
if err != nil {
return nil, nil, err

View File

@ -16,6 +16,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
tmtypes "github.com/tendermint/tendermint/types"
@ -31,12 +32,26 @@ var (
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
StakeData stake.GenesisState `json:"stake"`
MintData mint.GenesisState `json:"mint"`
DistrData distr.GenesisState `json:"distr"`
GovData gov.GenesisState `json:"gov"`
SlashingData slashing.GenesisState `json:"slashing"`
GenTxs []json.RawMessage `json:"gentxs"`
}
func NewGenesisState(accounts []GenesisAccount, stakeData stake.GenesisState, mintData mint.GenesisState,
distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState {
return GenesisState{
Accounts: accounts,
StakeData: stakeData,
MintData: mintData,
DistrData: distrData,
GovData: govData,
SlashingData: slashingData,
}
}
// GenesisAccount doesn't need pubkey or sequence
type GenesisAccount struct {
Address sdk.AccAddress `json:"address"`
@ -110,6 +125,7 @@ func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisStat
genesisState = GenesisState{
Accounts: genaccs,
StakeData: stakeData,
MintData: mint.DefaultGenesisState(),
DistrData: distr.DefaultGenesisState(),
GovData: gov.DefaultGenesisState(),
SlashingData: slashingData,

View File

@ -18,6 +18,7 @@ import (
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
"github.com/cosmos/cosmos-sdk/x/slashing"
slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation"
@ -79,13 +80,16 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
stakeGenesis.Pool.LooseTokens = sdk.NewDec(int64(100*250) + (numInitiallyBonded * 100))
stakeGenesis.Validators = validators
stakeGenesis.Bonds = delegations
// No inflation, for now
stakeGenesis.Params.InflationMax = sdk.NewDec(0)
stakeGenesis.Params.InflationMin = sdk.NewDec(0)
mintGenesis := mint.DefaultGenesisState()
mintGenesis.Params.InflationMax = sdk.NewDec(0)
mintGenesis.Params.InflationMin = sdk.NewDec(0)
genesis := GenesisState{
Accounts: genesisAccounts,
StakeData: stakeGenesis,
MintData: mintGenesis,
DistrData: distr.DefaultGenesisWithValidators(valAddrs),
SlashingData: slashingGenesis,
GovData: govGenesis,

View File

@ -231,7 +231,6 @@ func TestGaiaCLICreateValidator(t *testing.T) {
defaultParams := stake.DefaultParams()
initialPool := stake.InitialPool()
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(100)) // Delegate tx on GaiaAppGenState
initialPool = initialPool.ProcessProvisions(defaultParams) // provisions are added to the pool every hour
// create validator
cvStr := fmt.Sprintf("gaiacli tx create-validator %v", flags)
@ -290,8 +289,6 @@ func TestGaiaCLICreateValidator(t *testing.T) {
require.True(t, defaultParams.Equal(params))
pool := executeGetPool(t, fmt.Sprintf("gaiacli query pool --output=json %v", flags))
require.Equal(t, initialPool.DateLastCommissionReset, pool.DateLastCommissionReset)
require.Equal(t, initialPool.PrevBondedShares, pool.PrevBondedShares)
require.Equal(t, initialPool.BondedTokens, pool.BondedTokens)
}

View File

@ -15,7 +15,7 @@ pool which validator holds individually
(`ValidatorDistribution.ProvisionsRewardPool`).
```
func AllocateFees(feesCollected sdk.Coins, feePool FeePool, proposer ValidatorDistribution,
func AllocateTokens(feesCollected sdk.Coins, feePool FeePool, proposer ValidatorDistribution,
sumPowerPrecommitValidators, totalBondedTokens, communityTax,
proposerCommissionRate sdk.Dec)

View File

@ -0,0 +1,28 @@
# Begin-Block
## Inflation
Inflation occurs at the beginning of each block.
### NextInflation
The target annual inflation rate is recalculated for each provisions cycle. The
inflation is also subject to a rate change (positive or negative) depending on
the distance from the desired ratio (67%). The maximum rate change possible is
defined to be 13% per year, however the annual inflation is capped as between
7% and 20%.
NextInflation(params Params, bondedRatio sdk.Dec) (inflation sdk.Dec) {
inflationRateChangePerYear = (1 - bondedRatio/params.GoalBonded) * params.InflationRateChange
inflationRateChange = inflationRateChangePerYear/hrsPerYr
// increase the new annual inflation for this next cycle
inflation += inflationRateChange
if inflation > params.InflationMax {
inflation = params.InflationMax
}
if inflation < params.InflationMin {
inflation = params.InflationMin
}
return inflation

31
docs/spec/mint/state.md Normal file
View File

@ -0,0 +1,31 @@
## State
### Minter
The minter is a space for holding current inflation information.
- Minter: `0x00 -> amino(minter)`
```golang
type Minter struct {
InflationLastTime time.Time // block time which the last inflation was processed
Inflation sdk.Dec // current annual inflation rate
}
```
### Params
Minting params are held in the global params store.
- Params: `mint/params -> amino(params)`
```golang
type Params struct {
MintDenom string // type of coin to mint
InflationRateChange sdk.Dec // maximum annual change in inflation rate
InflationMax sdk.Dec // maximum inflation rate
InflationMin sdk.Dec // minimum inflation rate
GoalBonded sdk.Dec // goal of percent bonded atoms
}
```

View File

@ -12,10 +12,6 @@ inflation information, etc.
type Pool struct {
LooseTokens int64 // tokens not associated with any bonded validator
BondedTokens int64 // reserve of bonded tokens
InflationLastTime int64 // block which the last inflation was processed // TODO make time
Inflation sdk.Dec // current annual inflation rate
DateLastCommissionReset int64 // unix timestamp for last commission accounting reset (daily)
}
```
@ -28,11 +24,6 @@ overall functioning of the stake module.
```golang
type Params struct {
InflationRateChange sdk.Dec // maximum annual change in inflation rate
InflationMax sdk.Dec // maximum inflation rate
InflationMin sdk.Dec // minimum inflation rate
GoalBonded sdk.Dec // Goal of percent bonded atoms
MaxValidators uint16 // maximum number of validators
BondDenom string // bondable coin denomination
}

View File

@ -92,7 +92,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
if !res.IsOK() {
return newCtx, res, true
}
fck.addCollectedFees(newCtx, stdTx.Fee.Amount)
fck.AddCollectedFees(newCtx, stdTx.Fee.Amount)
}
for i := 0; i < len(stdSigs); i++ {

View File

@ -46,7 +46,8 @@ func (fck FeeCollectionKeeper) setCollectedFees(ctx sdk.Context, coins sdk.Coins
store.Set(collectedFeesKey, bz)
}
func (fck FeeCollectionKeeper) addCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins {
// add to the fee pool
func (fck FeeCollectionKeeper) AddCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins {
newCoins := fck.GetCollectedFees(ctx).Plus(coins)
fck.setCollectedFees(ctx, newCoins)

View File

@ -49,11 +49,11 @@ func TestFeeCollectionKeeperAdd(t *testing.T) {
require.True(t, fck.GetCollectedFees(ctx).IsEqual(emptyCoins))
// add oneCoin and check that pool is now oneCoin
fck.addCollectedFees(ctx, oneCoin)
fck.AddCollectedFees(ctx, oneCoin)
require.True(t, fck.GetCollectedFees(ctx).IsEqual(oneCoin))
// add oneCoin again and check that pool is now twoCoins
fck.addCollectedFees(ctx, oneCoin)
fck.AddCollectedFees(ctx, oneCoin)
require.True(t, fck.GetCollectedFees(ctx).IsEqual(twoCoins))
}

View File

@ -13,7 +13,7 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
if ctx.BlockHeight() > 1 {
previousPercentPrecommitVotes := getPreviousPercentPrecommitVotes(req)
previousProposer := k.GetPreviousProposerConsAddr(ctx)
k.AllocateFees(ctx, previousPercentPrecommitVotes, previousProposer)
k.AllocateTokens(ctx, previousPercentPrecommitVotes, previousProposer)
}
consAddr := sdk.ConsAddress(req.Header.ProposerAddress)

View File

@ -8,7 +8,7 @@ import (
)
// Allocate fees handles distribution of the collected fees
func (k Keeper) AllocateFees(ctx sdk.Context, percentVotes sdk.Dec, proposer sdk.ConsAddress) {
func (k Keeper) AllocateTokens(ctx sdk.Context, percentVotes sdk.Dec, proposer sdk.ConsAddress) {
ctx.Logger().With("module", "x/distribution").Error(fmt.Sprintf("allocation height: %v", ctx.BlockHeight()))
// get the proposer of this block

View File

@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/require"
)
func TestAllocateFeesBasic(t *testing.T) {
func TestAllocateTokensBasic(t *testing.T) {
// no community tax on inputs
ctx, _, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
@ -41,7 +41,7 @@ func TestAllocateFeesBasic(t *testing.T) {
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// verify that these fees have been received by the feePool
percentProposer := sdk.NewDecWithPrec(5, 2)
@ -52,7 +52,7 @@ func TestAllocateFeesBasic(t *testing.T) {
require.True(sdk.DecEq(t, expRes, feePool.Pool[0].Amount))
}
func TestAllocateFeesWithCommunityTax(t *testing.T) {
func TestAllocateTokensWithCommunityTax(t *testing.T) {
communityTax := sdk.NewDecWithPrec(1, 2) //1%
ctx, _, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, communityTax)
stakeHandler := stake.NewHandler(sk)
@ -68,7 +68,7 @@ func TestAllocateFeesWithCommunityTax(t *testing.T) {
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// verify that these fees have been received by the feePool
feePool := keeper.GetFeePool(ctx)
@ -80,7 +80,7 @@ func TestAllocateFeesWithCommunityTax(t *testing.T) {
require.True(sdk.DecEq(t, expRes, feePool.Pool[0].Amount))
}
func TestAllocateFeesWithPartialPrecommitPower(t *testing.T) {
func TestAllocateTokensWithPartialPrecommitPower(t *testing.T) {
communityTax := sdk.NewDecWithPrec(1, 2)
ctx, _, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, communityTax)
stakeHandler := stake.NewHandler(sk)
@ -97,7 +97,7 @@ func TestAllocateFeesWithPartialPrecommitPower(t *testing.T) {
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
percentPrecommitVotes := sdk.NewDecWithPrec(25, 2)
keeper.AllocateFees(ctx, percentPrecommitVotes, valConsAddr1)
keeper.AllocateTokens(ctx, percentPrecommitVotes, valConsAddr1)
// verify that these fees have been received by the feePool
feePool := keeper.GetFeePool(ctx)

View File

@ -30,7 +30,7 @@ func TestWithdrawDelegationRewardBasic(t *testing.T) {
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// withdraw delegation
ctx = ctx.WithBlockHeight(1)
@ -64,7 +64,7 @@ func TestWithdrawDelegationRewardWithCommission(t *testing.T) {
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// withdraw delegation
ctx = ctx.WithBlockHeight(1)
@ -104,7 +104,7 @@ func TestWithdrawDelegationRewardTwoDelegators(t *testing.T) {
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// delegator 1 withdraw delegation
ctx = ctx.WithBlockHeight(1)
@ -146,7 +146,7 @@ func TestWithdrawDelegationRewardTwoDelegatorsUneven(t *testing.T) {
feeInputs := sdk.NewInt(90)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
ctx = ctx.WithBlockHeight(1)
// delegator 1 withdraw delegation early, delegator 2 just keeps it's accum
@ -160,7 +160,7 @@ func TestWithdrawDelegationRewardTwoDelegatorsUneven(t *testing.T) {
feeInputs = sdk.NewInt(180)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
ctx = ctx.WithBlockHeight(2)
// delegator 2 now withdraws everything it's entitled to
@ -228,7 +228,7 @@ func TestWithdrawDelegationRewardsAll(t *testing.T) {
feeInputs := sdk.NewInt(1000)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// withdraw delegation
ctx = ctx.WithBlockHeight(1)

View File

@ -23,7 +23,7 @@ func TestWithdrawValidatorRewardsAllNoDelegator(t *testing.T) {
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// withdraw self-delegation reward
ctx = ctx.WithBlockHeight(1)
@ -55,7 +55,7 @@ func TestWithdrawValidatorRewardsAllDelegatorNoCommission(t *testing.T) {
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// withdraw self-delegation reward
ctx = ctx.WithBlockHeight(1)
@ -89,7 +89,7 @@ func TestWithdrawValidatorRewardsAllDelegatorWithCommission(t *testing.T) {
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// withdraw validator reward
ctx = ctx.WithBlockHeight(1)
@ -129,7 +129,7 @@ func TestWithdrawValidatorRewardsAllMultipleValidator(t *testing.T) {
feeInputs := sdk.NewInt(1000)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// withdraw validator reward
ctx = ctx.WithBlockHeight(1)
@ -175,7 +175,7 @@ func TestWithdrawValidatorRewardsAllMultipleDelegator(t *testing.T) {
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
keeper.AllocateTokens(ctx, sdk.OneDec(), valConsAddr1)
// withdraw validator reward
ctx = ctx.WithBlockHeight(1)

25
x/mint/abci_app.go Normal file
View File

@ -0,0 +1,25 @@
package mint
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Called every block, process inflation on the first block of every hour
func BeginBlocker(ctx sdk.Context, k Keeper) {
blockTime := ctx.BlockHeader().Time
minter := k.GetMinter(ctx)
if blockTime.Sub(minter.InflationLastTime) < time.Hour { // only mint on the hour!
return
}
params := k.GetParams(ctx)
totalSupply := k.sk.TotalPower(ctx)
bondedRatio := k.sk.BondedRatio(ctx)
minter.InflationLastTime = blockTime
minter, mintedCoin := minter.ProcessProvisions(params, totalSupply, bondedRatio)
k.fck.AddCollectedFees(ctx, sdk.Coins{mintedCoin})
k.SetMinter(ctx, minter)
}

View File

@ -0,0 +1,15 @@
package mint
import sdk "github.com/cosmos/cosmos-sdk/types"
// expected stake keeper
type StakeKeeper interface {
TotalPower(ctx sdk.Context) sdk.Dec
BondedRatio(ctx sdk.Context) sdk.Dec
InflateSupply(ctx sdk.Context, newTokens sdk.Dec)
}
// expected fee collection keeper interface
type FeeCollectionKeeper interface {
AddCollectedFees(sdk.Context, sdk.Coins) sdk.Coins
}

55
x/mint/genesis.go Normal file
View File

@ -0,0 +1,55 @@
package mint
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GenesisState - all distribution state that must be provided at genesis
type GenesisState struct {
Minter Minter `json:"Minter"` // minter object
Params Params `json:"params"` // inflation params
}
func NewGenesisState(minter Minter, params Params) GenesisState {
return GenesisState{
Minter: minter,
Params: params,
}
}
// get raw genesis raw message for testing
func DefaultGenesisState() GenesisState {
return GenesisState{
Minter: InitialMinter(),
Params: DefaultParams(),
}
}
// new mint genesis
func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) {
keeper.SetMinter(ctx, data.Minter)
keeper.SetParams(ctx, data.Params)
}
// WriteGenesis returns a GenesisState for a given context and keeper. The
// GenesisState will contain the pool, and validator/delegator distribution info's
func WriteGenesis(ctx sdk.Context, keeper Keeper) GenesisState {
minter := keeper.GetMinter(ctx)
params := keeper.GetParams(ctx)
return NewGenesisState(minter, params)
}
// ValidateGenesis validates the provided staking genesis state to ensure the
// expected invariants holds. (i.e. params in correct bounds, no duplicate validators)
func ValidateGenesis(data GenesisState) error {
err := validateParams(data.Params)
if err != nil {
return err
}
err = validateMinter(data.Minter)
if err != nil {
return err
}
return nil
}

85
x/mint/keeper.go Normal file
View File

@ -0,0 +1,85 @@
package mint
import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/params"
)
// keeper of the stake store
type Keeper struct {
storeKey sdk.StoreKey
cdc *codec.Codec
paramSpace params.Subspace
sk StakeKeeper
fck FeeCollectionKeeper
}
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey,
paramSpace params.Subspace, sk StakeKeeper, fck FeeCollectionKeeper) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
paramSpace: paramSpace.WithTypeTable(ParamTypeTable()),
sk: sk,
fck: fck,
}
return keeper
}
//____________________________________________________________________
// Keys
var (
minterKey = []byte{0x00} // the one key to use for the keeper store
// params store for inflation params
ParamStoreKeyParams = []byte("params")
)
// ParamTable for stake module
func ParamTypeTable() params.TypeTable {
return params.NewTypeTable(
ParamStoreKeyParams, Params{},
)
}
const (
// default paramspace for params keeper
DefaultParamspace = "mint"
)
//______________________________________________________________________
// get the minter
func (k Keeper) GetMinter(ctx sdk.Context) (minter Minter) {
store := ctx.KVStore(k.storeKey)
b := store.Get(minterKey)
if b == nil {
panic("Stored fee pool should not have been nil")
}
k.cdc.MustUnmarshalBinary(b, &minter)
return
}
// set the minter
func (k Keeper) SetMinter(ctx sdk.Context, minter Minter) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(minter)
store.Set(minterKey, b)
}
//______________________________________________________________________
// get inflation params from the global param store
func (k Keeper) GetParams(ctx sdk.Context) Params {
var params Params
k.paramSpace.Get(ctx, ParamStoreKeyParams, &params)
return params
}
// set inflation params from the global param store
func (k Keeper) SetParams(ctx sdk.Context, params Params) {
k.paramSpace.Set(ctx, ParamStoreKeyParams, &params)
}

71
x/mint/minter.go Normal file
View File

@ -0,0 +1,71 @@
package mint
import (
"fmt"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// current inflation state
type Minter struct {
InflationLastTime time.Time `json:"inflation_last_time"` // block time which the last inflation was processed
Inflation sdk.Dec `json:"inflation"` // current annual inflation rate
}
// minter object for a new minter
func InitialMinter() Minter {
return Minter{
InflationLastTime: time.Unix(0, 0),
Inflation: sdk.NewDecWithPrec(13, 2),
}
}
func validateMinter(minter Minter) error {
if minter.Inflation.LT(sdk.ZeroDec()) {
return fmt.Errorf("mint parameter Inflation should be positive, is %s ", minter.Inflation.String())
}
if minter.Inflation.GT(sdk.OneDec()) {
return fmt.Errorf("mint parameter Inflation must be <= 1, is %s", minter.Inflation.String())
}
return nil
}
var hrsPerYr = sdk.NewDec(8766) // as defined by a julian year of 365.25 days
// process provisions for an hour period
func (m Minter) ProcessProvisions(params Params, totalSupply, bondedRatio sdk.Dec) (
minter Minter, provisions sdk.Coin) {
m.Inflation = m.NextInflation(params, bondedRatio)
provisionsDec := m.Inflation.Mul(totalSupply).Quo(hrsPerYr)
provisions = sdk.NewCoin(params.MintDenom, provisionsDec.TruncateInt())
return m, provisions
}
// get the next inflation rate for the hour
func (m Minter) NextInflation(params Params, bondedRatio sdk.Dec) (inflation sdk.Dec) {
// The target annual inflation rate is recalculated for each previsions cycle. The
// inflation is also subject to a rate change (positive or negative) depending on
// the distance from the desired ratio (67%). The maximum rate change possible is
// defined to be 13% per year, however the annual inflation is capped as between
// 7% and 20%.
// (1 - bondedRatio/GoalBonded) * InflationRateChange
inflationRateChangePerYear := sdk.OneDec().
Sub(bondedRatio.Quo(params.GoalBonded)).
Mul(params.InflationRateChange)
inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYr)
// increase the new annual inflation for this next cycle
inflation = m.Inflation.Add(inflationRateChange)
if inflation.GT(params.InflationMax) {
inflation = params.InflationMax
}
if inflation.LT(params.InflationMin) {
inflation = params.InflationMin
}
return inflation
}

53
x/mint/minter_test.go Normal file
View File

@ -0,0 +1,53 @@
package mint
import (
"testing"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestNextInflation(t *testing.T) {
minter := InitialMinter()
params := DefaultParams()
// Governing Mechanism:
// inflationRateChangePerYear = (1- BondedRatio/ GoalBonded) * MaxInflationRateChange
tests := []struct {
bondedRatio, setInflation, expChange sdk.Dec
}{
// with 0% bonded atom supply the inflation should increase by InflationRateChange
{sdk.ZeroDec(), sdk.NewDecWithPrec(7, 2), params.InflationRateChange.Quo(hrsPerYr)},
// 100% bonded, starting at 20% inflation and being reduced
// (1 - (1/0.67))*(0.13/8667)
{sdk.OneDec(), sdk.NewDecWithPrec(20, 2),
sdk.OneDec().Sub(sdk.OneDec().Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYr)},
// 50% bonded, starting at 10% inflation and being increased
{sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(10, 2),
sdk.OneDec().Sub(sdk.NewDecWithPrec(5, 1).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYr)},
// test 7% minimum stop (testing with 100% bonded)
{sdk.OneDec(), sdk.NewDecWithPrec(7, 2), sdk.ZeroDec()},
{sdk.OneDec(), sdk.NewDecWithPrec(70001, 6), sdk.NewDecWithPrec(-1, 6)},
// test 20% maximum stop (testing with 0% bonded)
{sdk.ZeroDec(), sdk.NewDecWithPrec(20, 2), sdk.ZeroDec()},
{sdk.ZeroDec(), sdk.NewDecWithPrec(199999, 6), sdk.NewDecWithPrec(1, 6)},
// perfect balance shouldn't change inflation
{sdk.NewDecWithPrec(67, 2), sdk.NewDecWithPrec(15, 2), sdk.ZeroDec()},
}
for i, tc := range tests {
minter.Inflation = tc.setInflation
inflation := minter.NextInflation(params, tc.bondedRatio)
diffInflation := inflation.Sub(tc.setInflation)
require.True(t, diffInflation.Equal(tc.expChange),
"Test Index: %v\nDiff: %v\nExpected: %v\n", i, diffInflation, tc.expChange)
}
}

43
x/mint/params.go Normal file
View File

@ -0,0 +1,43 @@
package mint
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// mint parameters
type Params struct {
MintDenom string `json:"mint_denom"` // type of coin to mint
InflationRateChange sdk.Dec `json:"inflation_rate_change"` // maximum annual change in inflation rate
InflationMax sdk.Dec `json:"inflation_max"` // maximum inflation rate
InflationMin sdk.Dec `json:"inflation_min"` // minimum inflation rate
GoalBonded sdk.Dec `json:"goal_bonded"` // goal of percent bonded atoms
}
// default minting module parameters
func DefaultParams() Params {
return Params{
MintDenom: "steak",
InflationRateChange: sdk.NewDecWithPrec(13, 2),
InflationMax: sdk.NewDecWithPrec(20, 2),
InflationMin: sdk.NewDecWithPrec(7, 2),
GoalBonded: sdk.NewDecWithPrec(67, 2),
}
}
func validateParams(params Params) error {
if params.GoalBonded.LT(sdk.ZeroDec()) {
return fmt.Errorf("mint parameter GoalBonded should be positive, is %s ", params.GoalBonded.String())
}
if params.GoalBonded.GT(sdk.OneDec()) {
return fmt.Errorf("mint parameter GoalBonded must be <= 1, is %s", params.GoalBonded.String())
}
if params.InflationMax.LT(params.InflationMin) {
return fmt.Errorf("mint parameter Max inflation must be greater than or equal to min inflation")
}
if params.MintDenom == "" {
return fmt.Errorf("mint parameter MintDenom can't be an empty string")
}
return nil
}

View File

@ -103,20 +103,9 @@ func ValidateGenesis(data types.GenesisState) error {
}
func validateParams(params types.Params) error {
if params.GoalBonded.LTE(sdk.ZeroDec()) {
bondedPercent := params.GoalBonded.MulInt(sdk.NewInt(100)).String()
return fmt.Errorf("staking parameter GoalBonded should be positive, instead got %s percent", bondedPercent)
}
if params.GoalBonded.GT(sdk.OneDec()) {
bondedPercent := params.GoalBonded.MulInt(sdk.NewInt(100)).String()
return fmt.Errorf("staking parameter GoalBonded should be less than 100 percent, instead got %s percent", bondedPercent)
}
if params.BondDenom == "" {
return fmt.Errorf("staking parameter BondDenom can't be an empty string")
}
if params.InflationMax.LT(params.InflationMin) {
return fmt.Errorf("staking parameter Max inflation must be greater than or equal to min inflation")
}
return nil
}

View File

@ -121,16 +121,6 @@ func TestValidateGenesis(t *testing.T) {
wantErr bool
}{
{"default", func(*types.GenesisState) {}, false},
// validate params
{"200% goalbonded", func(data *types.GenesisState) { (*data).Params.GoalBonded = sdk.OneDec().Add(sdk.OneDec()) }, true},
{"-67% goalbonded", func(data *types.GenesisState) { (*data).Params.GoalBonded = sdk.OneDec().Neg() }, true},
{"no bond denom", func(data *types.GenesisState) { (*data).Params.BondDenom = "" }, true},
{"min inflation > max inflation", func(data *types.GenesisState) {
(*data).Params.InflationMin = (*data).Params.InflationMax.Add(sdk.OneDec())
}, true},
{"min inflation = max inflation", func(data *types.GenesisState) {
(*data).Params.InflationMax = (*data).Params.InflationMin
}, false},
// validate genesis validators
{"duplicate validator", func(data *types.GenesisState) {
(*data).Validators = genValidators1

View File

@ -2,7 +2,6 @@ package stake
import (
"bytes"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
@ -31,7 +30,7 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
}
}
// Called every block, process inflation, update validator set
// Called every block, update validator set
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.ValidatorUpdate) {
endBlockerTags := sdk.EmptyTags()
@ -64,17 +63,6 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid
))
}
pool := k.GetPool(ctx)
// Process provision inflation
blockTime := ctx.BlockHeader().Time
if blockTime.Sub(pool.InflationLastTime) >= time.Hour {
params := k.GetParams(ctx)
pool.InflationLastTime = blockTime
pool = pool.ProcessProvisions(params)
k.SetPool(ctx, pool)
}
// reset the intra-transaction counter
k.SetIntraTxCounter(ctx, 0)

View File

@ -85,13 +85,6 @@ func TestValidatorByPowerIndex(t *testing.T) {
power2 := GetValidatorsByPowerIndexKey(validator, pool)
require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power2))
// inflate a bunch
params := keeper.GetParams(ctx)
for i := 0; i < 200; i++ {
pool = pool.ProcessProvisions(params)
keeper.SetPool(ctx, pool)
}
// now the new record power index should be the same as the original record
power3 := GetValidatorsByPowerIndexKey(validator, pool)
require.Equal(t, power2, power3)

View File

@ -18,30 +18,6 @@ func ParamTypeTable() params.TypeTable {
return params.NewTypeTable().RegisterParamSet(&types.Params{})
}
// InflationRateChange - Maximum annual change in inflation rate
func (k Keeper) InflationRateChange(ctx sdk.Context) (res sdk.Dec) {
k.paramstore.Get(ctx, types.KeyInflationRateChange, &res)
return
}
// InflationMax - Maximum inflation rate
func (k Keeper) InflationMax(ctx sdk.Context) (res sdk.Dec) {
k.paramstore.Get(ctx, types.KeyInflationMax, &res)
return
}
// InflationMin - Minimum inflation rate
func (k Keeper) InflationMin(ctx sdk.Context) (res sdk.Dec) {
k.paramstore.Get(ctx, types.KeyInflationMin, &res)
return
}
// GoalBonded - Goal of percent bonded atoms
func (k Keeper) GoalBonded(ctx sdk.Context) (res sdk.Dec) {
k.paramstore.Get(ctx, types.KeyGoalBonded, &res)
return
}
// UnbondingTime
func (k Keeper) UnbondingTime(ctx sdk.Context) (res time.Duration) {
k.paramstore.Get(ctx, types.KeyUnbondingTime, &res)
@ -62,10 +38,6 @@ func (k Keeper) BondDenom(ctx sdk.Context) (res string) {
// Get all parameteras as types.Params
func (k Keeper) GetParams(ctx sdk.Context) (res types.Params) {
res.InflationRateChange = k.InflationRateChange(ctx)
res.InflationMax = k.InflationMax(ctx)
res.InflationMin = k.InflationMin(ctx)
res.GoalBonded = k.GoalBonded(ctx)
res.UnbondingTime = k.UnbondingTime(ctx)
res.MaxValidators = k.MaxValidators(ctx)
res.BondDenom = k.BondDenom(ctx)

View File

@ -72,6 +72,19 @@ func (k Keeper) TotalPower(ctx sdk.Context) sdk.Dec {
return pool.BondedTokens
}
// total power from the bond
func (k Keeper) BondedRatio(ctx sdk.Context) sdk.Dec {
pool := k.GetPool(ctx)
return pool.BondedRatio()
}
// when minting new tokens
func (k Keeper) InflateSupply(ctx sdk.Context, newTokens sdk.Dec) {
pool := k.GetPool(ctx)
pool.LooseTokens = pool.LooseTokens.Add(newTokens)
k.SetPool(ctx, pool)
}
//__________________________________________________________________________
// Implements DelegationSet

View File

@ -73,18 +73,6 @@ func MakeTestCodec() *codec.Codec {
return cdc
}
// default params without inflation
func ParamsNoInflation() types.Params {
return types.Params{
InflationRateChange: sdk.ZeroDec(),
InflationMax: sdk.ZeroDec(),
InflationMin: sdk.ZeroDec(),
GoalBonded: sdk.NewDecWithPrec(67, 2),
MaxValidators: 100,
BondDenom: "steak",
}
}
// hogpodge of all sorts of input required for testing
func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context, auth.AccountMapper, Keeper) {

View File

@ -234,8 +234,6 @@ func Setup(mapp *mock.App, k stake.Keeper) simulation.RandSetup {
return func(r *rand.Rand, accs []simulation.Account) {
ctx := mapp.NewContext(false, abci.Header{})
gen := stake.DefaultGenesisState()
gen.Params.InflationMax = sdk.NewDec(0)
gen.Params.InflationMin = sdk.NewDec(0)
stake.InitGenesis(ctx, k, gen)
params := k.GetParams(ctx)
denom := params.BondDenom

View File

@ -58,13 +58,10 @@ var (
GetREDsByDelToValDstIndexKey = keeper.GetREDsByDelToValDstIndexKey
TestingUpdateValidator = keeper.TestingUpdateValidator
DefaultParamspace = keeper.DefaultParamspace
KeyInflationRateChange = types.KeyInflationRateChange
KeyInflationMax = types.KeyInflationMax
KeyGoalBonded = types.KeyGoalBonded
KeyUnbondingTime = types.KeyUnbondingTime
KeyMaxValidators = types.KeyMaxValidators
KeyBondDenom = types.KeyBondDenom
DefaultParamspace = keeper.DefaultParamspace
KeyUnbondingTime = types.KeyUnbondingTime
KeyMaxValidators = types.KeyMaxValidators
KeyBondDenom = types.KeyBondDenom
DefaultParams = types.DefaultParams
InitialPool = types.InitialPool

View File

@ -1,145 +0,0 @@
package types
import (
"math/rand"
"testing"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
)
//changing the int in NewSource will allow you to test different, deterministic, sets of operations
var r = rand.New(rand.NewSource(6595))
func TestGetInflation(t *testing.T) {
pool := InitialPool()
params := DefaultParams()
// Governing Mechanism:
// BondedRatio = BondedTokens / TotalSupply
// inflationRateChangePerYear = (1- BondedRatio/ GoalBonded) * MaxInflationRateChange
tests := []struct {
name string
setBondedTokens, setLooseTokens,
setInflation, expectedChange sdk.Dec
}{
// with 0% bonded atom supply the inflation should increase by InflationRateChange
{"test 1", sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(7, 2), params.InflationRateChange.Quo(hrsPerYrDec)},
// 100% bonded, starting at 20% inflation and being reduced
// (1 - (1/0.67))*(0.13/8667)
{"test 2", sdk.OneDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(20, 2),
sdk.OneDec().Sub(sdk.OneDec().Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrDec)},
// 50% bonded, starting at 10% inflation and being increased
{"test 3", sdk.OneDec(), sdk.OneDec(), sdk.NewDecWithPrec(10, 2),
sdk.OneDec().Sub(sdk.NewDecWithPrec(5, 1).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrDec)},
// test 7% minimum stop (testing with 100% bonded)
{"test 4", sdk.OneDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(7, 2), sdk.ZeroDec()},
{"test 5", sdk.OneDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(70001, 6), sdk.NewDecWithPrec(-1, 6)},
// test 20% maximum stop (testing with 0% bonded)
{"test 6", sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(20, 2), sdk.ZeroDec()},
{"test 7", sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(199999, 6), sdk.NewDecWithPrec(1, 6)},
// perfect balance shouldn't change inflation
{"test 8", sdk.NewDec(67), sdk.NewDec(33), sdk.NewDecWithPrec(15, 2), sdk.ZeroDec()},
}
for _, tc := range tests {
pool.BondedTokens, pool.LooseTokens = tc.setBondedTokens, tc.setLooseTokens
pool.Inflation = tc.setInflation
inflation := pool.NextInflation(params)
diffInflation := inflation.Sub(tc.setInflation)
require.True(t, diffInflation.Equal(tc.expectedChange),
"Name: %v\nDiff: %v\nExpected: %v\n", tc.name, diffInflation, tc.expectedChange)
}
}
// Test that provisions are correctly added to the pool and validators each hour for 1 year
func TestProcessProvisions(t *testing.T) {
pool := InitialPool()
params := DefaultParams()
var (
initialTotalTokens int64 = 550000000
cumulativeExpProvs = sdk.ZeroDec()
)
pool.LooseTokens = sdk.NewDec(initialTotalTokens)
// process the provisions for a year
for hr := 0; hr < 100; hr++ {
var expProvisions sdk.Dec
_, expProvisions, pool = updateProvisions(t, pool, params, hr)
cumulativeExpProvs = cumulativeExpProvs.Add(expProvisions)
}
//get the pool and do the final value checks from checkFinalPoolValues
checkFinalPoolValues(t, pool, sdk.NewDec(initialTotalTokens), cumulativeExpProvs)
}
//_________________________________________________________________________________________
////////////////////////////////HELPER FUNCTIONS BELOW/////////////////////////////////////
// Final check on the global pool values for what the total tokens accumulated from each hour of provisions
func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens, cumulativeExpProvs sdk.Dec) {
calculatedTotalTokens := initialTotalTokens.Add(cumulativeExpProvs)
require.True(sdk.DecEq(t, calculatedTotalTokens, pool.TokenSupply()))
}
// Processes provisions are added to the pool correctly every hour
// Returns expected Provisions, expected Inflation, and pool, to help with cumulative calculations back in main Tests
func updateProvisions(t *testing.T, pool Pool, params Params, hr int) (sdk.Dec, sdk.Dec, Pool) {
expInflation := pool.NextInflation(params)
expProvisions := expInflation.Mul(pool.TokenSupply()).Quo(hrsPerYrDec)
startTotalSupply := pool.TokenSupply()
pool = pool.ProcessProvisions(params)
//check provisions were added to pool
require.True(sdk.DecEq(t, startTotalSupply.Add(expProvisions), pool.TokenSupply()))
return expInflation, expProvisions, pool
}
// Checks that The inflation will correctly increase or decrease after an update to the pool
func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Dec, msg string) {
inflationChange := updatedInflation.Sub(previousInflation)
switch {
//BELOW 67% - Rate of change positive and increasing, while we are between 7% <= and < 20% inflation
case pool.BondedRatio().LT(sdk.NewDecWithPrec(67, 2)) && updatedInflation.LT(sdk.NewDecWithPrec(20, 2)):
require.Equal(t, true, inflationChange.GT(sdk.ZeroDec()), msg)
//BELOW 67% - Rate of change should be 0 while inflation continually stays at 20% until we reach 67% bonded ratio
case pool.BondedRatio().LT(sdk.NewDecWithPrec(67, 2)) && updatedInflation.Equal(sdk.NewDecWithPrec(20, 2)):
if previousInflation.Equal(sdk.NewDecWithPrec(20, 2)) {
require.Equal(t, true, inflationChange.IsZero(), msg)
//This else statement covers the one off case where we first hit 20%, but we still needed a positive ROC to get to 67% bonded ratio (i.e. we went from 19.99999% to 20%)
} else {
require.Equal(t, true, inflationChange.GT(sdk.ZeroDec()), msg)
}
//ABOVE 67% - Rate of change should be negative while the bond is above 67, and should stay negative until we reach inflation of 7%
case pool.BondedRatio().GT(sdk.NewDecWithPrec(67, 2)) &&
updatedInflation.LT(sdk.NewDecWithPrec(20, 2)) && updatedInflation.GT(sdk.NewDecWithPrec(7, 2)):
require.Equal(t, true, inflationChange.LT(sdk.ZeroDec()), msg)
//ABOVE 67% - Rate of change should be 0 while inflation continually stays at 7%.
case pool.BondedRatio().GT(sdk.NewDecWithPrec(67, 2)) &&
updatedInflation.Equal(sdk.NewDecWithPrec(7, 2)):
if previousInflation.Equal(sdk.NewDecWithPrec(7, 2)) {
require.Equal(t, true, inflationChange.IsZero(), msg)
//This else statement covers the one off case where we first hit 7%, but we still needed a negative ROC to continue to get down to 67%. (i.e. we went from 7.00001% to 7%)
} else {
require.Equal(t, true, inflationChange.LT(sdk.ZeroDec()), msg)
}
}
}

View File

@ -6,7 +6,6 @@ import (
"time"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/params"
)
@ -24,24 +23,15 @@ const (
// nolint - Keys for parameter access
var (
KeyInflationRateChange = []byte("InflationRateChange")
KeyInflationMax = []byte("InflationMax")
KeyInflationMin = []byte("InflationMin")
KeyGoalBonded = []byte("GoalBonded")
KeyUnbondingTime = []byte("UnbondingTime")
KeyMaxValidators = []byte("MaxValidators")
KeyBondDenom = []byte("BondDenom")
KeyUnbondingTime = []byte("UnbondingTime")
KeyMaxValidators = []byte("MaxValidators")
KeyBondDenom = []byte("BondDenom")
)
var _ params.ParamSet = (*Params)(nil)
// Params defines the high level settings for staking
type Params struct {
InflationRateChange sdk.Dec `json:"inflation_rate_change"` // maximum annual change in inflation rate
InflationMax sdk.Dec `json:"inflation_max"` // maximum inflation rate
InflationMin sdk.Dec `json:"inflation_min"` // minimum inflation rate
GoalBonded sdk.Dec `json:"goal_bonded"` // Goal of percent bonded atoms
UnbondingTime time.Duration `json:"unbonding_time"`
MaxValidators uint16 `json:"max_validators"` // maximum number of validators
@ -51,10 +41,6 @@ type Params struct {
// Implements params.ParamSet
func (p *Params) KeyValuePairs() params.KeyValuePairs {
return params.KeyValuePairs{
{KeyInflationRateChange, &p.InflationRateChange},
{KeyInflationMax, &p.InflationMax},
{KeyInflationMin, &p.InflationMin},
{KeyGoalBonded, &p.GoalBonded},
{KeyUnbondingTime, &p.UnbondingTime},
{KeyMaxValidators, &p.MaxValidators},
{KeyBondDenom, &p.BondDenom},
@ -71,13 +57,9 @@ func (p Params) Equal(p2 Params) bool {
// DefaultParams returns a default set of parameters.
func DefaultParams() Params {
return Params{
InflationRateChange: sdk.NewDecWithPrec(13, 2),
InflationMax: sdk.NewDecWithPrec(20, 2),
InflationMin: sdk.NewDecWithPrec(7, 2),
GoalBonded: sdk.NewDecWithPrec(67, 2),
UnbondingTime: defaultUnbondingTime,
MaxValidators: 100,
BondDenom: "steak",
UnbondingTime: defaultUnbondingTime,
MaxValidators: 100,
BondDenom: "steak",
}
}
@ -85,11 +67,7 @@ func DefaultParams() Params {
// parameters.
func (p Params) HumanReadableString() string {
resp := "Pool \n"
resp += fmt.Sprintf("Maximum Annual Inflation Rate Change: %s\n", p.InflationRateChange)
resp += fmt.Sprintf("Max Inflation Rate: %s\n", p.InflationMax)
resp += fmt.Sprintf("Min Inflation Tate: %s\n", p.InflationMin)
resp += fmt.Sprintf("Bonded Token Goal (%s): %s\n", "s", p.GoalBonded)
resp := "Params \n"
resp += fmt.Sprintf("Unbonding Time: %s\n", p.UnbondingTime)
resp += fmt.Sprintf("Max Validators: %d: \n", p.MaxValidators)
resp += fmt.Sprintf("Bonded Coin Denomination: %s\n", p.BondDenom)

View File

@ -3,7 +3,6 @@ package types
import (
"bytes"
"fmt"
"time"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -11,15 +10,8 @@ import (
// Pool - dynamic parameters of the current state
type Pool struct {
LooseTokens sdk.Dec `json:"loose_tokens"` // tokens which are not bonded in a validator
BondedTokens sdk.Dec `json:"bonded_tokens"` // reserve of bonded tokens
InflationLastTime time.Time `json:"inflation_last_time"` // block which the last inflation was processed
Inflation sdk.Dec `json:"inflation"` // current annual inflation rate
DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily)
// Fee Related
PrevBondedShares sdk.Dec `json:"prev_bonded_shares"` // last recorded bonded shares - for fee calculations
LooseTokens sdk.Dec `json:"loose_tokens"` // tokens which are not bonded in a validator
BondedTokens sdk.Dec `json:"bonded_tokens"` // reserve of bonded tokens
}
// nolint
@ -32,12 +24,8 @@ func (p Pool) Equal(p2 Pool) bool {
// initial pool for testing
func InitialPool() Pool {
return Pool{
LooseTokens: sdk.ZeroDec(),
BondedTokens: sdk.ZeroDec(),
InflationLastTime: time.Unix(0, 0),
Inflation: sdk.NewDecWithPrec(7, 2),
DateLastCommissionReset: 0,
PrevBondedShares: sdk.ZeroDec(),
LooseTokens: sdk.ZeroDec(),
BondedTokens: sdk.ZeroDec(),
}
}
@ -79,52 +67,6 @@ func (p Pool) bondedTokensToLoose(bondedTokens sdk.Dec) Pool {
return p
}
//_______________________________________________________________________
// Inflation
const precision = 10000 // increased to this precision for accuracy
var hrsPerYrDec = sdk.NewDec(8766) // as defined by a julian year of 365.25 days
// process provisions for an hour period
func (p Pool) ProcessProvisions(params Params) Pool {
p.Inflation = p.NextInflation(params)
provisions := p.Inflation.
Mul(p.TokenSupply()).
Quo(hrsPerYrDec)
// TODO add to the fees provisions
p.LooseTokens = p.LooseTokens.Add(provisions)
return p
}
// get the next inflation rate for the hour
func (p Pool) NextInflation(params Params) (inflation sdk.Dec) {
// The target annual inflation rate is recalculated for each previsions cycle. The
// inflation is also subject to a rate change (positive or negative) depending on
// the distance from the desired ratio (67%). The maximum rate change possible is
// defined to be 13% per year, however the annual inflation is capped as between
// 7% and 20%.
// (1 - bondedRatio/GoalBonded) * InflationRateChange
inflationRateChangePerYear := sdk.OneDec().
Sub(p.BondedRatio().
Quo(params.GoalBonded)).
Mul(params.InflationRateChange)
inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYrDec)
// increase the new annual inflation for this next cycle
inflation = p.Inflation.Add(inflationRateChange)
if inflation.GT(params.InflationMax) {
inflation = params.InflationMax
}
if inflation.LT(params.InflationMin) {
inflation = params.InflationMin
}
return inflation
}
// HumanReadableString returns a human readable string representation of a
// pool.
func (p Pool) HumanReadableString() string {
@ -134,10 +76,6 @@ func (p Pool) HumanReadableString() string {
resp += fmt.Sprintf("Bonded Tokens: %s\n", p.BondedTokens)
resp += fmt.Sprintf("Token Supply: %s\n", p.TokenSupply())
resp += fmt.Sprintf("Bonded Ratio: %v\n", p.BondedRatio())
resp += fmt.Sprintf("Previous Inflation Block: %s\n", p.InflationLastTime)
resp += fmt.Sprintf("Inflation: %v\n", p.Inflation)
resp += fmt.Sprintf("Date of Last Commission Reset: %d\n", p.DateLastCommissionReset)
resp += fmt.Sprintf("Previous Bonded Shares: %v\n", p.PrevBondedShares)
return resp
}

View File

@ -3,7 +3,6 @@ package types
import (
"fmt"
"testing"
"time"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -185,10 +184,8 @@ func TestRemoveDelShares(t *testing.T) {
DelegatorShares: delShares,
}
pool := Pool{
BondedTokens: sdk.NewDec(248305),
LooseTokens: sdk.NewDec(232147),
InflationLastTime: time.Unix(0, 0),
Inflation: sdk.NewDecWithPrec(7, 2),
BondedTokens: sdk.NewDec(248305),
LooseTokens: sdk.NewDec(232147),
}
shares := sdk.NewDec(29)
_, newPool, tokens := validator.RemoveDelShares(pool, shares)
@ -238,10 +235,8 @@ func TestPossibleOverflow(t *testing.T) {
DelegatorShares: delShares,
}
pool := Pool{
LooseTokens: sdk.NewDec(100),
BondedTokens: poolTokens,
InflationLastTime: time.Unix(0, 0),
Inflation: sdk.NewDecWithPrec(7, 2),
LooseTokens: sdk.NewDec(100),
BondedTokens: poolTokens,
}
tokens := int64(71)
msg := fmt.Sprintf("validator %#v", validator)