feat(inflation): Implement Akash custom inflation function according to the whitepaper.#1352
feat(inflation): Implement Akash custom inflation function according to the whitepaper.#1352
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1352 +/- ##
==========================================
+ Coverage 38.23% 42.94% +4.71%
==========================================
Files 347 276 -71
Lines 20047 15528 -4519
==========================================
- Hits 7664 6668 -996
+ Misses 11552 8115 -3437
+ Partials 831 745 -86 |
app/app.go
Outdated
| panic(err) | ||
| } | ||
| minInflation := minter.Inflation.Sub(minter.Inflation.Mul(r)) | ||
| maxInflation := minter.Inflation.Add(minter.Inflation.Mul(r)) |
There was a problem hiding this comment.
There are more parameters to consider. In the default cosmos inflation calculation , there is:
- current bonded ratio
- target bonded ratio
- max inflation rate change
We should continue to use these. My thought was to have:
I(t)is the "ideal" inflation:ideal_inflation.- If
bonded_ratio=desired_ratiothentarget_inflation=ideal_inflation. - If
bonded_ratio<desired_ratiothentarget_inflation=ideal_inflation + adjustment. - If
bonded_ratio>desired_ratiothentarget_inflation=ideal_inflation - adjustment. max_inflation=I(t)*(1 + 0.05),min_inflation=I(t)*(1 - 0.05)- Change
current_inflationtotarget_inflation, bound by bothmax_inflation_changeand{min,max}_inflation.
Is minter.Inflation eventually set to the return value of this function?
There was a problem hiding this comment.
We should continue to use these. My thought was to have:
I(t)is the "ideal" inflation:ideal_inflation.- If
bonded_ratio=desired_ratiothentarget_inflation=ideal_inflation.- If
bonded_ratio<desired_ratiothentarget_inflation=ideal_inflation + adjustment.- If
bonded_ratio>desired_ratiothentarget_inflation=ideal_inflation - adjustment.
By adjustment did you mean inflationRateChange as in the new commit?
Is
minter.Inflationeventually set to the return value of this function?
Yes, that's why I am calculating {min, max}_inflation based on minter.Inflation instead of ideal_inflation. Otherwise, there seems no point of applying {min, max} logic using ideal_inflation as the base for calculating {min, max}.
There was a problem hiding this comment.
I guess the issue I have is: does using idealInflation + inflationRateChange overly restrict the possible range? If so then the entire range of acceptable inflation rates isn't available.
I think that we should use idealInflation to get [max,min], and calculate currentInflation = minter.Inflation + inflationRateChange. This way it will continue moving until an acceptable bonded ratio is found (in theory).
We also need to make a minimum minInflation - it can be 0 for now.
There was a problem hiding this comment.
I think that we should use
idealInflationto get[max,min], and calculatecurrentInflation = minter.Inflation + inflationRateChange. This way it will continue moving until an acceptable bonded ratio is found (in theory).We also need to make a minimum
minInflation- it can be0for now.
Done
app/app.go
Outdated
|
|
||
| func init() { | ||
| var err error | ||
| genesisTimeStr := "2020-09-25T14:00:00Z" |
There was a problem hiding this comment.
Might want to make this a var so that it can be set at build time. Would prefer to not have a global genesisTime, but it's okay for now.
|
Marked as stale; will be closed in five days. Cut bait or go fishing! |
2e40623 to
539b9a9
Compare
go.mod
Outdated
|
|
||
| replace ( | ||
| github.com/cosmos/cosmos-sdk => github.com/ovrclk/cosmos-sdk v0.43.0-patches | ||
| github.com/cosmos/cosmos-sdk => github.com/ovrclk/cosmos-sdk v0.42.0-alpha1.0.20210909100325-11e54c8fc931 |
There was a problem hiding this comment.
The latest master uses v0.44.1-patches branch. Should we wait for cosmos/cosmos-sdk#10441 to get released or should we just make the changes required for custom inflation function in our v0.44.1-patches branch so that we can go ahead with merging this PR?
There was a problem hiding this comment.
Hmm, let's carry the patch over into v0.44.4-patches for now.
boz
left a comment
There was a problem hiding this comment.
Looking good. Some comments inline.
app/app.go
Outdated
| func init() { | ||
| var err error | ||
|
|
||
| genesisTime, err = time.Parse(time.RFC3339, GenesisTimeStr) |
There was a problem hiding this comment.
This should be passed in; it's defined in the genesis and will be different for different chains (testnet, etc...)
There was a problem hiding this comment.
Done. Reading from genesis file now.
app/app.go
Outdated
|
|
||
| func inflationCalculator(ctx sdk.Context, minter minttypes.Minter, params minttypes.Params, bondedRatio sdk.Dec) sdk.Dec { | ||
| i0 := 100.0 | ||
| t := ctx.BlockTime().Sub(genesisTime).Seconds() / (60 * 60 * 8766) // years passed |
There was a problem hiding this comment.
Should t be an integer number of years since genesis?
It's hard to understand where the 60*60*8766 comes from.
I think we should use days since genesis.
There was a problem hiding this comment.
Both t and tHalf have been defined in years in the whitepaper. It doesn't matter whether we use days or years to represent both of them, we are finally calculating t/tHalf which is a float value with no units.
Added an explanation for 60*60*8766
There was a problem hiding this comment.
But t will be a severe step function at each year passing, instead of a more incremental daily increase.
There was a problem hiding this comment.
No, t is a float value. It isn't an int, so it won't be a step function. Float value will increase continuously, unlike int.
i.e., t can represent 0.5 years. It won't go to 1 year directly from 0 years.
app/app.go
Outdated
| const ( | ||
| AppName = "akash" | ||
|
|
||
| tHalf = 2 // years |
There was a problem hiding this comment.
This should be an on-chain parameter.
There was a problem hiding this comment.
Made it a parameter of deployment module. There is no way to extend the parameters of the mint module.
It is not a clean way of handling this parameter, but the other way would mean creating a new module just to store this parameter.
app/app.go
Outdated
| } | ||
|
|
||
| func inflationCalculator(ctx sdk.Context, minter minttypes.Minter, params minttypes.Params, bondedRatio sdk.Dec) sdk.Dec { | ||
| i0 := 100.0 |
There was a problem hiding this comment.
this is unnecessary; use a comment with 100.0 when it's used.
boz
left a comment
There was a problem hiding this comment.
Looking great.
Let's not pollute x/deployment - make a new module for it (x/inflation), and put the actual inflation calculation in there as well.
I commented inline re a couple more parameters that we should make driven by governance.
go.mod
Outdated
|
|
||
| replace ( | ||
| github.com/cosmos/cosmos-sdk => github.com/ovrclk/cosmos-sdk v0.43.0-patches | ||
| github.com/cosmos/cosmos-sdk => github.com/ovrclk/cosmos-sdk v0.42.0-alpha1.0.20210909100325-11e54c8fc931 |
There was a problem hiding this comment.
Hmm, let's carry the patch over into v0.44.4-patches for now.
app/app.go
Outdated
| // Number of seconds in a minute = 60 | ||
| // => 60 * 60 * 8766 = Number of seconds per year | ||
| t := ctx.BlockTime().Sub(genesisTime).Seconds() / (60 * 60 * 8766) // years passed | ||
| i := 100.0 * math.Pow(2, -t/tHalf) // 100 = initial inflation (i0) |
There was a problem hiding this comment.
Initial inflation was not 100%. Let's make this a parameter as well.
app/app.go
Outdated
|
|
||
| // min, max currentInflation based on a defined range parameter 'r' | ||
| // currentInflation range = [I(t) - I(t) * R, I(t) + I(t) * R] | ||
| r, err := sdk.NewDecFromStr("0.05") // let's say we allow 5% variance |
There was a problem hiding this comment.
Let's make the variance a parameter too.
1d68f92 to
c65f9fe
Compare
de7ebee to
349cdbc
Compare
This lint failure can't be fixed. The function from cosmos-sdk that we need to use, accepts only |
you can disable the check for that line with |
|
Please rebase against master. |
349cdbc to
be8972a
Compare
Thanks, Done. |
app/app.go
Outdated
| if v := appOpts.Get("GenesisTime"); v != nil { | ||
| // in tests, GenesisTime is supplied using appOpts | ||
| genTime, ok := v.(time.Time) | ||
| if !ok { | ||
| panic("expected GenesisTime to be a Time value") | ||
| } | ||
| inflationtypes.GenesisTime = genTime | ||
| } else if genDoc, err := tmtypes.GenesisDocFromFile(filepath.Join(homePath, "config/genesis.json")); err != nil { | ||
| panic(err) | ||
| } else { | ||
| inflationtypes.GenesisTime = genDoc.GenesisTime | ||
| } |
There was a problem hiding this comment.
Put this in a helper function that returns the genesis time.
Please don't use global variables for state. Find another way to pass the parameters into the inflation calculation.
| InflationParamSubspace paramstypes.Subspace | ||
| ) | ||
|
|
||
| func InflationCalculator(ctx sdk.Context, minter minttypes.Minter, params minttypes.Params, bondedRatio sdk.Dec) sdk.Dec { |
There was a problem hiding this comment.
agreed. in particular, rounding issues will likely cause different nodes to produce different values.
| var inflationParams Params | ||
| InflationParamSubspace.GetParamSet(ctx, &inflationParams) | ||
| tHalf := float64(inflationParams.InflationDecayFactor) | ||
| initialInflation, err := strconv.ParseFloat(inflationParams.InitialInflation, 32) |
There was a problem hiding this comment.
Params should be represented by the appropriate type here. See how x/deploy does it here.
I agree with @hydrogen18 - we should use a decimal or rational here.
| // Number of minutes in an hour = 60 | ||
| // Number of seconds in a minute = 60 | ||
| // => 60 * 60 * 8766 = Number of seconds per year | ||
| t := ctx.BlockTime().Sub(GenesisTime).Seconds() / (60 * 60 * 8766) // years passed |
There was a problem hiding this comment.
Yes, I believe it's equivalent. I can double check.
I still think that the above calculation should not use integer division - it will be 0 for a whole year, then 1 for a year, then 2, etc..., causing unnecessary and unwanted discontinuities on the anniversary of genesis.
| // => 60 * 60 * 8766 = Number of seconds per year | ||
| t := ctx.BlockTime().Sub(GenesisTime).Seconds() / (60 * 60 * 8766) // years passed | ||
| i := initialInflation * math.Pow(2, -t/tHalf) | ||
| idealInflation := sdk.NewDec(int64(i)) |
There was a problem hiding this comment.
I don't think that there is any loss of precision from int to decimal.
I do think that we should use a consistent type from the outset of this whole thing - rational and decimal for arbitrary precision, then truncate as necessary at the end (or continuously via some large fixed precision).
x/inflation/types/v1beta2/params.go
Outdated
| DefaultInitialInflation string = "100.0" | ||
| DefaultVarince string = "0.05" |
There was a problem hiding this comment.
Make these sdk.Decimal or even integers that are used to populate a decimal later. Maybe look at how some other parameters in cosmos-sdk are implemented.
20f6a63 to
e37f306
Compare
x/inflation/types/v1beta2/params.go
Outdated
| DefaultInitialInflation = sdk.NewDec(100) | ||
| DefaultVarince = sdk.MustNewDecFromStr("0.05") | ||
|
|
||
| MaxInitialInflation = sdk.NewDec(100) | ||
| MinInitialInflation = sdk.ZeroDec() | ||
|
|
||
| MaxVariance = sdk.NewDec(1) | ||
| MinVariance = sdk.ZeroDec() |
There was a problem hiding this comment.
Make these functions so that they're constant-like.
| t := ctx.BlockTime().Sub(genesisTime).Seconds() / 31557600 // years passed (can be a fraction, eg: 0.5) | ||
| idealInflation := inflationParams.InitialInflation.Mul(sdk.MustNewDecFromStr( | ||
| strconv.FormatFloat(math.Pow(2, -t/tHalf), 'f', -1, 64), | ||
| )) |
There was a problem hiding this comment.
Use sdk.Dec or standard big.*.
There was a problem hiding this comment.
There's no Pow() equivalent on sdk.Dec or big.* that accepts a fraction. That's why using this way of formatting the Pow() float value to a string and parsing a sdk.Dec from that.
There was a problem hiding this comment.
Done. Using: https://github.com/ericlagergren/decimal
The option is used to to supply the genesis time, only to make unit tests pass. Actual validators would have the genesis time in the config file, and that should be same for all validators, I think. |
Can't the tests just assume some random arbitrary genesis time then? Just pick a time that makes sense, i.e. sometime in 2018 or something? |
|
There's also this, which looks like it's more actively maintained: https://github.com/ericlagergren/decimal |
No, having a random genesis time doesn't work with tests. They start failing in that case. Seems, there are some parts in tests which depend on the genesis time being correctly set as per the test seed. |
| ), | ||
| ), | ||
| ) | ||
| idealInflation := inflationParams.InitialInflation.Mul(sdk.MustNewDecFromStr(pow.String())) |
There was a problem hiding this comment.
inflationParams.InitialInflation is a sdk.Dec. sdk.Dec has 18 precision digits.
pow is a decimal.Big. I am using the same precision of 18 digits for decimal.Big everywhere.
So, converting pow to string and parsing it into sdk.Dec won't lose any precision.
Could you please elaborate why and where should we use Int()/Float()? Also, why and where we need to multiply and divide by precision?
There was a problem hiding this comment.
We don't need that much precision, especially for the final result.
If the result is less than one and we use 6 units of precision then you can make an sdk.Decimal by with something like:
sdk.Decimal(big.Int(val * 10^6)) / 10^6
| (gogoproto.moretags) = "yaml:\"initial_inflation\"" | ||
| ]; | ||
|
|
||
| // Variance defines the fraction by which inflation can vary from its previous value in a block. |
There was a problem hiding this comment.
Isn't it how much it can vary from the ideal inflation curve?
There was a problem hiding this comment.
Yes, updated comment.
| } | ||
|
|
||
| func validateInitialInflation(i interface{}) error { | ||
| v, ok := i.(sdk.Dec) |
There was a problem hiding this comment.
Will this work if the param type is a string? https://github.com/ovrclk/akash/pull/1352/files#diff-bbf326520f4fa8aa2e6f40542d4dfb257fe6bc3233f0d5b382b7021416619d3bR18
There was a problem hiding this comment.
That is a string only in proto definition. In Go code, it maps to sdk.Dec because of this configured option in the proto definition:
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
This is how other modules in cosmos-sdk are mapping their parameters to sdk.Dec.
If that is the case then can |
review comments review comments rebase cosmos-sdk patches review comment address comments
1cfa4ad to
93b2b94
Compare
No, it doesn't seem feasible. There are existing test utility functions in non |
boz
left a comment
There was a problem hiding this comment.
Looks great!
We'll have to adjust some of the defaults to make sure it matches mainnet, but this is good to go for now.
|
The e2e tests are failing with @troian is this something added with storage? |
|
@hydrogen18 correct, i’ll give it a check why it updated multiple times |
Description
Motivation and Context
design/approvedlabelHow Has This Been Tested?
Types of changes
Checklist:
git commit -s