Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: proof-of-concept of using the standard go fuzzing during the executing of the simapp simulation #18946

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions simapp/sim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"runtime/debug"
"strings"
"testing"
"encoding/binary"

abci "github.com/cometbft/cometbft/abci/types"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
Expand Down Expand Up @@ -56,6 +57,104 @@ func interBlockCacheOpt() func(*baseapp.BaseApp) {
return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager())
}

func FuzzFullAppSimulation(f *testing.F) {
f.Fuzz(func(t *testing.T, raw_seeds [] byte) {
config := simcli.NewConfigFromFlags()
var seeds []int64
for {
if (len(raw_seeds) < 8) {
break
}

seeds = append(seeds, int64(binary.BigEndian.Uint64(raw_seeds)))
raw_seeds = raw_seeds[8:]
}

if len(seeds) == 0 {
return
}

config.Seeds = seeds
config.ChainID = SimAppChainID

appOptions := make(simtestutil.AppOptionsMap, 0)
appOptions[flags.FlagHome] = DefaultNodeHome
appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue
db := dbm.NewMemDB()
logger := log.NewNopLogger()

app := NewSimApp(logger, db, nil, true, appOptions, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID))
require.Equal(t, "SimApp", app.Name())

defer func() {
if r := recover(); r != nil {
err := fmt.Sprintf("%v", r)
shouldPanic := true
if strings.Contains(err, "validator set is empty after InitGenesis") {
shouldPanic = false
}

if strings.Contains(err, "duplicate account in genesis state") {
shouldPanic = false
}

if strings.Contains(err, "group policies: unique constraint violation") {
shouldPanic = false
}

if strings.Contains(err, "nft class already exists") {
shouldPanic = false
}

if strings.Contains(err, "nft already exists") {
shouldPanic = false
}

if strings.Contains(err, "invalid coins") {
shouldPanic = false
}

if strings.Contains(err, "invalid argument to Int63n") {
shouldPanic = false
}

if strings.Contains(err, "v.DelegatorShares is zero") {
shouldPanic = false
}

if strings.Contains(err, "group: not found") {
shouldPanic = false
}

if shouldPanic {
panic(r)
}

logger.Info("Skipping simulation as all validators have been unbonded")
logger.Info("err", err, "stacktrace", string(debug.Stack()))
}
}()

// run randomized simulation
_,_, err := simulation.SimulateFromSeed(
t,
os.Stdout,
app.BaseApp,
simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
simtestutil.SimulationOperations(app, app.AppCodec(), config),
BlockedAddresses(),
config,
app.AppCodec(),
)

if err != nil {
panic(err)
}
})
}


func TestFullAppSimulation(t *testing.T) {
config := simcli.NewConfigFromFlags()
config.ChainID = SimAppChainID
Expand Down
1 change: 1 addition & 0 deletions types/simulation/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Config struct {
ExportStatsPath string // custom file path to save the exported simulation statistics JSON

Seed int64 // simulation random seed
Seeds [] int64 // Added
InitialBlockHeight int // initial block to start the simulation
GenesisTime int64 // genesis time to start the simulation
NumBlocks int // number of new blocks to simulate from the initial block height
Expand Down
9 changes: 1 addition & 8 deletions types/simulation/rand_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,7 @@ func RandSubsetCoins(r *rand.Rand, coins sdk.Coins) sdk.Coins {
//
// NOTE: not crypto safe.
func DeriveRand(r *rand.Rand) *rand.Rand {
const num = 8 // TODO what's a good number? Too large is too slow.
ms := multiSource(make([]rand.Source, num))

for i := 0; i < num; i++ {
ms[i] = rand.NewSource(r.Int63())
}

return rand.New(ms)
return r
}

type multiSource []rand.Source
Expand Down
47 changes: 44 additions & 3 deletions x/simulation/simulate.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,39 @@ import (

const AverageBlockTime = 6 * time.Second

const (
rngMax = 1 << 63
rngMask = rngMax - 1
)

type arraySource struct {
pos int
arr []int64
src *rand.Rand
}

// Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
func (rng *arraySource) Int63() int64 {
return int64(rng.Uint64() & rngMask)
}

// Uint64 returns a non-negative pseudo-random 64-bit integer as an uint64.
func (rng *arraySource) Uint64() uint64 {
if (rng.pos >= len(rng.arr)) {
return rng.src.Uint64()
}
val := rng.arr[rng.pos]
rng.pos = rng.pos + 1
if val < 0 {
return uint64(-val)
}

return uint64(val)
}

func (rng *arraySource) Seed(seed int64) {}


// initialize the chain for the simulation
func initChain(
r *rand.Rand,
Expand Down Expand Up @@ -71,7 +104,12 @@ func SimulateFromSeed(
// in case we have to end early, don't os.Exit so that we can run cleanup code.
testingMode, _, b := getTestingMode(tb)

r := rand.New(rand.NewSource(config.Seed))
//r := rand.New(rand.NewSource(config.Seed))
var rng arraySource
rng.arr = config.Seeds
rng.src = rand.New(rand.NewSource(config.Seed))
r := rand.New(&rng)

params := RandomParams(r)

fmt.Fprintf(w, "Starting SimulateFromSeed with randomness created with seed %d\n", int(config.Seed))
Expand Down Expand Up @@ -339,17 +377,20 @@ func createBlockSimulator(tb testing.TB, testingMode bool, w io.Writer, params P
op, r2 := opAndR.op, opAndR.rand
opMsg, futureOps, err := op(r2, app, ctx, accounts, config.ChainID)
opMsg.LogEvent(event)
fmt.Printf("\n Executing: %s\n", opMsg.String())

if !config.Lean || opMsg.OK {
logWriter.AddEntry(MsgEntry(header.Height, int64(i), opMsg))
}

if err != nil {
logWriter.PrintLogs()
tb.Fatalf(`error on block %d/%d, operation (%d/%d) from x/%s:
if opMsg.OK {
tb.Fatalf(`error on block %d/%d, operation (%d/%d) from x/%s:
%v
Comment: %s`,
header.Height, config.NumBlocks, opCount, blocksize, opMsg.Route, err, opMsg.Comment)
header.Height, config.NumBlocks, opCount, blocksize, opMsg.Route, err, opMsg.String())
}
}

queueOperations(operationQueue, timeOperationQueue, futureOps)
Expand Down
3 changes: 3 additions & 0 deletions x/staking/types/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ func (v Validator) TokensFromShares(shares math.LegacyDec) math.LegacyDec {

// calculate the token worth of provided shares, truncated
func (v Validator) TokensFromSharesTruncated(shares math.LegacyDec) math.LegacyDec {
if (v.DelegatorShares.IsZero()) {
panic("v.DelegatorShares is zero");
}
return (shares.MulInt(v.Tokens)).QuoTruncate(v.DelegatorShares)
}

Expand Down
Loading