From a805bdef0237832bc55003527b3c501824dcc4ac Mon Sep 17 00:00:00 2001 From: poorphd Date: Wed, 26 Jul 2023 17:43:39 +0900 Subject: [PATCH 1/9] feat: add workflow to build, test, codecov for ci/cd --- .github/.codecov.yml | 23 +++++++ .github/workflows/build.yml | 40 ++++++++++++ .github/workflows/sims.yml | 121 ++++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 43 +++++++++++++ .gitignore | 4 ++ Makefile | 4 +- 6 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 .github/.codecov.yml create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/sims.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/.codecov.yml b/.github/.codecov.yml new file mode 100644 index 00000000..e98756f1 --- /dev/null +++ b/.github/.codecov.yml @@ -0,0 +1,23 @@ +# To validate: +# cat codecov.yml | curl --data-binary @- https://codecov.io/validate + +codecov: + notify: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "50...80" + + status: + project: + default: + target: auto + threshold: 1% + patch: + default: + enabled: no # disable patch since it is noisy and not correct + if_not_found: success + +comment: true \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..8846a32c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,40 @@ +name: Build Canto Core +# This workflow is run on pushes to main & every Pull Requests where a .go, .mod, .sum have been changed +on: + pull_request: + push: + branches: + - main + - release/** +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }}-build + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + go-arch: ["amd64"] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: "^1.18" + - uses: technote-space/get-diff-action@v6.1.2 + id: git_diff + with: + PATTERNS: | + **/*.go + go.mod + go.sum + **/go.mod + **/go.sum + **/Makefile + Makefile + - name: Build + if: env.GIT_DIFF + run: GOARCH=${{ matrix.go-arch }} LEDGER_ENABLED=false make build diff --git a/.github/workflows/sims.yml b/.github/workflows/sims.yml new file mode 100644 index 00000000..c9447eb0 --- /dev/null +++ b/.github/workflows/sims.yml @@ -0,0 +1,121 @@ +#name: Sims +# Sims workflow runs multiple types of simulations (nondeterminism, import-export, after-import) +# This workflow will run on all Pull Requests, if a .go, .mod or .sum file have been changed +# Temporary disable until it is fixed +#on: +# pull_request: +# push: +# branches: +# - main +# - develop +# +#jobs: +# build: +# runs-on: ubuntu-latest +# if: "!contains(github.event.head_commit.message, 'skip-sims')" +# steps: +# - uses: actions/checkout@v2 +# - uses: actions/setup-go@v2.1.3 +# with: +# go-version: 1.18 +# - name: Display go version +# run: go version +# - run: make build +# +# install-runsim: +# runs-on: ubuntu-latest +# needs: build +# steps: +# - uses: actions/setup-go@v2.1.3 +# with: +# go-version: 1.18 +# - name: Display go version +# run: go version +# - name: Install runsim +# run: export GO111MODULE="on" && go install github.com/cosmos/tools/cmd/runsim@v1.0.0 +# - uses: actions/cache@v2.1.6 +# with: +# path: ~/go/bin +# key: ${{ runner.os }}-go-runsim-binary +# +# test-sim-nondeterminism: +# runs-on: ubuntu-latest +# needs: [build, install-runsim] +# steps: +# - uses: actions/checkout@v2 +# - uses: actions/setup-go@v2.1.3 +# with: +# go-version: 1.18 +# - name: Display go version +# run: go version +# - uses: technote-space/get-diff-action@v4 +# with: +# PATTERNS: | +# **/**.go +# go.mod +# go.sum +# - uses: actions/cache@v2.1.6 +# with: +# path: ~/go/bin +# key: ${{ runner.os }}-go-runsim-binary +# if: env.GIT_DIFF +# - name: test-sim-nondeterminism +# run: | +# make test-sim-nondeterminism +# if: env.GIT_DIFF +# +# test-sim-import-export: +# runs-on: ubuntu-latest +# needs: [build, install-runsim] +# steps: +# - uses: actions/checkout@v2 +# - uses: actions/setup-go@v2.1.3 +# with: +# go-version: 1.18 +# - name: Display go version +# run: go version +# - uses: technote-space/get-diff-action@v4 +# with: +# SUFFIX_FILTER: | +# **/**.go +# go.mod +# go.sum +# SET_ENV_NAME_INSERTIONS: 1 +# SET_ENV_NAME_LINES: 1 +# - uses: actions/cache@v2.1.6 +# with: +# path: ~/go/bin +# key: ${{ runner.os }}-go-runsim-binary +# if: env.GIT_DIFF +# - name: test-sim-import-export +# run: | +# make test-sim-import-export +# if: env.GIT_DIFF +# +# test-sim-after-import: +# runs-on: ubuntu-latest +# needs: [build, install-runsim] +# steps: +# - uses: actions/checkout@v2 +# - uses: actions/setup-go@v2.1.3 +# with: +# go-version: 1.18 +# - name: Display go version +# run: go version +# - uses: technote-space/get-diff-action@v4 +# with: +# SUFFIX_FILTER: | +# **/**.go +# go.mod +# go.sum +# SET_ENV_NAME_INSERTIONS: 1 +# SET_ENV_NAME_LINES: 1 +# - uses: actions/cache@v2.1.6 +# with: +# path: ~/go/bin +# key: ${{ runner.os }}-go-runsim-binary +# if: env.GIT_DIFF +# - name: test-sim-after-import +# run: | +# make test-sim-after-import +# if: env.GIT_DIFF diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..a923c0b7 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: Tests / Code Coverage +# Tests / Code Coverage workflow runs unit tests and uploads a code coverage report +# This workflow is run on pushes to main & every Pull Requests where a .go, .mod, .sum have been changed +on: + pull_request: + push: + branches: + - main +jobs: + cleanup-runs: + runs-on: ubuntu-latest + steps: + - uses: rokroskar/workflow-run-cleanup-action@master + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/main'" + + test-coverage: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: 1.18 + - name: display go version + run: go version + - name: test & coverage report creation + run: make test-unit-cover + - name: filter out DONTCOVER + run: | + excludelist="$(find ./ -type f -name '*.go' | xargs grep -l 'DONTCOVER')" + excludelist+=" $(find ./ -type f -name '*.pb.go')" + excludelist+=" $(find ./ -type f -name '*.pb.gw.go')" + excludelist+=" $(find ./ -type f -path './tests/mocks/*.go')" + for filename in ${excludelist}; do + filename=$(echo $filename | sed 's/^./github.com\/Canto-Network\/Canto\/v6/g') + echo "Excluding ${filename} from coverage report..." + sed -i "/$(echo $filename | sed 's/\//\\\//g')/d" coverage.txt + done + - uses: codecov/codecov-action@v2.1.0 + with: + file: ./coverage.txt diff --git a/.gitignore b/.gitignore index 4c46023d..ed08f892 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ .testnets .bencher DS_Store +node_modules +.idea +coverage.txt +build diff --git a/Makefile b/Makefile index 710650ae..a6553022 100755 --- a/Makefile +++ b/Makefile @@ -329,14 +329,14 @@ TEST_TARGETS := test-unit test-unit-cover test-race # Test runs-specific rules. To add a new test target, just add # a new rule, customise ARGS or TEST_PACKAGES ad libitum, and # append the new rule to the TEST_TARGETS list. -test-unit: ARGS=-timeout=10m -race +test-unit: ARGS=-timeout=30m -race test-unit: TEST_PACKAGES=$(PACKAGES_UNIT) test-race: ARGS=-race test-race: TEST_PACKAGES=$(PACKAGES_NOSIMULATION) $(TEST_TARGETS): run-tests -test-unit-cover: ARGS=-timeout=10m -race -coverprofile=coverage.txt -covermode=atomic +test-unit-cover: ARGS=-timeout=30m -race -coverprofile=coverage.txt -covermode=atomic test-unit-cover: TEST_PACKAGES=$(PACKAGES_UNIT) run-tests: From 1ad3ad854609eae4d2efbbcd7c292b5f4931126f Mon Sep 17 00:00:00 2001 From: poorphd Date: Wed, 26 Jul 2023 17:53:25 +0900 Subject: [PATCH 2/9] fix: seperate antehandler for simulation --- Makefile | 12 +- app/ante/ante.go | 6 +- app/ante/handler_options.go | 35 +++ app/app.go | 19 +- app/app_test.go | 7 +- app/export.go | 2 +- app/genesis.go | 14 ++ app/sim_test.go | 413 ++++++++++++++++++++++++++++++++++++ app/sim_utils.go | 81 +++++++ app/state.go | 243 +++++++++++++++++++++ app/test_helpers.go | 10 +- cmd/cantod/root.go | 5 +- cmd/config/config.go | 1 + testutil/network/network.go | 4 +- 14 files changed, 828 insertions(+), 24 deletions(-) create mode 100644 app/genesis.go create mode 100644 app/sim_test.go create mode 100644 app/sim_utils.go create mode 100644 app/state.go diff --git a/Makefile b/Makefile index a6553022..61c76de5 100755 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ COMMIT := $(shell git log -1 --format='%H') LEDGER_ENABLED ?= true BINDIR ?= $(GOPATH)/bin canto_BINARY = cantod -canto_DIR = canto +canto_DIR = cantod BUILDDIR ?= $(CURDIR)/build SIMAPP = ./app HTTPS_GIT := https://github.com/canto/canto.git @@ -362,13 +362,13 @@ test-rpc-pending: 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 + -NumBlocks=100 -BlockSize=200 -Commit=true -Period=1 -v -timeout 1h test-sim-custom-genesis-fast: @echo "Running custom genesis simulation..." @echo "By default, ${HOME}/.$(canto_DIR)/config/genesis.json will be used." @go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation -Genesis=${HOME}/.$(canto_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=5 -Period=1 -v -timeout 1h test-sim-import-export: runsim @echo "Running application import/export simulation. This may take several minutes..." @@ -381,15 +381,15 @@ test-sim-after-import: runsim test-sim-custom-genesis-multi-seed: runsim @echo "Running multi-seed custom genesis simulation..." @echo "By default, ${HOME}/.$(canto_DIR)/config/genesis.json will be used." - @$(BINDIR)/runsim -Genesis=${HOME}/.$(canto_DIR)/config/genesis.json -SimAppPkg=$(SIMAPP) -ExitOnFail 400 5 TestFullAppSimulation + @$(BINDIR)/runsim -Genesis=${HOME}/.$(canto_DIR)/config/genesis.json -SimAppPkg=$(SIMAPP) -ExitOnFail 50 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 + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 500 10 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 + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestFullAppSimulation test-sim-benchmark-invariants: @echo "Running simulation invariant benchmarks..." diff --git a/app/ante/ante.go b/app/ante/ante.go index afe7e5ed..7cc12289 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -45,7 +45,11 @@ func NewAnteHandler(options HandlerOptions) sdk.AnteHandler { // handle as totally normal Cosmos SDK tx switch tx.(type) { case sdk.Tx: - anteHandler = newCosmosAnteHandler(options) + if options.Simulation { + anteHandler = newCosmosSimulationAnteHandler(options) + } else { + anteHandler = newCosmosAnteHandler(options) + } default: return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) } diff --git a/app/ante/handler_options.go b/app/ante/handler_options.go index 68278638..39f4fad4 100644 --- a/app/ante/handler_options.go +++ b/app/ante/handler_options.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/ante" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" ibcante "github.com/cosmos/ibc-go/v3/modules/core/ante" ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" @@ -25,12 +26,14 @@ type HandlerOptions struct { BankKeeper evmtypes.BankKeeper IBCKeeper *ibckeeper.Keeper FeeMarketKeeper evmtypes.FeeMarketKeeper + SlashingKeeper *slashingkeeper.Keeper EvmKeeper ethante.EVMKeeper FeegrantKeeper ante.FeegrantKeeper SignModeHandler authsigning.SignModeHandler SigGasConsumer func(meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params) error Cdc codec.BinaryCodec MaxTxGasWanted uint64 + Simulation bool } // Validate checks if the keepers are defined @@ -41,6 +44,9 @@ func (options HandlerOptions) Validate() error { if options.BankKeeper == nil { return sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler") } + if options.SlashingKeeper == nil { + return sdkerrors.Wrap(sdkerrors.ErrLogic, "slashing keeper is required for AnteHandler") + } if options.SignModeHandler == nil { return sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder") } @@ -99,6 +105,35 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler { ) } +// newCosmosSimulationAnteHandler creates the ante handler for simulation, skipped few decorators for simulation +func newCosmosSimulationAnteHandler(options HandlerOptions) sdk.AnteHandler { + return sdk.ChainAnteDecorators( + ethante.RejectMessagesDecorator{}, // reject MsgEthereumTxs + cosmosante.NewAuthzLimiterDecorator( + sdk.MsgTypeURL(&evmtypes.MsgEthereumTx{}), + sdk.MsgTypeURL(&sdkvesting.MsgCreateVestingAccount{}), + ), + ante.NewSetUpContextDecorator(), + ante.NewRejectExtensionOptionsDecorator(), + ante.NewValidateBasicDecorator(), + ante.NewMempoolFeeDecorator(), + ethante.NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper), + ante.NewTxTimeoutHeightDecorator(), + ante.NewValidateMemoDecorator(options.AccountKeeper), + ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), + // NewParamChangeLimitDecorator(options.SlashingKeeper, options.Cdc), + ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), + // NewValidatorCommissionDecorator(options.Cdc), + //ante.NewSetPubKeyDecorator(options.AccountKeeper), + ante.NewValidateSigCountDecorator(options.AccountKeeper), + //ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer), + //ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), + ante.NewIncrementSequenceDecorator(options.AccountKeeper), + ibcante.NewAnteDecorator(options.IBCKeeper), + ethante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper), + ) +} + // newCosmosAnteHandlerEip712 creates the ante handler for transactions signed with EIP712 func newCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler { return sdk.ChainAnteDecorators( diff --git a/app/app.go b/app/app.go index 891f330a..a5bad631 100644 --- a/app/app.go +++ b/app/app.go @@ -299,6 +299,7 @@ func NewCanto( skipUpgradeHeights map[int64]bool, homePath string, invCheckPeriod uint, + simulation bool, encodingConfig simappparams.EncodingConfig, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp), @@ -723,19 +724,19 @@ func NewCanto( bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), capability.NewAppModule(appCodec, *app.CapabilityKeeper), gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), - // mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), params.NewAppModule(app.ParamsKeeper), evidence.NewAppModule(app.EvidenceKeeper), - feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), - authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), - ibc.NewAppModule(app.IBCKeeper), - transferModule, - evm.NewAppModule(app.EvmKeeper, app.AccountKeeper), + // feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + // authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + // ibc.NewAppModule(app.IBCKeeper), + // transferModule, + // evm.NewAppModule(app.EvmKeeper, app.AccountKeeper), epochs.NewAppModule(appCodec, app.EpochsKeeper), - feemarket.NewAppModule(app.FeeMarketKeeper), + inflation.NewAppModule(app.InflationKeeper, app.AccountKeeper, app.StakingKeeper), + // feemarket.NewAppModule(app.FeeMarketKeeper), ) app.sm.RegisterStoreDecoders() @@ -754,7 +755,8 @@ func NewCanto( AccountKeeper: app.AccountKeeper, BankKeeper: app.BankKeeper, EvmKeeper: app.EvmKeeper, - // StakingKeeper: app.StakingKeeper, + //StakingKeeper: app.StakingKeeper, + SlashingKeeper: &app.SlashingKeeper, FeegrantKeeper: app.FeeGrantKeeper, IBCKeeper: app.IBCKeeper, FeeMarketKeeper: app.FeeMarketKeeper, @@ -762,6 +764,7 @@ func NewCanto( SigGasConsumer: SigVerificationGasConsumer, Cdc: appCodec, MaxTxGasWanted: maxGasWanted, + Simulation: simulation, } if err := options.Validate(); err != nil { diff --git a/app/app_test.go b/app/app_test.go index c327839e..14da5c99 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -13,13 +13,14 @@ import ( "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" - "github.com/Canto-Network/Canto/v6/types" "github.com/evmos/ethermint/encoding" + + "github.com/Canto-Network/Canto/v6/types" ) func TestCantoExport(t *testing.T) { db := dbm.NewMemDB() - app := NewCanto(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{}) + app := NewCanto(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, false, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{}) genesisState := NewDefaultGenesisState() stateBytes, err := json.MarshalIndent(genesisState, "", " ") @@ -36,7 +37,7 @@ func TestCantoExport(t *testing.T) { app.Commit() // Making a new app object with the db, so that initchain hasn't been called - app2 := NewCanto(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{}) + app2 := NewCanto(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, false, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{}) _, err = app2.ExportAppStateAndValidators(false, []string{}) require.NoError(t, err, "ExportAppStateAndValidators should not have an error") } diff --git a/app/export.go b/app/export.go index ba599134..8db7ec13 100644 --- a/app/export.go +++ b/app/export.go @@ -173,7 +173,7 @@ func (app *Canto) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []s counter := int16(0) for ; iter.Valid(); iter.Next() { - addr := sdk.ValAddress(iter.Key()[1:]) + addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) validator, found := app.StakingKeeper.GetValidator(ctx, addr) if !found { return fmt.Errorf("expected validator %s not found", addr) diff --git a/app/genesis.go b/app/genesis.go new file mode 100644 index 00000000..69e3fb36 --- /dev/null +++ b/app/genesis.go @@ -0,0 +1,14 @@ +package app + +import ( + "encoding/json" +) + +// The genesis state of the blockchain is represented here as a map of raw json +// messages key'd by a identifier string. +// The identifier is used to determine which module genesis information belongs +// to so it may be appropriately routed during init chain. +// Within this application default genesis information is retrieved from +// the ModuleBasicManager which populates json from each BasicModule +// object provided to it during init. +type GenesisState map[string]json.RawMessage diff --git a/app/sim_test.go b/app/sim_test.go new file mode 100644 index 00000000..99e1e8cd --- /dev/null +++ b/app/sim_test.go @@ -0,0 +1,413 @@ +package app + +import ( + "encoding/json" + "fmt" + "math/rand" + "os" + "testing" + + "github.com/evmos/ethermint/encoding" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/store" + 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" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/cosmos-sdk/x/simulation" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host" + feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + dbm "github.com/tendermint/tm-db" + + cantoconfig "github.com/Canto-Network/Canto/v6/cmd/config" +) + +// Get flags every time the simulator is run +func init() { + simapp.GetSimulatorFlags() +} + +type StoreKeysPrefixes struct { + A sdk.StoreKey + B sdk.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() +} + +// interBlockCacheOpt returns a BaseApp option function that sets the persistent +// inter-block write-through cache. +func interBlockCacheOpt() func(app *baseapp.BaseApp) { + return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager()) +} + +func TestFullAppSimulation(t *testing.T) { + config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-cantoApp-sim", "Simulation") + if skip { + t.Skip("skipping application simulation") + } + config.ChainID = "canto_9000-1" + require.NoError(t, err, "simulation setup failed") + + defer func() { + db.Close() + require.NoError(t, os.RemoveAll(dir)) + }() + + // TODO: shadowed + cantoApp := NewCanto(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, + true, encoding.MakeConfig(ModuleBasics), EmptyAppOptions{}, fauxMerkleModeOpt) + require.Equal(t, cantoconfig.AppName, cantoApp.Name()) + + // run randomized simulation + _, simParams, simErr := simulation.SimulateFromSeed( + t, + os.Stdout, + cantoApp.BaseApp, + AppStateFn(cantoApp.AppCodec(), cantoApp.SimulationManager()), + RandomAccounts, // replace with own random account function if using keys other than secp256k1 + simapp.SimulationOperations(cantoApp, cantoApp.AppCodec(), config), + cantoApp.ModuleAccountAddrs(), + config, + cantoApp.AppCodec(), + ) + + // export state and simParams before the simulation error is checked + err = simapp.CheckExportSimulation(cantoApp, 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") + } + config.ChainID = "canto_9000-1" + require.NoError(t, err, "simulation setup failed") + + defer func() { + db.Close() + require.NoError(t, os.RemoveAll(dir)) + }() + + sdk.DefaultPowerReduction = sdk.NewIntFromUint64(1000000) + + app := NewCanto( + logger, + db, + nil, + true, + map[int64]bool{}, + DefaultNodeHome, + simapp.FlagPeriodValue, + true, + encoding.MakeConfig(ModuleBasics), + EmptyAppOptions{}, + fauxMerkleModeOpt, + ) + require.Equal(t, cantoconfig.AppName, app.Name()) + + // run randomized simulation + _, simParams, simErr := simulation.SimulateFromSeed( + t, + os.Stdout, + app.BaseApp, + AppStateFn(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.Println("exporting genesis...") + + exported, err := app.ExportAppStateAndValidators(false, []string{}) + require.NoError(t, err) + + fmt.Println("importing genesis...") + + _, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2") + require.NoError(t, err, "simulation setup failed") + + defer func() { + newDB.Close() + require.NoError(t, os.RemoveAll(newDir)) + }() + + newApp := NewCanto( + log.NewNopLogger(), + newDB, + nil, + true, + map[int64]bool{}, + DefaultNodeHome, + simapp.FlagPeriodValue, + true, + encoding.MakeConfig(ModuleBasics), + EmptyAppOptions{}, + fauxMerkleModeOpt, + ) + require.Equal(t, cantoconfig.AppName, newApp.Name()) + + var genesisState GenesisState + err = json.Unmarshal(exported.AppState, &genesisState) + require.NoError(t, err) + + ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + newApp.mm.InitGenesis(ctxB, app.AppCodec(), genesisState) + newApp.StoreConsensusParams(ctxB, exported.ConsensusParams) + + fmt.Println("comparing stores...") + + storeKeysPrefixes := []StoreKeysPrefixes{ + {app.keys[authtypes.StoreKey], newApp.keys[authtypes.StoreKey], [][]byte{}}, + {app.keys[banktypes.StoreKey], newApp.keys[banktypes.StoreKey], [][]byte{banktypes.BalancesPrefix}}, + {app.keys[stakingtypes.StoreKey], newApp.keys[stakingtypes.StoreKey], + [][]byte{ + stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey, + stakingtypes.HistoricalInfoKey, + }, + }, + {app.keys[distrtypes.StoreKey], newApp.keys[distrtypes.StoreKey], [][]byte{}}, + {app.keys[paramstypes.StoreKey], newApp.keys[paramstypes.StoreKey], [][]byte{}}, + //{app.keys[upgradetypes.StoreKey], newApp.keys[upgradetypes.StoreKey], [][]byte{}}, + {app.keys[evidencetypes.StoreKey], newApp.keys[evidencetypes.StoreKey], [][]byte{}}, + {app.keys[capabilitytypes.StoreKey], newApp.keys[capabilitytypes.StoreKey], [][]byte{}}, + // {app.keys[feegrant.StoreKey], newApp.keys[feegrant.StoreKey], [][]byte{}}, + {app.keys[authzkeeper.StoreKey], newApp.keys[authzkeeper.StoreKey], [][]byte{}}, + {app.keys[ibchost.StoreKey], newApp.keys[ibchost.StoreKey], [][]byte{}}, + {app.keys[ibctransfertypes.StoreKey], newApp.keys[ibctransfertypes.StoreKey], [][]byte{}}, + // {app.keys[evmtypes.StoreKey], newApp.keys[evmtypes.StoreKey], [][]byte{}}, + {app.keys[feemarkettypes.StoreKey], newApp.keys[feemarkettypes.StoreKey], [][]byte{}}, + //{app.keys[inflationtypes.StoreKey], newApp.keys[inflationtypes.StoreKey], [][]byte{}}, + // {app.keys[erc20types.StoreKey], newApp.keys[erc20types.StoreKey], [][]byte{}}, + //{app.keys[epochstypes.StoreKey], newApp.keys[epochstypes.StoreKey], [][]byte{}}, + // {app.keys[vestingtypes.StoreKey], newApp.keys[vestingtypes.StoreKey], [][]byte{}}, + // {app.keys[recoverytypes.StoreKey], newApp.keys[recoverytypes.StoreKey], [][]byte{}}, + // {app.keys[feestypes.StoreKey], newApp.keys[feestypes.StoreKey], [][]byte{}}, + // {app.keys[csrtypes.StoreKey], newApp.keys[csrtypes.StoreKey], [][]byte{}}, + // {app.keys[govshuttletypes.StoreKey], newApp.keys[govshuttletypes.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 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 = "canto_9000-1" + + numSeeds := 3 + numTimesToRunPerSeed := 5 + appHashList := make([]json.RawMessage, numTimesToRunPerSeed) + + sdk.DefaultPowerReduction = sdk.NewIntFromUint64(1000000) + + 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 := NewCanto( + logger, + db, + nil, + true, + map[int64]bool{}, + DefaultNodeHome, + simapp.FlagPeriodValue, + true, + encoding.MakeConfig(ModuleBasics), + EmptyAppOptions{}, + fauxMerkleModeOpt, + ) + fmt.Printf("running simulation with seed %d\n", config.Seed) + + _, _, err := simulation.SimulateFromSeed( + t, + os.Stdout, + app.BaseApp, + AppStateFn(app.AppCodec(), app.SimulationManager()), + RandomAccounts, + 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) + } + } + } +} + +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 = "canto_9000-1" + + defer func() { + db.Close() + require.NoError(t, os.RemoveAll(dir)) + }() + + sdk.DefaultPowerReduction = sdk.NewIntFromUint64(1000000) + + app := NewCanto( + logger, + db, + nil, + true, + map[int64]bool{}, + DefaultNodeHome, + simapp.FlagPeriodValue, + true, + encoding.MakeConfig(ModuleBasics), + EmptyAppOptions{}, + fauxMerkleModeOpt, + ) + require.Equal(t, cantoconfig.AppName, app.Name()) + + // Run randomized simulation + stopEarly, simParams, simErr := simulation.SimulateFromSeed( + t, + os.Stdout, + app.BaseApp, + AppStateFn(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() { + newDB.Close() + require.NoError(t, os.RemoveAll(newDir)) + }() + + newApp := NewCanto( + log.NewNopLogger(), + newDB, + nil, + true, + map[int64]bool{}, + DefaultNodeHome, + simapp.FlagPeriodValue, + true, + encoding.MakeConfig(ModuleBasics), + EmptyAppOptions{}, + fauxMerkleModeOpt, + ) + require.Equal(t, cantoconfig.AppName, newApp.Name()) + + newApp.InitChain(abci.RequestInitChain{ + AppStateBytes: exported.AppState, + }) + + _, _, err = simulation.SimulateFromSeed( + t, + os.Stdout, + newApp.BaseApp, + AppStateFn(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) +} diff --git a/app/sim_utils.go b/app/sim_utils.go new file mode 100644 index 00000000..e74a1d02 --- /dev/null +++ b/app/sim_utils.go @@ -0,0 +1,81 @@ +package app + +import ( + "math/rand" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/simapp/helpers" + sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" + + "github.com/evmos/ethermint/crypto/ethsecp256k1" +) + +// GenAndDeliverTxWithRandFees generates a transaction with a random fee and delivers it. +func GenAndDeliverTxWithRandFees(txCtx simulation.OperationInput) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + account := txCtx.AccountKeeper.GetAccount(txCtx.Context, txCtx.SimAccount.Address) + spendable := txCtx.Bankkeeper.SpendableCoins(txCtx.Context, account.GetAddress()) + + var fees sdk.Coins + var err error + + coins, hasNeg := spendable.SafeSub(txCtx.CoinsSpentInMsg) + if hasNeg { + return simtypes.NoOpMsg(txCtx.ModuleName, txCtx.MsgType, "message doesn't leave room for fees"), nil, err + } + + fees, err = simtypes.RandomFees(txCtx.R, txCtx.Context, coins) + if err != nil { + return simtypes.NoOpMsg(txCtx.ModuleName, txCtx.MsgType, "unable to generate fees"), nil, err + } + return GenAndDeliverTx(txCtx, fees) +} + +// GenAndDeliverTx generates a transactions and delivers it. +func GenAndDeliverTx(txCtx simulation.OperationInput, fees sdk.Coins) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + account := txCtx.AccountKeeper.GetAccount(txCtx.Context, txCtx.SimAccount.Address) + tx, err := helpers.GenTx( + txCtx.TxGen, + []sdk.Msg{txCtx.Msg}, + fees, + helpers.DefaultGenTxGas, + txCtx.Context.ChainID(), + []uint64{account.GetAccountNumber()}, + []uint64{account.GetSequence()}, + txCtx.SimAccount.PrivKey, + ) + if err != nil { + return simtypes.NoOpMsg(txCtx.ModuleName, txCtx.MsgType, "unable to generate mock tx"), nil, err + } + + _, _, err = txCtx.App.Deliver(txCtx.TxGen.TxEncoder(), tx) + if err != nil { + return simtypes.NoOpMsg(txCtx.ModuleName, txCtx.MsgType, "unable to deliver tx"), nil, err + } + + return simtypes.NewOperationMsg(txCtx.Msg, true, "", txCtx.Cdc), nil, nil +} + +// RandomAccounts generates n random accounts +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) + + privKey, err := ethsecp256k1.GenerateKey() + if err != nil { + panic(err) + } + accs[i].PrivKey = privKey + accs[i].PubKey = accs[i].PrivKey.PubKey() + accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address()) + + accs[i].ConsKey = ed25519.GenPrivKeyFromSecret(privkeySeed) + } + + return accs +} diff --git a/app/state.go b/app/state.go new file mode 100644 index 00000000..b1f6b670 --- /dev/null +++ b/app/state.go @@ -0,0 +1,243 @@ +package app + +import ( + "encoding/json" + "fmt" + "io" + "math/rand" + "os" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + tmjson "github.com/tendermint/tendermint/libs/json" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/evmos/ethermint/crypto/ethsecp256k1" +) + +var FlagGenesisTimeValue = int64(1640995200) + +// AppStateFn returns the initial application state using a genesis or the simulation parameters. +// It panics if the user provides files for both of them. +// If a file is not given for the genesis or the sim params, it creates a randomized one. +func AppStateFn(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) { + if FlagGenesisTimeValue == 0 { + genesisTimestamp = simtypes.RandTimestamp(r) + } else { + genesisTimestamp = time.Unix(FlagGenesisTimeValue, 0) + } + + chainID = config.ChainID + switch { + case config.ParamsFile != "" && config.GenesisFile != "": + panic("cannot provide both a genesis file and a params file") + + case config.GenesisFile != "": + // override the default chain-id from simapp to set it later to the config + genesisDoc, accounts := AppStateFromGenesisFileFn(r, cdc, config.GenesisFile) + + if FlagGenesisTimeValue == 0 { + // use genesis timestamp if no custom timestamp is provided (i.e no random timestamp) + genesisTimestamp = genesisDoc.GenesisTime + } + + appState = genesisDoc.AppState + chainID = genesisDoc.ChainID + simAccs = accounts + + case config.ParamsFile != "": + appParams := make(simtypes.AppParams) + bz, err := os.ReadFile(config.ParamsFile) + if err != nil { + panic(err) + } + + err = json.Unmarshal(bz, &appParams) + if err != nil { + panic(err) + } + appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) + + default: + appParams := make(simtypes.AppParams) + appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) + } + rawState := make(map[string]json.RawMessage) + err := json.Unmarshal(appState, &rawState) + if err != nil { + panic(err) + } + + stakingStateBz, ok := rawState[stakingtypes.ModuleName] + if !ok { + panic(fmt.Sprintf("%s genesis state not found", stakingtypes.ModuleName)) + } + stakingState := new(stakingtypes.GenesisState) + err = cdc.UnmarshalJSON(stakingStateBz, stakingState) + if err != nil { + panic(err) + } + // compute not bonded balance + notBondedTokens := sdk.ZeroInt() + for _, val := range stakingState.Validators { + if val.Status != stakingtypes.Unbonded { + continue + } + notBondedTokens = notBondedTokens.Add(val.GetTokens()) + } + notBondedCoin := sdk.NewCoin(stakingState.Params.BondDenom, notBondedTokens) + bankStateBz, ok := rawState[banktypes.ModuleName] + if !ok { + panic(fmt.Sprintf("%s genesis state not found", banktypes.ModuleName)) + } + bankState := new(banktypes.GenesisState) + err = cdc.UnmarshalJSON(bankStateBz, bankState) + if err != nil { + panic(err) + } + + stakingAddr := authtypes.NewModuleAddress(stakingtypes.NotBondedPoolName).String() + var found bool + for _, balance := range bankState.Balances { + if balance.Address == stakingAddr { + found = true + break + } + } + if !found { + bankState.Balances = append(bankState.Balances, banktypes.Balance{ + Address: stakingAddr, + Coins: sdk.NewCoins(notBondedCoin), + }) + } + + // chagnge appState back + rawState[stakingtypes.ModuleName], err = cdc.MarshalJSON(stakingState) + rawState[banktypes.ModuleName], err = cdc.MarshalJSON(bankState) + + // replace appstate + appState, err = json.Marshal(rawState) + if err != nil { + panic(err) + } + return appState, simAccs, chainID, genesisTimestamp + } +} + +// AppStateRandomizedFn creates calls each module's GenesisState generator function +// and creates the simulation params +func AppStateRandomizedFn( + simManager *module.SimulationManager, r *rand.Rand, cdc codec.JSONCodec, + accs []simtypes.Account, genesisTimestamp time.Time, appParams simtypes.AppParams, +) (json.RawMessage, []simtypes.Account) { + numAccs := int64(len(accs)) + genesisState := NewDefaultGenesisState() + + var initalStake, numInitiallyBonded int64 + appParams.GetOrGenerate( + cdc, + simappparams.StakePerAccount, + &initalStake, + r, + func(r *rand.Rand) { initalStake = r.Int63n(1e12) }, + ) + appParams.GetOrGenerate( + cdc, + simappparams.InitiallyBondedValidators, + &numInitiallyBonded, + r, + func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(300)) }, + ) + + if numInitiallyBonded > numAccs { + numInitiallyBonded = numAccs + } + + fmt.Printf( + `Selected randomly generated parameters for simulated genesis: +{ + StakePerAccount: %d, + InitiallyBondedValidators: %d, +} +`, initalStake, numInitiallyBonded) + + simState := &module.SimulationState{ + AppParams: appParams, + Cdc: cdc, + Rand: r, + GenState: genesisState, + Accounts: accs, + InitialStake: initalStake, + NumBonded: numInitiallyBonded, + GenTimestamp: genesisTimestamp, + } + + simManager.GenerateGenesisStates(simState) + + appState, err := json.Marshal(genesisState) + if err != nil { + panic(err) + } + + return appState, accs +} + +// AppStateFromGenesisFileFn util function to generate the genesis AppState +// from a genesis.json file. +func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONCodec, genesisFile string) (tmtypes.GenesisDoc, []simtypes.Account) { + bytes, err := os.ReadFile(genesisFile) + if err != nil { + panic(err) + } + + var genesis tmtypes.GenesisDoc + if err := tmjson.Unmarshal(bytes, &genesis); err != nil { + panic(err) + } + + var appState GenesisState + err = json.Unmarshal(genesis.AppState, &appState) + if err != nil { + panic(err) + } + + var authGenesis authtypes.GenesisState + if appState[authtypes.ModuleName] != nil { + cdc.MustUnmarshalJSON(appState[authtypes.ModuleName], &authGenesis) + } + + newAccs := make([]simtypes.Account, len(authGenesis.Accounts)) + for i, acc := range authGenesis.Accounts { + privkeySeed := make([]byte, 15) + if _, err := r.Read(privkeySeed); err != nil { + panic(err) + } + privKey, err := ethsecp256k1.GenerateKey() + if err != nil { + panic(err) + } + + a, ok := acc.GetCachedValue().(authtypes.AccountI) + if !ok { + panic("account error") + } + + simAcc := simtypes.Account{ + PrivKey: privKey, + PubKey: privKey.PubKey(), + Address: a.GetAddress(), + } + newAccs[i] = simAcc + } + + return genesis, newAccs +} diff --git a/app/test_helpers.go b/app/test_helpers.go index 7f2ab434..27348e63 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -61,7 +61,7 @@ func Setup( feemarketGenesis *feemarkettypes.GenesisState, ) *Canto { db := dbm.NewMemDB() - app := NewCanto(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{}) + app := NewCanto(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, false, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{}) if !isCheckTx { // init chain must be called to stop deliverState from being nil genesisState := NewDefaultGenesisState() @@ -93,10 +93,16 @@ func Setup( return app } +type EmptyAppOptions struct{} + +func (ao EmptyAppOptions) Get(o string) interface{} { + return nil +} + // SetupTestingApp initializes the IBC-go testing application func SetupTestingApp() (ibctesting.TestingApp, map[string]json.RawMessage) { db := dbm.NewMemDB() cfg := encoding.MakeConfig(ModuleBasics) - app := NewCanto(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, cfg, simapp.EmptyAppOptions{}) + app := NewCanto(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, false, cfg, simapp.EmptyAppOptions{}) return app, NewDefaultGenesisState() } diff --git a/cmd/cantod/root.go b/cmd/cantod/root.go index 1609d451..10448857 100644 --- a/cmd/cantod/root.go +++ b/cmd/cantod/root.go @@ -242,6 +242,7 @@ func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, a logger, db, traceStore, true, skipUpgradeHeights, cast.ToString(appOpts.Get(flags.FlagHome)), cast.ToUint(appOpts.Get(sdkserver.FlagInvCheckPeriod)), + false, a.encCfg, appOpts, baseapp.SetPruning(pruningOpts), @@ -273,13 +274,13 @@ func (a appCreator) appExport( } if height != -1 { - cantoApp = app.NewCanto(logger, db, traceStore, false, map[int64]bool{}, "", uint(1), a.encCfg, appOpts) + cantoApp = app.NewCanto(logger, db, traceStore, false, map[int64]bool{}, "", uint(1), false, a.encCfg, appOpts) if err := cantoApp.LoadHeight(height); err != nil { return servertypes.ExportedApp{}, err } } else { - cantoApp = app.NewCanto(logger, db, traceStore, true, map[int64]bool{}, "", uint(1), a.encCfg, appOpts) + cantoApp = app.NewCanto(logger, db, traceStore, true, map[int64]bool{}, "", uint(1), false, a.encCfg, appOpts) } return cantoApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs) diff --git a/cmd/config/config.go b/cmd/config/config.go index 913d10ba..0a84f6e0 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -7,6 +7,7 @@ import ( ) const ( + AppName = "cantod" // Bech32Prefix defines the Bech32 prefix used for EthAccounts Bech32Prefix = "canto" diff --git a/testutil/network/network.go b/testutil/network/network.go index c68caf90..e1858059 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -27,7 +27,6 @@ import ( dbm "github.com/tendermint/tm-db" "google.golang.org/grpc" - "github.com/Canto-Network/Canto/v6/app" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" @@ -50,6 +49,8 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/evmos/ethermint/crypto/hd" + "github.com/Canto-Network/Canto/v6/app" + "github.com/evmos/ethermint/encoding" "github.com/evmos/ethermint/server/config" ethermint "github.com/evmos/ethermint/types" @@ -127,6 +128,7 @@ func NewAppConstructor(encodingCfg params.EncodingConfig) AppConstructor { return func(val Validator) servertypes.Application { return app.NewCanto( val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0, + false, encodingCfg, simapp.EmptyAppOptions{}, baseapp.SetPruning(storetypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), From f107ff3bcb07327f3c58e019ff295a7508609ff8 Mon Sep 17 00:00:00 2001 From: poorphd Date: Wed, 26 Jul 2023 17:57:18 +0900 Subject: [PATCH 3/9] fix: simulation errors --- app/sim_test.go | 16 +++++++++------- app/state.go | 2 ++ init_testnet.sh | 30 +++++++++++++++++++----------- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/app/sim_test.go b/app/sim_test.go index 99e1e8cd..755fcfa5 100644 --- a/app/sim_test.go +++ b/app/sim_test.go @@ -7,9 +7,6 @@ import ( "os" "testing" - "github.com/evmos/ethermint/encoding" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/store" @@ -25,6 +22,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/evmos/ethermint/encoding" feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/log" @@ -185,7 +183,7 @@ func TestAppImportExport(t *testing.T) { require.NoError(t, err) ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) - ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + ctxB := newApp.NewContext(true, tmproto.Header{ChainID: config.ChainID, Height: app.LastBlockHeight()}) newApp.mm.InitGenesis(ctxB, app.AppCodec(), genesisState) newApp.StoreConsensusParams(ctxB, exported.ConsensusParams) @@ -394,9 +392,13 @@ func TestAppSimulationAfterImport(t *testing.T) { ) require.Equal(t, cantoconfig.AppName, newApp.Name()) - newApp.InitChain(abci.RequestInitChain{ - AppStateBytes: exported.AppState, - }) + var genesisState GenesisState + err = json.Unmarshal(exported.AppState, &genesisState) + require.NoError(t, err) + + ctx := newApp.NewContext(true, tmproto.Header{ChainID: config.ChainID, Height: app.LastBlockHeight()}) + newApp.mm.InitGenesis(ctx, app.AppCodec(), genesisState) + newApp.StoreConsensusParams(ctx, exported.ConsensusParams) _, _, err = simulation.SimulateFromSeed( t, diff --git a/app/state.go b/app/state.go index b1f6b670..84b812e5 100644 --- a/app/state.go +++ b/app/state.go @@ -9,6 +9,7 @@ import ( "time" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" simappparams "github.com/cosmos/cosmos-sdk/simapp/params" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" @@ -235,6 +236,7 @@ func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONCodec, genesisFile str PrivKey: privKey, PubKey: privKey.PubKey(), Address: a.GetAddress(), + ConsKey: ed25519.GenPrivKeyFromSecret(privkeySeed), } newAccs[i] = simAcc } diff --git a/init_testnet.sh b/init_testnet.sh index e1611f23..9845d5a9 100755 --- a/init_testnet.sh +++ b/init_testnet.sh @@ -1,5 +1,6 @@ -KEY="mykey" -KEY2="mykey2" +KEY1="key1" +KEY2="key2" +KEY3="key3" CHAINID="canto_7701-1" MONIKER="plex-validator" KEYRING="test" @@ -14,15 +15,18 @@ command -v jq > /dev/null 2>&1 || { echo >&2 "jq not installed. More info: https # Reinstall daemon rm -rf ~/.cantod* -make install +make install-testing # Set client config cantod config keyring-backend $KEYRING cantod config chain-id $CHAINID # if $KEY exists it should be deleted -cantod keys add $KEY --keyring-backend $KEYRING --algo $KEYALGO +cantod keys add $KEY1 --keyring-backend $KEYRING --algo $KEYALGO cantod keys add $KEY2 --keyring-backend $KEYRING --algo $KEYALGO +cantod keys add $KEY3 --keyring-backend $KEYRING --algo $KEYALGO + + # Set moniker and chain-id for Canto (Moniker can be anything, chain-id must be an integer) cantod init $MONIKER --chain-id $CHAINID @@ -70,20 +74,24 @@ if [[ $1 == "pending" ]]; then fi # Allocate genesis accounts (cosmos formatted addresses) -cantod add-genesis-account $KEY 964723926400000000000000000acanto --keyring-backend $KEYRING -cantod add-genesis-account $KEY2 35276073600000000000000000acanto --keyring-backend $KEYRING - +cantod add-genesis-account $KEY1 1050000000000000000000000000acanto --keyring-backend $KEYRING +cantod add-genesis-account $KEY2 1000000000000000000000000000acanto --keyring-backend $KEYRING +cantod add-genesis-account $KEY3 1000000000000000000000000000acanto --keyring-backend $KEYRING + + + # Update total supply with claim values #validators_supply=$(cat $HOME/.cantod/config/genesis.json | jq -r '.app_state["bank"]["supply"][0]["amount"]') # Bc is required to add this big numbers # total_supply=$(bc <<< "$amount_to_claim+$validators_supply") -total_supply=1000000000000000000000000000 +total_supply=3050000000000000000000000000 cat $HOME/.cantod/config/genesis.json | jq -r --arg total_supply "$total_supply" '.app_state["bank"]["supply"][0]["amount"]=$total_supply' > $HOME/.cantod/config/tmp_genesis.json && mv $HOME/.cantod/config/tmp_genesis.json $HOME/.cantod/config/genesis.json echo $KEYRING -echo $KEY +echo $KEY1 # Sign genesis transaction -cantod gentx $KEY2 100000000000000000000000acanto --keyring-backend $KEYRING --chain-id $CHAINID +mkdir $HOME/.cantod/config/gentx +cantod gentx $KEY1 900000000000000000000000acanto --keyring-backend $KEYRING --chain-id $CHAINID --output-document $HOME/.cantod/config/gentx/gentx-1.json #cantod gentx $KEY2 1000000000000000000000acanto --keyring-backend $KEYRING --chain-id $CHAINID # Collect genesis tx @@ -97,5 +105,5 @@ if [[ $1 == "pending" ]]; then fi # Start the node (remove the --pruning=nothing flag if historical queries are not needed) -cantod start --pruning=nothing --trace --log_level trace --minimum-gas-prices=1.000acanto --json-rpc.api eth,txpool,personal,net,debug,web3 --rpc.laddr "tcp://0.0.0.0:26657" --api.enable true --api.enabled-unsafe-cors true +#cantod start --pruning=nothing --trace --log_level trace --minimum-gas-prices=1.000acanto --json-rpc.api eth,txpool,personal,net,debug,web3 --rpc.laddr "tcp://0.0.0.0:26657" --api.enable true --api.enabled-unsafe-cors true From 98bc8d30be6161c1113d49207929244fc619513a Mon Sep 17 00:00:00 2001 From: poorphd Date: Wed, 26 Jul 2023 17:59:09 +0900 Subject: [PATCH 4/9] fix: simulation workflow, update sims.yml, add GOPATH on makefile --- .github/workflows/sims.yml | 234 ++++++++++++++++++------------------- Makefile | 41 ++++++- 2 files changed, 152 insertions(+), 123 deletions(-) diff --git a/.github/workflows/sims.yml b/.github/workflows/sims.yml index c9447eb0..7cb31de8 100644 --- a/.github/workflows/sims.yml +++ b/.github/workflows/sims.yml @@ -1,121 +1,117 @@ -#name: Sims +name: Sims # Sims workflow runs multiple types of simulations (nondeterminism, import-export, after-import) # This workflow will run on all Pull Requests, if a .go, .mod or .sum file have been changed -# Temporary disable until it is fixed -#on: -# pull_request: -# push: -# branches: -# - main -# - develop -# -#jobs: -# build: -# runs-on: ubuntu-latest -# if: "!contains(github.event.head_commit.message, 'skip-sims')" -# steps: -# - uses: actions/checkout@v2 -# - uses: actions/setup-go@v2.1.3 -# with: -# go-version: 1.18 -# - name: Display go version -# run: go version -# - run: make build -# -# install-runsim: -# runs-on: ubuntu-latest -# needs: build -# steps: -# - uses: actions/setup-go@v2.1.3 -# with: -# go-version: 1.18 -# - name: Display go version -# run: go version -# - name: Install runsim -# run: export GO111MODULE="on" && go install github.com/cosmos/tools/cmd/runsim@v1.0.0 -# - uses: actions/cache@v2.1.6 -# with: -# path: ~/go/bin -# key: ${{ runner.os }}-go-runsim-binary -# -# test-sim-nondeterminism: -# runs-on: ubuntu-latest -# needs: [build, install-runsim] -# steps: -# - uses: actions/checkout@v2 -# - uses: actions/setup-go@v2.1.3 -# with: -# go-version: 1.18 -# - name: Display go version -# run: go version -# - uses: technote-space/get-diff-action@v4 -# with: -# PATTERNS: | -# **/**.go -# go.mod -# go.sum -# - uses: actions/cache@v2.1.6 -# with: -# path: ~/go/bin -# key: ${{ runner.os }}-go-runsim-binary -# if: env.GIT_DIFF -# - name: test-sim-nondeterminism -# run: | -# make test-sim-nondeterminism -# if: env.GIT_DIFF -# -# test-sim-import-export: -# runs-on: ubuntu-latest -# needs: [build, install-runsim] -# steps: -# - uses: actions/checkout@v2 -# - uses: actions/setup-go@v2.1.3 -# with: -# go-version: 1.18 -# - name: Display go version -# run: go version -# - uses: technote-space/get-diff-action@v4 -# with: -# SUFFIX_FILTER: | -# **/**.go -# go.mod -# go.sum -# SET_ENV_NAME_INSERTIONS: 1 -# SET_ENV_NAME_LINES: 1 -# - uses: actions/cache@v2.1.6 -# with: -# path: ~/go/bin -# key: ${{ runner.os }}-go-runsim-binary -# if: env.GIT_DIFF -# - name: test-sim-import-export -# run: | -# make test-sim-import-export -# if: env.GIT_DIFF -# -# test-sim-after-import: -# runs-on: ubuntu-latest -# needs: [build, install-runsim] -# steps: -# - uses: actions/checkout@v2 -# - uses: actions/setup-go@v2.1.3 -# with: -# go-version: 1.18 -# - name: Display go version -# run: go version -# - uses: technote-space/get-diff-action@v4 -# with: -# SUFFIX_FILTER: | -# **/**.go -# go.mod -# go.sum -# SET_ENV_NAME_INSERTIONS: 1 -# SET_ENV_NAME_LINES: 1 -# - uses: actions/cache@v2.1.6 -# with: -# path: ~/go/bin -# key: ${{ runner.os }}-go-runsim-binary -# if: env.GIT_DIFF -# - name: test-sim-after-import -# run: | -# make test-sim-after-import -# if: env.GIT_DIFF +on: + pull_request: + push: + branches: + - main + - develop + +jobs: + build: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, 'skip-sims')" + steps: + - uses: actions/checkout@v3.5.2 + - uses: actions/setup-go@v4 + with: + go-version: 1.18 + - name: Install runsim + run: go install github.com/cosmos/tools/cmd/runsim@v1.0.0 + - name: Display go version + run: go version + - run: make build + + test-sim-nondeterminism: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v3.5.2 + - uses: actions/setup-go@v4 + with: + go-version: 1.18 + - name: Display go version + run: go version + - uses: technote-space/get-diff-action@v4 + with: + PATTERNS: | + **/**.go + go.mod + go.sum + - uses: actions/cache@v3.3.1 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + if: env.GIT_DIFF + - name: test-sim-nondeterminism + run: | + make test-sim-nondeterminism + if: env.GIT_DIFF + + install-runsim: + runs-on: ubuntu-latest + needs: build + steps: + - name: Install runsim + run: go install github.com/cosmos/tools/cmd/runsim@v1.0.0 + - uses: actions/cache@v3.3.1 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + + test-sim-import-export: + runs-on: ubuntu-latest + needs: [build, install-runsim] + steps: + - uses: actions/checkout@v3.5.2 + - uses: actions/setup-go@v4 + with: + go-version: 1.18 + - name: Display go version + run: go version + - uses: technote-space/get-diff-action@v4 + with: + SUFFIX_FILTER: | + **/**.go + go.mod + go.sum + SET_ENV_NAME_INSERTIONS: 1 + SET_ENV_NAME_LINES: 1 + - uses: actions/cache@v3.3.1 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + if: env.GIT_DIFF + - name: test-sim-import-export + run: | + make test-sim-import-export + if: env.GIT_DIFF + + test-sim-after-import: + runs-on: ubuntu-latest + needs: [build, install-runsim] + steps: + - uses: actions/checkout@v3.5.2 + - uses: actions/setup-go@v4 + with: + go-version: 1.18 + - name: Display go version + run: go version + - uses: technote-space/get-diff-action@v4 + with: + SUFFIX_FILTER: | + **/**.go + go.mod + go.sum + SET_ENV_NAME_INSERTIONS: 1 + SET_ENV_NAME_LINES: 1 + - uses: actions/cache@v3.3.1 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + if: env.GIT_DIFF + - name: test-sim-after-import + run: | + make test-sim-after-import + if: env.GIT_DIFF diff --git a/Makefile b/Makefile index 61c76de5..f8ed784b 100755 --- a/Makefile +++ b/Makefile @@ -1,5 +1,22 @@ #!/usr/bin/make -f +### +# Find OS and Go environment +# GO contains the Go binary +# FS contains the OS file separator +### +ifeq ($(OS),Windows_NT) + GO := $(shell where go.exe 2> NUL) + FS := \\ +else + GO := $(shell command -v go 2> /dev/null) + FS := / +endif + +ifeq ($(GO),) + $(error could not find go. Is it in PATH? $(GO)) +endif + PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation') PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation') DIFF_TAG=$(shell git rev-list --tags="v*" --max-count=1 --not $(shell git rev-list --tags="v*" "HEAD..origin")) @@ -8,6 +25,7 @@ VERSION ?= $(shell echo $(shell git describe --tags $(or $(DIFF_TAG), $(DEFAULT_ TMVERSION := $(shell go list -m github.com/tendermint/tendermint | sed 's:.* ::') COMMIT := $(shell git log -1 --format='%H') LEDGER_ENABLED ?= true +GOPATH ?= $(shell $(GO) env GOPATH) BINDIR ?= $(GOPATH)/bin canto_BINARY = cantod canto_DIR = cantod @@ -188,7 +206,7 @@ RUNSIM = $(TOOLS_DESTDIR)/runsim runsim: $(RUNSIM) $(RUNSIM): @echo "Installing runsim..." - @(cd /tmp && ${GO_MOD} go get github.com/cosmos/tools/cmd/runsim@master) + @(cd /tmp && go install github.com/cosmos/tools/cmd/runsim@v1.0.0) statik: $(STATIK) $(STATIK): @@ -362,7 +380,12 @@ test-rpc-pending: test-sim-nondeterminism: @echo "Running non-determinism test..." @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ - -NumBlocks=100 -BlockSize=200 -Commit=true -Period=1 -v -timeout 1h + -NumBlocks=20 -BlockSize=100 -Commit=true -Period=1 -v -timeout 10m + +test-sim-nondeterminism-long: + @echo "Running non-determinism-long test..." + @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ + -NumBlocks=100 -BlockSize=200 -Commit=true -Period=1 -v -timeout 10h test-sim-custom-genesis-fast: @echo "Running custom genesis simulation..." @@ -372,11 +395,21 @@ test-sim-custom-genesis-fast: 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 + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -Seeds=1,10,100,1000 -ExitOnFail 50 5 TestAppImportExport + +test-sim-import-export-long: runsim + @echo "Running application simulation-import-export-long. This may take several minutes..." + $(eval SEED := $(shell awk 'BEGIN{srand(); for (i=1; i<=50; i++) {n=int(10000*rand())+1; printf "%d%s", n, (i==50 ? "" : ",")}}')) + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -Seeds="$(SEED)" -ExitOnFail 500 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 + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -Seeds=1,10,100,1000 -ExitOnFail 50 5 TestAppSimulationAfterImport + +test-sim-after-import-long: runsim + @echo "Running application simulation-after-import-long. This may take several minutes..." + $(eval SEED := $(shell awk 'BEGIN{srand(); for (i=1; i<=50; i++) {n=int(10000*rand())+1; printf "%d%s", n, (i==50 ? "" : ",")}}')) + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -Seeds="$(SEED)" -ExitOnFail 500 5 TestAppSimulationAfterImport test-sim-custom-genesis-multi-seed: runsim @echo "Running multi-seed custom genesis simulation..." From a719d632c2f3b5f0afb2378df07fe4b9c3b81bfd Mon Sep 17 00:00:00 2001 From: poorphd Date: Thu, 27 Jul 2023 15:38:22 +0900 Subject: [PATCH 5/9] fix: removing unused keeper from handler options --- app/ante/handler_options.go | 5 ----- app/app.go | 1 - 2 files changed, 6 deletions(-) diff --git a/app/ante/handler_options.go b/app/ante/handler_options.go index 39f4fad4..f518e1b8 100644 --- a/app/ante/handler_options.go +++ b/app/ante/handler_options.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/ante" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" ibcante "github.com/cosmos/ibc-go/v3/modules/core/ante" ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" @@ -26,7 +25,6 @@ type HandlerOptions struct { BankKeeper evmtypes.BankKeeper IBCKeeper *ibckeeper.Keeper FeeMarketKeeper evmtypes.FeeMarketKeeper - SlashingKeeper *slashingkeeper.Keeper EvmKeeper ethante.EVMKeeper FeegrantKeeper ante.FeegrantKeeper SignModeHandler authsigning.SignModeHandler @@ -44,9 +42,6 @@ func (options HandlerOptions) Validate() error { if options.BankKeeper == nil { return sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler") } - if options.SlashingKeeper == nil { - return sdkerrors.Wrap(sdkerrors.ErrLogic, "slashing keeper is required for AnteHandler") - } if options.SignModeHandler == nil { return sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder") } diff --git a/app/app.go b/app/app.go index a5bad631..9e9855ca 100644 --- a/app/app.go +++ b/app/app.go @@ -756,7 +756,6 @@ func NewCanto( BankKeeper: app.BankKeeper, EvmKeeper: app.EvmKeeper, //StakingKeeper: app.StakingKeeper, - SlashingKeeper: &app.SlashingKeeper, FeegrantKeeper: app.FeeGrantKeeper, IBCKeeper: app.IBCKeeper, FeeMarketKeeper: app.FeeMarketKeeper, From ab3aadf632f2f254c4de7a38f62cdf70560381ab Mon Sep 17 00:00:00 2001 From: dongsam Date: Thu, 27 Jul 2023 18:20:41 +0900 Subject: [PATCH 6/9] fix: update simulation target modules, refactor duplicated struct --- app/app.go | 17 ++++++++++++----- app/sim_test.go | 37 +++++++++++++++++++++---------------- app/test_helpers.go | 6 ------ 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/app/app.go b/app/app.go index 9e9855ca..af6bd163 100644 --- a/app/app.go +++ b/app/app.go @@ -720,6 +720,7 @@ func NewCanto( // NOTE: this is not required apps that don't use the simulator for fuzz testing // transactions app.sm = module.NewSimulationManager( + // basic sdk modules auth.NewAppModule(appCodec, app.AccountKeeper, ethermintapp.RandomGenesisAccounts), bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), capability.NewAppModule(appCodec, *app.CapabilityKeeper), @@ -729,14 +730,20 @@ func NewCanto( slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), params.NewAppModule(app.ParamsKeeper), evidence.NewAppModule(app.EvidenceKeeper), - // feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + ibc.NewAppModule(app.IBCKeeper), + transferModule, + // TODO: Temporary exclusion authz due to issues with implementation of authz simulation // authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), - // ibc.NewAppModule(app.IBCKeeper), - // transferModule, - // evm.NewAppModule(app.EvmKeeper, app.AccountKeeper), + + // canto, ethermint modules + evm.NewAppModule(app.EvmKeeper, app.AccountKeeper), epochs.NewAppModule(appCodec, app.EpochsKeeper), inflation.NewAppModule(app.InflationKeeper, app.AccountKeeper, app.StakingKeeper), - // feemarket.NewAppModule(app.FeeMarketKeeper), + feemarket.NewAppModule(app.FeeMarketKeeper), + + // TODO: Modules that have not yet been implemented for simulation + // govshuttle, csr, recovery, erc20 ) app.sm.RegisterStoreDecoders() diff --git a/app/sim_test.go b/app/sim_test.go index 755fcfa5..560506ae 100644 --- a/app/sim_test.go +++ b/app/sim_test.go @@ -17,12 +17,14 @@ import ( 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" + "github.com/cosmos/cosmos-sdk/x/feegrant" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/cosmos/cosmos-sdk/x/simulation" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/evmos/ethermint/encoding" + evmtypes "github.com/evmos/ethermint/x/evm/types" feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/log" @@ -30,6 +32,11 @@ import ( dbm "github.com/tendermint/tm-db" cantoconfig "github.com/Canto-Network/Canto/v6/cmd/config" + csrtypes "github.com/Canto-Network/Canto/v6/x/csr/types" + erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types" + govshuttletypes "github.com/Canto-Network/Canto/v6/x/govshuttle/types" + inflationtypes "github.com/Canto-Network/Canto/v6/x/inflation/types" + recoverytypes "github.com/Canto-Network/Canto/v6/x/recovery/types" ) // Get flags every time the simulator is run @@ -70,7 +77,7 @@ func TestFullAppSimulation(t *testing.T) { // TODO: shadowed cantoApp := NewCanto(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, - true, encoding.MakeConfig(ModuleBasics), EmptyAppOptions{}, fauxMerkleModeOpt) + true, encoding.MakeConfig(ModuleBasics), simapp.EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, cantoconfig.AppName, cantoApp.Name()) // run randomized simulation @@ -121,7 +128,7 @@ func TestAppImportExport(t *testing.T) { simapp.FlagPeriodValue, true, encoding.MakeConfig(ModuleBasics), - EmptyAppOptions{}, + simapp.EmptyAppOptions{}, fauxMerkleModeOpt, ) require.Equal(t, cantoconfig.AppName, app.Name()) @@ -173,7 +180,7 @@ func TestAppImportExport(t *testing.T) { simapp.FlagPeriodValue, true, encoding.MakeConfig(ModuleBasics), - EmptyAppOptions{}, + simapp.EmptyAppOptions{}, fauxMerkleModeOpt, ) require.Equal(t, cantoconfig.AppName, newApp.Name()) @@ -200,23 +207,21 @@ func TestAppImportExport(t *testing.T) { }, {app.keys[distrtypes.StoreKey], newApp.keys[distrtypes.StoreKey], [][]byte{}}, {app.keys[paramstypes.StoreKey], newApp.keys[paramstypes.StoreKey], [][]byte{}}, - //{app.keys[upgradetypes.StoreKey], newApp.keys[upgradetypes.StoreKey], [][]byte{}}, {app.keys[evidencetypes.StoreKey], newApp.keys[evidencetypes.StoreKey], [][]byte{}}, {app.keys[capabilitytypes.StoreKey], newApp.keys[capabilitytypes.StoreKey], [][]byte{}}, - // {app.keys[feegrant.StoreKey], newApp.keys[feegrant.StoreKey], [][]byte{}}, + {app.keys[feegrant.StoreKey], newApp.keys[feegrant.StoreKey], [][]byte{}}, {app.keys[authzkeeper.StoreKey], newApp.keys[authzkeeper.StoreKey], [][]byte{}}, {app.keys[ibchost.StoreKey], newApp.keys[ibchost.StoreKey], [][]byte{}}, {app.keys[ibctransfertypes.StoreKey], newApp.keys[ibctransfertypes.StoreKey], [][]byte{}}, - // {app.keys[evmtypes.StoreKey], newApp.keys[evmtypes.StoreKey], [][]byte{}}, + {app.keys[evmtypes.StoreKey], newApp.keys[evmtypes.StoreKey], [][]byte{}}, {app.keys[feemarkettypes.StoreKey], newApp.keys[feemarkettypes.StoreKey], [][]byte{}}, - //{app.keys[inflationtypes.StoreKey], newApp.keys[inflationtypes.StoreKey], [][]byte{}}, - // {app.keys[erc20types.StoreKey], newApp.keys[erc20types.StoreKey], [][]byte{}}, + {app.keys[inflationtypes.StoreKey], newApp.keys[inflationtypes.StoreKey], [][]byte{}}, + {app.keys[erc20types.StoreKey], newApp.keys[erc20types.StoreKey], [][]byte{}}, + // In the case of epoch module, the value is updated when importing genesis, so the store consistency is broken //{app.keys[epochstypes.StoreKey], newApp.keys[epochstypes.StoreKey], [][]byte{}}, - // {app.keys[vestingtypes.StoreKey], newApp.keys[vestingtypes.StoreKey], [][]byte{}}, - // {app.keys[recoverytypes.StoreKey], newApp.keys[recoverytypes.StoreKey], [][]byte{}}, - // {app.keys[feestypes.StoreKey], newApp.keys[feestypes.StoreKey], [][]byte{}}, - // {app.keys[csrtypes.StoreKey], newApp.keys[csrtypes.StoreKey], [][]byte{}}, - // {app.keys[govshuttletypes.StoreKey], newApp.keys[govshuttletypes.StoreKey], [][]byte{}}, + {app.keys[recoverytypes.StoreKey], newApp.keys[recoverytypes.StoreKey], [][]byte{}}, + {app.keys[csrtypes.StoreKey], newApp.keys[csrtypes.StoreKey], [][]byte{}}, + {app.keys[govshuttletypes.StoreKey], newApp.keys[govshuttletypes.StoreKey], [][]byte{}}, } for _, skp := range storeKeysPrefixes { @@ -271,7 +276,7 @@ func TestAppStateDeterminism(t *testing.T) { simapp.FlagPeriodValue, true, encoding.MakeConfig(ModuleBasics), - EmptyAppOptions{}, + simapp.EmptyAppOptions{}, fauxMerkleModeOpt, ) fmt.Printf("running simulation with seed %d\n", config.Seed) @@ -330,7 +335,7 @@ func TestAppSimulationAfterImport(t *testing.T) { simapp.FlagPeriodValue, true, encoding.MakeConfig(ModuleBasics), - EmptyAppOptions{}, + simapp.EmptyAppOptions{}, fauxMerkleModeOpt, ) require.Equal(t, cantoconfig.AppName, app.Name()) @@ -387,7 +392,7 @@ func TestAppSimulationAfterImport(t *testing.T) { simapp.FlagPeriodValue, true, encoding.MakeConfig(ModuleBasics), - EmptyAppOptions{}, + simapp.EmptyAppOptions{}, fauxMerkleModeOpt, ) require.Equal(t, cantoconfig.AppName, newApp.Name()) diff --git a/app/test_helpers.go b/app/test_helpers.go index 27348e63..0d190921 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -93,12 +93,6 @@ func Setup( return app } -type EmptyAppOptions struct{} - -func (ao EmptyAppOptions) Get(o string) interface{} { - return nil -} - // SetupTestingApp initializes the IBC-go testing application func SetupTestingApp() (ibctesting.TestingApp, map[string]json.RawMessage) { db := dbm.NewMemDB() From 4d1979ad6ab36d29bf94724b2791ada316792813 Mon Sep 17 00:00:00 2001 From: poorphd Date: Mon, 31 Jul 2023 15:25:01 +0900 Subject: [PATCH 7/9] fix: remove recovery from storeKeysPrefixes --- app/sim_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/sim_test.go b/app/sim_test.go index 560506ae..9f6788de 100644 --- a/app/sim_test.go +++ b/app/sim_test.go @@ -36,7 +36,6 @@ import ( erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types" govshuttletypes "github.com/Canto-Network/Canto/v6/x/govshuttle/types" inflationtypes "github.com/Canto-Network/Canto/v6/x/inflation/types" - recoverytypes "github.com/Canto-Network/Canto/v6/x/recovery/types" ) // Get flags every time the simulator is run @@ -219,7 +218,6 @@ func TestAppImportExport(t *testing.T) { {app.keys[erc20types.StoreKey], newApp.keys[erc20types.StoreKey], [][]byte{}}, // In the case of epoch module, the value is updated when importing genesis, so the store consistency is broken //{app.keys[epochstypes.StoreKey], newApp.keys[epochstypes.StoreKey], [][]byte{}}, - {app.keys[recoverytypes.StoreKey], newApp.keys[recoverytypes.StoreKey], [][]byte{}}, {app.keys[csrtypes.StoreKey], newApp.keys[csrtypes.StoreKey], [][]byte{}}, {app.keys[govshuttletypes.StoreKey], newApp.keys[govshuttletypes.StoreKey], [][]byte{}}, } From f810e8d13043d6d00378f173e247e48d43c2e13d Mon Sep 17 00:00:00 2001 From: dongsam Date: Mon, 31 Jul 2023 20:08:21 +0900 Subject: [PATCH 8/9] fix: simulation seed randomness was removed to make the ci/cd result deterministic --- Makefile | 4 ++-- app/sim_test.go | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index f8ed784b..ed6ba4f3 100755 --- a/Makefile +++ b/Makefile @@ -380,12 +380,12 @@ test-rpc-pending: test-sim-nondeterminism: @echo "Running non-determinism test..." @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ - -NumBlocks=20 -BlockSize=100 -Commit=true -Period=1 -v -timeout 10m + -NumBlocks=20 -BlockSize=100 -Commit=true -Seed=42 -Period=1 -v -timeout 10m test-sim-nondeterminism-long: @echo "Running non-determinism-long test..." @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ - -NumBlocks=100 -BlockSize=200 -Commit=true -Period=1 -v -timeout 10h + -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=42 -Period=1 -v -timeout 10h test-sim-custom-genesis-fast: @echo "Running custom genesis simulation..." diff --git a/app/sim_test.go b/app/sim_test.go index 9f6788de..2a002cff 100644 --- a/app/sim_test.go +++ b/app/sim_test.go @@ -3,7 +3,6 @@ package app import ( "encoding/json" "fmt" - "math/rand" "os" "testing" @@ -246,15 +245,14 @@ func TestAppStateDeterminism(t *testing.T) { config.AllInvariants = false config.ChainID = "canto_9000-1" - numSeeds := 3 - numTimesToRunPerSeed := 5 + numSeeds := config.NumBlocks / 10 + numTimesToRunPerSeed := 2 appHashList := make([]json.RawMessage, numTimesToRunPerSeed) sdk.DefaultPowerReduction = sdk.NewIntFromUint64(1000000) for i := 0; i < numSeeds; i++ { - config.Seed = rand.Int63() - + config.Seed = config.Seed + int64(i) for j := 0; j < numTimesToRunPerSeed; j++ { var logger log.Logger if simapp.FlagVerboseValue { From 30ebfbc9833bee795ff169d714f8954d486d72ea Mon Sep 17 00:00:00 2001 From: dongsam Date: Mon, 31 Jul 2023 21:01:05 +0900 Subject: [PATCH 9/9] chore: add branches rule for workflow --- .github/workflows/build.yml | 1 + .github/workflows/sims.yml | 4 ++-- .github/workflows/test.yml | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8846a32c..c7cb169f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,7 @@ on: branches: - main - release/** + - upgrade/** permissions: contents: read diff --git a/.github/workflows/sims.yml b/.github/workflows/sims.yml index 7cb31de8..6684f949 100644 --- a/.github/workflows/sims.yml +++ b/.github/workflows/sims.yml @@ -6,8 +6,8 @@ on: push: branches: - main - - develop - + - release/** + - upgrade/** jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a923c0b7..840c6968 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,8 @@ on: push: branches: - main + - release/** + - upgrade/** jobs: cleanup-runs: runs-on: ubuntu-latest