fix(simulation): Fix all problems make test-sim-custom-genesis-fast for simulation test. (backport #17911) (#20909)

Co-authored-by: Chenqun Lu <luchenqun@qq.com>
Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
mergify[bot] 2024-07-11 08:28:37 +00:00 committed by GitHub
parent c51f58c799
commit 698627016d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 79 additions and 27 deletions

View File

@ -63,6 +63,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Bug Fixes
* (simulation) [#17911](https://github.com/cosmos/cosmos-sdk/pull/17911) Fix all problems with executing command `make test-sim-custom-genesis-fast` for simulation test.
* (baseapp) [#20346](https://github.com/cosmos/cosmos-sdk/pull/20346) Correctly assign `execModeSimulate` to context for `simulateTx`.
* (baseapp) [#20144](https://github.com/cosmos/cosmos-sdk/pull/20144) Remove txs from mempool when AnteHandler fails in recheck.
* (baseapp) [#20107](https://github.com/cosmos/cosmos-sdk/pull/20107) Avoid header height overwrite block height.

View File

@ -277,9 +277,9 @@ test-sim-nondeterminism-streaming:
test-sim-custom-genesis-fast:
@echo "Running custom genesis simulation..."
@echo "By default, ${HOME}/.gaiad/config/genesis.json will be used."
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -run TestFullAppSimulation -Genesis=${HOME}/.gaiad/config/genesis.json \
-Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h
@echo "By default, ${HOME}/.simapp/config/genesis.json will be used."
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -run TestFullAppSimulation -Genesis=${HOME}/.simapp/config/genesis.json \
-Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -SigverifyTx=false -v -timeout 24h
test-sim-import-export: runsim
@echo "Running application import/export simulation. This may take several minutes..."
@ -291,8 +291,8 @@ test-sim-after-import: runsim
test-sim-custom-genesis-multi-seed: runsim
@echo "Running multi-seed custom genesis simulation..."
@echo "By default, ${HOME}/.gaiad/config/genesis.json will be used."
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Genesis=${HOME}/.gaiad/config/genesis.json -SimAppPkg=. -ExitOnFail 400 5 TestFullAppSimulation
@echo "By default, ${HOME}/.simapp/config/genesis.json will be used."
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Genesis=${HOME}/.simapp/config/genesis.json -SigverifyTx=false -SimAppPkg=. -ExitOnFail 400 5 TestFullAppSimulation
test-sim-multi-seed-long: runsim
@echo "Running long multi-seed application simulation. This may take awhile!"

View File

@ -91,6 +91,7 @@ type BaseApp struct {
addrPeerFilter sdk.PeerFilter // filter peers by address and port
idPeerFilter sdk.PeerFilter // filter peers by node ID
fauxMerkleMode bool // if true, IAVL MountStores uses MountStoresDB for simulation speed.
sigverifyTx bool // in the simulation test, since the account does not have a private key, we have to ignore the tx sigverify.
// manages snapshots, i.e. dumps of app state at certain intervals
snapshotManager *snapshots.Manager
@ -211,6 +212,7 @@ func NewBaseApp(
msgServiceRouter: NewMsgServiceRouter(),
txDecoder: txDecoder,
fauxMerkleMode: false,
sigverifyTx: true,
queryGasLimit: math.MaxUint64,
}
@ -667,6 +669,8 @@ func (app *BaseApp) getContextForTx(mode execMode, txBytes []byte) sdk.Context {
WithGasMeter(storetypes.NewInfiniteGasMeter())
// WithVoteInfos(app.voteInfos) // TODO: identify if this is needed
ctx = ctx.WithIsSigverifyTx(app.sigverifyTx)
ctx = ctx.WithConsensusParams(app.GetConsensusParams(ctx))
if mode == execModeReCheck {

View File

@ -260,6 +260,11 @@ func (app *BaseApp) SetFauxMerkleMode() {
app.fauxMerkleMode = true
}
// SetNotSigverify during simulation testing, transaction signature verification needs to be ignored.
func (app *BaseApp) SetNotSigverifyTx() {
app.sigverifyTx = false
}
// SetCommitMultiStoreTracer sets the store tracer on the BaseApp's underlying
// CommitMultiStore.
func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) {

View File

@ -35,6 +35,7 @@ func (app *BaseApp) SimDeliver(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo,
if err != nil {
return sdk.GasInfo{}, nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "%s", err)
}
gasInfo, result, _, err := app.runTx(execModeFinalize, bz)
return gasInfo, result, err
}

View File

@ -76,6 +76,9 @@ func TestFullAppSimulation(t *testing.T) {
appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue
app := NewSimApp(logger, db, nil, true, appOptions, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID))
if !simcli.FlagSigverifyTxValue {
app.SetNotSigverifyTx()
}
require.Equal(t, "SimApp", app.Name())
// run randomized simulation
@ -121,6 +124,9 @@ func TestAppImportExport(t *testing.T) {
appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue
app := NewSimApp(logger, db, nil, true, appOptions, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID))
if !simcli.FlagSigverifyTxValue {
app.SetNotSigverifyTx()
}
require.Equal(t, "SimApp", app.Name())
// Run randomized simulation
@ -240,6 +246,9 @@ func TestAppSimulationAfterImport(t *testing.T) {
appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue
app := NewSimApp(logger, db, nil, true, appOptions, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID))
if !simcli.FlagSigverifyTxValue {
app.SetNotSigverifyTx()
}
require.Equal(t, "SimApp", app.Name())
// Run randomized simulation
@ -362,6 +371,9 @@ func TestAppStateDeterminism(t *testing.T) {
db := dbm.NewMemDB()
app := NewSimApp(logger, db, nil, true, appOptions, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID))
if !simcli.FlagSigverifyTxValue {
app.SetNotSigverifyTx()
}
fmt.Printf(
"running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n",

View File

@ -1,11 +1,13 @@
package sims
import (
"bufio"
"encoding/json"
"fmt"
"io"
"math/rand"
"os"
"path/filepath"
"time"
"github.com/cosmos/gogoproto/proto"
@ -13,6 +15,7 @@ import (
"cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
@ -250,19 +253,23 @@ func AppStateRandomizedFn(
// AppStateFromGenesisFileFn util function to generate the genesis AppState
// from a genesis.json file.
func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONCodec, genesisFile string) (genutiltypes.AppGenesis, []simtypes.Account, error) {
bytes, err := os.ReadFile(genesisFile)
file, err := os.Open(filepath.Clean(genesisFile))
if err != nil {
panic(err)
}
var genesis genutiltypes.AppGenesis
if err = json.Unmarshal(bytes, &genesis); err != nil {
return genesis, nil, err
genesis, err := genutiltypes.AppGenesisFromReader(bufio.NewReader(file))
if err != nil {
return *genesis, nil, err
}
if err := file.Close(); err != nil {
return *genesis, nil, err
}
var appState map[string]json.RawMessage
if err = json.Unmarshal(genesis.AppState, &appState); err != nil {
return genesis, nil, err
return *genesis, nil, err
}
var authGenesis authtypes.GenesisState
@ -284,13 +291,13 @@ func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONCodec, genesisFile str
a, ok := acc.GetCachedValue().(sdk.AccountI)
if !ok {
return genesis, nil, fmt.Errorf("expected account")
return *genesis, nil, fmt.Errorf("expected account")
}
// create simulator accounts
simAcc := simtypes.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: a.GetAddress()}
simAcc := simtypes.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: a.GetAddress(), ConsKey: ed25519.GenPrivKeyFromSecret(privkeySeed)}
newAccs[i] = simAcc
}
return genesis, newAccs, nil
return *genesis, newAccs, nil
}

View File

@ -54,6 +54,7 @@ type Context struct {
blockGasMeter storetypes.GasMeter
checkTx bool
recheckTx bool // if recheckTx == true, then checkTx must also be true
sigverifyTx bool // when run simulation, because the private key corresponding to the account in the genesis.json randomly generated, we must skip the sigverify.
execMode ExecMode
minGasPrice DecCoins
consParams cmtproto.ConsensusParams
@ -82,6 +83,7 @@ func (c Context) GasMeter() storetypes.GasMeter { return c.gasMe
func (c Context) BlockGasMeter() storetypes.GasMeter { return c.blockGasMeter }
func (c Context) IsCheckTx() bool { return c.checkTx }
func (c Context) IsReCheckTx() bool { return c.recheckTx }
func (c Context) IsSigverifyTx() bool { return c.sigverifyTx }
func (c Context) ExecMode() ExecMode { return c.execMode }
func (c Context) MinGasPrices() DecCoins { return c.minGasPrice }
func (c Context) EventManager() EventManagerI { return c.eventManager }
@ -131,6 +133,7 @@ func NewContext(ms storetypes.MultiStore, header cmtproto.Header, isCheckTx bool
header: header,
chainID: header.ChainID,
checkTx: isCheckTx,
sigverifyTx: true,
logger: logger,
gasMeter: storetypes.NewInfiniteGasMeter(),
minGasPrice: DecCoins{},
@ -260,6 +263,12 @@ func (c Context) WithIsReCheckTx(isRecheckTx bool) Context {
return c
}
// WithIsSigverifyTx called with true will sigverify in auth module
func (c Context) WithIsSigverifyTx(isSigverifyTx bool) Context {
c.sigverifyTx = isSigverifyTx
return c
}
// WithExecMode returns a Context with an updated ExecMode.
func (c Context) WithExecMode(m ExecMode) Context {
c.execMode = m

View File

@ -90,7 +90,7 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b
pk = simSecp256k1Pubkey
}
// Only make check if simulate=false
if !simulate && !bytes.Equal(pk.Address(), signers[i]) {
if !simulate && !bytes.Equal(pk.Address(), signers[i]) && ctx.IsSigverifyTx() {
return ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidPubKey,
"pubKey does not match signer address %s with signer index: %d", signerStrs[i], i)
}
@ -302,7 +302,7 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
}
// no need to verify signatures on recheck tx
if !simulate && !ctx.IsReCheckTx() {
if !simulate && !ctx.IsReCheckTx() && ctx.IsSigverifyTx() {
anyPk, _ := codectypes.NewAnyWithValue(pubKey)
signerData := txsigning.SignerData{

View File

@ -190,23 +190,26 @@ func TestSigVerification(t *testing.T) {
accSeqs []uint64
invalidSigs bool // used for testing sigverify on RecheckTx
recheck bool
sigverify bool
shouldErr bool
}
validSigs := false
testCases := []testCase{
{"no signers", []cryptotypes.PrivKey{}, []uint64{}, []uint64{}, validSigs, false, true},
{"not enough signers", []cryptotypes.PrivKey{priv1, priv2}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber()}, []uint64{0, 0}, validSigs, false, true},
{"wrong order signers", []cryptotypes.PrivKey{priv3, priv2, priv1}, []uint64{accs[2].GetAccountNumber(), accs[1].GetAccountNumber(), accs[0].GetAccountNumber()}, []uint64{0, 0, 0}, validSigs, false, true},
{"wrong accnums", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{7, 8, 9}, []uint64{0, 0, 0}, validSigs, false, true},
{"wrong sequences", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber(), accs[2].GetAccountNumber()}, []uint64{3, 4, 5}, validSigs, false, true},
{"valid tx", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber(), accs[2].GetAccountNumber()}, []uint64{0, 0, 0}, validSigs, false, false},
{"no err on recheck", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{0, 0, 0}, []uint64{0, 0, 0}, !validSigs, true, false},
{"no signers", []cryptotypes.PrivKey{}, []uint64{}, []uint64{}, validSigs, false, true, true},
{"not enough signers", []cryptotypes.PrivKey{priv1, priv2}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber()}, []uint64{0, 0}, validSigs, false, true, true},
{"wrong order signers", []cryptotypes.PrivKey{priv3, priv2, priv1}, []uint64{accs[2].GetAccountNumber(), accs[1].GetAccountNumber(), accs[0].GetAccountNumber()}, []uint64{0, 0, 0}, validSigs, false, true, true},
{"wrong accnums", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{7, 8, 9}, []uint64{0, 0, 0}, validSigs, false, true, true},
{"wrong sequences", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber(), accs[2].GetAccountNumber()}, []uint64{3, 4, 5}, validSigs, false, true, true},
{"valid tx", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber(), accs[2].GetAccountNumber()}, []uint64{0, 0, 0}, validSigs, false, true, false},
{"sigverify tx with wrong order signers", []cryptotypes.PrivKey{priv3, priv2, priv1}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber(), accs[2].GetAccountNumber()}, []uint64{0, 0, 0}, validSigs, false, true, true},
{"skip sigverify tx with wrong order signers", []cryptotypes.PrivKey{priv3, priv2, priv1}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber(), accs[2].GetAccountNumber()}, []uint64{0, 0, 0}, validSigs, false, false, false},
{"no err on recheck", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{0, 0, 0}, []uint64{0, 0, 0}, !validSigs, true, true, false},
}
for i, tc := range testCases {
for _, signMode := range enabledSignModes {
t.Run(fmt.Sprintf("%s with %s", tc.name, signMode), func(t *testing.T) {
suite.ctx = suite.ctx.WithIsReCheckTx(tc.recheck)
suite.ctx = suite.ctx.WithIsReCheckTx(tc.recheck).WithIsSigverifyTx(tc.sigverify)
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // Create new txBuilder for each test
require.NoError(t, suite.txBuilder.SetMsgs(msgs...))

View File

@ -1229,8 +1229,9 @@ func randomGroup(r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
switch {
case groupID > initialGroupID:
// select a random ID between [initialGroupID, groupID]
groupID = uint64(simtypes.RandIntBetween(r, int(initialGroupID), int(groupID)))
// select a random ID between (initialGroupID, groupID]
// if there is at least one group information, then the groupID at this time must be greater than or equal to 1
groupID = uint64(simtypes.RandIntBetween(r, int(initialGroupID+1), int(groupID+1)))
default:
// This is called on the first call to this function
@ -1238,6 +1239,11 @@ func randomGroup(r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
initialGroupID = groupID
}
// when groupID is 0, it proves that SimulateMsgCreateGroup has never been called. that is, no group exists in the chain
if groupID == 0 {
return nil, simtypes.Account{}, nil, nil
}
res, err := k.GroupInfo(ctx, &group.QueryGroupInfoRequest{GroupId: groupID})
if err != nil {
return nil, simtypes.Account{}, nil, err

View File

@ -30,6 +30,7 @@ var (
FlagVerboseValue bool
FlagPeriodValue uint
FlagGenesisTimeValue int64
FlagSigverifyTxValue bool
)
// GetSimulatorFlags gets the values of all the available simulation flags
@ -56,6 +57,7 @@ func GetSimulatorFlags() {
flag.BoolVar(&FlagVerboseValue, "Verbose", false, "verbose log output")
flag.UintVar(&FlagPeriodValue, "Period", 0, "run slow invariants only once every period assertions")
flag.Int64Var(&FlagGenesisTimeValue, "GenesisTime", 0, "override genesis UNIX time instead of using a random UNIX time")
flag.BoolVar(&FlagSigverifyTxValue, "SigverifyTx", true, "whether to sigverify check for transaction ")
}
// NewConfigFromFlags creates a simulation from the retrieved values of the flags.

View File

@ -81,8 +81,10 @@ func SimulateFromSeed(
// Second variable to keep pending validator set (delayed one block since
// TM 0.24) Initially this is the same as the initial validator set
validators, blockTime, accs, chainID := initChain(r, params, accs, app, appStateFn, config, cdc)
if len(accs) == 0 {
return true, params, fmt.Errorf("must have greater than zero genesis accounts")
// At least 2 accounts must be added here, otherwise when executing SimulateMsgSend
// two accounts will be selected to meet the conditions from != to and it will fall into an infinite loop.
if len(accs) <= 1 {
return true, params, fmt.Errorf("at least two genesis accounts are required")
}
config.ChainID = chainID