-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: strict inflation schedule (#1578)
### Description Part of celestiaorg/celestia-app#1570 Implement the strict inflation schedule specified in celestiaorg/celestia-app#1584 ### Remaining tasks - [x] implement inflation rate change based on block timestamp rather than block height - [x] set genesis time in x/mint state - [x] move `MintDenom` from param to `x/mint` state - [x] remove `TestRandomizedGenState` - [x] investigate why `TestQueryGRPC` / other integration tests are never run - [x] write a test that 1. starts a new chain, 2. verifies state of x/mint module for first few blocks, 3. skips ahead to a block one year later, 4. verifies inflation rate went down a bit - [x] remove `Params` entirely - [x] annual provisions for a year should be based on total supply at the beginning of that year - [x] for time-based inflation, calculate block provision as current block timestamp - previous block timestamp = seconds elapsed / seconds in a year * annual provisions for a year - [x] remove `BlocksPerYear` - [x] elaborate in README.md ### FLUPs - celestiaorg/celestia-app#1755 - celestiaorg/celestia-app#1757 - celestiaorg/celestia-app#1758 - celestiaorg/celestia-app#1756 --------- Co-authored-by: Rootul P <rootulp@gmail.com> Co-authored-by: Evan Forbes <42654277+evan-forbes@users.noreply.github.com> Co-authored-by: Callum Waters <cmwaters19@gmail.com>
- Loading branch information
1 parent
fbe7cf7
commit e870412
Showing
36 changed files
with
4,220 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,5 +9,7 @@ coverage.txt | |
tools-stamp | ||
__debug_bin | ||
profile.out | ||
tmp/ | ||
run.sh | ||
testing/e2e/networks/*/ | ||
square/testdata |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
syntax = "proto3"; | ||
package celestia.mint.v1; | ||
|
||
import "gogoproto/gogo.proto"; | ||
import "celestia/mint/v1/mint.proto"; | ||
|
||
option go_package = "github.com/celestiaorg/celestia-app/x/mint/types"; | ||
|
||
// GenesisState defines the mint module's genesis state. | ||
message GenesisState { | ||
// minter is a space for holding current inflation information. | ||
Minter minter = 1 [(gogoproto.nullable) = false]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
syntax = "proto3"; | ||
package celestia.mint.v1; | ||
|
||
option go_package = "github.com/celestiaorg/celestia-app/x/mint/types"; | ||
|
||
import "gogoproto/gogo.proto"; | ||
import "cosmos_proto/cosmos.proto"; | ||
import "google/protobuf/timestamp.proto"; | ||
|
||
// Minter represents the mint state. | ||
message Minter { | ||
// InflationRate is the rate at which new tokens should be minted for the | ||
// current year. For example if InflationRate=0.1, then 10% of the total | ||
// supply will be minted over the course of the year. | ||
string inflation_rate = 1 [ | ||
(cosmos_proto.scalar) = "cosmos.Dec", | ||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", | ||
(gogoproto.nullable) = false | ||
]; | ||
// AnnualProvisions is the total number of tokens to be minted in the current | ||
// year due to inflation. | ||
string annual_provisions = 2 [ | ||
(cosmos_proto.scalar) = "cosmos.Dec", | ||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", | ||
(gogoproto.nullable) = false | ||
]; | ||
|
||
// GenesisTime is the timestamp of the genesis block. | ||
google.protobuf.Timestamp genesis_time = 3 [(gogoproto.stdtime) = true]; | ||
|
||
// PreviousBlockTime is the timestamp of the previous block. | ||
google.protobuf.Timestamp previous_block_time = 4 [(gogoproto.stdtime) = true]; | ||
|
||
// BondDenom is the denomination of the token that should be minted. | ||
string bond_denom = 5; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
syntax = "proto3"; | ||
package celestia.mint.v1; | ||
|
||
import "gogoproto/gogo.proto"; | ||
import "google/api/annotations.proto"; | ||
import "celestia/mint/v1/mint.proto"; | ||
import "google/protobuf/timestamp.proto"; | ||
|
||
option go_package = "github.com/celestiaorg/celestia-app/x/mint/types"; | ||
|
||
// Query defines the gRPC querier service. | ||
service Query { | ||
// InflationRate returns the current inflation rate. | ||
rpc InflationRate(QueryInflationRateRequest) returns (QueryInflationRateResponse) { | ||
option (google.api.http).get = "/cosmos/mint/v1beta1/inflation_rate"; | ||
} | ||
|
||
// AnnualProvisions returns the current annual provisions. | ||
rpc AnnualProvisions(QueryAnnualProvisionsRequest) returns (QueryAnnualProvisionsResponse) { | ||
option (google.api.http).get = "/cosmos/mint/v1beta1/annual_provisions"; | ||
} | ||
|
||
// GenesisTime returns the genesis time. | ||
rpc GenesisTime(QueryGenesisTimeRequest) returns (QueryGenesisTimeResponse) { | ||
option (google.api.http).get = "/cosmos/mint/v1beta1/genesis_time"; | ||
} | ||
} | ||
|
||
// QueryInflationRateRequest is the request type for the Query/InflationRate RPC method. | ||
message QueryInflationRateRequest {} | ||
|
||
// QueryInflationRateResponse is the response type for the Query/InflationRate RPC | ||
// method. | ||
message QueryInflationRateResponse { | ||
// InflationRate is the current inflation rate. | ||
bytes inflation_rate = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; | ||
} | ||
|
||
// QueryAnnualProvisionsRequest is the request type for the | ||
// Query/AnnualProvisions RPC method. | ||
message QueryAnnualProvisionsRequest {} | ||
|
||
// QueryAnnualProvisionsResponse is the response type for the | ||
// Query/AnnualProvisions RPC method. | ||
message QueryAnnualProvisionsResponse { | ||
// AnnualProvisions is the current annual provisions. | ||
bytes annual_provisions = 1 | ||
[(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; | ||
} | ||
|
||
// QueryGenesisTimeRequest is the request type for the Query/GenesisTime RPC method. | ||
message QueryGenesisTimeRequest {} | ||
|
||
// QueryGenesisTimeResponse is the response type for the Query/GenesisTime RPC method. | ||
message QueryGenesisTimeResponse { | ||
// GenesisTime is the timestamp associated with the first block. | ||
google.protobuf.Timestamp genesis_time = 1 [(gogoproto.stdtime) = true]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# `x/mint` | ||
|
||
`x/mint` is a fork of the Cosmos SDK `x/mint` module that makes some changes to the inflation mechanism. | ||
|
||
1. Remove all parameters from the module | ||
1. Calculate inflation rate once per year based on the number of years since genesis | ||
1. Calculate annual provisions once per year based on the total supply | ||
1. Calculate block provision once per block based on the number of seconds elapsed between the current block and the previous block | ||
|
||
## Constants | ||
|
||
See [./types/constants.go](./types/constants.go) for the constants used in this module. | ||
|
||
Note: this module assumes `DaysPerYear = 365.2425` so when modifying tests, developers must define durations based on this assumption because ordinary durations won't return the expected results. In other words: | ||
|
||
```go | ||
// oneYear is 31,556,952 seconds which will likely return expected results in tests | ||
oneYear, err := time.ParseDuration(fmt.Sprintf("%vs", minttypes.SecondsPerYear)) | ||
|
||
// this oneYear is 31,536,000 seconds which will likely return unexpected results in tests | ||
oneYear := time.Hour * 24 * 365 | ||
``` | ||
|
||
## Inflation Schedule | ||
|
||
| Year | Inflation (%) | | ||
|------|-------------------| | ||
| 0 | 8.00 | | ||
| 1 | 7.20 | | ||
| 2 | 6.48 | | ||
| 3 | 5.832 | | ||
| 4 | 5.2488 | | ||
| 5 | 4.72392 | | ||
| 6 | 4.251528 | | ||
| 7 | 3.8263752 | | ||
| 8 | 3.44373768 | | ||
| 9 | 3.099363912 | | ||
| 10 | 2.7894275208 | | ||
| 11 | 2.51048476872 | | ||
| 12 | 2.259436291848 | | ||
| 13 | 2.0334926626632 | | ||
| 14 | 1.83014339639688 | | ||
| 15 | 1.647129056757192 | | ||
| 16 | 1.50 | | ||
| 17 | 1.50 | | ||
| 18 | 1.50 | | ||
| 19 | 1.50 | | ||
| 20 | 1.50 | | ||
|
||
## References | ||
|
||
1. ADR-019 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package mint | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/celestiaorg/celestia-app/x/mint/keeper" | ||
"github.com/celestiaorg/celestia-app/x/mint/types" | ||
"github.com/cosmos/cosmos-sdk/telemetry" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
// BeginBlocker updates the inflation rate, annual provisions, and then mints | ||
// the block provision for the current block. | ||
func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { | ||
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) | ||
|
||
maybeSetGenesisTime(ctx, k) | ||
maybeUpdateMinter(ctx, k) | ||
mintBlockProvision(ctx, k) | ||
setPreviousBlockTime(ctx, k) | ||
} | ||
|
||
// maybeSetGenesisTime sets the genesis time if the current block height is 1. | ||
func maybeSetGenesisTime(ctx sdk.Context, k keeper.Keeper) { | ||
if ctx.BlockHeight() == 1 { | ||
genesisTime := ctx.BlockTime() | ||
minter := k.GetMinter(ctx) | ||
minter.GenesisTime = &genesisTime | ||
k.SetMinter(ctx, minter) | ||
} | ||
} | ||
|
||
// maybeUpdateMinter updates the inflation rate and annual provisions if the | ||
// inflation rate has changed. | ||
func maybeUpdateMinter(ctx sdk.Context, k keeper.Keeper) { | ||
minter := k.GetMinter(ctx) | ||
newInflationRate := minter.CalculateInflationRate(ctx) | ||
if newInflationRate == minter.InflationRate { | ||
// The minter's InflationRate AnnualProvisions already reflect the | ||
// values for this year. Exit early because we don't need to update | ||
// them. | ||
return | ||
} | ||
minter.InflationRate = newInflationRate | ||
k.SetMinter(ctx, minter) | ||
|
||
totalSupply := k.StakingTokenSupply(ctx) | ||
minter.AnnualProvisions = minter.CalculateAnnualProvisions(totalSupply) | ||
k.SetMinter(ctx, minter) | ||
} | ||
|
||
// mintBlockProvision mints the block provision for the current block. | ||
func mintBlockProvision(ctx sdk.Context, k keeper.Keeper) { | ||
minter := k.GetMinter(ctx) | ||
if minter.PreviousBlockTime == nil { | ||
// exit early if previous block time is nil | ||
// this is expected to happen for block height = 1 | ||
return | ||
} | ||
|
||
toMintCoin := minter.CalculateBlockProvision(ctx.BlockTime(), *minter.PreviousBlockTime) | ||
toMintCoins := sdk.NewCoins(toMintCoin) | ||
|
||
err := k.MintCoins(ctx, toMintCoins) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
err = k.SendCoinsToFeeCollector(ctx, toMintCoins) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
if toMintCoin.Amount.IsInt64() { | ||
defer telemetry.ModuleSetGauge(types.ModuleName, float32(toMintCoin.Amount.Int64()), "minted_tokens") | ||
} | ||
|
||
ctx.EventManager().EmitEvent( | ||
sdk.NewEvent( | ||
types.EventTypeMint, | ||
sdk.NewAttribute(types.AttributeKeyInflationRate, minter.InflationRate.String()), | ||
sdk.NewAttribute(types.AttributeKeyAnnualProvisions, minter.AnnualProvisions.String()), | ||
sdk.NewAttribute(sdk.AttributeKeyAmount, toMintCoin.Amount.String()), | ||
), | ||
) | ||
} | ||
|
||
func setPreviousBlockTime(ctx sdk.Context, k keeper.Keeper) { | ||
minter := k.GetMinter(ctx) | ||
blockTime := ctx.BlockTime() | ||
minter.PreviousBlockTime = &blockTime | ||
k.SetMinter(ctx, minter) | ||
} |
Oops, something went wrong.