-
Notifications
You must be signed in to change notification settings - Fork 215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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_ratio
thentarget_inflation=ideal_inflation
. - If
bonded_ratio<desired_ratio
thentarget_inflation=ideal_inflation + adjustment
. - If
bonded_ratio>desired_ratio
thentarget_inflation=ideal_inflation - adjustment
. max_inflation=I(t)*(1 + 0.05)
,min_inflation=I(t)*(1 - 0.05)
- Change
current_inflation
totarget_inflation
, bound by bothmax_inflation_change
and{min,max}_inflation
.
Is minter.Inflation
eventually set to the return value of this function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should continue to use these. My thought was to have:
I(t)
is the "ideal" inflation:ideal_inflation
.- If
bonded_ratio=desired_ratio
thentarget_inflation=ideal_inflation
.- If
bonded_ratio<desired_ratio
thentarget_inflation=ideal_inflation + adjustment
.- If
bonded_ratio>desired_ratio
thentarget_inflation=ideal_inflation - adjustment
.
By adjustment
did you mean inflationRateChange
as in the new commit?
Is
minter.Inflation
eventually 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that we should use
idealInflation
to 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 be0
for now.
Done
app/app.go
Outdated
) | ||
|
||
func init() { | ||
var err error | ||
genesisTimeStr := "2020-09-25T14:00:00Z" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Marked as stale; will be closed in five days. Cut bait or go fishing! |
2e40623
to
539b9a9
Compare
go.mod
Outdated
@@ -52,7 +52,7 @@ require ( | |||
) | |||
|
|||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, let's carry the patch over into v0.44.4-patches
for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Reading from genesis file now.
app/app.go
Outdated
@@ -463,6 +479,45 @@ func NewApp( | |||
return app | |||
} | |||
|
|||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
@@ -102,6 +104,8 @@ import ( | |||
|
|||
const ( | |||
AppName = "akash" | |||
|
|||
tHalf = 2 // years |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be an on-chain parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
@@ -463,6 +479,45 @@ func NewApp( | |||
return app | |||
} | |||
|
|||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is unnecessary; use a comment with 100.0
when it's used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
@@ -52,7 +52,7 @@ require ( | |||
) | |||
|
|||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial inflation was not 100%. Let's make this a parameter as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make the variance a parameter too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make these functions so that they're constant-like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use sdk.Dec
or standard big.*
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
(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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it how much it can vary from the ideal inflation curve?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, updated comment.
} | ||
|
||
func validateInitialInflation(i interface{}) error { | ||
v, ok := i.(sdk.Dec) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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/approved
labelHow Has This Been Tested?
Types of changes
Checklist:
git commit -s