mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 06:07:26 +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