mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-25 15:55:18 +00:00
[WIP] Kavadist Simulations (#435)
* feat: kavadist sims * refactor genesis, add validation * implement params * rename simulation to genesis Co-authored-by: Kevin Davis <kjydavis3@gmail.com>
This commit is contained in:
parent
acc96952a7
commit
45e40fe357
84
x/kavadist/simulation/genesis.go
Normal file
84
x/kavadist/simulation/genesis.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/x/kavadist/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SecondsPerYear is the number of seconds in a year
|
||||||
|
const SecondsPerYear = 31536000
|
||||||
|
|
||||||
|
// BaseAprPadding prevents the calculated SPR inflation rate from being 0.0
|
||||||
|
const BaseAprPadding = "0.000000000100000000"
|
||||||
|
|
||||||
|
// RandomizedGenState generates a random GenesisState for kavadist module
|
||||||
|
func RandomizedGenState(simState *module.SimulationState) {
|
||||||
|
params := genRandomParams(simState)
|
||||||
|
if err := params.Validate(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
kavadistGenesis := types.NewGenesisState(params, types.DefaultPreviousBlockTime)
|
||||||
|
if err := kavadistGenesis.Validate(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, kavadistGenesis))
|
||||||
|
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(kavadistGenesis)
|
||||||
|
}
|
||||||
|
|
||||||
|
func genRandomParams(simState *module.SimulationState) types.Params {
|
||||||
|
periods := genRandomPeriods(simState.Rand, simState.GenTimestamp)
|
||||||
|
params := types.NewParams(true, periods)
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
func genRandomPeriods(r *rand.Rand, timestamp time.Time) types.Periods {
|
||||||
|
var periods types.Periods
|
||||||
|
numPeriods := simulation.RandIntBetween(r, 1, 10)
|
||||||
|
periodStart := timestamp
|
||||||
|
for i := 0; i < numPeriods; i++ {
|
||||||
|
// set periods to be between 2 weeks and 2 years
|
||||||
|
durationMultiplier := simulation.RandIntBetween(r, 14, 104)
|
||||||
|
duration := time.Duration(int64(24*durationMultiplier)) * time.Hour
|
||||||
|
periodEnd := periodStart.Add(duration)
|
||||||
|
inflation := genRandomInflation(r)
|
||||||
|
period := types.NewPeriod(periodStart, periodEnd, inflation)
|
||||||
|
periods = append(periods, period)
|
||||||
|
periodStart = periodEnd
|
||||||
|
}
|
||||||
|
return periods
|
||||||
|
}
|
||||||
|
|
||||||
|
func genRandomInflation(r *rand.Rand) sdk.Dec {
|
||||||
|
// If sim.RandomDecAmount returns 0 (happens frequently by design), add BaseAprPadding
|
||||||
|
extraAprInflation := simulation.RandomDecAmount(r, sdk.MustNewDecFromStr("0.25"))
|
||||||
|
for extraAprInflation.Equal(sdk.ZeroDec()) {
|
||||||
|
extraAprInflation = extraAprInflation.Add(sdk.MustNewDecFromStr(BaseAprPadding))
|
||||||
|
}
|
||||||
|
|
||||||
|
aprInflation := sdk.OneDec().Add(extraAprInflation)
|
||||||
|
// convert APR inflation to SPR (inflation per second)
|
||||||
|
inflationSpr, err := approxRoot(aprInflation, uint64(SecondsPerYear))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("error generating random inflation %v", err))
|
||||||
|
}
|
||||||
|
return inflationSpr
|
||||||
|
}
|
||||||
|
|
||||||
|
func genRandomActive(r *rand.Rand) bool {
|
||||||
|
threshold := 50
|
||||||
|
value := simulation.RandIntBetween(r, 1, 100)
|
||||||
|
if value > threshold {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -1,14 +1,34 @@
|
|||||||
package simulation
|
package simulation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/x/kavadist/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParamChanges defines the parameters that can be modified by param change proposals
|
// ParamChanges defines the parameters that can be modified by param change proposals
|
||||||
// on the simulation
|
// on the simulation
|
||||||
func ParamChanges(r *rand.Rand) []simulation.ParamChange {
|
func ParamChanges(r *rand.Rand) []simulation.ParamChange {
|
||||||
// TODO implement this
|
// Hacky way to validate periods since validation is wrapped in params
|
||||||
return []simulation.ParamChange{}
|
active := genRandomActive(r)
|
||||||
|
periods := genRandomPeriods(r, simulation.RandTimestamp(r))
|
||||||
|
if err := types.NewParams(active, periods).Validate(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []simulation.ParamChange{
|
||||||
|
simulation.NewSimParamChange(types.ModuleName, string(types.KeyActive), "",
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
return fmt.Sprintf("\"%t\"", active)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
simulation.NewSimParamChange(types.ModuleName, string(types.KeyPeriods), "",
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
return fmt.Sprintf("\"%v\"", periods)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package simulation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
|
||||||
|
|
||||||
"github.com/kava-labs/kava/x/kavadist/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RandomizedGenState generates a random GenesisState for cdp
|
|
||||||
func RandomizedGenState(simState *module.SimulationState) {
|
|
||||||
|
|
||||||
// TODO implement this fully
|
|
||||||
// - randomly generating the genesis params
|
|
||||||
// - overwriting with genesis provided to simulation
|
|
||||||
genesis := types.DefaultGenesisState()
|
|
||||||
|
|
||||||
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, genesis))
|
|
||||||
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesis)
|
|
||||||
}
|
|
64
x/kavadist/simulation/utils.go
Normal file
64
x/kavadist/simulation/utils.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO use the official version available in v0.38.2 of the sdk
|
||||||
|
// https://github.com/cosmos/cosmos-sdk/blob/e8d89a2fe26175b73545a3e79ae783032b4e975e/types/decimal.go#L328
|
||||||
|
func approxRoot(d sdk.Dec, root uint64) (guess sdk.Dec, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
var ok bool
|
||||||
|
err, ok = r.(error)
|
||||||
|
if !ok {
|
||||||
|
err = errors.New("out of bounds")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if d.IsNegative() {
|
||||||
|
absRoot, err := approxRoot(d.MulInt64(-1), root)
|
||||||
|
return absRoot.MulInt64(-1), err
|
||||||
|
}
|
||||||
|
if root == 1 || d.IsZero() || d.Equal(sdk.OneDec()) {
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
if root == 0 {
|
||||||
|
return sdk.OneDec(), nil
|
||||||
|
}
|
||||||
|
rootInt := sdk.NewInt(int64(root))
|
||||||
|
guess, delta := sdk.OneDec(), sdk.OneDec()
|
||||||
|
for delta.Abs().GT(sdk.SmallestDec()) {
|
||||||
|
prev := power(guess, (root - 1))
|
||||||
|
if prev.IsZero() {
|
||||||
|
prev = sdk.SmallestDec()
|
||||||
|
}
|
||||||
|
delta = d.Quo(prev)
|
||||||
|
delta = delta.Sub(guess)
|
||||||
|
delta = delta.QuoInt(rootInt)
|
||||||
|
|
||||||
|
guess = guess.Add(delta)
|
||||||
|
}
|
||||||
|
return guess, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Power returns a the result of raising to a positive integer power
|
||||||
|
func power(d sdk.Dec, power uint64) sdk.Dec {
|
||||||
|
if power == 0 {
|
||||||
|
return sdk.OneDec()
|
||||||
|
}
|
||||||
|
tmp := sdk.OneDec()
|
||||||
|
for i := power; i > 1; {
|
||||||
|
if i%2 == 0 {
|
||||||
|
i /= 2
|
||||||
|
} else {
|
||||||
|
tmp = tmp.Mul(d)
|
||||||
|
i = (i - 1) / 2
|
||||||
|
}
|
||||||
|
d = d.Mul(d)
|
||||||
|
}
|
||||||
|
return d.Mul(tmp)
|
||||||
|
}
|
30
x/kavadist/simulation/utils_test.go
Normal file
30
x/kavadist/simulation/utils_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestApproxRoot(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
input sdk.Dec
|
||||||
|
root uint64
|
||||||
|
expected sdk.Dec
|
||||||
|
}{
|
||||||
|
{sdk.OneDec(), 10, sdk.OneDec()}, // 1.0 ^ (0.1) => 1.0
|
||||||
|
{sdk.NewDecWithPrec(25, 2), 2, sdk.NewDecWithPrec(5, 1)}, // 0.25 ^ (0.5) => 0.5
|
||||||
|
{sdk.NewDecWithPrec(4, 2), 2, sdk.NewDecWithPrec(2, 1)}, // 0.04 => 0.2
|
||||||
|
{sdk.NewDecFromInt(sdk.NewInt(27)), 3, sdk.NewDecFromInt(sdk.NewInt(3))}, // 27 ^ (1/3) => 3
|
||||||
|
{sdk.NewDecFromInt(sdk.NewInt(-81)), 4, sdk.NewDecFromInt(sdk.NewInt(-3))}, // -81 ^ (0.25) => -3
|
||||||
|
{sdk.NewDecFromInt(sdk.NewInt(2)), 2, sdk.NewDecWithPrec(1414213562373095049, 18)}, // 2 ^ (0.5) => 1.414213562373095049
|
||||||
|
{sdk.NewDecWithPrec(1005, 3), 31536000, sdk.MustNewDecFromStr("1.000000000158153904")}, // 1.005 ^ (1/31536000) = 1.000000000158153904
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testCases {
|
||||||
|
res, err := approxRoot(tc.input, tc.root)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, tc.expected.Sub(res).Abs().LTE(sdk.SmallestDec()), "unexpected result for test case %d, input: %v", i, tc.input)
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,15 @@ type Period struct {
|
|||||||
Inflation sdk.Dec `json:"inflation" yaml:"inflation"` // example "1.000000003022265980" - 10% inflation
|
Inflation sdk.Dec `json:"inflation" yaml:"inflation"` // example "1.000000003022265980" - 10% inflation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPeriod returns a new instance of Period
|
||||||
|
func NewPeriod(start time.Time, end time.Time, inflation sdk.Dec) Period {
|
||||||
|
return Period{
|
||||||
|
Start: start,
|
||||||
|
End: end,
|
||||||
|
Inflation: inflation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// String implements fmt.Stringer
|
// String implements fmt.Stringer
|
||||||
func (pr Period) String() string {
|
func (pr Period) String() string {
|
||||||
return fmt.Sprintf(`Period:
|
return fmt.Sprintf(`Period:
|
||||||
|
Loading…
Reference in New Issue
Block a user