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:
parent
c51f58c799
commit
698627016d
@ -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.
|
||||
|
||||
10
Makefile
10
Makefile
@ -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!"
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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...))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user