Remove simulation checks (#1507)
* Add cli rollback command it's useful in app-hash mismatch situation. * Update CHANGELOG.md * (refactor): removed old sim tests logic * (fix): removed tests from CI * (fix): fix test.yml * (fix): format and lint * (fix): fix linter issue * (fix): fix linter issues v2 * (fix): linter * (fix): removed sim-test references * Applied changes from code review Co-authored-by: HuangYi <huang@crypto.com>
This commit is contained in:
parent
831588c72c
commit
a5c927bf5b
82
.github/workflows/test.yml
vendored
82
.github/workflows/test.yml
vendored
@ -127,84 +127,4 @@ jobs:
|
|||||||
name: ethermint
|
name: ethermint
|
||||||
signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}"
|
signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}"
|
||||||
- name: 'instantiate integration test env'
|
- name: 'instantiate integration test env'
|
||||||
run: nix-store -r $(nix-instantiate tests/integration_tests/shell.nix)
|
run: nix-store -r "$(nix-instantiate tests/integration_tests/shell.nix)"
|
||||||
|
|
||||||
test-sim-nondeterminism:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 25
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: 1.19
|
|
||||||
check-latest: true
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: technote-space/get-diff-action@v6.1.1
|
|
||||||
with:
|
|
||||||
PATTERNS: |
|
|
||||||
**/**.go
|
|
||||||
go.mod
|
|
||||||
go.sum
|
|
||||||
- name: Test simulation nondeterminism
|
|
||||||
run: |
|
|
||||||
make test-sim-nondeterminism
|
|
||||||
if: env.GIT_DIFF
|
|
||||||
|
|
||||||
test-sim-random-genesis-fast:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 25
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: 1.19
|
|
||||||
check-latest: true
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: technote-space/get-diff-action@v6.1.1
|
|
||||||
with:
|
|
||||||
PATTERNS: |
|
|
||||||
**/**.go
|
|
||||||
go.mod
|
|
||||||
go.sum
|
|
||||||
- name: Test simulation with random genesis
|
|
||||||
run: |
|
|
||||||
make test-sim-random-genesis-fast
|
|
||||||
if: env.GIT_DIFF
|
|
||||||
|
|
||||||
test-sim-import-export:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 25
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: 1.19
|
|
||||||
check-latest: true
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: technote-space/get-diff-action@v6.1.1
|
|
||||||
with:
|
|
||||||
PATTERNS: |
|
|
||||||
**/**.go
|
|
||||||
go.mod
|
|
||||||
go.sum
|
|
||||||
- name: Simulation of import and export genesis
|
|
||||||
run: |
|
|
||||||
make test-sim-import-export
|
|
||||||
if: env.GIT_DIFF
|
|
||||||
|
|
||||||
test-sim-after-import:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 25
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: 1.19
|
|
||||||
check-latest: true
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: technote-space/get-diff-action@v6.1.1
|
|
||||||
with:
|
|
||||||
PATTERNS: |
|
|
||||||
**/**.go
|
|
||||||
go.mod
|
|
||||||
go.sum
|
|
||||||
- name: Test simulation after import
|
|
||||||
run: |
|
|
||||||
make test-sim-after-import
|
|
||||||
if: env.GIT_DIFF
|
|
||||||
|
@ -62,6 +62,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
||||||
|
* (tests) [#1507](https://github.com/evmos/ethermint/pull/1507) Remove legacy sim tests
|
||||||
* (feemarket) [#1508](https://github.com/evmos/ethermint/pull/1508) Remove old x/params migration logic
|
* (feemarket) [#1508](https://github.com/evmos/ethermint/pull/1508) Remove old x/params migration logic
|
||||||
* (evm) [#1499](https://github.com/evmos/ethermint/pull/1499) Add Shanghai and Cancun block
|
* (evm) [#1499](https://github.com/evmos/ethermint/pull/1499) Add Shanghai and Cancun block
|
||||||
* (ante) [#1455](https://github.com/evmos/ethermint/pull/1455) Refactor `AnteHandler` logic
|
* (ante) [#1455](https://github.com/evmos/ethermint/pull/1455) Refactor `AnteHandler` logic
|
||||||
|
45
Makefile
45
Makefile
@ -343,51 +343,6 @@ test-solidity:
|
|||||||
|
|
||||||
.PHONY: run-tests test test-all test-import test-rpc test-contract test-solidity $(TEST_TARGETS)
|
.PHONY: run-tests test test-all test-import test-rpc test-contract test-solidity $(TEST_TARGETS)
|
||||||
|
|
||||||
test-sim-nondeterminism:
|
|
||||||
@echo "Running non-determinism test..."
|
|
||||||
@go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \
|
|
||||||
-NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h
|
|
||||||
|
|
||||||
test-sim-random-genesis-fast:
|
|
||||||
@echo "Running random genesis simulation..."
|
|
||||||
@go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation \
|
|
||||||
-Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h
|
|
||||||
|
|
||||||
test-sim-import-export: runsim
|
|
||||||
@echo "Running application import/export simulation. This may take several minutes..."
|
|
||||||
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestAppImportExport
|
|
||||||
|
|
||||||
test-sim-after-import: runsim
|
|
||||||
@echo "Running application simulation-after-import. This may take several minutes..."
|
|
||||||
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestAppSimulationAfterImport
|
|
||||||
|
|
||||||
test-sim-random-genesis-multi-seed: runsim
|
|
||||||
@echo "Running multi-seed custom genesis simulation..."
|
|
||||||
@$(BINDIR)/runsim -SimAppPkg=$(SIMAPP) -ExitOnFail 400 5 TestFullAppSimulation
|
|
||||||
|
|
||||||
test-sim-multi-seed-long: runsim
|
|
||||||
@echo "Running long multi-seed application simulation. This may take awhile!"
|
|
||||||
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 500 50 TestFullAppSimulation
|
|
||||||
|
|
||||||
test-sim-multi-seed-short: runsim
|
|
||||||
@echo "Running short multi-seed application simulation. This may take awhile!"
|
|
||||||
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 10 TestFullAppSimulation
|
|
||||||
|
|
||||||
test-sim-benchmark-invariants:
|
|
||||||
@echo "Running simulation invariant benchmarks..."
|
|
||||||
@go test -mod=readonly $(SIMAPP) -benchmem -bench=BenchmarkInvariants -run=^$ \
|
|
||||||
-Enabled=true -NumBlocks=1000 -BlockSize=200 \
|
|
||||||
-Period=1 -Commit=true -Seed=57 -v -timeout 24h
|
|
||||||
|
|
||||||
.PHONY: \
|
|
||||||
test-sim-nondeterminism \
|
|
||||||
test-sim-custom-genesis-fast \
|
|
||||||
test-sim-import-export \
|
|
||||||
test-sim-after-import \
|
|
||||||
test-sim-custom-genesis-multi-seed \
|
|
||||||
test-sim-multi-seed-short \
|
|
||||||
test-sim-multi-seed-long \
|
|
||||||
test-sim-benchmark-invariants
|
|
||||||
|
|
||||||
benchmark:
|
benchmark:
|
||||||
@go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION)
|
@go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION)
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func (suite AnteTestSuite) TestEthSigVerificationDecorator() {
|
func (suite AnteTestSuite) TestEthSigVerificationDecorator() {
|
||||||
addr, privKey := tests.NewAddrKey()
|
addr, privKey := tests.NewAddrKey()
|
||||||
|
|
||||||
|
21
app/app.go
21
app/app.go
@ -182,8 +182,6 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ simapp.App = (*EthermintApp)(nil)
|
|
||||||
|
|
||||||
// var _ server.Application (*EthermintApp)(nil)
|
// var _ server.Application (*EthermintApp)(nil)
|
||||||
|
|
||||||
// EthermintApp implements an extended ABCI application. It is an application
|
// EthermintApp implements an extended ABCI application. It is an application
|
||||||
@ -233,9 +231,6 @@ type EthermintApp struct {
|
|||||||
// the module manager
|
// the module manager
|
||||||
mm *module.Manager
|
mm *module.Manager
|
||||||
|
|
||||||
// simulation manager
|
|
||||||
sm *module.SimulationManager
|
|
||||||
|
|
||||||
// the configurator
|
// the configurator
|
||||||
configurator module.Configurator
|
configurator module.Configurator
|
||||||
}
|
}
|
||||||
@ -600,17 +595,6 @@ func NewEthermintApp(
|
|||||||
// add test gRPC service for testing gRPC queries in isolation
|
// add test gRPC service for testing gRPC queries in isolation
|
||||||
// testdata.RegisterTestServiceServer(app.GRPCQueryRouter(), testdata.TestServiceImpl{})
|
// testdata.RegisterTestServiceServer(app.GRPCQueryRouter(), testdata.TestServiceImpl{})
|
||||||
|
|
||||||
// create the simulation manager and define the order of the modules for deterministic simulations
|
|
||||||
//
|
|
||||||
// NOTE: this is not required apps that don't use the simulator for fuzz testing
|
|
||||||
// transactions
|
|
||||||
overrideModules := map[string]module.AppModuleSimulation{
|
|
||||||
authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts),
|
|
||||||
}
|
|
||||||
app.sm = module.NewSimulationManagerFromAppModules(app.mm.Modules, overrideModules)
|
|
||||||
|
|
||||||
app.sm.RegisterStoreDecoders()
|
|
||||||
|
|
||||||
// initialize stores
|
// initialize stores
|
||||||
app.MountKVStores(keys)
|
app.MountKVStores(keys)
|
||||||
app.MountTransientStores(tkeys)
|
app.MountTransientStores(tkeys)
|
||||||
@ -780,11 +764,6 @@ func (app *EthermintApp) GetSubspace(moduleName string) paramstypes.Subspace {
|
|||||||
return subspace
|
return subspace
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimulationManager implements the SimulationApp interface
|
|
||||||
func (app *EthermintApp) SimulationManager() *module.SimulationManager {
|
|
||||||
return app.sm
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterAPIRoutes registers all application module routes with the provided
|
// RegisterAPIRoutes registers all application module routes with the provided
|
||||||
// API server.
|
// API server.
|
||||||
func (app *EthermintApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) {
|
func (app *EthermintApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) {
|
||||||
|
@ -1,403 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
// TODO: COsmos SDK fix for the simulator issue for custom keys
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"runtime/debug"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
||||||
"github.com/cosmos/cosmos-sdk/simapp"
|
|
||||||
"github.com/cosmos/cosmos-sdk/simapp/params"
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
|
||||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
||||||
authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
|
|
||||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
||||||
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
|
|
||||||
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
|
||||||
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
|
|
||||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
|
||||||
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
|
||||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
|
||||||
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
|
|
||||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
||||||
ibctransfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types"
|
|
||||||
ibchost "github.com/cosmos/ibc-go/v5/modules/core/24-host"
|
|
||||||
"github.com/evmos/ethermint/app/ante"
|
|
||||||
evmenc "github.com/evmos/ethermint/encoding"
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
||||||
dbm "github.com/tendermint/tm-db"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MakeEncodingConfig creates the EncodingConfig
|
|
||||||
func MakeEncodingConfig() params.EncodingConfig {
|
|
||||||
return evmenc.MakeConfig(ModuleBasics)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
simapp.GetSimulatorFlags()
|
|
||||||
}
|
|
||||||
|
|
||||||
const SimAppChainID = "simulation_777-1"
|
|
||||||
|
|
||||||
type storeKeysPrefixes struct {
|
|
||||||
A storetypes.StoreKey
|
|
||||||
B storetypes.StoreKey
|
|
||||||
Prefixes [][]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of
|
|
||||||
// an IAVLStore for faster simulation speed.
|
|
||||||
func fauxMerkleModeOpt(bapp *baseapp.BaseApp) {
|
|
||||||
bapp.SetFauxMerkleMode()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSimApp disable feemarket on native tx, otherwise the cosmos-sdk simulation tests will fail.
|
|
||||||
func NewSimApp(logger log.Logger, db dbm.DB) (*EthermintApp, error) {
|
|
||||||
encodingConfig := MakeEncodingConfig()
|
|
||||||
app := NewEthermintApp(logger, db, nil, false, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, encodingConfig, simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
|
||||||
// disable feemarket on native tx
|
|
||||||
anteHandler, err := ante.NewAnteHandler(ante.HandlerOptions{
|
|
||||||
AccountKeeper: app.AccountKeeper,
|
|
||||||
BankKeeper: app.BankKeeper,
|
|
||||||
SignModeHandler: encodingConfig.TxConfig.SignModeHandler(),
|
|
||||||
FeegrantKeeper: app.FeeGrantKeeper,
|
|
||||||
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
|
|
||||||
IBCKeeper: app.IBCKeeper,
|
|
||||||
EvmKeeper: app.EvmKeeper,
|
|
||||||
FeeMarketKeeper: app.FeeMarketKeeper,
|
|
||||||
MaxTxGasWanted: 0,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
app.SetAnteHandler(anteHandler)
|
|
||||||
if err := app.LoadLatestVersion(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return app, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// interBlockCacheOpt returns a BaseApp option function that sets the persistent
|
|
||||||
// inter-block write-through cache.
|
|
||||||
func interBlockCacheOpt() func(*baseapp.BaseApp) {
|
|
||||||
return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFullAppSimulation(t *testing.T) {
|
|
||||||
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
|
||||||
if skip {
|
|
||||||
t.Skip("skipping application simulation")
|
|
||||||
}
|
|
||||||
require.NoError(t, err, "simulation setup failed")
|
|
||||||
|
|
||||||
config.ChainID = SimAppChainID
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
require.NoError(t, db.Close())
|
|
||||||
require.NoError(t, os.RemoveAll(dir))
|
|
||||||
}()
|
|
||||||
|
|
||||||
app, err := NewSimApp(logger, db)
|
|
||||||
require.Equal(t, appName, app.Name())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// run randomized simulation
|
|
||||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
|
||||||
t,
|
|
||||||
os.Stdout,
|
|
||||||
app.BaseApp,
|
|
||||||
StateFn(app.AppCodec(), app.SimulationManager()),
|
|
||||||
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
|
||||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
|
||||||
app.ModuleAccountAddrs(),
|
|
||||||
config,
|
|
||||||
app.AppCodec(),
|
|
||||||
)
|
|
||||||
|
|
||||||
// export state and simParams before the simulation error is checked
|
|
||||||
err = simapp.CheckExportSimulation(app, config, simParams)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, simErr)
|
|
||||||
|
|
||||||
if config.Commit {
|
|
||||||
simapp.PrintStats(db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppImportExport(t *testing.T) {
|
|
||||||
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
|
||||||
if skip {
|
|
||||||
t.Skip("skipping application import/export simulation")
|
|
||||||
}
|
|
||||||
require.NoError(t, err, "simulation setup failed")
|
|
||||||
|
|
||||||
config.ChainID = SimAppChainID
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
require.NoError(t, db.Close())
|
|
||||||
require.NoError(t, os.RemoveAll(dir))
|
|
||||||
}()
|
|
||||||
app, err := NewSimApp(logger, db)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, appName, app.Name())
|
|
||||||
|
|
||||||
// Run randomized simulation
|
|
||||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
|
||||||
t,
|
|
||||||
os.Stdout,
|
|
||||||
app.BaseApp,
|
|
||||||
StateFn(app.AppCodec(), app.SimulationManager()),
|
|
||||||
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
|
||||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
|
||||||
app.ModuleAccountAddrs(),
|
|
||||||
config,
|
|
||||||
app.AppCodec(),
|
|
||||||
)
|
|
||||||
|
|
||||||
// export state and simParams before the simulation error is checked
|
|
||||||
err = simapp.CheckExportSimulation(app, config, simParams)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, simErr)
|
|
||||||
|
|
||||||
if config.Commit {
|
|
||||||
simapp.PrintStats(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("exporting genesis...\n")
|
|
||||||
|
|
||||||
exported, err := app.ExportAppStateAndValidators(false, []string{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
fmt.Printf("importing genesis...\n")
|
|
||||||
|
|
||||||
//nolint: dogsled
|
|
||||||
_, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2")
|
|
||||||
require.NoError(t, err, "simulation setup failed")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
require.NoError(t, newDB.Close())
|
|
||||||
require.NoError(t, os.RemoveAll(newDir))
|
|
||||||
}()
|
|
||||||
|
|
||||||
newApp, err := NewSimApp(log.NewNopLogger(), newDB)
|
|
||||||
require.Equal(t, appName, newApp.Name())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var genesisState simapp.GenesisState
|
|
||||||
err = json.Unmarshal(exported.AppState, &genesisState)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
err := fmt.Sprintf("%v", r)
|
|
||||||
if !strings.Contains(err, "validator set is empty after InitGenesis") {
|
|
||||||
panic(r)
|
|
||||||
}
|
|
||||||
logger.Info("Skipping simulation as all validators have been unbonded")
|
|
||||||
logger.Info("err", err, "stacktrace", string(debug.Stack()))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight(), ChainID: SimAppChainID})
|
|
||||||
ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight(), ChainID: SimAppChainID})
|
|
||||||
newApp.mm.InitGenesis(ctxB, app.AppCodec(), genesisState)
|
|
||||||
newApp.StoreConsensusParams(ctxB, exported.ConsensusParams)
|
|
||||||
|
|
||||||
fmt.Printf("comparing stores...\n")
|
|
||||||
|
|
||||||
storeKeysPrefixes := []storeKeysPrefixes{
|
|
||||||
{app.keys[authtypes.StoreKey], newApp.keys[authtypes.StoreKey], [][]byte{}},
|
|
||||||
{
|
|
||||||
app.keys[stakingtypes.StoreKey], newApp.keys[stakingtypes.StoreKey],
|
|
||||||
[][]byte{
|
|
||||||
stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey,
|
|
||||||
stakingtypes.HistoricalInfoKey,
|
|
||||||
},
|
|
||||||
}, // ordering may change but it doesn't matter
|
|
||||||
{app.keys[slashingtypes.StoreKey], newApp.keys[slashingtypes.StoreKey], [][]byte{}},
|
|
||||||
{app.keys[minttypes.StoreKey], newApp.keys[minttypes.StoreKey], [][]byte{}},
|
|
||||||
{app.keys[distrtypes.StoreKey], newApp.keys[distrtypes.StoreKey], [][]byte{}},
|
|
||||||
{app.keys[banktypes.StoreKey], newApp.keys[banktypes.StoreKey], [][]byte{banktypes.BalancesPrefix}},
|
|
||||||
{app.keys[paramtypes.StoreKey], newApp.keys[paramtypes.StoreKey], [][]byte{}},
|
|
||||||
{app.keys[govtypes.StoreKey], newApp.keys[govtypes.StoreKey], [][]byte{}},
|
|
||||||
{app.keys[evidencetypes.StoreKey], newApp.keys[evidencetypes.StoreKey], [][]byte{}},
|
|
||||||
{app.keys[capabilitytypes.StoreKey], newApp.keys[capabilitytypes.StoreKey], [][]byte{}},
|
|
||||||
{app.keys[authzkeeper.StoreKey], newApp.keys[authzkeeper.StoreKey], [][]byte{authzkeeper.GrantKey, authzkeeper.GrantQueuePrefix}},
|
|
||||||
{app.keys[ibchost.StoreKey], newApp.keys[ibchost.StoreKey], [][]byte{}},
|
|
||||||
{app.keys[ibctransfertypes.StoreKey], newApp.keys[ibctransfertypes.StoreKey], [][]byte{}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, skp := range storeKeysPrefixes {
|
|
||||||
storeA := ctxA.KVStore(skp.A)
|
|
||||||
storeB := ctxB.KVStore(skp.B)
|
|
||||||
|
|
||||||
failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, skp.Prefixes)
|
|
||||||
require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare")
|
|
||||||
|
|
||||||
fmt.Printf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B)
|
|
||||||
require.Equal(t, len(failedKVAs), 0, simapp.GetSimulationLog(skp.A.Name(), app.SimulationManager().StoreDecoders, failedKVAs, failedKVBs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppSimulationAfterImport(t *testing.T) {
|
|
||||||
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
|
||||||
if skip {
|
|
||||||
t.Skip("skipping application simulation after import")
|
|
||||||
}
|
|
||||||
require.NoError(t, err, "simulation setup failed")
|
|
||||||
|
|
||||||
config.ChainID = SimAppChainID
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
require.NoError(t, db.Close())
|
|
||||||
require.NoError(t, os.RemoveAll(dir))
|
|
||||||
}()
|
|
||||||
|
|
||||||
app, err := NewSimApp(logger, db)
|
|
||||||
require.Equal(t, appName, app.Name())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Run randomized simulation
|
|
||||||
stopEarly, simParams, simErr := simulation.SimulateFromSeed(
|
|
||||||
t,
|
|
||||||
os.Stdout,
|
|
||||||
app.BaseApp,
|
|
||||||
StateFn(app.AppCodec(), app.SimulationManager()),
|
|
||||||
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
|
||||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
|
||||||
app.ModuleAccountAddrs(),
|
|
||||||
config,
|
|
||||||
app.AppCodec(),
|
|
||||||
)
|
|
||||||
|
|
||||||
// export state and simParams before the simulation error is checked
|
|
||||||
err = simapp.CheckExportSimulation(app, config, simParams)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, simErr)
|
|
||||||
|
|
||||||
if config.Commit {
|
|
||||||
simapp.PrintStats(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
if stopEarly {
|
|
||||||
fmt.Println("can't export or import a zero-validator genesis, exiting test...")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("exporting genesis...\n")
|
|
||||||
|
|
||||||
exported, err := app.ExportAppStateAndValidators(true, []string{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
fmt.Printf("importing genesis...\n")
|
|
||||||
|
|
||||||
_, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2")
|
|
||||||
require.NoError(t, err, "simulation setup failed")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
require.NoError(t, newDB.Close())
|
|
||||||
require.NoError(t, os.RemoveAll(newDir))
|
|
||||||
}()
|
|
||||||
|
|
||||||
newApp, err := NewSimApp(log.NewNopLogger(), newDB)
|
|
||||||
require.Equal(t, appName, newApp.Name())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
newApp.InitChain(abci.RequestInitChain{
|
|
||||||
ChainId: SimAppChainID,
|
|
||||||
AppStateBytes: exported.AppState,
|
|
||||||
})
|
|
||||||
|
|
||||||
_, _, err = simulation.SimulateFromSeed(
|
|
||||||
t,
|
|
||||||
os.Stdout,
|
|
||||||
newApp.BaseApp,
|
|
||||||
StateFn(app.AppCodec(), app.SimulationManager()),
|
|
||||||
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
|
||||||
simapp.SimulationOperations(newApp, newApp.AppCodec(), config),
|
|
||||||
app.ModuleAccountAddrs(),
|
|
||||||
config,
|
|
||||||
app.AppCodec(),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Make another test for the fuzzer itself, which just has noOp txs
|
|
||||||
// and doesn't depend on the application.
|
|
||||||
func TestAppStateDeterminism(t *testing.T) {
|
|
||||||
if !simapp.FlagEnabledValue {
|
|
||||||
t.Skip("skipping application simulation")
|
|
||||||
}
|
|
||||||
|
|
||||||
config := simapp.NewConfigFromFlags()
|
|
||||||
config.InitialBlockHeight = 1
|
|
||||||
config.ExportParamsPath = ""
|
|
||||||
config.OnOperation = false
|
|
||||||
config.AllInvariants = false
|
|
||||||
config.ChainID = SimAppChainID
|
|
||||||
|
|
||||||
numSeeds := 3
|
|
||||||
numTimesToRunPerSeed := 5
|
|
||||||
appHashList := make([]json.RawMessage, numTimesToRunPerSeed)
|
|
||||||
|
|
||||||
for i := 0; i < numSeeds; i++ {
|
|
||||||
config.Seed = rand.Int63()
|
|
||||||
|
|
||||||
for j := 0; j < numTimesToRunPerSeed; j++ {
|
|
||||||
var logger log.Logger
|
|
||||||
if simapp.FlagVerboseValue {
|
|
||||||
logger = log.TestingLogger()
|
|
||||||
} else {
|
|
||||||
logger = log.NewNopLogger()
|
|
||||||
}
|
|
||||||
|
|
||||||
db := dbm.NewMemDB()
|
|
||||||
app, err := NewSimApp(logger, db)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
fmt.Printf(
|
|
||||||
"running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n",
|
|
||||||
config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed,
|
|
||||||
)
|
|
||||||
|
|
||||||
_, _, err = simulation.SimulateFromSeed(
|
|
||||||
t,
|
|
||||||
os.Stdout,
|
|
||||||
app.BaseApp,
|
|
||||||
StateFn(app.AppCodec(), app.SimulationManager()),
|
|
||||||
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
|
||||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
|
||||||
app.ModuleAccountAddrs(),
|
|
||||||
config,
|
|
||||||
app.AppCodec(),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
if config.Commit {
|
|
||||||
simapp.PrintStats(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
appHash := app.LastCommitID().Hash
|
|
||||||
appHashList[j] = appHash
|
|
||||||
|
|
||||||
if j != 0 {
|
|
||||||
require.Equal(
|
|
||||||
t, string(appHashList[0]), string(appHashList[j]),
|
|
||||||
"non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
102
app/utils.go
102
app/utils.go
@ -2,7 +2,6 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/rand"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
@ -11,21 +10,12 @@ import (
|
|||||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||||
"github.com/cosmos/cosmos-sdk/simapp"
|
"github.com/cosmos/cosmos-sdk/simapp"
|
||||||
"github.com/cosmos/cosmos-sdk/testutil/mock"
|
"github.com/cosmos/cosmos-sdk/testutil/mock"
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
|
||||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
|
||||||
"github.com/evmos/ethermint/encoding"
|
|
||||||
ethermint "github.com/evmos/ethermint/types"
|
|
||||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
"github.com/evmos/ethermint/encoding"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/evmos/ethermint/crypto/ethsecp256k1"
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||||
@ -94,96 +84,6 @@ func SetupWithDB(isCheckTx bool, patchGenesis func(*EthermintApp, simapp.Genesis
|
|||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandomGenesisAccounts is used by the auth module to create random genesis accounts in simulation when a genesis.json is not specified.
|
|
||||||
// In contrast, the default auth module's RandomGenesisAccounts implementation creates only base accounts and vestings accounts.
|
|
||||||
func RandomGenesisAccounts(simState *module.SimulationState) authtypes.GenesisAccounts {
|
|
||||||
emptyCodeHash := crypto.Keccak256(nil)
|
|
||||||
genesisAccs := make(authtypes.GenesisAccounts, len(simState.Accounts))
|
|
||||||
for i, acc := range simState.Accounts {
|
|
||||||
bacc := authtypes.NewBaseAccountWithAddress(acc.Address)
|
|
||||||
|
|
||||||
ethacc := ðermint.EthAccount{
|
|
||||||
BaseAccount: bacc,
|
|
||||||
CodeHash: common.BytesToHash(emptyCodeHash).String(),
|
|
||||||
}
|
|
||||||
genesisAccs[i] = ethacc
|
|
||||||
}
|
|
||||||
|
|
||||||
return genesisAccs
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomAccounts creates random accounts with an ethsecp256k1 private key
|
|
||||||
// TODO: replace secp256k1.GenPrivKeyFromSecret() with similar function in go-ethereum
|
|
||||||
func RandomAccounts(r *rand.Rand, n int) []simtypes.Account {
|
|
||||||
accs := make([]simtypes.Account, n)
|
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
// don't need that much entropy for simulation
|
|
||||||
privkeySeed := make([]byte, 15)
|
|
||||||
_, _ = r.Read(privkeySeed)
|
|
||||||
|
|
||||||
prv := secp256k1.GenPrivKeyFromSecret(privkeySeed)
|
|
||||||
ethPrv := ðsecp256k1.PrivKey{}
|
|
||||||
_ = ethPrv.UnmarshalAmino(prv.Bytes()) // UnmarshalAmino simply copies the bytes and assigns them to ethPrv.Key
|
|
||||||
accs[i].PrivKey = ethPrv
|
|
||||||
accs[i].PubKey = accs[i].PrivKey.PubKey()
|
|
||||||
accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address())
|
|
||||||
|
|
||||||
accs[i].ConsKey = ed25519.GenPrivKeyFromSecret(privkeySeed)
|
|
||||||
}
|
|
||||||
|
|
||||||
return accs
|
|
||||||
}
|
|
||||||
|
|
||||||
// StateFn returns the initial application state using a genesis or the simulation parameters.
|
|
||||||
// It is a wrapper of simapp.AppStateFn to replace evm param EvmDenom with staking param BondDenom.
|
|
||||||
func StateFn(cdc codec.JSONCodec, simManager *module.SimulationManager) simtypes.AppStateFn {
|
|
||||||
return func(r *rand.Rand, accs []simtypes.Account, config simtypes.Config,
|
|
||||||
) (appState json.RawMessage, simAccs []simtypes.Account, chainID string, genesisTimestamp time.Time) {
|
|
||||||
appStateFn := simapp.AppStateFn(cdc, simManager)
|
|
||||||
appState, simAccs, chainID, genesisTimestamp = appStateFn(r, accs, config)
|
|
||||||
|
|
||||||
rawState := make(map[string]json.RawMessage)
|
|
||||||
err := json.Unmarshal(appState, &rawState)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stakingStateBz, ok := rawState[stakingtypes.ModuleName]
|
|
||||||
if !ok {
|
|
||||||
panic("staking genesis state is missing")
|
|
||||||
}
|
|
||||||
|
|
||||||
stakingState := new(stakingtypes.GenesisState)
|
|
||||||
cdc.MustUnmarshalJSON(stakingStateBz, stakingState)
|
|
||||||
|
|
||||||
// we should get the BondDenom and make it the evmdenom.
|
|
||||||
// thus simulation accounts could have positive amount of gas token.
|
|
||||||
bondDenom := stakingState.Params.BondDenom
|
|
||||||
|
|
||||||
evmStateBz, ok := rawState[evmtypes.ModuleName]
|
|
||||||
if !ok {
|
|
||||||
panic("evm genesis state is missing")
|
|
||||||
}
|
|
||||||
|
|
||||||
evmState := new(evmtypes.GenesisState)
|
|
||||||
cdc.MustUnmarshalJSON(evmStateBz, evmState)
|
|
||||||
|
|
||||||
// we should replace the EvmDenom with BondDenom
|
|
||||||
evmState.Params.EvmDenom = bondDenom
|
|
||||||
|
|
||||||
// change appState back
|
|
||||||
rawState[evmtypes.ModuleName] = cdc.MustMarshalJSON(evmState)
|
|
||||||
|
|
||||||
// replace appstate
|
|
||||||
appState, err = json.Marshal(rawState)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return appState, simAccs, chainID, genesisTimestamp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTestGenesisState generate genesis state with single validator
|
// NewTestGenesisState generate genesis state with single validator
|
||||||
func NewTestGenesisState(codec codec.Codec) simapp.GenesisState {
|
func NewTestGenesisState(codec codec.Codec) simapp.GenesisState {
|
||||||
privVal := mock.NewPV()
|
privVal := mock.NewPV()
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
|
||||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
||||||
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
|
||||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
||||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
|
||||||
|
|
||||||
"github.com/evmos/ethermint/crypto/ethsecp256k1"
|
|
||||||
ethermint "github.com/evmos/ethermint/types"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/simapp"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
maxTestingAccounts = 100
|
|
||||||
seed = int64(233)
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRandomGenesisAccounts(t *testing.T) {
|
|
||||||
r := rand.New(rand.NewSource(seed))
|
|
||||||
accs := RandomAccounts(r, rand.Intn(maxTestingAccounts))
|
|
||||||
|
|
||||||
encodingConfig := MakeEncodingConfig()
|
|
||||||
appCodec := encodingConfig.Codec
|
|
||||||
cdc := encodingConfig.Amino
|
|
||||||
|
|
||||||
paramsKeeper := initParamsKeeper(appCodec, cdc, sdk.NewKVStoreKey(paramstypes.StoreKey), sdk.NewTransientStoreKey(paramstypes.StoreKey))
|
|
||||||
subSpace, find := paramsKeeper.GetSubspace(authtypes.ModuleName)
|
|
||||||
require.True(t, find)
|
|
||||||
accountKeeper := authkeeper.NewAccountKeeper(
|
|
||||||
appCodec, sdk.NewKVStoreKey(authtypes.StoreKey), subSpace, ethermint.ProtoAccount, maccPerms, sdk.GetConfig().GetBech32AccountAddrPrefix(),
|
|
||||||
)
|
|
||||||
authModule := auth.NewAppModule(appCodec, accountKeeper, RandomGenesisAccounts)
|
|
||||||
|
|
||||||
genesisState := simapp.NewDefaultGenesisState(appCodec)
|
|
||||||
simState := &module.SimulationState{Accounts: accs, Cdc: appCodec, Rand: r, GenState: genesisState}
|
|
||||||
authModule.GenerateGenesisState(simState)
|
|
||||||
|
|
||||||
authStateBz, find := genesisState[authtypes.ModuleName]
|
|
||||||
require.True(t, find)
|
|
||||||
|
|
||||||
authState := new(authtypes.GenesisState)
|
|
||||||
appCodec.MustUnmarshalJSON(authStateBz, authState)
|
|
||||||
accounts, err := authtypes.UnpackAccounts(authState.Accounts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
for _, acc := range accounts {
|
|
||||||
_, ok := acc.(ethermint.EthAccountI)
|
|
||||||
require.True(t, ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStateFn(t *testing.T) {
|
|
||||||
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
|
||||||
if skip {
|
|
||||||
t.Skip("skipping AppStateFn testing")
|
|
||||||
}
|
|
||||||
require.NoError(t, err, "simulation setup failed")
|
|
||||||
|
|
||||||
config.ChainID = SimAppChainID
|
|
||||||
config.Commit = true
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
db.Close()
|
|
||||||
require.NoError(t, os.RemoveAll(dir))
|
|
||||||
}()
|
|
||||||
|
|
||||||
app := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
|
||||||
require.Equal(t, appName, app.Name())
|
|
||||||
|
|
||||||
appStateFn := StateFn(app.AppCodec(), app.SimulationManager())
|
|
||||||
r := rand.New(rand.NewSource(seed))
|
|
||||||
accounts := RandomAccounts(r, rand.Intn(maxTestingAccounts))
|
|
||||||
appState, _, _, _ := appStateFn(r, accounts, config)
|
|
||||||
|
|
||||||
rawState := make(map[string]json.RawMessage)
|
|
||||||
err = json.Unmarshal(appState, &rawState)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
stakingStateBz, ok := rawState[stakingtypes.ModuleName]
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
stakingState := new(stakingtypes.GenesisState)
|
|
||||||
app.AppCodec().MustUnmarshalJSON(stakingStateBz, stakingState)
|
|
||||||
bondDenom := stakingState.Params.BondDenom
|
|
||||||
|
|
||||||
evmStateBz, ok := rawState[evmtypes.ModuleName]
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
evmState := new(evmtypes.GenesisState)
|
|
||||||
app.AppCodec().MustUnmarshalJSON(evmStateBz, evmState)
|
|
||||||
require.Equal(t, bondDenom, evmState.Params.EvmDenom)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRandomAccounts(t *testing.T) {
|
|
||||||
r := rand.New(rand.NewSource(seed))
|
|
||||||
accounts := RandomAccounts(r, rand.Intn(maxTestingAccounts))
|
|
||||||
for _, acc := range accounts {
|
|
||||||
_, ok := acc.PrivKey.(*ethsecp256k1.PrivKey)
|
|
||||||
require.True(t, ok)
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,7 +21,6 @@ import (
|
|||||||
|
|
||||||
"github.com/evmos/ethermint/x/evm/client/cli"
|
"github.com/evmos/ethermint/x/evm/client/cli"
|
||||||
"github.com/evmos/ethermint/x/evm/keeper"
|
"github.com/evmos/ethermint/x/evm/keeper"
|
||||||
"github.com/evmos/ethermint/x/evm/simulation"
|
|
||||||
"github.com/evmos/ethermint/x/evm/types"
|
"github.com/evmos/ethermint/x/evm/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,7 +64,7 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingCo
|
|||||||
|
|
||||||
// RegisterRESTRoutes performs a no-op as the EVM module doesn't expose REST
|
// RegisterRESTRoutes performs a no-op as the EVM module doesn't expose REST
|
||||||
// endpoints
|
// endpoints
|
||||||
func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) {
|
func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b AppModuleBasic) RegisterGRPCGatewayRoutes(c client.Context, serveMux *runtime.ServeMux) {
|
func (b AppModuleBasic) RegisterGRPCGatewayRoutes(c client.Context, serveMux *runtime.ServeMux) {
|
||||||
@ -114,7 +113,7 @@ func (AppModule) Name() string {
|
|||||||
|
|
||||||
// RegisterInvariants interface for registering invariants. Performs a no-op
|
// RegisterInvariants interface for registering invariants. Performs a no-op
|
||||||
// as the evm module doesn't expose invariants.
|
// as the evm module doesn't expose invariants.
|
||||||
func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
|
func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterServices registers a GRPC query service to respond to the
|
// RegisterServices registers a GRPC query service to respond to the
|
||||||
@ -134,7 +133,7 @@ func (AppModule) QuerierRoute() string { return types.RouterKey }
|
|||||||
|
|
||||||
// LegacyQuerierHandler returns nil as the evm module doesn't expose a legacy
|
// LegacyQuerierHandler returns nil as the evm module doesn't expose a legacy
|
||||||
// Querier.
|
// Querier.
|
||||||
func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier {
|
func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,28 +166,24 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RandomizedParams creates randomized evm param changes for the simulator.
|
// RandomizedParams creates randomized evm param changes for the simulator.
|
||||||
func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange {
|
func (AppModule) RandomizedParams(_ *rand.Rand) []simtypes.ParamChange {
|
||||||
return simulation.ParamChanges(r)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterStoreDecoder registers a decoder for evm module's types
|
// RegisterStoreDecoder registers a decoder for evm module's types
|
||||||
func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {
|
func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {
|
||||||
sdr[types.StoreKey] = simulation.NewDecodeStore()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProposalContents doesn't return any content functions for governance proposals.
|
// ProposalContents doesn't return any content functions for governance proposals.
|
||||||
func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent {
|
func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateGenesisState creates a randomized GenState of the evm module.
|
// GenerateGenesisState creates a randomized GenState of the evm module.
|
||||||
func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
|
func (AppModule) GenerateGenesisState(_ *module.SimulationState) {
|
||||||
simulation.RandomizedGenState(simState)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedOperations returns the all the evm module operations with their respective weights.
|
// WeightedOperations returns the all the evm module operations with their respective weights.
|
||||||
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
|
func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation {
|
||||||
return simulation.WeightedOperations(
|
return nil
|
||||||
simState.AppParams, simState.Cdc, am.ak, am.keeper,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package simulation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/kv"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/evmos/ethermint/x/evm/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's
|
|
||||||
// value to the corresponding EVM type.
|
|
||||||
func NewDecodeStore() func(kvA, kvB kv.Pair) string {
|
|
||||||
return func(kvA, kvB kv.Pair) string {
|
|
||||||
switch {
|
|
||||||
case bytes.Equal(kvA.Key[:1], types.KeyPrefixStorage):
|
|
||||||
storageA := common.BytesToHash(kvA.Value).Hex()
|
|
||||||
storageB := common.BytesToHash(kvB.Value).Hex()
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v\n%v", storageA, storageB)
|
|
||||||
case bytes.Equal(kvA.Key[:1], types.KeyPrefixCode):
|
|
||||||
codeHashA := common.Bytes2Hex(kvA.Value)
|
|
||||||
codeHashB := common.Bytes2Hex(kvB.Value)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v\n%v", codeHashA, codeHashB)
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("invalid evm key prefix %X", kvA.Key[:1]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package simulation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/kv"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/evmos/ethermint/x/evm/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestDecodeStore tests that evm simulation decoder decodes the key value pairs as expected.
|
|
||||||
func TestDecodeStore(t *testing.T) {
|
|
||||||
dec := NewDecodeStore()
|
|
||||||
|
|
||||||
hash := common.BytesToHash([]byte("hash"))
|
|
||||||
code := common.Bytes2Hex([]byte{1, 2, 3})
|
|
||||||
|
|
||||||
kvPairs := kv.Pairs{
|
|
||||||
Pairs: []kv.Pair{
|
|
||||||
{Key: types.KeyPrefixCode, Value: common.FromHex(code)},
|
|
||||||
{Key: types.KeyPrefixStorage, Value: hash.Bytes()},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
expectedLog string
|
|
||||||
}{
|
|
||||||
{"Code", fmt.Sprintf("%v\n%v", code, code)},
|
|
||||||
{"Storage", fmt.Sprintf("%v\n%v", hash, hash)},
|
|
||||||
{"other", ""},
|
|
||||||
}
|
|
||||||
for i, tt := range tests {
|
|
||||||
i, tt := i, tt
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
switch i {
|
|
||||||
case len(tests) - 1:
|
|
||||||
require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name)
|
|
||||||
default:
|
|
||||||
require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package simulation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
|
||||||
|
|
||||||
"github.com/evmos/ethermint/x/evm/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
extraEIPsKey = "extra_eips"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GenExtraEIPs defines a set of extra EIPs with 50% probability
|
|
||||||
func GenExtraEIPs(r *rand.Rand) []int64 {
|
|
||||||
var extraEIPs []int64
|
|
||||||
// 50% chance of having extra EIPs
|
|
||||||
if r.Intn(2) == 0 {
|
|
||||||
extraEIPs = []int64{1344, 1884, 2200, 2929, 3198, 3529}
|
|
||||||
}
|
|
||||||
return extraEIPs
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenEnableCreate enables the EnableCreate param with 80% probability
|
|
||||||
func GenEnableCreate(r *rand.Rand) bool {
|
|
||||||
// 80% chance of enabling create contract
|
|
||||||
enableCreate := r.Intn(100) < 80
|
|
||||||
return enableCreate
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenEnableCall enables the EnableCall param with 80% probability
|
|
||||||
func GenEnableCall(r *rand.Rand) bool {
|
|
||||||
// 80% chance of enabling evm account transfer and calling contract
|
|
||||||
enableCall := r.Intn(100) < 80
|
|
||||||
return enableCall
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomizedGenState generates a random GenesisState for the EVM module
|
|
||||||
func RandomizedGenState(simState *module.SimulationState) {
|
|
||||||
// evm params
|
|
||||||
var extraEIPs []int64
|
|
||||||
|
|
||||||
simState.AppParams.GetOrGenerate(
|
|
||||||
simState.Cdc, extraEIPsKey, &extraEIPs, simState.Rand,
|
|
||||||
func(r *rand.Rand) { extraEIPs = GenExtraEIPs(r) },
|
|
||||||
)
|
|
||||||
|
|
||||||
params := types.NewParams(types.DefaultEVMDenom, true, true, types.DefaultChainConfig(), extraEIPs...)
|
|
||||||
evmGenesis := types.NewGenesisState(params, []types.GenesisAccount{})
|
|
||||||
|
|
||||||
bz, err := json.MarshalIndent(evmGenesis, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, bz)
|
|
||||||
|
|
||||||
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(evmGenesis)
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package simulation_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"math/rand"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
sdkmath "cosmossdk.io/math"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
|
||||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
|
||||||
"github.com/evmos/ethermint/x/evm/simulation"
|
|
||||||
"github.com/evmos/ethermint/x/evm/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState.
|
|
||||||
// Abonormal scenarios are not tested here.
|
|
||||||
func TestRandomizedGenState(t *testing.T) {
|
|
||||||
registry := codectypes.NewInterfaceRegistry()
|
|
||||||
types.RegisterInterfaces(registry)
|
|
||||||
cdc := codec.NewProtoCodec(registry)
|
|
||||||
|
|
||||||
s := rand.NewSource(1)
|
|
||||||
r := rand.New(s)
|
|
||||||
|
|
||||||
simState := module.SimulationState{
|
|
||||||
AppParams: make(simtypes.AppParams),
|
|
||||||
Cdc: cdc,
|
|
||||||
Rand: r,
|
|
||||||
NumBonded: 3,
|
|
||||||
Accounts: simtypes.RandomAccounts(r, 3),
|
|
||||||
InitialStake: sdkmath.NewInt(1000),
|
|
||||||
GenState: make(map[string]json.RawMessage),
|
|
||||||
}
|
|
||||||
|
|
||||||
simulation.RandomizedGenState(&simState)
|
|
||||||
|
|
||||||
var evmGenesis types.GenesisState
|
|
||||||
simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &evmGenesis)
|
|
||||||
|
|
||||||
require.Equal(t, true, evmGenesis.Params.GetEnableCreate())
|
|
||||||
require.Equal(t, true, evmGenesis.Params.GetEnableCall())
|
|
||||||
require.Equal(t, types.DefaultEVMDenom, evmGenesis.Params.GetEvmDenom())
|
|
||||||
require.Equal(t, simulation.GenExtraEIPs(r), evmGenesis.Params.GetExtraEIPs())
|
|
||||||
require.Equal(t, types.DefaultChainConfig(), evmGenesis.Params.GetChainConfig())
|
|
||||||
|
|
||||||
require.Equal(t, len(evmGenesis.Accounts), 0)
|
|
||||||
}
|
|
@ -1,313 +0,0 @@
|
|||||||
package simulation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
sdkmath "cosmossdk.io/math"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
|
||||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
|
||||||
sdktx "github.com/cosmos/cosmos-sdk/types/tx"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth/tx"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
|
||||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/evmos/ethermint/encoding"
|
|
||||||
"github.com/evmos/ethermint/tests"
|
|
||||||
"github.com/evmos/ethermint/x/evm/keeper"
|
|
||||||
"github.com/evmos/ethermint/x/evm/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
/* #nosec */
|
|
||||||
OpWeightMsgEthSimpleTransfer = "op_weight_msg_eth_simple_transfer"
|
|
||||||
/* #nosec */
|
|
||||||
OpWeightMsgEthCreateContract = "op_weight_msg_eth_create_contract"
|
|
||||||
/* #nosec */
|
|
||||||
OpWeightMsgEthCallContract = "op_weight_msg_eth_call_contract"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
WeightMsgEthSimpleTransfer = 50
|
|
||||||
WeightMsgEthCreateContract = 50
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrNoEnoughBalance = fmt.Errorf("no enough balance")
|
|
||||||
|
|
||||||
var maxWaitSeconds = 10
|
|
||||||
|
|
||||||
type simulateContext struct {
|
|
||||||
context sdk.Context
|
|
||||||
bapp *baseapp.BaseApp
|
|
||||||
rand *rand.Rand
|
|
||||||
keeper *keeper.Keeper
|
|
||||||
}
|
|
||||||
|
|
||||||
// WeightedOperations generate Two kinds of operations: SimulateEthSimpleTransfer, SimulateEthCreateContract.
|
|
||||||
// Contract call operations work as the future operations of SimulateEthCreateContract.
|
|
||||||
func WeightedOperations(
|
|
||||||
appParams simtypes.AppParams, cdc codec.JSONCodec, ak types.AccountKeeper, k *keeper.Keeper,
|
|
||||||
) simulation.WeightedOperations {
|
|
||||||
var (
|
|
||||||
weightMsgEthSimpleTransfer int
|
|
||||||
weightMsgEthCreateContract int
|
|
||||||
)
|
|
||||||
|
|
||||||
appParams.GetOrGenerate(cdc, OpWeightMsgEthSimpleTransfer, &weightMsgEthSimpleTransfer, nil,
|
|
||||||
func(_ *rand.Rand) {
|
|
||||||
weightMsgEthSimpleTransfer = WeightMsgEthSimpleTransfer
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
appParams.GetOrGenerate(cdc, OpWeightMsgEthCreateContract, &weightMsgEthCreateContract, nil,
|
|
||||||
func(_ *rand.Rand) {
|
|
||||||
weightMsgEthCreateContract = WeightMsgEthCreateContract
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return simulation.WeightedOperations{
|
|
||||||
simulation.NewWeightedOperation(
|
|
||||||
weightMsgEthSimpleTransfer,
|
|
||||||
SimulateEthSimpleTransfer(ak, k),
|
|
||||||
),
|
|
||||||
simulation.NewWeightedOperation(
|
|
||||||
weightMsgEthCreateContract,
|
|
||||||
SimulateEthCreateContract(ak, k),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimulateEthSimpleTransfer simulate simple eth account transferring gas token.
|
|
||||||
// It randomly choose sender, recipient and transferable amount.
|
|
||||||
// Other tx details like nonce, gasprice, gaslimit are calculated to get valid value.
|
|
||||||
func SimulateEthSimpleTransfer(ak types.AccountKeeper, k *keeper.Keeper) simtypes.Operation {
|
|
||||||
return func(
|
|
||||||
r *rand.Rand, bapp *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
|
|
||||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
|
||||||
simAccount, _ := simtypes.RandomAcc(r, accs)
|
|
||||||
var recipient simtypes.Account
|
|
||||||
if r.Intn(2) == 1 {
|
|
||||||
recipient, _ = simtypes.RandomAcc(r, accs)
|
|
||||||
} else {
|
|
||||||
recipient = simtypes.RandomAccounts(r, 1)[0]
|
|
||||||
}
|
|
||||||
from := common.BytesToAddress(simAccount.Address)
|
|
||||||
to := common.BytesToAddress(recipient.Address)
|
|
||||||
|
|
||||||
simulateContext := &simulateContext{ctx, bapp, r, k}
|
|
||||||
|
|
||||||
return SimulateEthTx(simulateContext, &from, &to, nil, (*hexutil.Bytes)(&[]byte{}), simAccount.PrivKey, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimulateEthCreateContract simulate create an ERC20 contract.
|
|
||||||
// It makes operationSimulateEthCallContract the future operations of SimulateEthCreateContract
|
|
||||||
// to ensure valid contract call.
|
|
||||||
func SimulateEthCreateContract(ak types.AccountKeeper, k *keeper.Keeper) simtypes.Operation {
|
|
||||||
return func(
|
|
||||||
r *rand.Rand, bapp *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
|
|
||||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
|
||||||
simAccount, _ := simtypes.RandomAcc(r, accs)
|
|
||||||
|
|
||||||
from := common.BytesToAddress(simAccount.Address)
|
|
||||||
nonce := k.GetNonce(ctx, from)
|
|
||||||
|
|
||||||
ctorArgs, err := types.ERC20Contract.ABI.Pack("", from, sdkmath.NewIntWithDecimal(1000, 18).BigInt())
|
|
||||||
if err != nil {
|
|
||||||
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "can not pack owner and supply"), nil, err
|
|
||||||
}
|
|
||||||
data := types.ERC20Contract.Bin
|
|
||||||
data = append(data, ctorArgs...)
|
|
||||||
|
|
||||||
simulateContext := &simulateContext{ctx, bapp, r, k}
|
|
||||||
|
|
||||||
fops := make([]simtypes.FutureOperation, 1)
|
|
||||||
whenCall := ctx.BlockHeader().Time.Add(time.Duration(r.Intn(maxWaitSeconds)+1) * time.Second)
|
|
||||||
contractAddr := crypto.CreateAddress(from, nonce)
|
|
||||||
var tokenReceipient simtypes.Account
|
|
||||||
if r.Intn(2) == 1 {
|
|
||||||
tokenReceipient, _ = simtypes.RandomAcc(r, accs)
|
|
||||||
} else {
|
|
||||||
tokenReceipient = simtypes.RandomAccounts(r, 1)[0]
|
|
||||||
}
|
|
||||||
receipientAddr := common.BytesToAddress(tokenReceipient.Address)
|
|
||||||
fops[0] = simtypes.FutureOperation{
|
|
||||||
BlockTime: whenCall,
|
|
||||||
Op: operationSimulateEthCallContract(k, &contractAddr, &receipientAddr, nil),
|
|
||||||
}
|
|
||||||
return SimulateEthTx(simulateContext, &from, nil, nil, (*hexutil.Bytes)(&data), simAccount.PrivKey, fops)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// operationSimulateEthCallContract simulate calling an contract.
|
|
||||||
// It is always calling an ERC20 contract.
|
|
||||||
func operationSimulateEthCallContract(k *keeper.Keeper, contractAddr, to *common.Address, amount *big.Int) simtypes.Operation {
|
|
||||||
return func(
|
|
||||||
r *rand.Rand, bapp *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
|
|
||||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
|
||||||
simAccount, _ := simtypes.RandomAcc(r, accs)
|
|
||||||
|
|
||||||
from := common.BytesToAddress(simAccount.Address)
|
|
||||||
|
|
||||||
ctorArgs, err := types.ERC20Contract.ABI.Pack("transfer", to, amount)
|
|
||||||
if err != nil {
|
|
||||||
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "can not pack method and args"), nil, err
|
|
||||||
}
|
|
||||||
data := types.ERC20Contract.Bin
|
|
||||||
data = append(data, ctorArgs...)
|
|
||||||
|
|
||||||
simulateContext := &simulateContext{ctx, bapp, r, k}
|
|
||||||
|
|
||||||
return SimulateEthTx(simulateContext, &from, contractAddr, nil, (*hexutil.Bytes)(&data), simAccount.PrivKey, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimulateEthTx creates valid ethereum tx and pack it as cosmos tx, and deliver it.
|
|
||||||
func SimulateEthTx(
|
|
||||||
ctx *simulateContext, from, to *common.Address, amount *big.Int, data *hexutil.Bytes, prv cryptotypes.PrivKey, fops []simtypes.FutureOperation,
|
|
||||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
|
||||||
ethTx, err := CreateRandomValidEthTx(ctx, from, nil, nil, data)
|
|
||||||
if err == ErrNoEnoughBalance {
|
|
||||||
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "no enough balance"), nil, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "can not create valid eth tx"), nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
txConfig := encoding.MakeConfig(module.NewBasicManager()).TxConfig
|
|
||||||
txBuilder := txConfig.NewTxBuilder()
|
|
||||||
signedTx, err := GetSignedTx(ctx, txBuilder, ethTx, prv)
|
|
||||||
if err != nil {
|
|
||||||
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "can not sign ethereum tx"), nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = ctx.bapp.SimDeliver(txConfig.TxEncoder(), signedTx)
|
|
||||||
if err != nil {
|
|
||||||
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "failed to deliver tx"), nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return simtypes.OperationMsg{}, fops, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRandomValidEthTx create the ethereum tx with valid random values
|
|
||||||
func CreateRandomValidEthTx(ctx *simulateContext,
|
|
||||||
from,
|
|
||||||
to *common.Address,
|
|
||||||
amount *big.Int,
|
|
||||||
data *hexutil.Bytes,
|
|
||||||
) (ethTx *types.MsgEthereumTx, err error) {
|
|
||||||
gasCap := ctx.rand.Uint64()
|
|
||||||
estimateGas, err := EstimateGas(ctx, from, to, data, gasCap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// we suppose that gasLimit should be larger than estimateGas to ensure tx validity
|
|
||||||
gasLimit := estimateGas + uint64(ctx.rand.Intn(int(sdktx.MaxGasWanted-estimateGas)))
|
|
||||||
ethChainID := ctx.keeper.ChainID()
|
|
||||||
chainConfig := ctx.keeper.GetParams(ctx.context).ChainConfig.EthereumConfig(ethChainID)
|
|
||||||
gasPrice := ctx.keeper.GetBaseFee(ctx.context, chainConfig)
|
|
||||||
gasFeeCap := new(big.Int).Add(gasPrice, big.NewInt(int64(ctx.rand.Int())))
|
|
||||||
gasTipCap := big.NewInt(int64(ctx.rand.Int()))
|
|
||||||
nonce := ctx.keeper.GetNonce(ctx.context, *from)
|
|
||||||
|
|
||||||
if amount == nil {
|
|
||||||
amount, err = RandomTransferableAmount(ctx, *from, estimateGas, gasFeeCap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ethTx = types.NewTx(ethChainID, nonce, to, amount, gasLimit, gasPrice, gasFeeCap, gasTipCap, *data, nil)
|
|
||||||
ethTx.From = from.String()
|
|
||||||
return ethTx, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EstimateGas estimates the gas used by quering the keeper.
|
|
||||||
func EstimateGas(ctx *simulateContext, from, to *common.Address, data *hexutil.Bytes, gasCap uint64) (gas uint64, err error) {
|
|
||||||
args, err := json.Marshal(&types.TransactionArgs{To: to, From: from, Data: data})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := ctx.keeper.EstimateGas(sdk.WrapSDKContext(ctx.context), &types.EthCallRequest{
|
|
||||||
Args: args,
|
|
||||||
GasCap: gasCap,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return res.Gas, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomTransferableAmount generates a random valid transferable amount.
|
|
||||||
// Transferable amount is between the range [0, spendable), spendable = balance - gasFeeCap * GasLimit.
|
|
||||||
func RandomTransferableAmount(ctx *simulateContext, address common.Address, estimateGas uint64, gasFeeCap *big.Int) (amount *big.Int, err error) {
|
|
||||||
balance := ctx.keeper.GetBalance(ctx.context, address)
|
|
||||||
feeLimit := new(big.Int).Mul(gasFeeCap, big.NewInt(int64(estimateGas)))
|
|
||||||
if (feeLimit.Cmp(balance)) > 0 {
|
|
||||||
return nil, ErrNoEnoughBalance
|
|
||||||
}
|
|
||||||
spendable := new(big.Int).Sub(balance, feeLimit)
|
|
||||||
if spendable.Cmp(big.NewInt(0)) == 0 {
|
|
||||||
amount = new(big.Int).Set(spendable)
|
|
||||||
return amount, nil
|
|
||||||
}
|
|
||||||
simAmount, err := simtypes.RandPositiveInt(ctx.rand, sdkmath.NewIntFromBigInt(spendable))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
amount = simAmount.BigInt()
|
|
||||||
return amount, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSignedTx sign the ethereum tx and packs it as a signing.Tx .
|
|
||||||
func GetSignedTx(
|
|
||||||
ctx *simulateContext,
|
|
||||||
txBuilder client.TxBuilder,
|
|
||||||
msg *types.MsgEthereumTx,
|
|
||||||
prv cryptotypes.PrivKey,
|
|
||||||
) (signedTx signing.Tx, err error) {
|
|
||||||
builder, ok := txBuilder.(tx.ExtensionOptionsTxBuilder)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("can not initiate ExtensionOptionsTxBuilder")
|
|
||||||
}
|
|
||||||
option, err := codectypes.NewAnyWithValue(&types.ExtensionOptionsEthereumTx{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
builder.SetExtensionOptions(option)
|
|
||||||
|
|
||||||
if err := msg.Sign(ethtypes.LatestSignerForChainID(ctx.keeper.ChainID()), tests.NewSigner(prv)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = builder.SetMsgs(msg); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
txData, err := types.UnpackTxData(msg.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fees := sdk.NewCoins(sdk.NewCoin(ctx.keeper.GetParams(ctx.context).EvmDenom, sdkmath.NewIntFromBigInt(txData.Fee())))
|
|
||||||
builder.SetFeeAmount(fees)
|
|
||||||
builder.SetGasLimit(msg.GetGas())
|
|
||||||
|
|
||||||
signedTx = builder.GetTx()
|
|
||||||
return signedTx, nil
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package simulation
|
|
||||||
|
|
||||||
// DONTCOVER
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
amino "github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
|
||||||
"github.com/evmos/ethermint/x/evm/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParamChanges defines the parameters that can be modified by param change proposals
|
|
||||||
// on the simulation.
|
|
||||||
func ParamChanges(r *rand.Rand) []simtypes.ParamChange {
|
|
||||||
return []simtypes.ParamChange{
|
|
||||||
simulation.NewSimParamChange(types.ModuleName, string(types.ParamStoreKeyExtraEIPs),
|
|
||||||
func(r *rand.Rand) string {
|
|
||||||
extraEIPs := GenExtraEIPs(r)
|
|
||||||
amino := amino.NewLegacyAmino()
|
|
||||||
bz, err := amino.MarshalJSON(extraEIPs)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return string(bz)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
simulation.NewSimParamChange(types.ModuleName, string(types.ParamStoreKeyEnableCreate),
|
|
||||||
func(r *rand.Rand) string {
|
|
||||||
return fmt.Sprintf("%v", GenEnableCreate(r))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
simulation.NewSimParamChange(types.ModuleName, string(types.ParamStoreKeyEnableCall),
|
|
||||||
func(r *rand.Rand) string {
|
|
||||||
return fmt.Sprintf("%v", GenEnableCall(r))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package simulation_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/evmos/ethermint/x/evm/simulation"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestParamChanges tests the paramChanges are generated as expected.
|
|
||||||
func TestParamChanges(t *testing.T) {
|
|
||||||
s := rand.NewSource(1)
|
|
||||||
r := rand.New(s)
|
|
||||||
|
|
||||||
extraEIPs := simulation.GenExtraEIPs(r)
|
|
||||||
bz, err := json.Marshal(extraEIPs)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := []struct {
|
|
||||||
composedKey string
|
|
||||||
key string
|
|
||||||
simValue string
|
|
||||||
subspace string
|
|
||||||
}{
|
|
||||||
{"evm/EnableExtraEIPs", "EnableExtraEIPs", string(bz), "evm"},
|
|
||||||
{"evm/EnableCreate", "EnableCreate", fmt.Sprintf("%v", simulation.GenEnableCreate(r)), "evm"},
|
|
||||||
{"evm/EnableCall", "EnableCall", fmt.Sprintf("%v", simulation.GenEnableCall(r)), "evm"},
|
|
||||||
}
|
|
||||||
|
|
||||||
paramChanges := simulation.ParamChanges(r)
|
|
||||||
|
|
||||||
require.Len(t, paramChanges, 3)
|
|
||||||
|
|
||||||
for i, p := range paramChanges {
|
|
||||||
require.Equal(t, expected[i].composedKey, p.ComposedKey())
|
|
||||||
require.Equal(t, expected[i].key, p.Key())
|
|
||||||
require.Equal(t, expected[i].simValue, p.SimValue()(r))
|
|
||||||
require.Equal(t, expected[i].subspace, p.Subspace())
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,7 +21,6 @@ import (
|
|||||||
|
|
||||||
"github.com/evmos/ethermint/x/feemarket/client/cli"
|
"github.com/evmos/ethermint/x/feemarket/client/cli"
|
||||||
"github.com/evmos/ethermint/x/feemarket/keeper"
|
"github.com/evmos/ethermint/x/feemarket/keeper"
|
||||||
"github.com/evmos/ethermint/x/feemarket/simulation"
|
|
||||||
"github.com/evmos/ethermint/x/feemarket/types"
|
"github.com/evmos/ethermint/x/feemarket/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,7 +64,7 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingCo
|
|||||||
|
|
||||||
// RegisterRESTRoutes performs a no-op as the EVM module doesn't expose REST
|
// RegisterRESTRoutes performs a no-op as the EVM module doesn't expose REST
|
||||||
// endpoints
|
// endpoints
|
||||||
func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) {
|
func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b AppModuleBasic) RegisterGRPCGatewayRoutes(c client.Context, serveMux *runtime.ServeMux) {
|
func (b AppModuleBasic) RegisterGRPCGatewayRoutes(c client.Context, serveMux *runtime.ServeMux) {
|
||||||
@ -110,7 +109,7 @@ func (AppModule) Name() string {
|
|||||||
|
|
||||||
// RegisterInvariants interface for registering invariants. Performs a no-op
|
// RegisterInvariants interface for registering invariants. Performs a no-op
|
||||||
// as the fee market module doesn't expose invariants.
|
// as the fee market module doesn't expose invariants.
|
||||||
func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {}
|
func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
|
||||||
|
|
||||||
// RegisterServices registers the GRPC query service and migrator service to respond to the
|
// RegisterServices registers the GRPC query service and migrator service to respond to the
|
||||||
// module-specific GRPC queries and handle the upgrade store migration for the module.
|
// module-specific GRPC queries and handle the upgrade store migration for the module.
|
||||||
@ -128,7 +127,7 @@ func (AppModule) QuerierRoute() string { return types.RouterKey }
|
|||||||
|
|
||||||
// LegacyQuerierHandler returns nil as the fee market module doesn't expose a legacy
|
// LegacyQuerierHandler returns nil as the fee market module doesn't expose a legacy
|
||||||
// Querier.
|
// Querier.
|
||||||
func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier {
|
func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,24 +161,23 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RandomizedParams creates randomized fee market param changes for the simulator.
|
// RandomizedParams creates randomized fee market param changes for the simulator.
|
||||||
func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange {
|
func (AppModule) RandomizedParams(_ *rand.Rand) []simtypes.ParamChange {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterStoreDecoder registers a decoder for fee market module's types
|
// RegisterStoreDecoder registers a decoder for fee market module's types
|
||||||
func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {}
|
func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {}
|
||||||
|
|
||||||
// ProposalContents doesn't return any content functions for governance proposals.
|
// ProposalContents doesn't return any content functions for governance proposals.
|
||||||
func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent {
|
func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateGenesisState creates a randomized GenState of the fee market module.
|
// GenerateGenesisState creates a randomized GenState of the fee market module.
|
||||||
func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
|
func (AppModule) GenerateGenesisState(_ *module.SimulationState) {
|
||||||
simulation.RandomizedGenState(simState)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedOperations returns the all the fee market module operations with their respective weights.
|
// WeightedOperations returns the all the fee market module operations with their respective weights.
|
||||||
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
|
func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
package simulation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
|
||||||
|
|
||||||
"github.com/evmos/ethermint/x/feemarket/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RandomizedGenState generates a random GenesisState for nft
|
|
||||||
func RandomizedGenState(simState *module.SimulationState) {
|
|
||||||
params := types.NewParams(simState.Rand.Uint32()%2 == 0,
|
|
||||||
simState.Rand.Uint32(),
|
|
||||||
simState.Rand.Uint32(),
|
|
||||||
simState.Rand.Uint64(),
|
|
||||||
simState.Rand.Int63(),
|
|
||||||
sdk.ZeroDec(),
|
|
||||||
types.DefaultMinGasMultiplier)
|
|
||||||
|
|
||||||
blockGas := simState.Rand.Uint64()
|
|
||||||
feemarketGenesis := types.NewGenesisState(params, blockGas)
|
|
||||||
|
|
||||||
bz, err := json.MarshalIndent(feemarketGenesis, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, bz)
|
|
||||||
|
|
||||||
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(feemarketGenesis)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user