mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-24 23:35:19 +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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"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
|
||||
// on the simulation
|
||||
func ParamChanges(r *rand.Rand) []simulation.ParamChange {
|
||||
// TODO implement this
|
||||
return []simulation.ParamChange{}
|
||||
// Hacky way to validate periods since validation is wrapped in params
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
func (pr Period) String() string {
|
||||
return fmt.Sprintf(`Period:
|
||||
|
Loading…
Reference in New Issue
Block a user