mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-27 16:55:21 +00:00
5bdffd5c1c
* first pass * fix bid amount calculation * untested refactor of sim ops and genesis * refactor operations and fix auction bug * add param changes and genesis * address minor TODO * add first draft of invariants * improve param generation * complete invariants * fix genesis tests * log no-op better * small fixes * add missed comma Co-authored-by: John Maheswaran <john@kava.io>
172 lines
6.0 KiB
Go
172 lines
6.0 KiB
Go
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/auth"
|
|
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
|
"github.com/cosmos/cosmos-sdk/x/supply"
|
|
|
|
"github.com/kava-labs/kava/x/auction/types"
|
|
cdptypes "github.com/kava-labs/kava/x/cdp/types"
|
|
)
|
|
|
|
const (
|
|
// Block time params are un-exported constants in cosmos-sdk/x/simulation.
|
|
// Copy them here in lieu of importing them.
|
|
minTimePerBlock time.Duration = (10000 / 2) * time.Second
|
|
maxTimePerBlock time.Duration = 10000 * time.Second
|
|
|
|
// Calculate the average block time
|
|
AverageBlockTime time.Duration = (maxTimePerBlock - minTimePerBlock) / 2
|
|
// MaxBidDuration is a crude way of ensuring that BidDuration ≤ MaxAuctionDuration for all generated params
|
|
MaxBidDuration time.Duration = AverageBlockTime * 50
|
|
)
|
|
|
|
func GenBidDuration(r *rand.Rand) time.Duration {
|
|
d, err := RandomPositiveDuration(r, 0, MaxBidDuration)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return d
|
|
}
|
|
func GenMaxAuctionDuration(r *rand.Rand) time.Duration {
|
|
d, err := RandomPositiveDuration(r, MaxBidDuration, AverageBlockTime*200)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return d
|
|
}
|
|
|
|
func GenIncrementCollateral(r *rand.Rand) sdk.Dec {
|
|
return simulation.RandomDecAmount(r, sdk.MustNewDecFromStr("1"))
|
|
}
|
|
|
|
var GenIncrementDebt = GenIncrementCollateral
|
|
var GenIncrementSurplus = GenIncrementCollateral
|
|
|
|
// RandomizedGenState generates a random GenesisState for auction
|
|
func RandomizedGenState(simState *module.SimulationState) {
|
|
|
|
p := types.NewParams(
|
|
GenMaxAuctionDuration(simState.Rand),
|
|
GenBidDuration(simState.Rand),
|
|
GenIncrementSurplus(simState.Rand),
|
|
GenIncrementDebt(simState.Rand),
|
|
GenIncrementCollateral(simState.Rand),
|
|
)
|
|
if err := p.Validate(); err != nil {
|
|
panic(err)
|
|
}
|
|
auctionGenesis := types.NewGenesisState(
|
|
types.DefaultNextAuctionID,
|
|
p,
|
|
nil,
|
|
)
|
|
|
|
// Add auctions
|
|
auctions := types.GenesisAuctions{
|
|
types.NewDebtAuction(
|
|
cdptypes.LiquidatorMacc, // using cdp account rather than generic test one to avoid having to set permissions on the supply keeper
|
|
sdk.NewInt64Coin("usdx", 100),
|
|
sdk.NewInt64Coin("ukava", 1000000000000),
|
|
simState.GenTimestamp.Add(time.Hour*5),
|
|
sdk.NewInt64Coin("debt", 100), // same as usdx
|
|
),
|
|
}
|
|
var startingID = auctionGenesis.NextAuctionID
|
|
var ok bool
|
|
var totalAuctionCoins sdk.Coins
|
|
for i, a := range auctions {
|
|
auctions[i], ok = a.WithID(uint64(i) + startingID).(types.GenesisAuction)
|
|
if !ok {
|
|
panic("can't convert Auction to GenesisAuction")
|
|
}
|
|
totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins())
|
|
}
|
|
auctionGenesis.NextAuctionID = startingID + uint64(len(auctions))
|
|
auctionGenesis.Auctions = append(auctionGenesis.Auctions, auctions...)
|
|
|
|
// Also need to update the auction module account (to reflect the coins held in the auctions)
|
|
var authGenesis auth.GenesisState
|
|
simState.Cdc.MustUnmarshalJSON(simState.GenState[auth.ModuleName], &authGenesis)
|
|
|
|
auctionModAcc, found := getAccount(authGenesis.Accounts, supply.NewModuleAddress(types.ModuleName))
|
|
if !found {
|
|
auctionModAcc = supply.NewEmptyModuleAccount(types.ModuleName)
|
|
}
|
|
if err := auctionModAcc.SetCoins(totalAuctionCoins); err != nil {
|
|
panic(err)
|
|
}
|
|
authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, auctionModAcc)
|
|
|
|
// TODO adding bidder coins as well - this should be moved elsewhere
|
|
bidder, found := getAccount(authGenesis.Accounts, simState.Accounts[0].Address) // 0 is the bidder // FIXME
|
|
if !found {
|
|
panic("bidder not found")
|
|
}
|
|
bidderCoins := sdk.NewCoins(sdk.NewInt64Coin("usdx", 10000000000))
|
|
if err := bidder.SetCoins(bidder.GetCoins().Add(bidderCoins)); err != nil {
|
|
panic(err)
|
|
}
|
|
authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, bidder)
|
|
|
|
simState.GenState[auth.ModuleName] = simState.Cdc.MustMarshalJSON(authGenesis)
|
|
|
|
// Update the supply genesis state to reflect the new coins
|
|
// TODO find some way for this to happen automatically / move it elsewhere
|
|
var supplyGenesis supply.GenesisState
|
|
simState.Cdc.MustUnmarshalJSON(simState.GenState[supply.ModuleName], &supplyGenesis)
|
|
supplyGenesis.Supply = supplyGenesis.Supply.Add(totalAuctionCoins).Add(bidderCoins)
|
|
simState.GenState[supply.ModuleName] = simState.Cdc.MustMarshalJSON(supplyGenesis)
|
|
|
|
// TODO liquidator mod account doesn't need to be initialized for this example
|
|
// - it just mints kava, doesn't need a starting balance
|
|
// - and supply.GetModuleAccount creates one if it doesn't exist
|
|
|
|
// Note: this line prints out the auction genesis state, not just the auction parameters. Some sdk modules print out just the parameters.
|
|
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, auctionGenesis))
|
|
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(auctionGenesis)
|
|
}
|
|
|
|
// Return an account from a list of accounts that matches an address.
|
|
func getAccount(accounts []authexported.GenesisAccount, addr sdk.AccAddress) (authexported.GenesisAccount, bool) {
|
|
for _, a := range accounts {
|
|
if a.GetAddress().Equals(addr) {
|
|
return a, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// In a list of accounts, replace the first account found with the same address. If not found, append the account.
|
|
func replaceOrAppendAccount(accounts []authexported.GenesisAccount, acc authexported.GenesisAccount) []authexported.GenesisAccount {
|
|
newAccounts := accounts
|
|
for i, a := range accounts {
|
|
if a.GetAddress().Equals(acc.GetAddress()) {
|
|
newAccounts[i] = acc
|
|
return newAccounts
|
|
}
|
|
}
|
|
return append(newAccounts, acc)
|
|
}
|
|
|
|
func RandomPositiveDuration(r *rand.Rand, inclusiveMin, exclusiveMax time.Duration) (time.Duration, error) {
|
|
min := int64(inclusiveMin)
|
|
max := int64(exclusiveMax)
|
|
if min < 0 || max < 0 {
|
|
return 0, fmt.Errorf("min and max must be positive")
|
|
}
|
|
if min >= max {
|
|
return 0, fmt.Errorf("max must be < min")
|
|
}
|
|
randPositiveInt64 := r.Int63n(max-min) + min
|
|
return time.Duration(randPositiveInt64), nil
|
|
}
|