fix: simulations don't generate EVM transactions (#996)
* use SimAppChainId instead of the default cosmos-sdk simulation chainid * implement custom RandomGenesisAccounts for auth module simulation * use customized RandomGenesisAccounts * generate random ethereum txs operations * use sdk.DefaultBondDenom * implement WeightedOperations * fix self assignment * use customized RandomAccounts to generate accounts with ethsecp256k1 private key * RandomAccounts generate random accounts with ethsecp256k1 private key * implement SimulateEthSimpleTransfer * implement SimulateEthCreateContract * refactor and implement operationSimulateEthCallContract as future operations of SimulateEthCreateContract * Update app/app.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update app/test_helpers.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update x/evm/simulation/operations.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update x/evm/simulation/operations.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update app/test_helpers.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * fix r.Read return only 1 result * fix linter errors * change SimulateContext to private * return transferable amount immediately * fix linter errors * fix linter errors: append assign to different slice * fix linter error: remove else block * use gofumpt * Update x/evm/simulation/operations.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update x/evm/simulation/operations.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update x/evm/simulation/operations.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update x/evm/simulation/operations.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * fix recipient typo * add nosec comment to escape Potential hardcoded credentials check * add comments * use SimAppChainID as valid chainID * do not specify genesis.json to create random genesis state for simulation * sub estimateGas to avoid overflow * correct RandomGenesisAccounts comments Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * correct app/test_helpers.go comments Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * remove NewTxConfig * implement RegisterStoreDecoder * remove WeightMsgEthCallContract and adjust weights * use gofmt * update godoc * use types.DefaultEVMDenom in evm genesis params Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * use types.DefaultEVMDenom Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * update godoc * Update x/evm/simulation/genesis.go * replace evmdenom with bonddenom in AppStateFn * fix AppStateFn * fix lint error * add ParamChanges for RandomizedParams * rename app/test_helpers.go -> app/utils.go * testing for RandomGenesisAccounts * use cdc for marshaling * lint * changelog * changelog 2 Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: crypto-facs <84574577+crypto-facs@users.noreply.github.com> Co-authored-by: Freddy Caceres <freddy.caceres@crypto.com> Co-authored-by: Federico Kunze Küllmer <federico.kunze94@gmail.com>
This commit is contained in:
parent
e4abbe1bbc
commit
eaca3e09e2
@ -45,11 +45,11 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* (rpc) [tharsis#1050](https://github.com/tharsis/ethermint/pull/1050) `eth_getBlockByNumber` fix on batch transactions
|
* (rpc) [tharsis#1050](https://github.com/tharsis/ethermint/pull/1050) `eth_getBlockByNumber` fix on batch transactions
|
||||||
|
* (app) [tharsis#658](https://github.com/tharsis/ethermint/issues/658) Support simulations for the EVM.
|
||||||
|
|
||||||
### API Breaking
|
### API Breaking
|
||||||
|
|
||||||
* (evm) [tharsis#1051](https://github.com/tharsis/ethermint/pull/1051) Context block height fix on TraceTx. Removes `tx_index` on `QueryTraceTxRequest` proto type.
|
* (evm) [tharsis#1051](https://github.com/tharsis/ethermint/pull/1051) Context block height fix on TraceTx. Removes `tx_index` on `QueryTraceTxRequest` proto type.
|
||||||
|
|
||||||
|
|
||||||
## [v0.13.0] - 2022-04-05
|
## [v0.13.0] - 2022-04-05
|
||||||
|
|
||||||
|
10
Makefile
10
Makefile
@ -342,10 +342,9 @@ test-sim-nondeterminism:
|
|||||||
@go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \
|
@go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \
|
||||||
-NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h
|
-NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h
|
||||||
|
|
||||||
test-sim-custom-genesis-fast:
|
test-sim-random-genesis-fast:
|
||||||
@echo "Running custom genesis simulation..."
|
@echo "Running custom genesis simulation..."
|
||||||
@echo "By default, ${HOME}/.$(ETHERMINT_DIR)/config/genesis.json will be used."
|
@go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation \
|
||||||
@go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation -Genesis=${HOME}/.$(ETHERMINT_DIR)/config/genesis.json \
|
|
||||||
-Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h
|
-Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h
|
||||||
|
|
||||||
test-sim-import-export: runsim
|
test-sim-import-export: runsim
|
||||||
@ -356,10 +355,9 @@ test-sim-after-import: runsim
|
|||||||
@echo "Running application simulation-after-import. This may take several minutes..."
|
@echo "Running application simulation-after-import. This may take several minutes..."
|
||||||
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestAppSimulationAfterImport
|
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestAppSimulationAfterImport
|
||||||
|
|
||||||
test-sim-custom-genesis-multi-seed: runsim
|
test-sim-random-genesis-multi-seed: runsim
|
||||||
@echo "Running multi-seed custom genesis simulation..."
|
@echo "Running multi-seed custom genesis simulation..."
|
||||||
@echo "By default, ${HOME}/.$(ETHERMINT_DIR)/config/genesis.json will be used."
|
@$(BINDIR)/runsim -SimAppPkg=$(SIMAPP) -ExitOnFail 400 5 TestFullAppSimulation
|
||||||
@$(BINDIR)/runsim -Genesis=${HOME}/.$(ETHERMINT_DIR)/config/genesis.json -SimAppPkg=$(SIMAPP) -ExitOnFail 400 5 TestFullAppSimulation
|
|
||||||
|
|
||||||
test-sim-multi-seed-long: runsim
|
test-sim-multi-seed-long: runsim
|
||||||
@echo "Running long multi-seed application simulation. This may take awhile!"
|
@echo "Running long multi-seed application simulation. This may take awhile!"
|
||||||
|
@ -536,7 +536,8 @@ func NewEthermintApp(
|
|||||||
// NOTE: this is not required apps that don't use the simulator for fuzz testing
|
// NOTE: this is not required apps that don't use the simulator for fuzz testing
|
||||||
// transactions
|
// transactions
|
||||||
app.sm = module.NewSimulationManager(
|
app.sm = module.NewSimulationManager(
|
||||||
auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts),
|
// Use custom RandomGenesisAccounts so that auth module could create random EthAccounts in genesis state when genesis.json not specified
|
||||||
|
auth.NewAppModule(appCodec, app.AccountKeeper, RandomGenesisAccounts),
|
||||||
bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper),
|
bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper),
|
||||||
capability.NewAppModule(appCodec, *app.CapabilityKeeper),
|
capability.NewAppModule(appCodec, *app.CapabilityKeeper),
|
||||||
gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper),
|
gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper),
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/cosmos/cosmos-sdk/simapp/params"
|
"github.com/cosmos/cosmos-sdk/simapp/params"
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
|
||||||
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"
|
||||||
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
|
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
|
||||||
@ -72,6 +71,8 @@ func TestFullAppSimulation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.NoError(t, err, "simulation setup failed")
|
require.NoError(t, err, "simulation setup failed")
|
||||||
|
|
||||||
|
config.ChainID = SimAppChainID
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
db.Close()
|
db.Close()
|
||||||
require.NoError(t, os.RemoveAll(dir))
|
require.NoError(t, os.RemoveAll(dir))
|
||||||
@ -85,8 +86,8 @@ func TestFullAppSimulation(t *testing.T) {
|
|||||||
t,
|
t,
|
||||||
os.Stdout,
|
os.Stdout,
|
||||||
app.BaseApp,
|
app.BaseApp,
|
||||||
simapp.AppStateFn(app.AppCodec(), app.SimulationManager()),
|
StateFn(app.AppCodec(), app.SimulationManager()),
|
||||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
simapp.SimulationOperations(app, app.AppCodec(), config),
|
||||||
app.ModuleAccountAddrs(),
|
app.ModuleAccountAddrs(),
|
||||||
config,
|
config,
|
||||||
@ -110,6 +111,8 @@ func TestAppImportExport(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.NoError(t, err, "simulation setup failed")
|
require.NoError(t, err, "simulation setup failed")
|
||||||
|
|
||||||
|
config.ChainID = SimAppChainID
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
db.Close()
|
db.Close()
|
||||||
require.NoError(t, os.RemoveAll(dir))
|
require.NoError(t, os.RemoveAll(dir))
|
||||||
@ -123,8 +126,8 @@ func TestAppImportExport(t *testing.T) {
|
|||||||
t,
|
t,
|
||||||
os.Stdout,
|
os.Stdout,
|
||||||
app.BaseApp,
|
app.BaseApp,
|
||||||
simapp.AppStateFn(app.AppCodec(), app.SimulationManager()),
|
StateFn(app.AppCodec(), app.SimulationManager()),
|
||||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
simapp.SimulationOperations(app, app.AppCodec(), config),
|
||||||
app.ModuleAccountAddrs(),
|
app.ModuleAccountAddrs(),
|
||||||
config,
|
config,
|
||||||
@ -163,8 +166,8 @@ func TestAppImportExport(t *testing.T) {
|
|||||||
err = json.Unmarshal(exported.AppState, &genesisState)
|
err = json.Unmarshal(exported.AppState, &genesisState)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()})
|
ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight(), ChainID: SimAppChainID})
|
||||||
ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()})
|
ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight(), ChainID: SimAppChainID})
|
||||||
newApp.mm.InitGenesis(ctxB, app.AppCodec(), genesisState)
|
newApp.mm.InitGenesis(ctxB, app.AppCodec(), genesisState)
|
||||||
newApp.StoreConsensusParams(ctxB, exported.ConsensusParams)
|
newApp.StoreConsensusParams(ctxB, exported.ConsensusParams)
|
||||||
|
|
||||||
@ -210,6 +213,8 @@ func TestAppSimulationAfterImport(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.NoError(t, err, "simulation setup failed")
|
require.NoError(t, err, "simulation setup failed")
|
||||||
|
|
||||||
|
config.ChainID = SimAppChainID
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
db.Close()
|
db.Close()
|
||||||
require.NoError(t, os.RemoveAll(dir))
|
require.NoError(t, os.RemoveAll(dir))
|
||||||
@ -223,8 +228,8 @@ func TestAppSimulationAfterImport(t *testing.T) {
|
|||||||
t,
|
t,
|
||||||
os.Stdout,
|
os.Stdout,
|
||||||
app.BaseApp,
|
app.BaseApp,
|
||||||
simapp.AppStateFn(app.AppCodec(), app.SimulationManager()),
|
StateFn(app.AppCodec(), app.SimulationManager()),
|
||||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
simapp.SimulationOperations(app, app.AppCodec(), config),
|
||||||
app.ModuleAccountAddrs(),
|
app.ModuleAccountAddrs(),
|
||||||
config,
|
config,
|
||||||
@ -264,6 +269,7 @@ func TestAppSimulationAfterImport(t *testing.T) {
|
|||||||
require.Equal(t, appName, newApp.Name())
|
require.Equal(t, appName, newApp.Name())
|
||||||
|
|
||||||
newApp.InitChain(abci.RequestInitChain{
|
newApp.InitChain(abci.RequestInitChain{
|
||||||
|
ChainId: SimAppChainID,
|
||||||
AppStateBytes: exported.AppState,
|
AppStateBytes: exported.AppState,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -271,8 +277,8 @@ func TestAppSimulationAfterImport(t *testing.T) {
|
|||||||
t,
|
t,
|
||||||
os.Stdout,
|
os.Stdout,
|
||||||
newApp.BaseApp,
|
newApp.BaseApp,
|
||||||
simapp.AppStateFn(app.AppCodec(), app.SimulationManager()),
|
StateFn(app.AppCodec(), app.SimulationManager()),
|
||||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||||
simapp.SimulationOperations(newApp, newApp.AppCodec(), config),
|
simapp.SimulationOperations(newApp, newApp.AppCodec(), config),
|
||||||
app.ModuleAccountAddrs(),
|
app.ModuleAccountAddrs(),
|
||||||
config,
|
config,
|
||||||
@ -322,8 +328,8 @@ func TestAppStateDeterminism(t *testing.T) {
|
|||||||
t,
|
t,
|
||||||
os.Stdout,
|
os.Stdout,
|
||||||
app.BaseApp,
|
app.BaseApp,
|
||||||
simapp.AppStateFn(app.AppCodec(), app.SimulationManager()),
|
StateFn(app.AppCodec(), app.SimulationManager()),
|
||||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||||
simapp.SimulationOperations(app, app.AppCodec(), config),
|
simapp.SimulationOperations(app, app.AppCodec(), config),
|
||||||
app.ModuleAccountAddrs(),
|
app.ModuleAccountAddrs(),
|
||||||
config,
|
config,
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/simapp"
|
|
||||||
"github.com/tharsis/ethermint/encoding"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
|
||||||
dbm "github.com/tendermint/tm-db"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DefaultConsensusParams defines the default Tendermint consensus params used in
|
|
||||||
// EthermintApp testing.
|
|
||||||
var DefaultConsensusParams = &abci.ConsensusParams{
|
|
||||||
Block: &abci.BlockParams{
|
|
||||||
MaxBytes: 200000,
|
|
||||||
MaxGas: -1, // no limit
|
|
||||||
},
|
|
||||||
Evidence: &tmproto.EvidenceParams{
|
|
||||||
MaxAgeNumBlocks: 302400,
|
|
||||||
MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration
|
|
||||||
MaxBytes: 10000,
|
|
||||||
},
|
|
||||||
Validator: &tmproto.ValidatorParams{
|
|
||||||
PubKeyTypes: []string{
|
|
||||||
tmtypes.ABCIPubKeyTypeEd25519,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup initializes a new EthermintApp. A Nop logger is set in EthermintApp.
|
|
||||||
func Setup(isCheckTx bool, patchGenesis func(*EthermintApp, simapp.GenesisState) simapp.GenesisState) *EthermintApp {
|
|
||||||
db := dbm.NewMemDB()
|
|
||||||
app := NewEthermintApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{})
|
|
||||||
if !isCheckTx {
|
|
||||||
// init chain must be called to stop deliverState from being nil
|
|
||||||
genesisState := NewDefaultGenesisState()
|
|
||||||
if patchGenesis != nil {
|
|
||||||
genesisState = patchGenesis(app, genesisState)
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the chain
|
|
||||||
app.InitChain(
|
|
||||||
abci.RequestInitChain{
|
|
||||||
ChainId: "ethermint_9000-1",
|
|
||||||
Validators: []abci.ValidatorUpdate{},
|
|
||||||
ConsensusParams: DefaultConsensusParams,
|
|
||||||
AppStateBytes: stateBytes,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return app
|
|
||||||
}
|
|
169
app/utils.go
Normal file
169
app/utils.go
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||||
|
"github.com/cosmos/cosmos-sdk/simapp"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||||||
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
|
||||||
|
"github.com/tharsis/ethermint/encoding"
|
||||||
|
ethermint "github.com/tharsis/ethermint/types"
|
||||||
|
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||||
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
dbm "github.com/tendermint/tm-db"
|
||||||
|
"github.com/tharsis/ethermint/crypto/ethsecp256k1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultConsensusParams defines the default Tendermint consensus params used in
|
||||||
|
// EthermintApp testing.
|
||||||
|
var DefaultConsensusParams = &abci.ConsensusParams{
|
||||||
|
Block: &abci.BlockParams{
|
||||||
|
MaxBytes: 200000,
|
||||||
|
MaxGas: -1, // no limit
|
||||||
|
},
|
||||||
|
Evidence: &tmproto.EvidenceParams{
|
||||||
|
MaxAgeNumBlocks: 302400,
|
||||||
|
MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration
|
||||||
|
MaxBytes: 10000,
|
||||||
|
},
|
||||||
|
Validator: &tmproto.ValidatorParams{
|
||||||
|
PubKeyTypes: []string{
|
||||||
|
tmtypes.ABCIPubKeyTypeEd25519,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup initializes a new EthermintApp. A Nop logger is set in EthermintApp.
|
||||||
|
func Setup(isCheckTx bool, patchGenesis func(*EthermintApp, simapp.GenesisState) simapp.GenesisState) *EthermintApp {
|
||||||
|
db := dbm.NewMemDB()
|
||||||
|
app := NewEthermintApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{})
|
||||||
|
if !isCheckTx {
|
||||||
|
// init chain must be called to stop deliverState from being nil
|
||||||
|
genesisState := NewDefaultGenesisState()
|
||||||
|
if patchGenesis != nil {
|
||||||
|
genesisState = patchGenesis(app, genesisState)
|
||||||
|
}
|
||||||
|
|
||||||
|
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the chain
|
||||||
|
app.InitChain(
|
||||||
|
abci.RequestInitChain{
|
||||||
|
ChainId: "ethermint_9000-1",
|
||||||
|
Validators: []abci.ValidatorUpdate{},
|
||||||
|
ConsensusParams: DefaultConsensusParams,
|
||||||
|
AppStateBytes: stateBytes,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
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("staking 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
|
||||||
|
}
|
||||||
|
}
|
55
app/utils_test.go
Normal file
55
app/utils_test.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"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"
|
||||||
|
|
||||||
|
ethermint "github.com/tharsis/ethermint/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/simapp"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var maxTestingAccounts = 100
|
||||||
|
var seed = int64(233)
|
||||||
|
|
||||||
|
func TestRandomGenesisAccounts(t *testing.T) {
|
||||||
|
r := rand.New(rand.NewSource(seed))
|
||||||
|
accs := RandomAccounts(r, rand.Intn(maxTestingAccounts))
|
||||||
|
|
||||||
|
encodingConfig := MakeEncodingConfig()
|
||||||
|
appCodec := encodingConfig.Marshaler
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -170,11 +170,13 @@ 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(r *rand.Rand) []simtypes.ParamChange {
|
||||||
return nil
|
return simulation.ParamChanges(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(sdr 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(simState module.SimulationState) []simtypes.WeightedProposalContent {
|
||||||
@ -188,5 +190,7 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
|
|||||||
|
|
||||||
// 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(simState module.SimulationState) []simtypes.WeightedOperation {
|
||||||
return nil
|
return simulation.WeightedOperations(
|
||||||
|
simState.AppParams, simState.Cdc, am.ak, am.keeper,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
31
x/evm/simulation/decoder.go
Normal file
31
x/evm/simulation/decoder.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/kv"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/tharsis/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):
|
||||||
|
storageHashA := common.BytesToHash(kvA.Value).Hex()
|
||||||
|
storageHashB := common.BytesToHash(kvB.Value).Hex()
|
||||||
|
|
||||||
|
return fmt.Sprintf("%v\n%v", storageHashA, storageHashB)
|
||||||
|
case bytes.Equal(kvA.Key[:1], types.KeyPrefixCode):
|
||||||
|
codeHashA := common.BytesToHash(kvA.Value).Hex()
|
||||||
|
codeHashB := common.BytesToHash(kvB.Value).Hex()
|
||||||
|
|
||||||
|
return fmt.Sprintf("%v\n%v", codeHashA, codeHashB)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("invalid evm key prefix %X", kvA.Key[:1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,18 +3,26 @@ package simulation
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||||||
|
|
||||||
"github.com/tharsis/ethermint/x/evm/types"
|
"github.com/tharsis/ethermint/x/evm/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GenExtraEIPs randomly generates specific extra eips or not.
|
||||||
|
func genExtraEIPs(r *rand.Rand) []int64 {
|
||||||
|
var extraEIPs []int64
|
||||||
|
if r.Uint32()%2 == 0 {
|
||||||
|
extraEIPs = []int64{1344, 1884, 2200, 2929, 3198, 3529}
|
||||||
|
}
|
||||||
|
return extraEIPs
|
||||||
|
}
|
||||||
|
|
||||||
// RandomizedGenState generates a random GenesisState for nft
|
// RandomizedGenState generates a random GenesisState for nft
|
||||||
func RandomizedGenState(simState *module.SimulationState) {
|
func RandomizedGenState(simState *module.SimulationState) {
|
||||||
params := types.NewParams(types.DefaultEVMDenom, true, true, types.DefaultChainConfig())
|
extraEIPs := genExtraEIPs(simState.Rand)
|
||||||
if simState.Rand.Uint32()%2 == 0 {
|
params := types.NewParams(types.DefaultEVMDenom, true, true, types.DefaultChainConfig(), extraEIPs...)
|
||||||
params = types.NewParams(types.DefaultEVMDenom, true, true, types.DefaultChainConfig(), 1344, 1884, 2200, 2929, 3198, 3529)
|
|
||||||
}
|
|
||||||
evmGenesis := types.NewGenesisState(params, []types.GenesisAccount{})
|
evmGenesis := types.NewGenesisState(params, []types.GenesisAccount{})
|
||||||
|
|
||||||
bz, err := json.MarshalIndent(evmGenesis, "", " ")
|
bz, err := json.MarshalIndent(evmGenesis, "", " ")
|
||||||
|
299
x/evm/simulation/operations.go
Normal file
299
x/evm/simulation/operations.go
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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/tharsis/ethermint/encoding"
|
||||||
|
"github.com/tharsis/ethermint/server/config"
|
||||||
|
"github.com/tharsis/ethermint/tests"
|
||||||
|
"github.com/tharsis/ethermint/x/evm/keeper"
|
||||||
|
"github.com/tharsis/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, sdk.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.Deliver(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) {
|
||||||
|
estimateGas, err := EstimateGas(ctx, from, to, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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.BaseFee(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, gasLimit, 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) (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: config.DefaultGasCap,
|
||||||
|
})
|
||||||
|
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, gasLimit 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(gasLimit)))
|
||||||
|
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, sdk.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, sdk.NewIntFromBigInt(txData.Fee())))
|
||||||
|
builder.SetFeeAmount(fees)
|
||||||
|
builder.SetGasLimit(msg.GetGas())
|
||||||
|
|
||||||
|
signedTx = builder.GetTx()
|
||||||
|
return signedTx, nil
|
||||||
|
}
|
28
x/evm/simulation/params.go
Normal file
28
x/evm/simulation/params.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package simulation
|
||||||
|
|
||||||
|
// DONTCOVER
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||||
|
"github.com/tharsis/ethermint/x/evm/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
keyExtraEIPs = "ExtraEIPs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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, keyExtraEIPs,
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
return fmt.Sprintf("\"%d\"", genExtraEIPs(r))
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user