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