diff --git a/Makefile b/Makefile index 0c84679bc..a9241c20b 100644 --- a/Makefile +++ b/Makefile @@ -156,15 +156,15 @@ test-race: run-tests test-evmd: ARGS=-timeout=15m test-evmd: - @cd evmd && go test -count=1 -race -tags=test -mod=readonly $(ARGS) $(EXTRA_ARGS) $(PACKAGES_EVMD) + @cd evmd && go test -count=1 -tags=test -mod=readonly $(ARGS) $(EXTRA_ARGS) $(PACKAGES_EVMD) test-unit-cover: ARGS=-timeout=15m -coverprofile=coverage.txt -covermode=atomic test-unit-cover: TEST_PACKAGES=$(PACKAGES_UNIT) test-unit-cover: run-tests @echo "๐Ÿ” Running evm (root) coverage..." - @go test -race -tags=test $(COMMON_COVER_ARGS) -coverpkg=$(COVERPKG_ALL) -coverprofile=coverage.txt ./... + @go test -tags=test $(COMMON_COVER_ARGS) -coverpkg=$(COVERPKG_ALL) -coverprofile=coverage.txt ./... @echo "๐Ÿ” Running evmd coverage..." - @cd evmd && go test -race -tags=test $(COMMON_COVER_ARGS) -coverpkg=$(COVERPKG_ALL) -coverprofile=coverage_evmd.txt ./... + @cd evmd && go test -tags=test $(COMMON_COVER_ARGS) -coverpkg=$(COVERPKG_ALL) -coverprofile=coverage_evmd.txt ./... @echo "๐Ÿ”€ Merging evmd coverage into root coverage..." @tail -n +2 evmd/coverage_evmd.txt >> coverage.txt && rm evmd/coverage_evmd.txt @echo "๐Ÿงน Filtering ignored files from coverage.txt..." @@ -174,15 +174,15 @@ test: test-unit test-all: @echo "๐Ÿ” Running evm module tests..." - @go test -race -tags=test -mod=readonly -timeout=15m $(PACKAGES_NOSIMULATION) + @go test -tags=test -mod=readonly -timeout=15m $(PACKAGES_NOSIMULATION) @echo "๐Ÿ” Running evmd module tests..." - @cd evmd && go test -race -tags=test -mod=readonly -timeout=15m $(PACKAGES_EVMD) + @cd evmd && go test -tags=test -mod=readonly -timeout=15m $(PACKAGES_EVMD) run-tests: ifneq (,$(shell which tparse 2>/dev/null)) - go test -count=1 -race -tags=test -mod=readonly -json $(ARGS) $(EXTRA_ARGS) $(TEST_PACKAGES) | tparse + go test -count=1 -tags=test -mod=readonly -json $(ARGS) $(EXTRA_ARGS) $(TEST_PACKAGES) | tparse else - go test -count=1 -race -tags=test -mod=readonly $(ARGS) $(EXTRA_ARGS) $(TEST_PACKAGES) + go test -count=1 -tags=test -mod=readonly $(ARGS) $(EXTRA_ARGS) $(TEST_PACKAGES) endif # Use the old Apple linker to workaround broken xcode - https://github.com/golang/go/issues/65169 @@ -191,11 +191,11 @@ ifeq ($(OS_FAMILY),Darwin) endif test-fuzz: - go test -race -tags=test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzMintCoins ./x/precisebank/keeper - go test -race -tags=test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzBurnCoins ./x/precisebank/keeper - go test -race -tags=test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzSendCoins ./x/precisebank/keeper - go test -race -tags=test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzGenesisStateValidate_NonZeroRemainder ./x/precisebank/types - go test -race -tags=test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzGenesisStateValidate_ZeroRemainder ./x/precisebank/types + go test -tags=test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzMintCoins ./x/precisebank/keeper + go test -tags=test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzBurnCoins ./x/precisebank/keeper + go test -tags=test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzSendCoins ./x/precisebank/keeper + go test -tags=test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzGenesisStateValidate_NonZeroRemainder ./x/precisebank/types + go test -tags=test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzGenesisStateValidate_ZeroRemainder ./x/precisebank/types test-scripts: @echo "Running scripts tests" @@ -208,7 +208,7 @@ test-solidity: .PHONY: run-tests test test-all $(TEST_TARGETS) benchmark: - @go test -race -tags=test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION) + @go test -tags=test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION) .PHONY: benchmark diff --git a/evmd/.gitignore b/evmd/.gitignore new file mode 100644 index 000000000..26b91f9ef --- /dev/null +++ b/evmd/.gitignore @@ -0,0 +1 @@ +scripts/node-data \ No newline at end of file diff --git a/evmd/Dockerfile b/evmd/Dockerfile new file mode 100644 index 000000000..53262cb15 --- /dev/null +++ b/evmd/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.25-alpine AS builder + +RUN apk add --no-cache curl build-base git bash file linux-headers eudev-dev + +WORKDIR /src + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +RUN LEDGER_ENABLED=false make build + +FROM alpine:3.21 +COPY --from=builder /src/build/evmd /bin/evmd +ENTRYPOINT ["evmd"] diff --git a/evmd/Makefile b/evmd/Makefile new file mode 100644 index 000000000..16e9c1493 --- /dev/null +++ b/evmd/Makefile @@ -0,0 +1,159 @@ +BRANCH := $(shell git rev-parse --abbrev-ref HEAD) +COMMIT := $(shell git log -1 --format='%H') +APPNAME := evm +BUILDDIR ?= $(CURDIR)/build +DOCKER := $(shell which docker) + +# don't override user values +ifeq (,$(VERSION)) + VERSION := $(shell git describe --exact-match 2>/dev/null) + # if VERSION is empty, then populate it with branch's name and raw commit hash + ifeq (,$(VERSION)) + VERSION := $(BRANCH)-$(COMMIT) + endif +endif + +help: ## List Commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: help + +check-all: ## Convenience to make sure everything is all good +# @$(MAKE) proto-all # Uncomment if we add custom modules + @$(MAKE) build + @$(MAKE) test-unit + @$(MAKE) lint + @$(MAKE) e2e-contracts + @$(MAKE) test-e2e + +.PHONY: all + +################################################################################ +## Build ## +################################################################################ + +# forces Go to use its pure Go implementation of the DNS resolver +build_tags = netgo +build_tags += $(BUILD_TAGS) +build_tags := $(strip $(build_tags)) + +whitespace := +empty = $(whitespace) $(whitespace) +comma := , +build_tags_comma_sep := $(subst $(empty),$(comma),$(build_tags)) + +# process linker flags + +# TODO: Figure out if '-s -w' is still needed +# flags '-s -w' resolves an issue with xcode 16 and signing of go binaries +# ref: https://github.com/golang/go/issues/63997 +ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=$(APPNAME) \ + -X github.com/cosmos/cosmos-sdk/version.AppName=$(APPNAME) \ + -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ + -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \ + -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep)" \ + -s -w + +ifeq ($(LINK_STATICALLY),true) + ldflags += -linkmode=external -extldflags "-Wl,-z,muldefs -static" +endif +ldflags += $(LDFLAGS) +ldflags := $(strip $(ldflags)) + +BUILD_FLAGS := -tags "$(build_tags_comma_sep)" -ldflags '$(ldflags)' -trimpath + +clean: ## Clean build artifacts + rm -rf $(BUILDDIR)/ artifacts/ + +build: go.sum ## Build chain binary + @echo "Building chain binary" + go build -mod=readonly $(BUILD_FLAGS) -o $(BUILDDIR)/evmd ./cmd/evmd + + + +.PHONY: build clean + +################################################################################ +## Test ## +################################################################################ + +test-unit: ## Run unit tests + @echo Running unit tests... + @go test -mod=readonly -v -timeout 30m ./... + +test-race: ## Run unit tests with race condition reporting + @echo Running unit tests with race condition reporting... + @go test -mod=readonly -v -race -timeout 30m ./... + +test-e2e: ## Run e2e tests + @echo Running e2e tests... + @cd e2e && go test -parallel 8 -mod=readonly -v -count=1 -timeout 30m . + +.PHONY: test-unit test-race + +################################################################################ +## Install ## +################################################################################ + +install: ## Install dependencies + @echo "--> ensure dependencies have not been modified" + @go mod verify + @echo "--> installing $(APPNAME)d" + @go install $(BUILD_FLAGS) -mod=readonly ./cmd/$(APPNAME)d + +.PHONY: install + +################################################################################ +## Protobuf ## +################################################################################ + +protoVer=0.17.1 +protoImageName=ghcr.io/cosmos/proto-builder:$(protoVer) +protoImage=$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(protoImageName) + +proto-all: proto-format proto-lint proto-gen ## Generate Protobuf files + +proto-gen: ## Generate Protobuf files + @echo "Generating Protobuf files" + @$(protoImage) sh ./scripts/protocgen.sh + @go mod tidy + +proto-format: ## Format Protobuf files + @echo "Formatting Protobuf files" + @$(protoImage) find ./ -name "*.proto" -exec clang-format -i {} \; + +proto-lint: ## Lint Protobuf files + @$(protoImage) buf lint --error-format=json + +.PHONY: proto-all proto-gen proto-format proto-lint + +################################################################################ +## Linting ## +################################################################################ + +golangci_lint_cmd=golangci-lint + +lint: ## Run linter + @echo "--> Running linter" + @$(golangci_lint_cmd) run ./... --timeout 15m -c .golangci.yml + @cd e2e && $(golangci_lint_cmd) run ./... --timeout 15m -c ../.golangci.yml + +lint-fix: ## Run linter and fix issues + @echo "--> Running linter and fixing issues" + @$(golangci_lint_cmd) run ./... --fix --timeout 15m -c .golangci.yml + @cd e2e && $(golangci_lint_cmd) run ./... --fix --timeout 15m -c ../.golangci.yml + +.PHONY: lint lint-fix + +################################################################################ +## E2E Contract Artifacts ## +################################################################################ + +SOLC_IMAGE ?= ethereum/solc:0.8.28 +E2E_CONTRACTS_DIR ?= e2e/contracts + +e2e-contracts: ## Regenerate e2e embedded contract artifacts (ABI/BIN) + @echo "--> Regenerating e2e contract artifacts with $(SOLC_IMAGE)" + @docker run --rm -v $(CURDIR)/$(E2E_CONTRACTS_DIR):/contracts $(SOLC_IMAGE) --abi --bin -o /contracts --overwrite /contracts/Counter.sol /contracts/Reverter.sol + +.PHONY: e2e-contracts diff --git a/evmd/app.go b/evmd/app.go deleted file mode 100644 index 09634151d..000000000 --- a/evmd/app.go +++ /dev/null @@ -1,1112 +0,0 @@ -package evmd - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "os" - - "github.com/spf13/cast" - - // Force-load the tracer engines to trigger registration due to Go-Ethereum v1.10.15 changes - "github.com/ethereum/go-ethereum/common" - _ "github.com/ethereum/go-ethereum/eth/tracers/js" - _ "github.com/ethereum/go-ethereum/eth/tracers/native" - - abci "github.com/cometbft/cometbft/abci/types" - - dbm "github.com/cosmos/cosmos-db" - evmante "github.com/cosmos/evm/ante" - antetypes "github.com/cosmos/evm/ante/types" - evmencoding "github.com/cosmos/evm/encoding" - evmaddress "github.com/cosmos/evm/encoding/address" - evmconfig "github.com/cosmos/evm/evmd/config" - evmmempool "github.com/cosmos/evm/mempool" - precompiletypes "github.com/cosmos/evm/precompiles/types" - srvflags "github.com/cosmos/evm/server/flags" - "github.com/cosmos/evm/utils" - "github.com/cosmos/evm/x/erc20" - erc20keeper "github.com/cosmos/evm/x/erc20/keeper" - erc20types "github.com/cosmos/evm/x/erc20/types" - erc20v2 "github.com/cosmos/evm/x/erc20/v2" - "github.com/cosmos/evm/x/feemarket" - feemarketkeeper "github.com/cosmos/evm/x/feemarket/keeper" - feemarkettypes "github.com/cosmos/evm/x/feemarket/types" - ibccallbackskeeper "github.com/cosmos/evm/x/ibc/callbacks/keeper" - - "github.com/cosmos/evm/x/ibc/transfer" - transferkeeper "github.com/cosmos/evm/x/ibc/transfer/keeper" - transferv2 "github.com/cosmos/evm/x/ibc/transfer/v2" - "github.com/cosmos/evm/x/precisebank" - precisebankkeeper "github.com/cosmos/evm/x/precisebank/keeper" - precisebanktypes "github.com/cosmos/evm/x/precisebank/types" - "github.com/cosmos/evm/x/vm" - evmkeeper "github.com/cosmos/evm/x/vm/keeper" - evmtypes "github.com/cosmos/evm/x/vm/types" - "github.com/cosmos/gogoproto/proto" - ibccallbacks "github.com/cosmos/ibc-go/v10/modules/apps/callbacks" - ibctransfer "github.com/cosmos/ibc-go/v10/modules/apps/transfer" - ibctransfertypes "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types" - ibc "github.com/cosmos/ibc-go/v10/modules/core" - porttypes "github.com/cosmos/ibc-go/v10/modules/core/05-port/types" - ibcapi "github.com/cosmos/ibc-go/v10/modules/core/api" - ibcexported "github.com/cosmos/ibc-go/v10/modules/core/exported" - ibckeeper "github.com/cosmos/ibc-go/v10/modules/core/keeper" - ibctm "github.com/cosmos/ibc-go/v10/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v10/testing" - - autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" - reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" - "cosmossdk.io/client/v2/autocli" - "cosmossdk.io/core/appmodule" - "cosmossdk.io/log" - storetypes "cosmossdk.io/store/types" - "cosmossdk.io/x/evidence" - evidencekeeper "cosmossdk.io/x/evidence/keeper" - evidencetypes "cosmossdk.io/x/evidence/types" - "cosmossdk.io/x/feegrant" - feegrantkeeper "cosmossdk.io/x/feegrant/keeper" - feegrantmodule "cosmossdk.io/x/feegrant/module" - "cosmossdk.io/x/upgrade" - upgradekeeper "cosmossdk.io/x/upgrade/keeper" - upgradetypes "cosmossdk.io/x/upgrade/types" - - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" - "github.com/cosmos/cosmos-sdk/client/grpc/node" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/runtime" - runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" - sdkserver "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/server/api" - "github.com/cosmos/cosmos-sdk/server/config" - servertypes "github.com/cosmos/cosmos-sdk/server/types" - testdata_pulsar "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" - "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/types/msgservice" - signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" - "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/auth" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - "github.com/cosmos/cosmos-sdk/x/auth/posthandler" - authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" - txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/auth/vesting" - vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" - "github.com/cosmos/cosmos-sdk/x/authz" - authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" - authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" - "github.com/cosmos/cosmos-sdk/x/bank" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/consensus" - consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" - consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/x/genutil" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - "github.com/cosmos/cosmos-sdk/x/gov" - govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/cosmos/cosmos-sdk/x/mint" - mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - "github.com/cosmos/cosmos-sdk/x/slashing" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/cosmos-sdk/x/staking" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - cosmosevmserver "github.com/cosmos/evm/server" -) - -func init() { - // manually update the power reduction by replacing micro (u) -> atto (a) evmos - sdk.DefaultPowerReduction = utils.AttoPowerReduction - - defaultNodeHome = evmconfig.MustGetDefaultNodeHome() -} - -const appName = "evmd" - -// defaultNodeHome default home directories for the application daemon -var defaultNodeHome string - -var ( - _ runtime.AppI = (*EVMD)(nil) - _ cosmosevmserver.Application = (*EVMD)(nil) - _ ibctesting.TestingApp = (*EVMD)(nil) -) - -// EVMD extends an ABCI application, but with most of its parameters exported. -type EVMD struct { - *baseapp.BaseApp - - legacyAmino *codec.LegacyAmino - appCodec codec.Codec - interfaceRegistry types.InterfaceRegistry - txConfig client.TxConfig - clientCtx client.Context - - pendingTxListeners []evmante.PendingTxListener - - // keys to access the substores - keys map[string]*storetypes.KVStoreKey - oKeys map[string]*storetypes.ObjectStoreKey - - // keepers - AccountKeeper authkeeper.AccountKeeper - BankKeeper bankkeeper.Keeper - StakingKeeper *stakingkeeper.Keeper - SlashingKeeper slashingkeeper.Keeper - MintKeeper mintkeeper.Keeper - DistrKeeper distrkeeper.Keeper - GovKeeper govkeeper.Keeper - UpgradeKeeper *upgradekeeper.Keeper - AuthzKeeper authzkeeper.Keeper - EvidenceKeeper evidencekeeper.Keeper - FeeGrantKeeper feegrantkeeper.Keeper - ConsensusParamsKeeper consensusparamkeeper.Keeper - - // IBC keepers - IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly - TransferKeeper transferkeeper.Keeper - CallbackKeeper ibccallbackskeeper.ContractKeeper - - // Cosmos EVM keepers - FeeMarketKeeper feemarketkeeper.Keeper - EVMKeeper *evmkeeper.Keeper - Erc20Keeper erc20keeper.Keeper - PreciseBankKeeper precisebankkeeper.Keeper - EVMMempool *evmmempool.ExperimentalEVMMempool - - // the module manager - ModuleManager *module.Manager - BasicModuleManager module.BasicManager - - // simulation manager - sm *module.SimulationManager - - // module configurator - configurator module.Configurator -} - -// NewExampleApp returns a reference to an initialized EVMD. -func NewExampleApp( - logger log.Logger, - db dbm.DB, - traceStore io.Writer, - loadLatest bool, - appOpts servertypes.AppOptions, - baseAppOptions ...func(*baseapp.BaseApp), -) *EVMD { - evmChainID := cast.ToUint64(appOpts.Get(srvflags.EVMChainID)) - encodingConfig := evmencoding.MakeConfig(evmChainID) - - appCodec := encodingConfig.Codec - legacyAmino := encodingConfig.Amino - interfaceRegistry := encodingConfig.InterfaceRegistry - txConfig := encodingConfig.TxConfig - - bApp := baseapp.NewBaseApp( - appName, - logger, - db, - // use transaction decoder to support the sdk.Tx interface instead of sdk.StdTx - encodingConfig.TxConfig.TxDecoder(), - baseAppOptions..., - ) - bApp.SetCommitMultiStoreTracer(traceStore) - bApp.SetVersion(version.Version) - bApp.SetInterfaceRegistry(interfaceRegistry) - bApp.SetTxEncoder(txConfig.TxEncoder()) - - keys := storetypes.NewKVStoreKeys( - authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, - minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, - govtypes.StoreKey, consensusparamtypes.StoreKey, - upgradetypes.StoreKey, feegrant.StoreKey, evidencetypes.StoreKey, authzkeeper.StoreKey, - // ibc keys - ibcexported.StoreKey, ibctransfertypes.StoreKey, - // Cosmos EVM store keys - evmtypes.StoreKey, feemarkettypes.StoreKey, erc20types.StoreKey, precisebanktypes.StoreKey, - ) - oKeys := storetypes.NewObjectStoreKeys(banktypes.ObjectStoreKey, evmtypes.ObjectKey) - - var nonTransientKeys []storetypes.StoreKey - for _, k := range keys { - nonTransientKeys = append(nonTransientKeys, k) - } - for _, k := range oKeys { - nonTransientKeys = append(nonTransientKeys, k) - } - - // load state streaming if enabled - if err := bApp.RegisterStreamingServices(appOpts, keys); err != nil { - fmt.Printf("failed to load state streaming: %s", err) - os.Exit(1) - } - - // wire up the versiondb's `StreamingService` and `MultiStore`. - if cast.ToBool(appOpts.Get("versiondb.enable")) { - panic("version db not supported in this example chain") - } - - app := &EVMD{ - BaseApp: bApp, - legacyAmino: legacyAmino, - appCodec: appCodec, - txConfig: txConfig, - interfaceRegistry: interfaceRegistry, - keys: keys, - oKeys: oKeys, - } - - // removed x/params: no ParamsKeeper initialization - - // get authority address - authAddr := authtypes.NewModuleAddress(govtypes.ModuleName).String() - - // set the BaseApp's parameter store - app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper( - appCodec, - runtime.NewKVStoreService(keys[consensusparamtypes.StoreKey]), - authAddr, - runtime.EventService{}, - ) - bApp.SetParamStore(app.ConsensusParamsKeeper.ParamsStore) - - // add keepers - app.AccountKeeper = authkeeper.NewAccountKeeper( - appCodec, runtime.NewKVStoreService(keys[authtypes.StoreKey]), - authtypes.ProtoBaseAccount, evmconfig.GetMaccPerms(), - evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32AccountAddrPrefix()), - sdk.GetConfig().GetBech32AccountAddrPrefix(), - authAddr, - ) - - app.BankKeeper = bankkeeper.NewBaseKeeper( - appCodec, - runtime.NewKVStoreService(keys[banktypes.StoreKey]), - app.AccountKeeper, - evmconfig.BlockedAddresses(), - authAddr, - logger, - ) - app.BankKeeper = app.BankKeeper.WithObjStoreKey(oKeys[banktypes.ObjectStoreKey]) - - // optional: enable sign mode textual by overwriting the default tx config (after setting the bank keeper) - enabledSignModes := append(authtx.DefaultSignModes, signingtypes.SignMode_SIGN_MODE_TEXTUAL) //nolint:gocritic - txConfigOpts := authtx.ConfigOptions{ - EnabledSignModes: enabledSignModes, - TextualCoinMetadataQueryFn: txmodule.NewBankKeeperCoinMetadataQueryFn(app.BankKeeper), - } - txConfig, err := authtx.NewTxConfigWithOptions( - appCodec, - txConfigOpts, - ) - if err != nil { - panic(err) - } - app.txConfig = txConfig - - app.StakingKeeper = stakingkeeper.NewKeeper( - appCodec, - runtime.NewKVStoreService(keys[stakingtypes.StoreKey]), - app.AccountKeeper, - app.BankKeeper, - authAddr, - evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), - evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), - ) - - app.MintKeeper = mintkeeper.NewKeeper( - appCodec, - runtime.NewKVStoreService(keys[minttypes.StoreKey]), - app.StakingKeeper, - app.AccountKeeper, - app.BankKeeper, - authtypes.FeeCollectorName, - authAddr, - ) - - app.DistrKeeper = distrkeeper.NewKeeper( - appCodec, - runtime.NewKVStoreService(keys[distrtypes.StoreKey]), - app.AccountKeeper, - app.BankKeeper, - app.StakingKeeper, - authtypes.FeeCollectorName, - authAddr, - ) - - app.SlashingKeeper = slashingkeeper.NewKeeper( - appCodec, - app.LegacyAmino(), - runtime.NewKVStoreService(keys[slashingtypes.StoreKey]), - app.StakingKeeper, - authAddr, - ) - - app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, runtime.NewKVStoreService(keys[feegrant.StoreKey]), app.AccountKeeper) - - // register the staking hooks - // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks - app.StakingKeeper.SetHooks( - stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), - ) - - app.AuthzKeeper = authzkeeper.NewKeeper( - runtime.NewKVStoreService(keys[authzkeeper.StoreKey]), - appCodec, - app.MsgServiceRouter(), - app.AccountKeeper, - ) - - // get skipUpgradeHeights from the app options - skipUpgradeHeights := map[int64]bool{} - for _, h := range cast.ToIntSlice(appOpts.Get(sdkserver.FlagUnsafeSkipUpgrades)) { - skipUpgradeHeights[int64(h)] = true - } - homePath := cast.ToString(appOpts.Get(flags.FlagHome)) - // set the governance module account as the authority for conducting upgrades - app.UpgradeKeeper = upgradekeeper.NewKeeper( - skipUpgradeHeights, - runtime.NewKVStoreService(keys[upgradetypes.StoreKey]), - appCodec, - homePath, - app.BaseApp, - authAddr, - ) - - // Create IBC Keeper - app.IBCKeeper = ibckeeper.NewKeeper( - appCodec, - runtime.NewKVStoreService(keys[ibcexported.StoreKey]), - app.UpgradeKeeper, - authAddr, - ) - - govConfig := govtypes.DefaultConfig() - /* - Example of setting gov params: - govConfig.MaxMetadataLen = 10000 - */ - govKeeper := govkeeper.NewKeeper( - appCodec, runtime.NewKVStoreService(keys[govtypes.StoreKey]), app.AccountKeeper, app.BankKeeper, - app.StakingKeeper, app.DistrKeeper, app.MsgServiceRouter(), govConfig, authAddr, - ) - - app.GovKeeper = *govKeeper.SetHooks( - govtypes.NewMultiGovHooks( - // register the governance hooks - ), - ) - - // create evidence keeper with router - evidenceKeeper := evidencekeeper.NewKeeper( - appCodec, - runtime.NewKVStoreService(keys[evidencetypes.StoreKey]), - app.StakingKeeper, - app.SlashingKeeper, - app.AccountKeeper.AddressCodec(), - runtime.ProvideCometInfoService(), - ) - // If evidence needs to be handled for the app, set routes in router here and seal - app.EvidenceKeeper = *evidenceKeeper - - // Cosmos EVM keepers - app.FeeMarketKeeper = feemarketkeeper.NewKeeper( - appCodec, authtypes.NewModuleAddress(govtypes.ModuleName), - keys[feemarkettypes.StoreKey], - ) - - // Set up PreciseBank keeper - // - // NOTE: PreciseBank is not needed if SDK use 18 decimals for gas coin. Use BankKeeper instead. - app.PreciseBankKeeper = precisebankkeeper.NewKeeper( - appCodec, - keys[precisebanktypes.StoreKey], - app.BankKeeper, - app.AccountKeeper, - ) - - // Set up EVM keeper - tracer := cast.ToString(appOpts.Get(srvflags.EVMTracer)) - - // NOTE: it's required to set up the EVM keeper before the ERC-20 keeper, because it is used in its instantiation. - app.EVMKeeper = evmkeeper.NewKeeper( - // TODO: check why this is not adjusted to use the runtime module methods like SDK native keepers - appCodec, keys[evmtypes.StoreKey], oKeys[evmtypes.ObjectKey], nonTransientKeys, - authtypes.NewModuleAddress(govtypes.ModuleName), - app.AccountKeeper, - app.PreciseBankKeeper, - app.StakingKeeper, - app.FeeMarketKeeper, - &app.ConsensusParamsKeeper, - &app.Erc20Keeper, - evmChainID, - tracer, - ).WithStaticPrecompiles( - precompiletypes.DefaultStaticPrecompiles( - *app.StakingKeeper, - app.DistrKeeper, - app.PreciseBankKeeper, - &app.Erc20Keeper, - &app.TransferKeeper, - app.IBCKeeper.ChannelKeeper, - app.IBCKeeper.ClientKeeper, - app.GovKeeper, - app.SlashingKeeper, - appCodec, - ), - ) - - app.Erc20Keeper = erc20keeper.NewKeeper( - keys[erc20types.StoreKey], - appCodec, - authtypes.NewModuleAddress(govtypes.ModuleName), - app.AccountKeeper, - app.PreciseBankKeeper, - app.EVMKeeper, - app.StakingKeeper, - &app.TransferKeeper, - ) - - // instantiate IBC transfer keeper AFTER the ERC-20 keeper to use it in the instantiation - app.TransferKeeper = transferkeeper.NewKeeper( - appCodec, - evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32AccountAddrPrefix()), - runtime.NewKVStoreService(keys[ibctransfertypes.StoreKey]), - app.IBCKeeper.ChannelKeeper, - app.MsgServiceRouter(), - app.AccountKeeper, - app.BankKeeper, - app.Erc20Keeper, // Add ERC20 Keeper for ERC20 transfers - authAddr, - ) - - /* - Create Transfer Stack - - transfer stack contains (from bottom to top): - - IBC Callbacks Middleware (with EVM ContractKeeper) - - ERC-20 Middleware - - IBC Transfer - - SendPacket, since it is originating from the application to core IBC: - transferKeeper.SendPacket -> erc20.SendPacket -> callbacks.SendPacket -> channel.SendPacket - - RecvPacket, message that originates from core IBC and goes down to app, the flow is the other way - channel.RecvPacket -> callbacks.OnRecvPacket -> erc20.OnRecvPacket -> transfer.OnRecvPacket - */ - - // create IBC module from top to bottom of stack - var transferStack porttypes.IBCModule - - transferStack = transfer.NewIBCModule(app.TransferKeeper) - maxCallbackGas := uint64(1_000_000) - transferStack = erc20.NewIBCMiddleware(app.Erc20Keeper, transferStack) - app.CallbackKeeper = ibccallbackskeeper.NewKeeper( - app.AccountKeeper, - app.EVMKeeper, - app.Erc20Keeper, - ) - callbacksMiddleware := ibccallbacks.NewIBCMiddleware(app.CallbackKeeper, maxCallbackGas) - callbacksMiddleware.SetICS4Wrapper(app.IBCKeeper.ChannelKeeper) - callbacksMiddleware.SetUnderlyingApplication(transferStack) - transferStack = callbacksMiddleware - - var transferStackV2 ibcapi.IBCModule - transferStackV2 = transferv2.NewIBCModule(app.TransferKeeper) - transferStackV2 = erc20v2.NewIBCMiddleware(transferStackV2, app.Erc20Keeper) - - // Create static IBC router, add transfer route, then set and seal it - ibcRouter := porttypes.NewRouter() - ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack) - ibcRouterV2 := ibcapi.NewRouter() - ibcRouterV2.AddRoute(ibctransfertypes.ModuleName, transferStackV2) - - app.IBCKeeper.SetRouter(ibcRouter) - app.IBCKeeper.SetRouterV2(ibcRouterV2) - - clientKeeper := app.IBCKeeper.ClientKeeper - storeProvider := app.IBCKeeper.ClientKeeper.GetStoreProvider() - tmLightClientModule := ibctm.NewLightClientModule(appCodec, storeProvider) - clientKeeper.AddRoute(ibctm.ModuleName, &tmLightClientModule) - - // Override the ICS20 app module - transferModule := transfer.NewAppModule(app.TransferKeeper) - - /**** Module Options ****/ - - // NOTE: Any module instantiated in the module manager that is later modified - // must be passed by reference here. - app.ModuleManager = module.NewManager( - genutil.NewAppModule( - app.AccountKeeper, app.StakingKeeper, - app, app.txConfig, - ), - auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, nil), - bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, nil), - feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), - gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, nil), - mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, nil), - slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, nil, app.interfaceRegistry), - distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, nil), - staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, nil), - upgrade.NewAppModule(app.UpgradeKeeper, app.AccountKeeper.AddressCodec()), - evidence.NewAppModule(app.EvidenceKeeper), - authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), - consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper), - vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), - // IBC modules - ibc.NewAppModule(app.IBCKeeper), - ibctm.NewAppModule(tmLightClientModule), - transferModule, - // Cosmos EVM modules - vm.NewAppModule(app.EVMKeeper, app.AccountKeeper, app.BankKeeper, app.AccountKeeper.AddressCodec()), - feemarket.NewAppModule(app.FeeMarketKeeper), - erc20.NewAppModule(app.Erc20Keeper, app.AccountKeeper), - precisebank.NewAppModule(app.PreciseBankKeeper, app.BankKeeper, app.AccountKeeper), - ) - - // BasicModuleManager defines the module BasicManager which is in charge of setting up basic, - // non-dependant module elements, such as codec registration and genesis verification. - // By default, it is composed of all the modules from the module manager. - // Additionally, app module basics can be overwritten by passing them as an argument. - app.BasicModuleManager = module.NewBasicManagerFromManager( - app.ModuleManager, - map[string]module.AppModuleBasic{ - genutiltypes.ModuleName: genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), - stakingtypes.ModuleName: staking.AppModuleBasic{}, - govtypes.ModuleName: gov.NewAppModuleBasic(nil), - ibctransfertypes.ModuleName: transfer.AppModuleBasic{AppModuleBasic: &ibctransfer.AppModuleBasic{}}, - }, - ) - app.BasicModuleManager.RegisterLegacyAminoCodec(legacyAmino) - app.BasicModuleManager.RegisterInterfaces(interfaceRegistry) - - // NOTE: upgrade module is required to be prioritized - app.ModuleManager.SetOrderPreBlockers( - upgradetypes.ModuleName, - authtypes.ModuleName, - evmtypes.ModuleName, - ) - - // During begin block slashing happens after distr.BeginBlocker so that - // there is nothing left over in the validator fee pool, so as to keep the - // CanWithdrawInvariant invariant. - // - // NOTE: staking module is required if HistoricalEntries param > 0 - // NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC) - app.ModuleManager.SetOrderBeginBlockers( - minttypes.ModuleName, - - // IBC modules - ibcexported.ModuleName, ibctransfertypes.ModuleName, - - // Cosmos EVM BeginBlockers - erc20types.ModuleName, feemarkettypes.ModuleName, - evmtypes.ModuleName, // NOTE: EVM BeginBlocker must come after FeeMarket BeginBlocker - - // TODO: remove no-ops? check if all are no-ops before removing - distrtypes.ModuleName, slashingtypes.ModuleName, - evidencetypes.ModuleName, stakingtypes.ModuleName, - authtypes.ModuleName, banktypes.ModuleName, govtypes.ModuleName, genutiltypes.ModuleName, - authz.ModuleName, feegrant.ModuleName, - consensusparamtypes.ModuleName, - precisebanktypes.ModuleName, - vestingtypes.ModuleName, - ) - - // NOTE: the feemarket module should go last in order of end blockers that are actually doing something, - // to get the full block gas used. - app.ModuleManager.SetOrderEndBlockers( - banktypes.ModuleName, - govtypes.ModuleName, - stakingtypes.ModuleName, - authtypes.ModuleName, - - // Cosmos EVM EndBlockers - evmtypes.ModuleName, erc20types.ModuleName, feemarkettypes.ModuleName, - - // no-ops - ibcexported.ModuleName, ibctransfertypes.ModuleName, - distrtypes.ModuleName, - slashingtypes.ModuleName, minttypes.ModuleName, - genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, - feegrant.ModuleName, upgradetypes.ModuleName, consensusparamtypes.ModuleName, - precisebanktypes.ModuleName, - vestingtypes.ModuleName, - ) - - // NOTE: The genutils module must occur after staking so that pools are - // properly initialized with tokens from genesis accounts. - // NOTE: The genutils module must also occur after auth so that it can access the params from auth. - genesisModuleOrder := []string{ - authtypes.ModuleName, banktypes.ModuleName, - distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, - minttypes.ModuleName, - ibcexported.ModuleName, - - // Cosmos EVM modules - // - // NOTE: feemarket module needs to be initialized before genutil module: - // gentx transactions use MinGasPriceDecorator.AnteHandle - evmtypes.ModuleName, - feemarkettypes.ModuleName, - erc20types.ModuleName, - precisebanktypes.ModuleName, - - ibctransfertypes.ModuleName, - genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, - feegrant.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, - } - app.ModuleManager.SetOrderInitGenesis(genesisModuleOrder...) - app.ModuleManager.SetOrderExportGenesis(genesisModuleOrder...) - - // Uncomment if you want to set a custom migration order here. - // app.ModuleManager.SetOrderMigrations(custom order) - - app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) - if err = app.ModuleManager.RegisterServices(app.configurator); err != nil { - panic(fmt.Sprintf("failed to register services in module manager: %s", err.Error())) - } - - // RegisterUpgradeHandlers is used for registering any on-chain upgrades. - // Make sure it's called after `app.ModuleManager` and `app.configurator` are set. - app.RegisterUpgradeHandlers() - - autocliv1.RegisterQueryServer(app.GRPCQueryRouter(), runtimeservices.NewAutoCLIQueryService(app.ModuleManager.Modules)) - - reflectionSvc, err := runtimeservices.NewReflectionService() - if err != nil { - panic(err) - } - reflectionv1.RegisterReflectionServiceServer(app.GRPCQueryRouter(), reflectionSvc) - - // add test gRPC service for testing gRPC queries in isolation - testdata_pulsar.RegisterQueryServer(app.GRPCQueryRouter(), testdata_pulsar.QueryImpl{}) - - // create the simulation manager and define the order of the modules for deterministic simulations - // - // NOTE: this is not required apps that don't use the simulator for fuzz testing - // transactions - overrideModules := map[string]module.AppModuleSimulation{ - authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, nil), - } - app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules) - - app.sm.RegisterStoreDecoders() - - // initialize stores - app.MountKVStores(keys) - app.MountObjectStores(oKeys) - - maxGasWanted := cast.ToUint64(appOpts.Get(srvflags.EVMMaxTxGasWanted)) - - // initialize BaseApp - app.SetInitChainer(app.InitChainer) - app.SetPreBlocker(app.PreBlocker) - app.SetBeginBlocker(app.BeginBlocker) - app.SetEndBlocker(app.EndBlocker) - - app.setAnteHandler(app.txConfig, maxGasWanted) - - // set the EVM priority nonce mempool - // if you wish to use the noop mempool, remove this codeblock - if err := app.configureEVMMempool(appOpts, logger); err != nil { - panic(fmt.Sprintf("failed to configure EVM mempool: %s", err.Error())) - } - - // In v0.46, the SDK introduces _postHandlers_. PostHandlers are like - // antehandlers, but are run _after_ the `runMsgs` execution. They are also - // defined as a chain, and have the same signature as antehandlers. - // - // In baseapp, postHandlers are run in the same store branch as `runMsgs`, - // meaning that both `runMsgs` and `postHandler` state will be committed if - // both are successful, and both will be reverted if any of the two fails. - // - // The SDK exposes a default postHandlers chain, which comprises of only - // one decorator: the Transaction Tips decorator. However, some chains do - // not need it by default, so feel free to comment the next line if you do - // not need tips. - // To read more about tips: - // https://docs.cosmos.network/main/core/tips.html - // - // Please note that changing any of the anteHandler or postHandler chain is - // likely to be a state-machine breaking change, which needs a coordinated - // upgrade. - app.setPostHandler() - - // At startup, after all modules have been registered, check that all prot - // annotations are correct. - protoFiles, err := proto.MergedRegistry() - if err != nil { - panic(err) - } - err = msgservice.ValidateProtoAnnotations(protoFiles) - if err != nil { - // TODO: Once we switch to using protoreflect-based antehandlers, we might - // want to panic here instead of logging a warning. - fmt.Fprintln(os.Stderr, err.Error()) - } - - if loadLatest { - if err := app.LoadLatestVersion(); err != nil { - logger.Error("error on loading last version", "err", err) - os.Exit(1) - } - } - - return app -} - -func (app *EVMD) setAnteHandler(txConfig client.TxConfig, maxGasWanted uint64) { - options := evmante.HandlerOptions{ - Cdc: app.appCodec, - AccountKeeper: app.AccountKeeper, - BankKeeper: app.BankKeeper, - ExtensionOptionChecker: antetypes.HasDynamicFeeExtensionOption, - EvmKeeper: app.EVMKeeper, - FeegrantKeeper: app.FeeGrantKeeper, - IBCKeeper: app.IBCKeeper, - FeeMarketKeeper: app.FeeMarketKeeper, - SignModeHandler: txConfig.SignModeHandler(), - SigGasConsumer: evmante.SigVerificationGasConsumer, - MaxTxGasWanted: maxGasWanted, - DynamicFeeChecker: true, - PendingTxListener: app.onPendingTx, - } - if err := options.Validate(); err != nil { - panic(err) - } - - app.SetAnteHandler(evmante.NewAnteHandler(options)) -} - -func (app *EVMD) onPendingTx(hash common.Hash) { - for _, listener := range app.pendingTxListeners { - listener(hash) - } -} - -// RegisterPendingTxListener is used by json-rpc server to listen to pending transactions callback. -func (app *EVMD) RegisterPendingTxListener(listener func(common.Hash)) { - app.pendingTxListeners = append(app.pendingTxListeners, listener) -} - -func (app *EVMD) setPostHandler() { - postHandler, err := posthandler.NewPostHandler( - posthandler.HandlerOptions{}, - ) - if err != nil { - panic(err) - } - - app.SetPostHandler(postHandler) -} - -// Name returns the name of the App -func (app *EVMD) Name() string { return app.BaseApp.Name() } - -// BeginBlocker application updates every begin block -func (app *EVMD) BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) { - return app.ModuleManager.BeginBlock(ctx) -} - -// EndBlocker application updates every end block -func (app *EVMD) EndBlocker(ctx sdk.Context) (sdk.EndBlock, error) { - return app.ModuleManager.EndBlock(ctx) -} - -func (app *EVMD) FinalizeBlock(req *abci.RequestFinalizeBlock) (res *abci.ResponseFinalizeBlock, err error) { - return app.BaseApp.FinalizeBlock(req) -} - -func (app *EVMD) Configurator() module.Configurator { - return app.configurator -} - -// InitChainer application update at chain initialization -func (app *EVMD) InitChainer(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) { - var genesisState GenesisState - if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil { - panic(err) - } - - if err := app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()); err != nil { - panic(err) - } - - return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) -} - -func (app *EVMD) PreBlocker(ctx sdk.Context, _ *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) { - return app.ModuleManager.PreBlock(ctx) -} - -// LoadHeight loads a particular height -func (app *EVMD) LoadHeight(height int64) error { - return app.LoadVersion(height) -} - -// LegacyAmino returns EVMD's amino codec. -// -// NOTE: This is solely to be used for testing purposes as it may be desirable -// for modules to register their own custom testing types. -func (app *EVMD) LegacyAmino() *codec.LegacyAmino { - return app.legacyAmino -} - -// AppCodec returns EVMD's app codec. -// -// NOTE: This is solely to be used for testing purposes as it may be desirable -// for modules to register their own custom testing types. -func (app *EVMD) AppCodec() codec.Codec { - return app.appCodec -} - -// InterfaceRegistry returns EVMD's InterfaceRegistry -func (app *EVMD) InterfaceRegistry() types.InterfaceRegistry { - return app.interfaceRegistry -} - -// TxConfig returns EVMD's TxConfig -func (app *EVMD) TxConfig() client.TxConfig { - return app.txConfig -} - -// DefaultGenesis returns a default genesis from the registered AppModuleBasic's. -func (app *EVMD) DefaultGenesis() map[string]json.RawMessage { - genesis := app.BasicModuleManager.DefaultGenesis(app.appCodec) - - mintGenState := NewMintGenesisState() - genesis[minttypes.ModuleName] = app.appCodec.MustMarshalJSON(mintGenState) - - evmGenState := NewEVMGenesisState() - genesis[evmtypes.ModuleName] = app.appCodec.MustMarshalJSON(evmGenState) - - // NOTE: for the example chain implementation we are also adding a default token pair, - // which is the base denomination of the chain (i.e. the WEVMOS contract) - erc20GenState := NewErc20GenesisState() - genesis[erc20types.ModuleName] = app.appCodec.MustMarshalJSON(erc20GenState) - - return genesis -} - -// GetKey returns the KVStoreKey for the provided store key. -// -// NOTE: This is solely to be used for testing purposes. -func (app *EVMD) GetKey(storeKey string) *storetypes.KVStoreKey { - return app.keys[storeKey] -} - -// SimulationManager implements the SimulationApp interface -func (app *EVMD) SimulationManager() *module.SimulationManager { - return app.sm -} - -// RegisterAPIRoutes registers all application module routes with the provided -// API server. -func (app *EVMD) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { - clientCtx := apiSvr.ClientCtx - // Register new tx routes from grpc-gateway. - authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) - - // Register new cometbft queries routes from grpc-gateway. - cmtservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) - - // Register node gRPC service for grpc-gateway. - node.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) - - // Register grpc-gateway routes for all modules. - app.BasicModuleManager.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) - - // register swagger API from root so that other applications can override easily - if err := sdkserver.RegisterSwaggerAPI(apiSvr.ClientCtx, apiSvr.Router, apiConfig.Swagger); err != nil { - panic(err) - } -} - -// RegisterTxService implements the Application.RegisterTxService method. -func (app *EVMD) RegisterTxService(clientCtx client.Context) { - authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) -} - -// RegisterTendermintService implements the Application.RegisterTendermintService method. -func (app *EVMD) RegisterTendermintService(clientCtx client.Context) { - cmtservice.RegisterTendermintService( - clientCtx, - app.GRPCQueryRouter(), - app.interfaceRegistry, - app.Query, - ) -} - -func (app *EVMD) RegisterNodeService(clientCtx client.Context, cfg config.Config) { - node.RegisterNodeService(clientCtx, app.GRPCQueryRouter(), cfg) -} - -// --------------------------------------------- -// IBC Go TestingApp functions -// - -// GetBaseApp implements the TestingApp interface. -func (app *EVMD) GetBaseApp() *baseapp.BaseApp { - return app.BaseApp -} - -// GetStakingKeeperSDK implements the TestingApp interface. -func (app *EVMD) GetStakingKeeperSDK() stakingkeeper.Keeper { - return *app.StakingKeeper -} - -// GetIBCKeeper implements the TestingApp interface. -func (app *EVMD) GetIBCKeeper() *ibckeeper.Keeper { - return app.IBCKeeper -} - -func (app *EVMD) GetEVMKeeper() *evmkeeper.Keeper { - return app.EVMKeeper -} - -func (app *EVMD) GetErc20Keeper() *erc20keeper.Keeper { - return &app.Erc20Keeper -} - -func (app *EVMD) SetErc20Keeper(erc20Keeper erc20keeper.Keeper) { - app.Erc20Keeper = erc20Keeper -} - -func (app *EVMD) GetGovKeeper() govkeeper.Keeper { - return app.GovKeeper -} - -func (app *EVMD) GetEvidenceKeeper() *evidencekeeper.Keeper { - return &app.EvidenceKeeper -} - -func (app *EVMD) GetSlashingKeeper() slashingkeeper.Keeper { - return app.SlashingKeeper -} - -func (app *EVMD) GetBankKeeper() bankkeeper.Keeper { - return app.BankKeeper -} - -func (app *EVMD) GetFeeMarketKeeper() *feemarketkeeper.Keeper { - return &app.FeeMarketKeeper -} - -func (app *EVMD) GetFeeGrantKeeper() feegrantkeeper.Keeper { - return app.FeeGrantKeeper -} - -func (app *EVMD) GetConsensusParamsKeeper() consensusparamkeeper.Keeper { - return app.ConsensusParamsKeeper -} - -func (app *EVMD) GetAccountKeeper() authkeeper.AccountKeeper { - return app.AccountKeeper -} - -func (app *EVMD) GetAuthzKeeper() authzkeeper.Keeper { - return app.AuthzKeeper -} - -func (app *EVMD) GetDistrKeeper() distrkeeper.Keeper { - return app.DistrKeeper -} - -func (app *EVMD) GetStakingKeeper() *stakingkeeper.Keeper { - return app.StakingKeeper -} - -func (app *EVMD) GetMintKeeper() mintkeeper.Keeper { - return app.MintKeeper -} - -func (app *EVMD) GetPreciseBankKeeper() *precisebankkeeper.Keeper { - return &app.PreciseBankKeeper -} - -func (app *EVMD) GetCallbackKeeper() ibccallbackskeeper.ContractKeeper { - return app.CallbackKeeper -} - -func (app *EVMD) GetTransferKeeper() transferkeeper.Keeper { - return app.TransferKeeper -} - -func (app *EVMD) SetTransferKeeper(transferKeeper transferkeeper.Keeper) { - app.TransferKeeper = transferKeeper -} - -func (app *EVMD) GetMempool() sdkmempool.ExtMempool { - return app.EVMMempool -} - -func (app *EVMD) GetAnteHandler() sdk.AnteHandler { - return app.BaseApp.AnteHandler() -} - -// GetTxConfig implements the TestingApp interface. -func (app *EVMD) GetTxConfig() client.TxConfig { - return app.txConfig -} - -func (app *EVMD) SetClientCtx(clientCtx client.Context) { // TODO:VLAD - Remove this if possible - app.clientCtx = clientCtx -} - -// Close unsubscribes from the CometBFT event bus (if set) and closes the mempool and underlying BaseApp. -func (app *EVMD) Close() error { - var err error - if m, ok := app.GetMempool().(*evmmempool.ExperimentalEVMMempool); ok && m != nil { - app.Logger().Info("Shutting down mempool") - err = m.Close() - } - - msg := "Application gracefully shutdown" - err = errors.Join(err, app.BaseApp.Close()) - if err == nil { - app.Logger().Info(msg) - } else { - app.Logger().Error(msg, "error", err) - } - - return err -} - -// AutoCliOpts returns the autocli options for the app. -func (app *EVMD) AutoCliOpts() autocli.AppOptions { - modules := make(map[string]appmodule.AppModule, 0) - for _, m := range app.ModuleManager.Modules { - if moduleWithName, ok := m.(module.HasName); ok { - moduleName := moduleWithName.Name() - if appModule, ok := moduleWithName.(appmodule.AppModule); ok { - modules[moduleName] = appModule - } - } - } - - return autocli.AppOptions{ - Modules: modules, - ModuleOptions: runtimeservices.ExtractAutoCLIOptions(app.ModuleManager.Modules), - AddressCodec: evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32AccountAddrPrefix()), - ValidatorAddressCodec: evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), - ConsensusAddressCodec: evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), - } -} diff --git a/evmd/app/app.go b/evmd/app/app.go new file mode 100644 index 000000000..e1e1d836f --- /dev/null +++ b/evmd/app/app.go @@ -0,0 +1,782 @@ +package app + +import ( + "context" + "encoding/json" + "fmt" + evmconfig "github.com/cosmos/evm/evmd/cmd/evmd/config" + ibctesting "github.com/cosmos/ibc-go/v10/testing" + "io" + "os" + goruntime "runtime" + + dbm "github.com/cosmos/cosmos-db" + baseevmante "github.com/cosmos/evm/ante" + antetypes "github.com/cosmos/evm/ante/types" + evmosencoding "github.com/cosmos/evm/encoding" + evmmempool "github.com/cosmos/evm/mempool" + cosmosevmserver "github.com/cosmos/evm/server" + srvflags "github.com/cosmos/evm/server/flags" + "github.com/cosmos/evm/x/feemarket" + feemarketkeeper "github.com/cosmos/evm/x/feemarket/keeper" + feemarkettypes "github.com/cosmos/evm/x/feemarket/types" + "github.com/cosmos/evm/x/vm" + evmkeeper "github.com/cosmos/evm/x/vm/keeper" + evmtypes "github.com/cosmos/evm/x/vm/types" + "github.com/cosmos/gogoproto/proto" + "github.com/ethereum/go-ethereum/common" + "github.com/spf13/cast" + + // Needed for some reason? TODO: Figure out if needed and why + _ "github.com/ethereum/go-ethereum/eth/tracers/js" + _ "github.com/ethereum/go-ethereum/eth/tracers/native" + + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" + "cosmossdk.io/client/v2/autocli" + "cosmossdk.io/core/appmodule" + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + "cosmossdk.io/x/upgrade" + upgradekeeper "cosmossdk.io/x/upgrade/keeper" + upgradetypes "cosmossdk.io/x/upgrade/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/blockstm" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" + nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/address" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/auth" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/posthandler" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/bank" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/consensus" + consensuskeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + "github.com/cosmos/cosmos-sdk/x/distribution" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/gov" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" + + ibc "github.com/cosmos/ibc-go/v10/modules/core" + ibcexported "github.com/cosmos/ibc-go/v10/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v10/modules/core/keeper" + ibctm "github.com/cosmos/ibc-go/v10/modules/light-clients/07-tendermint" +) + +const AppName = "evmd" + +var ( + // DefaultNodeHome default home directories for the application daemon + DefaultNodeHome string + + // module account permissions + maccPerms = map[string][]string{ + authtypes.FeeCollectorName: nil, + distrtypes.ModuleName: nil, + stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + govtypes.ModuleName: {authtypes.Burner}, + // Cosmos EVM modules + evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + feemarkettypes.ModuleName: nil, + } +) + +var ( + _ runtime.AppI = (*App)(nil) + _ servertypes.Application = (*App)(nil) + _ cosmosevmserver.Application = (*App)(nil) + _ ibctesting.TestingApp = (*App)(nil) +) + +// App extends an ABCI application, but with most of its parameters exported. +// They are exported for convenience in creating helper functions, as object +// capabilities aren't needed for testing. +type App struct { + *baseapp.BaseApp + legacyAmino *codec.LegacyAmino + appCodec codec.Codec + txConfig client.TxConfig + // Client context is needed for the evm mempool unfortunately... + clientCtx client.Context + interfaceRegistry codectypes.InterfaceRegistry + + // keys to access the substores + keys map[string]*storetypes.KVStoreKey + + // pending tx listeners, used for websockets + pendingTxListeners []baseevmante.PendingTxListener + + // Cosmos SDK keepers + AccountKeeper authkeeper.AccountKeeper + BankKeeper bankkeeper.Keeper + StakingKeeper *stakingkeeper.Keeper + SlashingKeeper slashingkeeper.Keeper + DistributionKeeper distrkeeper.Keeper + GovKeeper govkeeper.Keeper + IBCKeeper *ibckeeper.Keeper + UpgradeKeeper *upgradekeeper.Keeper + ConsensusParamsKeeper consensuskeeper.Keeper + + // EVM keepers + EVMKeeper *evmkeeper.Keeper + FeeMarketKeeper feemarketkeeper.Keeper + EVMMempool *evmmempool.ExperimentalEVMMempool + + // the module manager + ModuleManager *module.Manager + BasicModuleManager module.BasicManager + + // module configurator + configurator module.Configurator +} + +// New returns a reference to an initialized App. +func New( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + appOpts servertypes.AppOptions, + baseAppOptions ...func(*baseapp.BaseApp), +) *App { + evmChainID := cast.ToUint64(appOpts.Get(srvflags.EVMChainID)) + + encodingConfig := evmosencoding.MakeConfig(evmChainID) + + appCodec := encodingConfig.Codec + legacyAmino := encodingConfig.Amino + interfaceRegistry := encodingConfig.InterfaceRegistry + txConfig := encodingConfig.TxConfig + + // Enable Optimistic Execution + baseAppOptions = append(baseAppOptions, baseapp.SetOptimisticExecution()) + + bApp := baseapp.NewBaseApp(AppName, logger, db, txConfig.TxDecoder(), baseAppOptions...) + bApp.SetCommitMultiStoreTracer(traceStore) + bApp.SetVersion(version.Version) + bApp.SetInterfaceRegistry(interfaceRegistry) + bApp.SetTxEncoder(txConfig.TxEncoder()) + + keys := storetypes.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, + distrtypes.StoreKey, slashingtypes.StoreKey, govtypes.StoreKey, + upgradetypes.StoreKey, consensustypes.StoreKey, ibcexported.StoreKey, + // Cosmos EVM store keys + evmtypes.StoreKey, feemarkettypes.StoreKey, + ) + oKeys := storetypes.NewObjectStoreKeys(banktypes.ObjectStoreKey, evmtypes.ObjectKey) + nonTransientKeys := make([]storetypes.StoreKey, len(keys)+len(oKeys)) + i := 0 + for _, k := range keys { + nonTransientKeys[i] = k + i++ + } + for _, k := range oKeys { + nonTransientKeys[i] = k + i++ + } + // Setup Parallel Execution via blockstm txn runner + bApp.SetBlockSTMTxRunner(blockstm.NewSTMRunner( + encodingConfig.TxConfig.TxDecoder(), + nonTransientKeys, + min(goruntime.GOMAXPROCS(0), goruntime.NumCPU()), + true, + "astake", + )) + + // register streaming services + if err := bApp.RegisterStreamingServices(appOpts, keys); err != nil { + panic(err) + } + + app := &App{ + BaseApp: bApp, + legacyAmino: legacyAmino, + appCodec: appCodec, + txConfig: txConfig, + interfaceRegistry: interfaceRegistry, + keys: keys, + } + + // Disable block gas meter--block gas is tracked elsewhere + app.SetDisableBlockGasMeter(true) + + // set the BaseApp's parameter store + app.ConsensusParamsKeeper = consensuskeeper.NewKeeper(appCodec, runtime.NewKVStoreService(keys[consensustypes.StoreKey]), authtypes.NewModuleAddress(govtypes.ModuleName).String(), runtime.EventService{}) + bApp.SetParamStore(app.ConsensusParamsKeeper.ParamsStore) + + authAddr := authtypes.NewModuleAddress(govtypes.ModuleName).String() + + // add keepers + app.AccountKeeper = authkeeper.NewAccountKeeper( + appCodec, + runtime.NewKVStoreService(keys[authtypes.StoreKey]), + authtypes.ProtoBaseAccount, + maccPerms, + address.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()), + sdk.GetConfig().GetBech32AccountAddrPrefix(), + authAddr, + ) + + app.BankKeeper = bankkeeper.NewBaseKeeper( + appCodec, + runtime.NewKVStoreService(keys[banktypes.StoreKey]), + app.AccountKeeper, + evmconfig.BlockedAddresses(), + authAddr, + logger, + ) + app.BankKeeper = app.BankKeeper.WithObjStoreKey(oKeys[banktypes.ObjectStoreKey]) + app.StakingKeeper = stakingkeeper.NewKeeper( + appCodec, runtime.NewKVStoreService(keys[stakingtypes.StoreKey]), app.AccountKeeper, app.BankKeeper, authAddr, address.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), address.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), + ) + + app.DistributionKeeper = distrkeeper.NewKeeper(appCodec, runtime.NewKVStoreService(keys[distrtypes.StoreKey]), app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName, authAddr) + + app.SlashingKeeper = slashingkeeper.NewKeeper(appCodec, app.LegacyAmino(), runtime.NewKVStoreService(keys[slashingtypes.StoreKey]), app.StakingKeeper, authAddr) + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.StakingKeeper.SetHooks( + stakingtypes.NewMultiStakingHooks(app.DistributionKeeper.Hooks(), app.SlashingKeeper.Hooks()), + ) + + // get skipUpgradeHeights from the app options + skipUpgradeHeights := map[int64]bool{} + for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { + skipUpgradeHeights[int64(h)] = true + } + homePath := cast.ToString(appOpts.Get(flags.FlagHome)) + // set the governance module account as the authority for conducting upgrades + app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, runtime.NewKVStoreService(keys[upgradetypes.StoreKey]), appCodec, homePath, app.BaseApp, authAddr) + + app.IBCKeeper = ibckeeper.NewKeeper( + appCodec, runtime.NewKVStoreService(keys[ibcexported.StoreKey]), app.UpgradeKeeper, authAddr, + ) + + govConfig := govtypes.DefaultConfig() + /* + Example of setting gov params: + govConfig.MaxMetadataLen = 10000 + */ + govKeeper := govkeeper.NewKeeper( + appCodec, runtime.NewKVStoreService(keys[govtypes.StoreKey]), app.AccountKeeper, app.BankKeeper, + app.StakingKeeper, app.DistributionKeeper, app.MsgServiceRouter(), govConfig, authAddr, + ) + + app.GovKeeper = *govKeeper.SetHooks( + govtypes.NewMultiGovHooks( + // register the governance hooks + ), + ) + + // EVM Keepers: + app.FeeMarketKeeper = feemarketkeeper.NewKeeper( + appCodec, authtypes.NewModuleAddress(govtypes.ModuleName), + keys[feemarkettypes.StoreKey], + ) + + // Using standard bank keeper with 18 decimals, so no need for the precision bank keeper + tracer := cast.ToString(appOpts.Get(srvflags.EVMTracer)) + app.EVMKeeper = evmkeeper.NewKeeper( + appCodec, + keys[evmtypes.StoreKey], + oKeys[evmtypes.ObjectKey], + nonTransientKeys, + authtypes.NewModuleAddress(govtypes.ModuleName), + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + app.FeeMarketKeeper, + &app.ConsensusParamsKeeper, + nil, + evmChainID, + tracer, + ).WithStaticPrecompiles( + StaticPrecompiles( + *app.StakingKeeper, + app.DistributionKeeper, + app.BankKeeper, + app.GovKeeper, + app.IBCKeeper.ClientKeeper, + app.AppCodec(), + ), + ) + // Enable Virtual Fee Collection for endblocker accumulation to fee collector module account + app.EVMKeeper.EnableVirtualFeeCollection() + + // ***** IBC Configuration ***** + + clientKeeper := app.IBCKeeper.ClientKeeper + storeProvider := clientKeeper.GetStoreProvider() + + tmLightClientModule := ibctm.NewLightClientModule(appCodec, storeProvider) + clientKeeper.AddRoute(ibctm.ModuleName, &tmLightClientModule) + + // **** Module Options **** + + // NOTE: Any module instantiated in the module manager that is later modified + // must be passed by reference here. + app.ModuleManager = module.NewManager( + genutil.NewAppModule( + app.AccountKeeper, app.StakingKeeper, app, + txConfig, + ), + auth.NewAppModule(appCodec, app.AccountKeeper, nil, nil), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, nil), + gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, nil), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, nil, app.interfaceRegistry), + distribution.NewAppModule(appCodec, app.DistributionKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, nil), + staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, nil), + upgrade.NewAppModule(app.UpgradeKeeper, app.AccountKeeper.AddressCodec()), + consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper), + ibc.NewAppModule(app.IBCKeeper), + ibctm.NewAppModule(tmLightClientModule), + + // Cosmos EVM modules + vm.NewAppModule(app.EVMKeeper, app.AccountKeeper, app.BankKeeper, app.AccountKeeper.AddressCodec()), + feemarket.NewAppModule(app.FeeMarketKeeper), + ) + + // BasicModuleManager defines the module BasicManager is in charge of setting up basic, + // non-dependant module elements, such as codec registration and genesis verification. + // By default it is composed of all the module from the module manager. + // Additionally, app module basics can be overwritten by passing them as argument. + app.BasicModuleManager = module.NewBasicManagerFromManager( + app.ModuleManager, + map[string]module.AppModuleBasic{ + genutiltypes.ModuleName: genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), + govtypes.ModuleName: gov.NewAppModuleBasic([]govclient.ProposalHandler{}), + }) + app.BasicModuleManager.RegisterLegacyAminoCodec(legacyAmino) + app.BasicModuleManager.RegisterInterfaces(interfaceRegistry) + + // NOTE: upgrade module is required to be prioritized + app.ModuleManager.SetOrderPreBlockers( + upgradetypes.ModuleName, + evmtypes.ModuleName, + authtypes.ModuleName, + ) + + // During begin block slashing happens after distr.BeginBlocker so that + // there is nothing left over in the validator fee pool, so as to keep the + // CanWithdrawInvariant invariant. + // NOTE: staking module is required if HistoricalEntries param > 0 + app.ModuleManager.SetOrderBeginBlockers( + // Cosmos EVM BeginBlockers + distrtypes.ModuleName, + slashingtypes.ModuleName, + stakingtypes.ModuleName, + genutiltypes.ModuleName, + ibcexported.ModuleName, + + // Cosmos EVM BeginBlockers (feemarket -> evm) + feemarkettypes.ModuleName, + evmtypes.ModuleName, // NOTE: EVM BeginBlocker must come after FeeMarket BeginBlocker + ) + + // NOTE: the feemarket module should go last in order of end blockers that are actually doing something, + // to get the full block gas used. + app.ModuleManager.SetOrderEndBlockers( + banktypes.ModuleName, + govtypes.ModuleName, + stakingtypes.ModuleName, + distrtypes.ModuleName, + slashingtypes.ModuleName, + authtypes.ModuleName, + ibcexported.ModuleName, + + // Cosmos EVM EndBlockers + evmtypes.ModuleName, + feemarkettypes.ModuleName, + + // no-ops + genutiltypes.ModuleName, + upgradetypes.ModuleName, + ) + + // NOTE: The genutils module must occur after staking so that pools are properly initialized with tokens from genesis accounts. + // NOTE: The genutils module must also occur after auth so that it can access the params from auth. + // NOTE: The genutils module must also also be initialized after genutil module so it can access MinGasPriceDecorator.AnteHandle + genesisModuleOrder := []string{ + authtypes.ModuleName, + banktypes.ModuleName, + distrtypes.ModuleName, + stakingtypes.ModuleName, + slashingtypes.ModuleName, + govtypes.ModuleName, + ibcexported.ModuleName, + + // Cosmos EVM modules + evmtypes.ModuleName, + feemarkettypes.ModuleName, + + genutiltypes.ModuleName, + upgradetypes.ModuleName, + consensustypes.ModuleName, + } + app.ModuleManager.SetOrderInitGenesis(genesisModuleOrder...) + app.ModuleManager.SetOrderExportGenesis(genesisModuleOrder...) + + // Uncomment if you want to set a custom migration order here. + // app.ModuleManager.SetOrderMigrations(custom order) + + app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) + if err := app.ModuleManager.RegisterServices(app.configurator); err != nil { + panic(err) + } + + // Register upgrade handlers + app.RegisterUpgradeHandlers() + + autocliv1.RegisterQueryServer(app.GRPCQueryRouter(), runtimeservices.NewAutoCLIQueryService(app.ModuleManager.Modules)) + + reflectionSvc, err := runtimeservices.NewReflectionService() + if err != nil { + panic(err) + } + reflectionv1.RegisterReflectionServiceServer(app.GRPCQueryRouter(), reflectionSvc) + + // add test gRPC service for testing gRPC queries in isolation + // testpb.RegisterQueryServer(app.GRPCQueryRouter(), testpb.QueryImpl{}) + + maxGasWanted := cast.ToUint64(appOpts.Get(srvflags.EVMMaxTxGasWanted)) + + // initialize stores + app.MountKVStores(keys) + app.MountObjectStores(oKeys) + + // initialize BaseApp + app.SetInitChainer(app.InitChainer) + app.SetPreBlocker(app.PreBlocker) + app.SetBeginBlocker(app.BeginBlocker) + app.SetEndBlocker(app.EndBlocker) + app.setAnteHandler(app.txConfig, maxGasWanted) + + app.configureEVMMempool(appOpts, logger) + + // In v0.46, the SDK introduces _postHandlers_. PostHandlers are like + // antehandlers, but are run _after_ the `runMsgs` execution. They are also + // defined as a chain, and have the same signature as antehandlers. + // + // In baseapp, postHandlers are run in the same store branch as `runMsgs`, + // meaning that both `runMsgs` and `postHandler` state will be committed if + // both are successful, and both will be reverted if any of the two fails. + // + // The SDK exposes a default postHandlers chain, which is comprised of only + // one decorator: the Transaction Tips decorator. However, some chains do + // not need it by default, so feel free to comment the next line if you do + // not need tips. + // To read more about tips: + // https://docs.cosmos.network/main/core/tips.html + // + // Please note that changing any of the anteHandler or postHandler chain is + // likely to be a state-machine breaking change, which needs a coordinated + // upgrade. + app.setPostHandler() + + // At startup, after all modules have been registered, check that all proto + // annotations are correct. + protoFiles, err := proto.MergedRegistry() + if err != nil { + panic(err) + } + err = msgservice.ValidateProtoAnnotations(protoFiles) + if err != nil { + // Once we switch to using protoreflect-based antehandlers, we might + // want to panic here instead of logging a warning. + _, err := fmt.Fprintln(os.Stderr, err.Error()) + if err != nil { + fmt.Println("could not write to stderr") + } + } + + if loadLatest { + if err := app.LoadLatestVersion(); err != nil { + panic(fmt.Errorf("error loading last version: %w", err)) + } + } + + return app +} + +func (app *App) setAnteHandler(txConfig client.TxConfig, maxGasWanted uint64) { + options := baseevmante.HandlerOptions{ + Cdc: app.appCodec, + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + ExtensionOptionChecker: antetypes.HasDynamicFeeExtensionOption, + EvmKeeper: app.EVMKeeper, + FeeMarketKeeper: app.FeeMarketKeeper, + SignModeHandler: txConfig.SignModeHandler(), + SigGasConsumer: baseevmante.SigVerificationGasConsumer, + MaxTxGasWanted: maxGasWanted, + DynamicFeeChecker: true, + PendingTxListener: app.onPendingTx, + IBCKeeper: app.IBCKeeper, + FeegrantKeeper: nil, + } + + if err := validateAnteHandlerOptions(options); err != nil { + panic(err) + } + + app.SetAnteHandler(baseevmante.NewAnteHandler(options)) +} + +func validateAnteHandlerOptions(options baseevmante.HandlerOptions) error { + if options.Cdc == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "codec is required for AnteHandler") + } + if options.AccountKeeper == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "account keeper is required for AnteHandler") + } + if options.BankKeeper == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "bank keeper is required for AnteHandler") + } + if options.IBCKeeper == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "ibc keeper is required for AnteHandler") + } + if options.FeeMarketKeeper == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "fee market keeper is required for AnteHandler") + } + if options.EvmKeeper == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "evm keeper is required for AnteHandler") + } + if options.SigGasConsumer == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "signature gas consumer is required for AnteHandler") + } + if options.SignModeHandler == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "sign mode handler is required for AnteHandler") + } + if options.PendingTxListener == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "pending tx listener is required for AnteHandler") + } + + return nil +} + +func (app *App) onPendingTx(hash common.Hash) { + for _, listener := range app.pendingTxListeners { + listener(hash) + } +} + +// RegisterPendingTxListener is used by json-rpc server to listen to pending transactions callback. +func (app *App) RegisterPendingTxListener(listener func(common.Hash)) { + app.pendingTxListeners = append(app.pendingTxListeners, listener) +} + +func (app *App) setPostHandler() { + postHandler, err := posthandler.NewPostHandler( + posthandler.HandlerOptions{}, + ) + if err != nil { + panic(err) + } + + app.SetPostHandler(postHandler) +} + +// Name returns the name of the App +func (app *App) Name() string { return app.BaseApp.Name() } + +// PreBlocker application updates every pre block +func (app *App) PreBlocker(ctx sdk.Context, _ *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) { + return app.ModuleManager.PreBlock(ctx) +} + +// BeginBlocker application updates every begin block +func (app *App) BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) { + return app.ModuleManager.BeginBlock(ctx) +} + +// EndBlocker application updates every end block +func (app *App) EndBlocker(ctx sdk.Context) (sdk.EndBlock, error) { + return app.ModuleManager.EndBlock(ctx) +} + +// Configurator returns the configurator for the app +func (app *App) Configurator() module.Configurator { + return app.configurator +} + +// InitChainer application update at chain initialization +func (app *App) InitChainer(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) { + var genesisState GenesisState + if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil { + panic(err) + } + if err := app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()); err != nil { + panic(err) + } + return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) +} + +// LoadHeight loads a particular height +func (app *App) LoadHeight(height int64) error { + return app.LoadVersion(height) +} + +// LegacyAmino returns SimApp's amino codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *App) LegacyAmino() *codec.LegacyAmino { + return app.legacyAmino +} + +// AppCodec returns SimApp's app codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *App) AppCodec() codec.Codec { + return app.appCodec +} + +// InterfaceRegistry returns SimApp's InterfaceRegistry +func (app *App) InterfaceRegistry() codectypes.InterfaceRegistry { + return app.interfaceRegistry +} + +// SetClientCtx sets the client context for the app. +// This is used for transaction handling and API routes. +func (app *App) SetClientCtx(clientCtx client.Context) { + app.clientCtx = clientCtx +} + +// AutoCliOpts returns the autocli options for the app. +func (app *App) AutoCliOpts() autocli.AppOptions { + modules := make(map[string]appmodule.AppModule, 0) + for _, m := range app.ModuleManager.Modules { + if moduleWithName, ok := m.(module.HasName); ok { + moduleName := moduleWithName.Name() + if appModule, ok := moduleWithName.(appmodule.AppModule); ok { + modules[moduleName] = appModule + } + } + } + + return autocli.AppOptions{ + Modules: modules, + ModuleOptions: runtimeservices.ExtractAutoCLIOptions(app.ModuleManager.Modules), + AddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()), + ValidatorAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), + ConsensusAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), + } +} + +// DefaultGenesis returns a default genesis from the registered AppModuleBasic's. +func (app *App) DefaultGenesis() map[string]json.RawMessage { + genesis := app.BasicModuleManager.DefaultGenesis(app.appCodec) + evmGenState := evmtypes.DefaultGenesisState() + evmGenState.Params.ActiveStaticPrecompiles = evmtypes.AvailableStaticPrecompiles + evmGenState.Preinstalls = evmtypes.DefaultPreinstalls + genesis[evmtypes.ModuleName] = app.appCodec.MustMarshalJSON(evmGenState) + return genesis +} + +// GetKey returns the KVStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *App) GetKey(storeKey string) *storetypes.KVStoreKey { + return app.keys[storeKey] +} + +// GetStoreKeys returns all the stored store keys. +func (app *App) GetStoreKeys() []storetypes.StoreKey { + keys := make([]storetypes.StoreKey, 0, len(app.keys)) + for _, key := range app.keys { + keys = append(keys, key) + } + + return keys +} + +// SimulationManager implements the SimulationApp interface +func (*App) SimulationManager() *module.SimulationManager { + return nil +} + +// API server. +func (app *App) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { + clientCtx := apiSvr.ClientCtx + // Register new tx routes from grpc-gateway. + authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register new CometBFT queries routes from grpc-gateway. + cmtservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register node gRPC service for grpc-gateway. + nodeservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register grpc-gateway routes for all modules. + app.BasicModuleManager.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // register swagger API from root so that other applications can override easily + if err := server.RegisterSwaggerAPI(apiSvr.ClientCtx, apiSvr.Router, apiConfig.Swagger); err != nil { + panic(err) + } +} + +// RegisterTxService implements the Application.RegisterTxService method. +func (app *App) RegisterTxService(clientCtx client.Context) { + authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) +} + +// RegisterTendermintService implements the Application.RegisterTendermintService method. +func (app *App) RegisterTendermintService(clientCtx client.Context) { + cmtApp := server.NewCometABCIWrapper(app) + cmtservice.RegisterTendermintService( + clientCtx, + app.GRPCQueryRouter(), + app.interfaceRegistry, + cmtApp.Query, + ) +} + +func (app *App) RegisterNodeService(clientCtx client.Context, cfg config.Config) { + nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter(), cfg) +} + +type dummyDistrKeeper struct{} + +func (dummyDistrKeeper) FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error { + return nil +} diff --git a/evmd/app/export.go b/evmd/app/export.go new file mode 100644 index 000000000..3360ec422 --- /dev/null +++ b/evmd/app/export.go @@ -0,0 +1,125 @@ +package app + +import ( + "encoding/json" + "errors" + "log" + + storetypes "cosmossdk.io/store/types" + + servertypes "github.com/cosmos/cosmos-sdk/server/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// ExportAppStateAndValidators exports the state of the application for a genesis +// file. +func (app *App) ExportAppStateAndValidators( + forZeroHeight bool, jailAllowedAddrs []string, modulesToExport []string, +) (servertypes.ExportedApp, error) { + // as if they could withdraw from the start of the next block + ctx := app.NewContext(true) + + // We export at last height + 1, because that's the height at which + // Tendermint will start InitChain. + height := app.LastBlockHeight() + 1 + if forZeroHeight { + height = 0 + app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) + } + + genState, err := app.ModuleManager.ExportGenesis(ctx, app.appCodec) + if err != nil { + return servertypes.ExportedApp{}, err + } + appState, err := json.MarshalIndent(genState, "", " ") + if err != nil { + return servertypes.ExportedApp{}, err + } + + validators, err := staking.WriteValidators(ctx, app.StakingKeeper) + return servertypes.ExportedApp{ + AppState: appState, + Validators: validators, + Height: height, + ConsensusParams: app.GetConsensusParams(ctx), + }, err +} + +// prepare for fresh start at zero height +// NOTE zero height genesis is a temporary feature which will be deprecated +// in favour of export at a block height +func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { + applyAllowedAddrs := len(jailAllowedAddrs) > 0 + + // check if there is an allowed address list + + allowedAddrsMap := make(map[string]bool) + + for _, addr := range jailAllowedAddrs { + _, err := sdk.ValAddressFromBech32(addr) + if err != nil { + log.Fatal(err) + } + allowedAddrsMap[addr] = true + } + + // set context height to zero + height := ctx.BlockHeight() + ctx = ctx.WithBlockHeight(0) + + // reset context height + ctx = ctx.WithBlockHeight(height) + + /* Handle staking state. */ + + // iterate through unbonding delegations, reset creation height + err := app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) bool { + for i := range ubd.Entries { + ubd.Entries[i].CreationHeight = 0 + } + err := app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + if err != nil { + panic(err) + } + return false + }) + if err != nil { + panic(err) + } + + // Iterate through validators by power descending, reset bond heights, and + // update bond intra-tx counters. + store := ctx.KVStore(app.keys[stakingtypes.StoreKey]) + iter := storetypes.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) + counter := int16(0) + + for ; iter.Valid(); iter.Next() { + addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) + validator, err := app.StakingKeeper.GetValidator(ctx, addr) + if err != nil { + panic(errors.New("expected validator, not found")) + } + + validator.UnbondingHeight = 0 + if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { + validator.Jailed = true + } + + err = app.StakingKeeper.SetValidator(ctx, validator) + if err != nil { + panic(errors.New("couldn't set validator")) + } + counter++ + } + + if err := iter.Close(); err != nil { + panic(err) + } + + _, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + if err != nil { + log.Fatal(err) + } +} diff --git a/evmd/app/genesis.go b/evmd/app/genesis.go new file mode 100644 index 000000000..e4e849fc2 --- /dev/null +++ b/evmd/app/genesis.go @@ -0,0 +1,14 @@ +package app + +import ( + "encoding/json" +) + +// GenesisState 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/evmd/app/interfaces.go b/evmd/app/interfaces.go new file mode 100644 index 000000000..975a8be0c --- /dev/null +++ b/evmd/app/interfaces.go @@ -0,0 +1,56 @@ +package app + +import ( + feemarketkeeper "github.com/cosmos/evm/x/feemarket/keeper" + evmkeeper "github.com/cosmos/evm/x/vm/keeper" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/mempool" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/ibc-go/v10/modules/core/keeper" +) + +func (app App) GetIBCKeeper() *keeper.Keeper { + return app.IBCKeeper +} + +func (app App) GetTxConfig() client.TxConfig { + return app.txConfig +} + +func (app *App) GetMempool() mempool.ExtMempool { + return app.EVMMempool +} + +func (app *App) GetAnteHandler() types.AnteHandler { + return app.AnteHandler() +} + +// Keeper getters required by evm.EvmApp interface +func (app *App) GetEVMKeeper() *evmkeeper.Keeper { + return app.EVMKeeper +} + +func (app *App) GetFeeMarketKeeper() *feemarketkeeper.Keeper { + return &app.FeeMarketKeeper +} + +func (app *App) GetGovKeeper() govkeeper.Keeper { + return app.GovKeeper +} + +func (app *App) GetBankKeeper() bankkeeper.Keeper { + return app.BankKeeper +} + +func (app *App) GetAccountKeeper() authkeeper.AccountKeeper { + return app.AccountKeeper +} + +func (app *App) GetStakingKeeper() *stakingkeeper.Keeper { + return app.StakingKeeper +} diff --git a/evmd/mempool.go b/evmd/app/mempool.go similarity index 89% rename from evmd/mempool.go rename to evmd/app/mempool.go index 297bdc295..3652c7071 100644 --- a/evmd/mempool.go +++ b/evmd/app/mempool.go @@ -1,8 +1,7 @@ -package evmd +package app import ( "fmt" - "github.com/cosmos/evm/server" "cosmossdk.io/log" @@ -16,7 +15,7 @@ import ( ) // configureEVMMempool sets up the EVM mempool and related handlers using viper configuration. -func (app *EVMD) configureEVMMempool(appOpts servertypes.AppOptions, logger log.Logger) error { +func (app *App) configureEVMMempool(appOpts servertypes.AppOptions, logger log.Logger) error { if evmtypes.GetChainConfig() == nil { logger.Debug("evm chain config is not set, skipping mempool configuration") return nil @@ -61,7 +60,7 @@ func (app *EVMD) configureEVMMempool(appOpts servertypes.AppOptions, logger log. // createMempoolConfig creates a new EVMMempoolConfig with the default configuration // and overrides it with values from appOpts if they exist and are non-zero. -func (app *EVMD) createMempoolConfig(appOpts servertypes.AppOptions, logger log.Logger) (*evmmempool.EVMMempoolConfig, error) { +func (app *App) createMempoolConfig(appOpts servertypes.AppOptions, logger log.Logger) (*evmmempool.EVMMempoolConfig, error) { return &evmmempool.EVMMempoolConfig{ AnteHandler: app.GetAnteHandler(), LegacyPoolConfig: server.GetLegacyPoolConfig(appOpts, logger), diff --git a/evmd/app/params/encoding.go b/evmd/app/params/encoding.go new file mode 100644 index 000000000..8ff9ea04b --- /dev/null +++ b/evmd/app/params/encoding.go @@ -0,0 +1,16 @@ +package params + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" +) + +// EncodingConfig specifies the concrete encoding types to use for a given app. +// This is provided for compatibility between protobuf and amino implementations. +type EncodingConfig struct { + InterfaceRegistry types.InterfaceRegistry + Codec codec.Codec + TxConfig client.TxConfig + Amino *codec.LegacyAmino +} diff --git a/evmd/app/precompiles.go b/evmd/app/precompiles.go new file mode 100644 index 000000000..cbcf53d8a --- /dev/null +++ b/evmd/app/precompiles.go @@ -0,0 +1,95 @@ +package app + +import ( + "fmt" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distributionprecompile "github.com/cosmos/evm/precompiles/distribution" + "maps" + + evmibcutils "github.com/cosmos/evm/ibc" + "github.com/cosmos/evm/precompiles/bech32" + cmn "github.com/cosmos/evm/precompiles/common" + govprecompile "github.com/cosmos/evm/precompiles/gov" + ics02precompile "github.com/cosmos/evm/precompiles/ics02" + "github.com/cosmos/evm/precompiles/p256" + stakingprecompile "github.com/cosmos/evm/precompiles/staking" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/cosmos/cosmos-sdk/codec" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + sdk "github.com/cosmos/cosmos-sdk/types" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" +) + +// NOTE: This is almost a full copy of the precompiles.go file in the evm repo, but we +// needed to remove some of the precompiles... +const ( + // defaultBech32BaseGas is the default gas cost for bech32 precompile operations. + // This value provides sufficient gas for address conversion operations while + // preventing excessive gas consumption attacks. + defaultBech32BaseGas = 6_000 +) + +// StaticPrecompiles returns the list of all available static precompiled contracts from Cosmos EVM. +// +// NOTE: this should only be used during initialization of the Keeper. +func StaticPrecompiles( + stakingKeeper stakingkeeper.Keeper, + distributionKeeper distrkeeper.Keeper, + bankKeeper cmn.BankKeeper, + govKeeper govkeeper.Keeper, + clientKeeper evmibcutils.ClientKeeper, + cdc codec.Codec, +) map[common.Address]vm.PrecompiledContract { + // Clone the mapping from the latest EVM fork. + precompiles := maps.Clone(vm.PrecompiledContractsPrague) + + addrCodec := addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()) + + // Stateless precompiles + bech32Precompile, err := bech32.NewPrecompile(defaultBech32BaseGas) + if err != nil { + panic(fmt.Errorf("failed to instantiate bech32 precompile: %w", err)) + } + precompiles[bech32Precompile.Address()] = bech32Precompile + + // secp256r1 precompile as per EIP-7212 + p256Precompile := &p256.Precompile{} + precompiles[p256Precompile.Address()] = p256Precompile + + // Stateful precompiles + stakingPrecompile := stakingprecompile.NewPrecompile( + stakingKeeper, + stakingkeeper.NewMsgServerImpl(&stakingKeeper), + stakingkeeper.NewQuerier(&stakingKeeper), + bankKeeper, + addrCodec, + ) + precompiles[stakingPrecompile.Address()] = stakingPrecompile + + distributionPrecompile := distributionprecompile.NewPrecompile( + distributionKeeper, + distrkeeper.NewMsgServerImpl(distributionKeeper), + distrkeeper.NewQuerier(distributionKeeper), + stakingKeeper, + bankKeeper, + addrCodec, + ) + precompiles[distributionPrecompile.Address()] = distributionPrecompile + + govPrecompile := govprecompile.NewPrecompile( + govkeeper.NewMsgServerImpl(&govKeeper), + govkeeper.NewQueryServer(&govKeeper), + bankKeeper, + cdc, + addrCodec, + ) + precompiles[govPrecompile.Address()] = govPrecompile + + ics02Precompile := ics02precompile.NewPrecompile(cdc, clientKeeper) + precompiles[ics02Precompile.Address()] = ics02Precompile + + return precompiles +} diff --git a/evmd/app/temp_interfaces.go b/evmd/app/temp_interfaces.go new file mode 100644 index 000000000..6704514be --- /dev/null +++ b/evmd/app/temp_interfaces.go @@ -0,0 +1,80 @@ +package app + +import ( + evidencekeeper "cosmossdk.io/x/evidence/keeper" + feegrantkeeper "cosmossdk.io/x/feegrant/keeper" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + consensuskeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + erc20keeper "github.com/cosmos/evm/x/erc20/keeper" + "github.com/cosmos/evm/x/ibc/callbacks/keeper" + transferkeeper "github.com/cosmos/evm/x/ibc/transfer/keeper" + precisebankkeeper "github.com/cosmos/evm/x/precisebank/keeper" +) + +func (app App) GetErc20Keeper() *erc20keeper.Keeper { + //TODO implement me + panic("implement me") +} + +func (app App) SetErc20Keeper(keeper erc20keeper.Keeper) { + //TODO implement me + panic("implement me") +} + +func (app App) GetSlashingKeeper() slashingkeeper.Keeper { + //TODO implement me + panic("implement me") +} + +func (app App) GetEvidenceKeeper() *evidencekeeper.Keeper { + //TODO implement me + panic("implement me") +} + +func (app App) GetAuthzKeeper() authzkeeper.Keeper { + //TODO implement me + panic("implement me") +} + +func (app App) GetDistrKeeper() distrkeeper.Keeper { + //TODO implement me + panic("implement me") +} + +func (app App) GetMintKeeper() mintkeeper.Keeper { + //TODO implement me + panic("implement me") +} + +func (app App) GetPreciseBankKeeper() *precisebankkeeper.Keeper { + //TODO implement me + panic("implement me") +} + +func (app App) GetFeeGrantKeeper() feegrantkeeper.Keeper { + //TODO implement me + panic("implement me") +} + +func (app App) GetConsensusParamsKeeper() consensuskeeper.Keeper { + //TODO implement me + panic("implement me") +} + +func (app App) GetCallbackKeeper() keeper.ContractKeeper { + //TODO implement me + panic("implement me") +} + +func (app App) GetTransferKeeper() transferkeeper.Keeper { + //TODO implement me + panic("implement me") +} + +func (app App) SetTransferKeeper(transferKeeper transferkeeper.Keeper) { + //TODO implement me + panic("implement me") +} diff --git a/evmd/app/upgrades.go b/evmd/app/upgrades.go new file mode 100644 index 000000000..4a6944612 --- /dev/null +++ b/evmd/app/upgrades.go @@ -0,0 +1,38 @@ +package app + +import ( + "context" + storetypes "cosmossdk.io/store/types" + upgradetypes "cosmossdk.io/x/upgrade/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" +) + +// UpgradeName defines the on-chain upgrade name for the sample EVMD upgrade +// from v0.5.0 to v0.6.0. +const UpgradeName = "v0.5.0-to-v0.6.0" + +func (app App) RegisterUpgradeHandlers() { + app.UpgradeKeeper.SetUpgradeHandler( + UpgradeName, + func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + sdkCtx.Logger().Debug("this is a debug level message to test that verbose logging mode has properly been enabled during a chain upgrade") + return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM) + }, + ) + + upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() + if err != nil { + panic(err) + } + + if upgradeInfo.Name == UpgradeName && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { + storeUpgrades := storetypes.StoreUpgrades{ + Added: []string{}, + } + // configure store loader that checks if version == upgradeHeight and applies store upgrades + app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) + } +} diff --git a/evmd/cmd/evmd/cmd/root.go b/evmd/cmd/evmd/cmd/root.go index b3dd89438..9d64a2b75 100644 --- a/evmd/cmd/evmd/cmd/root.go +++ b/evmd/cmd/evmd/cmd/root.go @@ -2,28 +2,23 @@ package cmd import ( "errors" + types2 "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/evm/evmd/app/params" + "github.com/cosmos/evm/evmd/cmd/evmd/cmd/testnet" + "github.com/cosmos/evm/evmd/cmd/evmd/config" + cosmosevmserverconfig "github.com/cosmos/evm/server/config" "io" "os" - "github.com/cosmos/evm/utils" - "github.com/cosmos/evm/x/vm/types" - - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/spf13/cast" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - cmtcfg "github.com/cometbft/cometbft/config" - cmtcli "github.com/cometbft/cometbft/libs/cli" - dbm "github.com/cosmos/cosmos-db" cosmosevmcmd "github.com/cosmos/evm/client" - evmdebug "github.com/cosmos/evm/client/debug" - "github.com/cosmos/evm/crypto/hd" - "github.com/cosmos/evm/evmd" - "github.com/cosmos/evm/evmd/config" + cosmosevmkeyring "github.com/cosmos/evm/crypto/keyring" + eapp "github.com/cosmos/evm/evmd/app" cosmosevmserver "github.com/cosmos/evm/server" srvflags "github.com/cosmos/evm/server/flags" + "github.com/cosmos/evm/x/vm/types" + "github.com/spf13/cast" + "github.com/spf13/cobra" "cosmossdk.io/log" "cosmossdk.io/store" @@ -33,44 +28,45 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" - clientcfg "github.com/cosmos/cosmos-sdk/client/config" + clientconfig "github.com/cosmos/cosmos-sdk/client/config" + "github.com/cosmos/cosmos-sdk/client/debug" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/pruning" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/snapshot" + "github.com/cosmos/cosmos-sdk/codec" sdkserver "github.com/cosmos/cosmos-sdk/server" servertypes "github.com/cosmos/cosmos-sdk/server/types" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - sdk "github.com/cosmos/cosmos-sdk/types" - sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/types/tx/signing" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + + cmtcfg "github.com/cometbft/cometbft/config" + cmtcli "github.com/cometbft/cometbft/libs/cli" ) -// NewRootCmd creates a new root command for evmd. It is called once in the -// main function. +// NewRootCmd creates a new root command. func NewRootCmd() *cobra.Command { // we "pre"-instantiate the application for getting the injected/configured encoding configuration - // and the CLI options for the modules - // add keyring to autocli opts - tempApp := evmd.NewExampleApp( + tempApp := eapp.New( log.NewNopLogger(), dbm.NewMemDB(), nil, true, simtestutil.EmptyAppOptions{}, ) - - encodingConfig := sdktestutil.TestEncodingConfig{ + encodingConfig := params.EncodingConfig{ InterfaceRegistry: tempApp.InterfaceRegistry(), Codec: tempApp.AppCodec(), TxConfig: tempApp.GetTxConfig(), Amino: tempApp.LegacyAmino(), } + initClientCtx := client.Context{}. WithCodec(encodingConfig.Codec). WithInterfaceRegistry(encodingConfig.InterfaceRegistry). @@ -80,14 +76,17 @@ func NewRootCmd() *cobra.Command { WithAccountRetriever(authtypes.AccountRetriever{}). WithBroadcastMode(flags.FlagBroadcastMode). WithHomeDir(config.MustGetDefaultNodeHome()). - WithViper(""). // In simapp, we don't use any prefix for env variables. + WithViper(""). + WithLedgerHasProtobuf(true). // Cosmos EVM specific setup - WithKeyringOptions(hd.EthSecp256k1Option()). - WithLedgerHasProtobuf(true) + WithKeyringOptions(cosmosevmkeyring.Option()) + + customAppTemplate, customAppConfig := config.InitAppConfig(types.DefaultEVMExtendedDenom, types.DefaultEVMChainID) rootCmd := &cobra.Command{ - Use: "evmd", - Short: "exemplary Cosmos EVM app", + Use: eapp.AppName + "d", + Short: "EVMD cli", + SilenceErrors: true, PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { // set the default command outputs cmd.SetOut(cmd.OutOrStdout()) @@ -99,7 +98,7 @@ func NewRootCmd() *cobra.Command { return err } - initClientCtx, err = clientcfg.ReadFromClientConfig(initClientCtx) + initClientCtx, err = clientconfig.ReadFromClientConfig(initClientCtx) if err != nil { return err } @@ -108,37 +107,34 @@ func NewRootCmd() *cobra.Command { // sets the RPC client needed for SIGN_MODE_TEXTUAL. This sign mode // is only available if the client is online. if !initClientCtx.Offline { - enabledSignModes := append(tx.DefaultSignModes, signing.SignMode_SIGN_MODE_TEXTUAL) //nolint:gocritic - txConfigOpts := tx.ConfigOptions{ - EnabledSignModes: enabledSignModes, + txConfigOpts := authtx.ConfigOptions{ + EnabledSignModes: append(authtx.DefaultSignModes, signing.SignMode_SIGN_MODE_TEXTUAL), TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx), } - txConfig, err := tx.NewTxConfigWithOptions( - initClientCtx.Codec, + txConfigWithTextual, err := authtx.NewTxConfigWithOptions( + codec.NewProtoCodec(encodingConfig.InterfaceRegistry), txConfigOpts, ) if err != nil { return err } - - initClientCtx = initClientCtx.WithTxConfig(txConfig) + initClientCtx = initClientCtx.WithTxConfig(txConfigWithTextual) } if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil { return err } - customAppTemplate, customAppConfig := config.InitAppConfig(types.DefaultEVMExtendedDenom, types.DefaultEVMChainID) // TODO:VLAD - Remove this customTMConfig := initCometConfig() return sdkserver.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customTMConfig) }, } - initRootCmd(rootCmd, tempApp) + initRootCmd(rootCmd, tempApp, customAppTemplate, customAppConfig.(cosmosevmserverconfig.Config)) autoCliOpts := tempApp.AutoCliOpts() - initClientCtx, _ = clientcfg.ReadFromClientConfig(initClientCtx) + initClientCtx, _ = clientconfig.ReadFromClientConfig(initClientCtx) autoCliOpts.ClientCtx = initClientCtx if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil { @@ -160,23 +156,21 @@ func initCometConfig() *cmtcfg.Config { return cfg } -func initRootCmd(rootCmd *cobra.Command, evmApp *evmd.EVMD) { - cfg := sdk.GetConfig() - cfg.Seal() - - defaultNodeHome := config.MustGetDefaultNodeHome() +func initRootCmd(rootCmd *cobra.Command, app *eapp.App, appTemplate string, appConfig cosmosevmserverconfig.Config) { sdkAppCreator := func(l log.Logger, d dbm.DB, w io.Writer, ao servertypes.AppOptions) servertypes.Application { return newApp(l, d, w, ao) } + defaultNodeHome := config.MustGetDefaultNodeHome() + rootCmd.AddCommand( - genutilcli.InitCmd(evmApp.BasicModuleManager, defaultNodeHome), - genutilcli.Commands(evmApp.TxConfig(), evmApp.BasicModuleManager, defaultNodeHome), + initInitCmd(app.BasicModuleManager, defaultNodeHome), + genutilcli.Commands(app.GetTxConfig(), app.BasicModuleManager, defaultNodeHome), cmtcli.NewCompletionCmd(rootCmd, true), - evmdebug.Cmd(), + debug.Cmd(), confixcmd.ConfigCommand(), pruning.Cmd(sdkAppCreator, defaultNodeHome), snapshot.Cmd(sdkAppCreator), - NewTestnetCmd(evmApp.BasicModuleManager, banktypes.GenesisBalancesIterator{}, appCreator{}), + testnet.NewTestnetCmd(app.BasicModuleManager, types2.GenesisBalancesIterator{}, testnet.AppCreator{}), ) // add Cosmos EVM' flavored TM commands to start server, etc. @@ -184,7 +178,13 @@ func initRootCmd(rootCmd *cobra.Command, evmApp *evmd.EVMD) { rootCmd, cosmosevmserver.NewDefaultStartOptions(newApp, defaultNodeHome), appExport, - addModuleInitFlags, + func(c *cobra.Command) { + c.Flags().String(flags.FlagChainID, "", "The network chain ID") + c.Flag(srvflags.EVMChainID).DefValue = "0" + if err := c.MarkFlagRequired(srvflags.EVMChainID); err != nil { + panic(err) // should never happen + } + }, ) // add Cosmos EVM key commands @@ -195,20 +195,19 @@ func initRootCmd(rootCmd *cobra.Command, evmApp *evmd.EVMD) { // add keybase, auxiliary RPC, query, genesis, and tx child commands rootCmd.AddCommand( sdkserver.StatusCommand(), - queryCommand(), txCommand(), + queryCommand(), ) +} - // add general tx flags to the root command - var err error - _, err = srvflags.AddTxFlags(rootCmd) - if err != nil { - panic(err) +func initInitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command { + c := genutilcli.InitCmd(mbm, defaultNodeHome) + if err := c.MarkFlagRequired(flags.FlagChainID); err != nil { + panic(err) // should never happen } + return c } -func addModuleInitFlags(_ *cobra.Command) {} - func queryCommand() *cobra.Command { cmd := &cobra.Command{ Use: "query", @@ -220,15 +219,20 @@ func queryCommand() *cobra.Command { } cmd.AddCommand( - rpc.QueryEventForTxCmd(), rpc.ValidatorCommand(), - authcmd.QueryTxsByEventsCmd(), - authcmd.QueryTxCmd(), sdkserver.QueryBlockCmd(), + authcmd.QueryTxsByEventsCmd(), + sdkserver.QueryBlocksCmd(), sdkserver.QueryBlockResultsCmd(), + authcmd.QueryTxCmd(), + authcmd.GetSimulateCmd(), ) - cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") + var err error + _, err = srvflags.AddTxFlags(cmd) + if err != nil { + panic(err) + } return cmd } @@ -254,8 +258,6 @@ func txCommand() *cobra.Command { authcmd.GetSimulateCmd(), ) - cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") - return cmd } @@ -277,8 +279,8 @@ func newApp( panic(err) } - // get the chain id - chainID, err := getChainIDFromOpts(appOpts) + // get the cosmos chain id + cosmosChainID, err := getCosmosChainIDFromOpts(appOpts) if err != nil { panic(err) } @@ -296,7 +298,6 @@ func newApp( baseappOptions := []func(*baseapp.BaseApp){ baseapp.SetPruning(pruningOpts), baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(sdkserver.FlagMinGasPrices))), - baseapp.SetQueryGasLimit(cast.ToUint64(appOpts.Get(sdkserver.FlagQueryGasLimit))), baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(sdkserver.FlagHaltHeight))), baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(sdkserver.FlagHaltTime))), baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(sdkserver.FlagMinRetainBlocks))), @@ -306,17 +307,17 @@ func newApp( baseapp.SetSnapshot(snapshotStore, snapshotOptions), baseapp.SetIAVLCacheSize(cast.ToInt(appOpts.Get(sdkserver.FlagIAVLCacheSize))), baseapp.SetIAVLDisableFastNode(cast.ToBool(appOpts.Get(sdkserver.FlagDisableIAVLFastNode))), - baseapp.SetChainID(chainID), + baseapp.SetChainID(cosmosChainID), } - return evmd.NewExampleApp( + return eapp.New( logger, db, traceStore, true, appOpts, baseappOptions..., ) } -// appExport creates a new application (optionally at a given height) and exports state. +// appExport creates a new simapp (optionally at a given height) and exports state. func appExport( logger log.Logger, db dbm.DB, @@ -327,7 +328,7 @@ func appExport( appOpts servertypes.AppOptions, modulesToExport []string, ) (servertypes.ExportedApp, error) { - var exampleApp *evmd.EVMD + var app *eapp.App // this check is necessary as we use the flag in x/upgrade. // we can exit more gracefully by checking the flag here. @@ -336,48 +337,29 @@ func appExport( return servertypes.ExportedApp{}, errors.New("application home not set") } - viperAppOpts, ok := appOpts.(*viper.Viper) - if !ok { - return servertypes.ExportedApp{}, errors.New("appOpts is not viper.Viper") - } - - // overwrite the FlagInvCheckPeriod - viperAppOpts.Set(sdkserver.FlagInvCheckPeriod, 1) - appOpts = viperAppOpts - - // get the chain id - chainID, err := getChainIDFromOpts(appOpts) - if err != nil { - return servertypes.ExportedApp{}, err - } - if height != -1 { - exampleApp = evmd.NewExampleApp(logger, db, traceStore, false, appOpts, baseapp.SetChainID(chainID)) + app = eapp.New(logger, db, traceStore, false, appOpts) - if err := exampleApp.LoadHeight(height); err != nil { + if err := app.LoadHeight(height); err != nil { return servertypes.ExportedApp{}, err } } else { - exampleApp = evmd.NewExampleApp(logger, db, traceStore, true, appOpts, baseapp.SetChainID(chainID)) // TODO:VLAD - Remove // TODO:VLAD - Remove appoptions and evmchainid + app = eapp.New(logger, db, traceStore, true, appOpts) } - return exampleApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport) + return app.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport) } -// getChainIDFromOpts returns the chain Id from app Opts +// getCosmosChainIDFromOpts returns the chain Id from app Opts // It first tries to get from the chainId flag, if not available // it will load from home -func getChainIDFromOpts(appOpts servertypes.AppOptions) (chainID string, err error) { +func getCosmosChainIDFromOpts(appOpts servertypes.AppOptions) (string, error) { // Get the chain Id from appOpts - chainID = cast.ToString(appOpts.Get(flags.FlagChainID)) - if chainID == "" { - // If not available load from home - homeDir := cast.ToString(appOpts.Get(flags.FlagHome)) - chainID, err = utils.GetChainIDFromHome(homeDir) - if err != nil { - return "", err - } + chainID := cast.ToString(appOpts.Get(flags.FlagChainID)) + if chainID != "" { + return chainID, nil } - return chainID, err + homeDir := cast.ToString(appOpts.Get(flags.FlagHome)) + return config.GetChainIDFromHome(homeDir) } diff --git a/evmd/cmd/evmd/cmd/creator.go b/evmd/cmd/evmd/cmd/testnet/creator.go similarity index 94% rename from evmd/cmd/evmd/cmd/creator.go rename to evmd/cmd/evmd/cmd/testnet/creator.go index 8ba418dcd..3ed0acbd8 100644 --- a/evmd/cmd/evmd/cmd/creator.go +++ b/evmd/cmd/evmd/cmd/testnet/creator.go @@ -1,7 +1,8 @@ -package cmd +package testnet import ( "errors" + "github.com/cosmos/evm/evmd/app" "io" "path/filepath" @@ -12,7 +13,6 @@ import ( servertypes "github.com/cosmos/cosmos-sdk/server/types" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - "github.com/cosmos/evm/evmd" "github.com/spf13/cast" "github.com/spf13/viper" @@ -23,9 +23,9 @@ import ( storetypes "cosmossdk.io/store/types" ) -type appCreator struct{} +type AppCreator struct{} -func (a appCreator) newApp( +func (a AppCreator) newApp( logger log.Logger, db dbm.DB, traceStore io.Writer, @@ -87,7 +87,7 @@ func (a appCreator) newApp( baseapp.SetIAVLCacheSize(cast.ToInt(appOpts.Get(server.FlagIAVLCacheSize))), } - return evmd.NewExampleApp( + return app.New( logger, db, traceStore, @@ -97,7 +97,7 @@ func (a appCreator) newApp( ) } -func (a appCreator) appExport( +func (a AppCreator) appExport( logger log.Logger, db dbm.DB, traceStore io.Writer, @@ -107,7 +107,7 @@ func (a appCreator) appExport( appOpts servertypes.AppOptions, modulesToExport []string, ) (servertypes.ExportedApp, error) { - var evmApp *evmd.EVMD + var evmApp *app.App homePath, ok := appOpts.Get(flags.FlagHome).(string) if !ok || homePath == "" { @@ -128,7 +128,7 @@ func (a appCreator) appExport( loadLatest = true } - evmApp = evmd.NewExampleApp( + evmApp = app.New( logger, db, traceStore, diff --git a/evmd/cmd/evmd/cmd/testnet.go b/evmd/cmd/evmd/cmd/testnet/testnet.go similarity index 98% rename from evmd/cmd/evmd/cmd/testnet.go rename to evmd/cmd/evmd/cmd/testnet/testnet.go index fcec7b977..55141b366 100644 --- a/evmd/cmd/evmd/cmd/testnet.go +++ b/evmd/cmd/evmd/cmd/testnet/testnet.go @@ -1,19 +1,19 @@ -package cmd +package testnet import ( "bufio" "encoding/json" "fmt" + eapp "github.com/cosmos/evm/evmd/app" "net" "os" "path/filepath" "time" - "github.com/cosmos/evm/evmd/config" + "github.com/cosmos/evm/evmd/cmd/evmd/config" cosmosevmhd "github.com/cosmos/evm/crypto/hd" cosmosevmkeyring "github.com/cosmos/evm/crypto/keyring" - "github.com/cosmos/evm/evmd" cosmosevmserverconfig "github.com/cosmos/evm/server/config" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -81,7 +81,7 @@ var mnemonics = []string{ "doll midnight silk carpet brush boring pluck office gown inquiry duck chief aim exit gain never tennis crime fragile ship cloud surface exotic patch", } -type UnsafeStartValidatorCmdCreator func(ac appCreator) *cobra.Command +type UnsafeStartValidatorCmdCreator func(ac AppCreator) *cobra.Command type initArgs struct { algo string @@ -136,7 +136,7 @@ func addTestnetFlagsToCmd(cmd *cobra.Command) { // 1. run an in-process testnet or // 2. initialize validator configuration files for running a multi-validator testnet in a separate process or // 3. update application and consensus state with the local validator info -func NewTestnetCmd(mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, appCreator appCreator) *cobra.Command { +func NewTestnetCmd(mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, appCreator AppCreator) *cobra.Command { testnetCmd := &cobra.Command{ Use: "testnet", Short: "subcommands for starting or configuring local testnets", @@ -325,7 +325,7 @@ func initTestnetFiles( appConfig.Telemetry.GlobalLabels = [][]string{{"chain_id", args.chainID}} evm := cosmosevmserverconfig.DefaultEVMConfig() evm.EVMChainID = evmtypes.DefaultEVMChainID - evmCfg := config.EVMAppConfig{ + evmCfg := cosmosevmserverconfig.Config{ Config: *appConfig, EVM: *evm, JSONRPC: *cosmosevmserverconfig.DefaultJSONRPCConfig(), @@ -756,7 +756,7 @@ func NewTestNetworkFixture() sdknetwork.TestFixture { } defer os.RemoveAll(dir) - app := evmd.NewExampleApp( + app := eapp.New( log.NewNopLogger(), dbm.NewMemDB(), nil, @@ -765,7 +765,7 @@ func NewTestNetworkFixture() sdknetwork.TestFixture { ) appCtr := func(val sdknetwork.ValidatorI) servertypes.Application { - return evmd.NewExampleApp( + return eapp.New( log.NewNopLogger(), dbm.NewMemDB(), nil, diff --git a/evmd/cmd/evmd/cmd/testnet_test.go b/evmd/cmd/evmd/cmd/testnet/testnet_test.go similarity index 99% rename from evmd/cmd/evmd/cmd/testnet_test.go rename to evmd/cmd/evmd/cmd/testnet/testnet_test.go index 0b0abe032..34c297669 100644 --- a/evmd/cmd/evmd/cmd/testnet_test.go +++ b/evmd/cmd/evmd/cmd/testnet/testnet_test.go @@ -1,4 +1,4 @@ -package cmd +package testnet import ( "testing" diff --git a/evmd/cmd/evmd/cmd/testnet_utils.go b/evmd/cmd/evmd/cmd/testnet/testnet_utils.go similarity index 99% rename from evmd/cmd/evmd/cmd/testnet_utils.go rename to evmd/cmd/evmd/cmd/testnet/testnet_utils.go index 2ba564c58..234cce047 100644 --- a/evmd/cmd/evmd/cmd/testnet_utils.go +++ b/evmd/cmd/evmd/cmd/testnet/testnet_utils.go @@ -1,4 +1,4 @@ -package cmd +package testnet import ( "fmt" diff --git a/evmd/cmd/evmd/cmd/testnet_utils_test.go b/evmd/cmd/evmd/cmd/testnet/testnet_utils_test.go similarity index 99% rename from evmd/cmd/evmd/cmd/testnet_utils_test.go rename to evmd/cmd/evmd/cmd/testnet/testnet_utils_test.go index 51617045e..03281f173 100644 --- a/evmd/cmd/evmd/cmd/testnet_utils_test.go +++ b/evmd/cmd/evmd/cmd/testnet/testnet_utils_test.go @@ -1,4 +1,4 @@ -package cmd +package testnet import ( "testing" diff --git a/evmd/config/config.go b/evmd/cmd/evmd/config/app_config.go similarity index 77% rename from evmd/config/config.go rename to evmd/cmd/evmd/config/app_config.go index 371616122..af84fa901 100644 --- a/evmd/config/config.go +++ b/evmd/cmd/evmd/config/app_config.go @@ -1,19 +1,10 @@ package config import ( - clienthelpers "cosmossdk.io/client/v2/helpers" serverconfig "github.com/cosmos/cosmos-sdk/server/config" cosmosevmserverconfig "github.com/cosmos/evm/server/config" ) -func MustGetDefaultNodeHome() string { - defaultNodeHome, err := clienthelpers.GetNodeHomeDirectory(".evmd") - if err != nil { - panic(err) - } - return defaultNodeHome -} - // InitAppConfig helps to override default appConfig template and configs. // return "", nil if no custom configuration is required for the application. func InitAppConfig(denom string, evmChainID uint64) (string, interface{}) { @@ -37,7 +28,7 @@ func InitAppConfig(denom string, evmChainID uint64) (string, interface{}) { evmCfg := cosmosevmserverconfig.DefaultEVMConfig() evmCfg.EVMChainID = evmChainID - customAppConfig := EVMAppConfig{ + customAppConfig := cosmosevmserverconfig.Config{ Config: *srvCfg, EVM: *evmCfg, JSONRPC: *cosmosevmserverconfig.DefaultJSONRPCConfig(), @@ -47,12 +38,4 @@ func InitAppConfig(denom string, evmChainID uint64) (string, interface{}) { return EVMAppTemplate, customAppConfig } -type EVMAppConfig struct { - serverconfig.Config - - EVM cosmosevmserverconfig.EVMConfig - JSONRPC cosmosevmserverconfig.JSONRPCConfig - TLS cosmosevmserverconfig.TLSConfig -} - const EVMAppTemplate = serverconfig.DefaultConfigTemplate + cosmosevmserverconfig.DefaultEVMConfigTemplate diff --git a/evmd/cmd/evmd/config/config.go b/evmd/cmd/evmd/config/config.go new file mode 100644 index 000000000..e995bf89b --- /dev/null +++ b/evmd/cmd/evmd/config/config.go @@ -0,0 +1,72 @@ +package config + +import ( + "os" + "path/filepath" + + "github.com/cosmos/evm/crypto/hd" + "github.com/spf13/viper" + + clienthelpers "cosmossdk.io/client/v2/helpers" + + "github.com/cosmos/cosmos-sdk/client/config" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + Bech32Prefix = "cosmos" + Bech32PrefixAccAddr = Bech32Prefix + Bech32PrefixAccPub = Bech32Prefix + sdk.PrefixPublic + Bech32PrefixValAddr = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixOperator + Bech32PrefixValPub = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixOperator + sdk.PrefixPublic + Bech32PrefixConsAddr = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixConsensus + Bech32PrefixConsPub = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixConsensus + sdk.PrefixPublic + DisplayDenom = "stake" + BaseDenom = "astake" +) + +// SetBech32Prefixes sets the global prefixes to be used when serializing addresses and public keys to Bech32 strings. +func SetBech32Prefixes(cfg *sdk.Config) { + cfg.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub) + cfg.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub) + cfg.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub) +} + +// SetBip44CoinType sets the global coin type to be used in hierarchical deterministic wallets. +func SetBip44CoinType(cfg *sdk.Config) { + cfg.SetCoinType(hd.Bip44CoinType) + cfg.SetPurpose(sdk.Purpose) // Shared +} + +func MustGetDefaultNodeHome() string { + defaultNodeHome, err := clienthelpers.GetNodeHomeDirectory(".evmd") + if err != nil { + panic(err) + } + + if err := os.MkdirAll(defaultNodeHome, 0o700); err != nil { + panic(err) + } + + return defaultNodeHome +} + +// GetChainIDFromHome returns the chain ID from the client configuration +// in the given home directory. +func GetChainIDFromHome(home string) (string, error) { + v := viper.New() + v.AddConfigPath(filepath.Join(home, "config")) + v.SetConfigName("client") + v.SetConfigType("toml") + + if err := v.ReadInConfig(); err != nil { + return "", err + } + conf := new(config.ClientConfig) + + if err := v.Unmarshal(conf); err != nil { + return "", err + } + + return conf.ChainID, nil +} diff --git a/evmd/config/permissions.go b/evmd/cmd/evmd/config/permissions.go similarity index 100% rename from evmd/config/permissions.go rename to evmd/cmd/evmd/config/permissions.go diff --git a/evmd/cmd/evmd/main.go b/evmd/cmd/evmd/main.go index 2a5d62fab..53675de0a 100644 --- a/evmd/cmd/evmd/main.go +++ b/evmd/cmd/evmd/main.go @@ -4,17 +4,21 @@ import ( "fmt" "os" + "github.com/cosmos/evm/evmd/cmd/evmd/cmd" + "github.com/cosmos/evm/evmd/cmd/evmd/config" + "github.com/cosmos/evm/utils" + + clienthelpers "cosmossdk.io/client/v2/helpers" + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/evm/evmd/cmd/evmd/cmd" - "github.com/cosmos/evm/evmd/config" ) func main() { setupSDKConfig() rootCmd := cmd.NewRootCmd() - if err := svrcmd.Execute(rootCmd, "evmd", config.MustGetDefaultNodeHome()); err != nil { + if err := svrcmd.Execute(rootCmd, clienthelpers.EnvPrefix, config.MustGetDefaultNodeHome()); err != nil { fmt.Fprintln(rootCmd.OutOrStderr(), err) os.Exit(1) } @@ -22,6 +26,8 @@ func main() { func setupSDKConfig() { cfg := sdk.GetConfig() + sdk.DefaultPowerReduction = utils.AttoPowerReduction config.SetBech32Prefixes(cfg) + config.SetBip44CoinType(cfg) cfg.Seal() } diff --git a/evmd/config/bech32.go b/evmd/config/bech32.go deleted file mode 100644 index 3c2cd384e..000000000 --- a/evmd/config/bech32.go +++ /dev/null @@ -1,37 +0,0 @@ -package config - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/evm/crypto/hd" -) - -const ( - // Bech32Prefix defines the Bech32 prefix used for accounts on the exemplary Cosmos EVM blockchain. - Bech32Prefix = "cosmos" - // Bech32PrefixAccAddr defines the Bech32 prefix of an account's address. - Bech32PrefixAccAddr = Bech32Prefix - // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key. - Bech32PrefixAccPub = Bech32Prefix + sdk.PrefixPublic - // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address. - Bech32PrefixValAddr = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixOperator - // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key. - Bech32PrefixValPub = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixOperator + sdk.PrefixPublic - // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address. - Bech32PrefixConsAddr = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixConsensus - // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key. - Bech32PrefixConsPub = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixConsensus + sdk.PrefixPublic -) - -// SetBech32Prefixes sets the global prefixes to be used when serializing addresses and public keys to Bech32 strings. -func SetBech32Prefixes(config *sdk.Config) { - config.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub) - config.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub) - config.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub) -} - -// SetBip44CoinType sets the global coin type to be used in hierarchical deterministic wallets. -func SetBip44CoinType(config *sdk.Config) { - config.SetCoinType(hd.Bip44CoinType) - config.SetPurpose(sdk.Purpose) // Shared - config.SetFullFundraiserPath(hd.BIP44HDPath) //nolint: staticcheck -} diff --git a/evmd/config/client.toml b/evmd/config/client.toml deleted file mode 100644 index 2a8569c81..000000000 --- a/evmd/config/client.toml +++ /dev/null @@ -1,17 +0,0 @@ -# This is a TOML config file. -# For more information, see https://github.com/toml-lang/toml - -############################################################################### -### Client Configuration ### -############################################################################### - -# The network chain ID -chain-id = "" -# The keyring's backend, where the keys are stored (os|file|kwallet|pass|test|memory) -keyring-backend = "os" -# CLI output format (text|json) -output = "text" -# : to CometBFT RPC interface for this chain -node = "tcp://localhost:26657" -# Transaction broadcasting mode (sync|async) -broadcast-mode = "sync" diff --git a/evmd/e2e/README.md b/evmd/e2e/README.md new file mode 100644 index 000000000..402a04413 --- /dev/null +++ b/evmd/e2e/README.md @@ -0,0 +1,11 @@ +`TODO: Add more information here` + +## Keep containers running after tests + +If you'd like to keep the docker containers running after the tests complete use the `KEEP_CONTAINERS` environment variable: + +``` +KEEP_CONTAINERS=true make test-e2e +``` + +Note that you should manually delete the created containers and networks before running the tests again. diff --git a/evmd/e2e/contracts/Counter.abi b/evmd/e2e/contracts/Counter.abi new file mode 100644 index 000000000..740dd4fe5 --- /dev/null +++ b/evmd/e2e/contracts/Counter.abi @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"ValueChanged","type":"event"},{"inputs":[{"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"value","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/evmd/e2e/contracts/Counter.bin b/evmd/e2e/contracts/Counter.bin new file mode 100644 index 000000000..817fe5257 --- /dev/null +++ b/evmd/e2e/contracts/Counter.bin @@ -0,0 +1 @@ +6080604052348015600e575f5ffd5b5061018e8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c80633fa4f2451461003857806360fe47b114610056575b5f5ffd5b610040610072565b60405161004d91906100e6565b60405180910390f35b610070600480360381019061006b919061012d565b610077565b005b5f5481565b805f819055503373ffffffffffffffffffffffffffffffffffffffff167fc53a6612a7428e1fc89cb87169d69d64eaefe94f5b45d43f80f1ad9d7065d2c8826040516100c391906100e6565b60405180910390a250565b5f819050919050565b6100e0816100ce565b82525050565b5f6020820190506100f95f8301846100d7565b92915050565b5f5ffd5b61010c816100ce565b8114610116575f5ffd5b50565b5f8135905061012781610103565b92915050565b5f60208284031215610142576101416100ff565b5b5f61014f84828501610119565b9150509291505056fea2646970667358221220ed1c27ba6ebef7288fd90bf84d73c72414f497954e2e86599ca6234076aa963664736f6c634300081c0033 \ No newline at end of file diff --git a/evmd/e2e/contracts/Counter.sol b/evmd/e2e/contracts/Counter.sol new file mode 100644 index 000000000..f4dbf4c57 --- /dev/null +++ b/evmd/e2e/contracts/Counter.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract Counter { + event ValueChanged(uint256 newValue, address indexed caller); + + uint256 public value; + + function set(uint256 newValue) external { + value = newValue; + emit ValueChanged(newValue, msg.sender); + } +} diff --git a/evmd/e2e/contracts/Reverter.abi b/evmd/e2e/contracts/Reverter.abi new file mode 100644 index 000000000..1ba0c79b2 --- /dev/null +++ b/evmd/e2e/contracts/Reverter.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"reason","type":"string"}],"name":"revertWithReason","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"v","type":"uint256"},{"internalType":"uint256","name":"iters","type":"uint256"}],"name":"setThenBurnGas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"x","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/evmd/e2e/contracts/Reverter.bin b/evmd/e2e/contracts/Reverter.bin new file mode 100644 index 000000000..69b15edaa --- /dev/null +++ b/evmd/e2e/contracts/Reverter.bin @@ -0,0 +1 @@ +6080604052348015600e575f5ffd5b506103ea8061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80630c55699c14610043578063268a556314610061578063f7a303811461007d575b5f5ffd5b61004b610099565b604051610058919061011f565b60405180910390f35b61007b60048036038101906100769190610173565b61009e565b005b610097600480360381019061009291906102ed565b6100ca565b005b5f5481565b815f819055505f5f5f90505b828110156100c457808201915080806001019150506100aa565b50505050565b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100fe9190610394565b60405180910390fd5b5f819050919050565b61011981610107565b82525050565b5f6020820190506101325f830184610110565b92915050565b5f604051905090565b5f5ffd5b5f5ffd5b61015281610107565b811461015c575f5ffd5b50565b5f8135905061016d81610149565b92915050565b5f5f6040838503121561018957610188610141565b5b5f6101968582860161015f565b92505060206101a78582860161015f565b9150509250929050565b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6101ff826101b9565b810181811067ffffffffffffffff8211171561021e5761021d6101c9565b5b80604052505050565b5f610230610138565b905061023c82826101f6565b919050565b5f67ffffffffffffffff82111561025b5761025a6101c9565b5b610264826101b9565b9050602081019050919050565b828183375f83830152505050565b5f61029161028c84610241565b610227565b9050828152602081018484840111156102ad576102ac6101b5565b5b6102b8848285610271565b509392505050565b5f82601f8301126102d4576102d36101b1565b5b81356102e484826020860161027f565b91505092915050565b5f6020828403121561030257610301610141565b5b5f82013567ffffffffffffffff81111561031f5761031e610145565b5b61032b848285016102c0565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f61036682610334565b610370818561033e565b935061038081856020860161034e565b610389816101b9565b840191505092915050565b5f6020820190508181035f8301526103ac818461035c565b90509291505056fea264697066735822122004aa5ff8a3ce15b7f2d7b90a36aca2cfb6dd5539a1b31cb2afaec09d7e9df99664736f6c634300081c0033 \ No newline at end of file diff --git a/evmd/e2e/contracts/Reverter.sol b/evmd/e2e/contracts/Reverter.sol new file mode 100644 index 000000000..ceb1481d3 --- /dev/null +++ b/evmd/e2e/contracts/Reverter.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract Reverter { + uint256 public x; + + function revertWithReason(string memory reason) external pure { + revert(reason); + } + + // Writes state then burns gas to ensure that, under insufficient gas, the + // entire transaction reverts with no state change persisted. + function setThenBurnGas(uint256 v, uint256 iters) external { + x = v; + uint256 sum; + for (uint256 i = 0; i < iters; i++) { + unchecked { + sum += i; + } + } + assembly { + pop(sum) + } + } +} diff --git a/evmd/e2e/contracts/contracts.go b/evmd/e2e/contracts/contracts.go new file mode 100644 index 000000000..21d5ea85a --- /dev/null +++ b/evmd/e2e/contracts/contracts.go @@ -0,0 +1,26 @@ +package contracts + +import ( + "strings" + + _ "embed" +) + +//go:embed Counter.abi +var counterABI []byte + +//go:embed Counter.bin +var counterBin []byte + +func CounterABIJSON() string { + return string(counterABI) +} + +func CounterBinHex() string { + // solc emits hex without 0x and usually with a trailing newline + hex := strings.TrimSpace(string(counterBin)) + if strings.HasPrefix(hex, "0x") { + return hex + } + return "0x" + hex +} diff --git a/evmd/e2e/contracts/reverter_contracts.go b/evmd/e2e/contracts/reverter_contracts.go new file mode 100644 index 000000000..d0fcfad3c --- /dev/null +++ b/evmd/e2e/contracts/reverter_contracts.go @@ -0,0 +1,25 @@ +package contracts + +import ( + "strings" + + _ "embed" +) + +//go:embed Reverter.abi +var reverterABI []byte + +//go:embed Reverter.bin +var reverterBin []byte + +func ReverterABIJSON() string { + return string(reverterABI) +} + +func ReverterBinHex() string { + hex := strings.TrimSpace(string(reverterBin)) + if strings.HasPrefix(hex, "0x") { + return hex + } + return "0x" + hex +} diff --git a/evmd/e2e/contracts_test.go b/evmd/e2e/contracts_test.go new file mode 100644 index 000000000..b26985409 --- /dev/null +++ b/evmd/e2e/contracts_test.go @@ -0,0 +1,121 @@ +package e2e + +import ( + "math/big" + "strings" + "testing" + + "github.com/cosmos/evm/evmd/e2e/contracts" + "github.com/cosmos/evm/evmd/e2e/testharness" + "github.com/cosmos/evm/evmd/e2e/utils" + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +// TestContractDeployEvents deploys a tiny contract, mutates state, and asserts event logs. +func TestContractDeployEvents(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + // Parse ABI and load creation bytecode + parsedABI, err := abi.JSON(strings.NewReader(contracts.CounterABIJSON())) + req.NoError(err) + creation := common.FromHex(contracts.CounterBinHex()) + + // Deploy contract (contract creation tx) + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, nil, big.NewInt(0), creation, 0) + req.NoError(err) + + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + contractAddr := rec.ContractAddress + req.NotEqual(common.Address{}, contractAddr) + + // Verify code exists at contract address + code, err := chain.EthClient.CodeAt(harness.Ctx, contractAddr, nil) + req.NoError(err) + req.NotEmpty(code) + + // Initial state via storage and call + slot0 := common.Hash{} // 0x00...00 + raw0, err := chain.EthClient.StorageAt(harness.Ctx, contractAddr, slot0, nil) + req.NoError(err) + req.Len(raw0, 32) + req.Zero(new(big.Int).SetBytes(raw0).Cmp(big.NewInt(0))) + + callValue, err := parsedABI.Pack("value") + req.NoError(err) + ret0, err := chain.EthClient.CallContract(harness.Ctx, ethereum.CallMsg{To: &contractAddr, Data: callValue}, nil) + req.NoError(err) + outs0, err := parsedABI.Unpack("value", ret0) + req.NoError(err) + req.Len(outs0, 1) + req.Zero(outs0[0].(*big.Int).Cmp(big.NewInt(0))) + + // Mutate state via set(42) + newVal := big.NewInt(42) + callSet, err := parsedABI.Pack("set", newVal) + req.NoError(err) + + txHash2, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &contractAddr, big.NewInt(0), callSet, 0) + req.NoError(err) + + rec2, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash2, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec2) + req.Equal(uint64(1), rec2.Status) + + // Re-verify state + raw1, err := chain.EthClient.StorageAt(harness.Ctx, contractAddr, slot0, nil) + req.NoError(err) + req.Len(raw1, 32) + req.Zero(new(big.Int).SetBytes(raw1).Cmp(newVal)) + + ret1, err := chain.EthClient.CallContract(harness.Ctx, ethereum.CallMsg{To: &contractAddr, Data: callValue}, nil) + req.NoError(err) + outs1, err := parsedABI.Unpack("value", ret1) + req.NoError(err) + req.Len(outs1, 1) + req.Zero(outs1[0].(*big.Int).Cmp(newVal)) + + // Assert emitted event via eth_getLogs + topic0 := crypto.Keccak256Hash([]byte("ValueChanged(uint256,address)")) + logs, err := chain.EthClient.FilterLogs(harness.Ctx, ethereum.FilterQuery{ + FromBlock: rec2.BlockNumber, + ToBlock: rec2.BlockNumber, + Addresses: []common.Address{contractAddr}, + Topics: [][]common.Hash{{topic0}}, + }) + req.NoError(err) + req.NotEmpty(logs) + + var matched *types.Log + for i := range logs { + if logs[i].TxHash == rec2.TxHash { + matched = &logs[i] + break + } + } + req.NotNil(matched) + req.GreaterOrEqual(len(matched.Topics), 2) + req.Equal(topic0, matched.Topics[0]) + + // topic1 is indexed caller address + expectedTopic1 := common.BytesToHash(common.LeftPadBytes(harness.SenderAddr.Bytes(), 32)) + req.Equal(expectedTopic1, matched.Topics[1]) + + ev, ok := parsedABI.Events["ValueChanged"] + req.True(ok) + vals, err := ev.Inputs.NonIndexed().Unpack(matched.Data) + req.NoError(err) + req.Len(vals, 1) + req.Zero(vals[0].(*big.Int).Cmp(newVal)) +} diff --git a/evmd/e2e/eip_conformance_test.go b/evmd/e2e/eip_conformance_test.go new file mode 100644 index 000000000..06179f9df --- /dev/null +++ b/evmd/e2e/eip_conformance_test.go @@ -0,0 +1,101 @@ +package e2e + +import ( + "context" + "math/big" + "testing" + + "github.com/cosmos/evm/evmd/e2e/testharness" + "github.com/cosmos/evm/evmd/e2e/utils" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +// TestEIP1559FeeSemantics validates EIP-1559 behavior: +// - A tx with maxFeePerGas below baseFee is rejected or not included. +// - A valid tx is mined with effectiveGasPrice >= block baseFee. +func TestEIP1559FeeSemantics(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + // Preconditions: baseFee present and > 0 + hdr, err := chain.EthClient.HeaderByNumber(harness.Ctx, nil) + req.NoError(err) + req.NotNil(hdr) + if hdr.BaseFee == nil || hdr.BaseFee.Sign() == 0 { + t.Skip("feemarket disabled or baseFee is zero") + } + + // Under-baseFee tx: reject or not included + lowKey, err := crypto.GenerateKey() + req.NoError(err) + lowAddr := crypto.PubkeyToAddress(lowKey.PublicKey) + + // Fund the ephemeral sender + fundAmt := big.NewInt(1_000_000_000_000_000) // 1e15 wei + txHashFund, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &lowAddr, fundAmt, nil, 21000) + req.NoError(err) + recFund, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHashFund, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(recFund) + req.Equal(uint64(1), recFund.Status) + + chainID, err := chain.EthClient.ChainID(harness.Ctx) + req.NoError(err) + nonce, err := chain.EthClient.PendingNonceAt(harness.Ctx, lowAddr) + req.NoError(err) + + to := harness.SenderAddr + gas := uint64(21000) + feeCap := big.NewInt(1) // deliberately far below baseFee + tipCap := big.NewInt(1) + + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + To: &to, + Value: big.NewInt(0), + Gas: gas, + GasFeeCap: feeCap, + GasTipCap: tipCap, + }) + signer := types.LatestSignerForChainID(chainID) + signed, err := types.SignTx(tx, signer, lowKey) + req.NoError(err) + + sendErr := chain.EthClient.SendTransaction(harness.Ctx, signed) + if sendErr == nil { + // Not immediately rejected: assert it is not included quickly + _, rerr := utils.WaitReceipt(harness.Ctx, chain.EthClient, signed.Hash(), utils.ShortReceiptTimeout) + req.Error(rerr, "underpriced tx unexpectedly got mined") + req.ErrorIs(rerr, context.DeadlineExceeded, "underpriced tx should not be included") + } else { + t.Logf("low-fee tx rejected by RPC as expected: %v", sendErr) + } + + // Valid tx: status=1 and effectiveGasPrice >= baseFee + recvKey, err := crypto.GenerateKey() + req.NoError(err) + recipient := crypto.PubkeyToAddress(recvKey.PublicKey) + value := big.NewInt(1) // 1 wei + + txHashGood, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &recipient, value, nil, 21000) + req.NoError(err) + + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHashGood, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + + hdr2, err := chain.EthClient.HeaderByNumber(harness.Ctx, rec.BlockNumber) + req.NoError(err) + req.NotNil(hdr2) + req.NotNil(hdr2.BaseFee) + req.GreaterOrEqual(rec.EffectiveGasPrice.Cmp(hdr2.BaseFee), 0, + "effectiveGasPrice %s < baseFee %s", rec.EffectiveGasPrice.String(), hdr2.BaseFee.String()) + + t.Logf("valid tx=%s baseFee=%s effectiveGasPrice=%s", txHashGood.Hex(), hdr2.BaseFee.String(), rec.EffectiveGasPrice.String()) +} diff --git a/evmd/e2e/evm_conformance_test.go b/evmd/e2e/evm_conformance_test.go new file mode 100644 index 000000000..9d874e623 --- /dev/null +++ b/evmd/e2e/evm_conformance_test.go @@ -0,0 +1,247 @@ +package e2e + +import ( + "math/big" + "strings" + "testing" + + "github.com/cosmos/evm/evmd/e2e/contracts" + "github.com/cosmos/evm/evmd/e2e/testharness" + "github.com/cosmos/evm/evmd/e2e/utils" + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +// TestEVMTransfer verifies EVM value transfer under EIP-1559 (type-2). +// Steps: send 1e18 wei Aโ†’B; assert receipt.status=1; balances reflect amount and gas. +func TestEVMTransfer(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + // Generate a recipient address (B) + recvKey, err := crypto.GenerateKey() + req.NoError(err) + recipient := crypto.PubkeyToAddress(recvKey.PublicKey) + recipientBech32, err := utils.AddressToBech32(recipient) + req.NoError(err) + + // Value to transfer: 1e18 wei + value := big.NewInt(1_000_000_000_000_000_000) + + // Pre-transfer balances + preSender, err := utils.GetBalance(harness.Ctx, chain.EthClient, harness.SenderAddr) + req.NoError(err) + preRecipient, err := utils.GetBalance(harness.Ctx, chain.EthClient, recipient) + req.NoError(err) + + // Send EIP-1559 dynamic fee transaction A -> B + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &recipient, value, nil, utils.BasicTransferGas) + req.NoError(err) + + // Wait for inclusion and fetch receipt + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + req.NotZero(rec.GasUsed) + + // Post-transfer balances + postSender, err := utils.GetBalance(harness.Ctx, chain.EthClient, harness.SenderAddr) + req.NoError(err) + postSenderBankBalance, err := utils.GetBankBalance(harness.Ctx, chain.GrpcClient, harness.SenderBech32, utils.TestDenom) + req.NoError(err) + postRecipient, err := utils.GetBalance(harness.Ctx, chain.EthClient, recipient) + req.NoError(err) + postRecipientBankBalance, err := utils.GetBankBalance(harness.Ctx, chain.GrpcClient, recipientBech32, utils.TestDenom) + req.NoError(err) + + // Ethereum balance should be equal to bank balance + req.Equal(postSender, postSenderBankBalance.Balance.Amount.BigInt()) + req.Equal(postRecipient, postRecipientBankBalance.Balance.Amount.BigInt()) + + // Recipient delta must equal value exactly + recipientDelta := new(big.Int).Sub(postRecipient, preRecipient) + req.Equalf(0, recipientDelta.Cmp(value), "recipient delta %s != value %s", recipientDelta.String(), value.String()) + + // Sender decrease should be >= value + gasUsed*effectiveGasPrice + gasCost := new(big.Int).Mul(new(big.Int).SetUint64(rec.GasUsed), rec.EffectiveGasPrice) + minDecrease := new(big.Int).Add(new(big.Int).Set(value), gasCost) + senderDelta := new(big.Int).Sub(preSender, postSender) + req.GreaterOrEqual(senderDelta.Cmp(minDecrease), 0, "sender decrease %s < expected minimum %s", senderDelta.String(), minDecrease.String()) + + t.Logf("tx=%s gasUsed=%d effectiveGasPrice=%s", txHash.Hex(), rec.GasUsed, rec.EffectiveGasPrice.String()) +} + +// TestEVMNonceGap verifies that out-of-order EVM transactions (with nonce gaps) +// are queued and only become includable once gaps are filled. +// Flow: +// 1. Send tx0 at nonce N and wait for success +// 2. Send tx2 at nonce N+2 and assert it is NOT included before gap is filled +// 3. Send tx1 at nonce N+1, wait for success +// 4. Verify tx2 is eventually included after the gap is filled +func TestEVMNonceGap(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + ctx := harness.Ctx + + // Recipient: ephemeral EOA; use value=0 to avoid changing balances + toKey, err := crypto.GenerateKey() + req.NoError(err) + to := crypto.PubkeyToAddress(toKey.PublicKey) + + // Determine starting nonce for the funded sender + baseNonce, err := chain.EthClient.PendingNonceAt(ctx, harness.SenderAddr) + req.NoError(err) + + // Helper to build a signed tx with explicit nonce using shared utility + buildSignedTx := func(nonce uint64) (*types.Transaction, error) { + n := nonce + return utils.BuildSignedTx(ctx, chain.EthClient, harness.SenderKey, &to, big.NewInt(0), nil, 21000, &n) + } + + // 1) Send tx0 (nonce N) and wait for inclusion + signed0, err := buildSignedTx(baseNonce) + req.NoError(err) + req.NoError(chain.EthClient.SendTransaction(ctx, signed0)) + rec0, err := utils.WaitReceipt(ctx, chain.EthClient, signed0.Hash(), utils.StakingReceiptTimeout) + req.NoError(err) + req.NotNil(rec0) + req.Equal(uint64(1), rec0.Status) + + // 2) Send tx2 (nonce N+2) โ€” should be queued due to nonce gap + signed2, err := buildSignedTx(baseNonce + 2) + req.NoError(err) + req.NoError(chain.EthClient.SendTransaction(ctx, signed2)) + // Ensure it is NOT included before we fill the gap + _, err = utils.WaitReceipt(ctx, chain.EthClient, signed2.Hash(), utils.FailureReceiptTimeout) + req.Error(err) + + // 3) Send tx1 (nonce N+1) and wait for inclusion + signed1, err := buildSignedTx(baseNonce + 1) + req.NoError(err) + req.NoError(chain.EthClient.SendTransaction(ctx, signed1)) + rec1, err := utils.WaitReceipt(ctx, chain.EthClient, signed1.Hash(), utils.StakingReceiptTimeout) + req.NoError(err) + req.NotNil(rec1) + req.Equal(uint64(1), rec1.Status) + + // 4) Now tx2 should become includable and get mined + rec2, err := utils.WaitReceipt(ctx, chain.EthClient, signed2.Hash(), utils.StakingReceiptTimeout) + req.NoError(err) + req.NotNil(rec2) + req.Equal(uint64(1), rec2.Status) + + // Sanity: verify the mined transactions carry the intended nonces + mined0, _, err := chain.EthClient.TransactionByHash(ctx, signed0.Hash()) + req.NoError(err) + mined1, _, err := chain.EthClient.TransactionByHash(ctx, signed1.Hash()) + req.NoError(err) + mined2, _, err := chain.EthClient.TransactionByHash(ctx, signed2.Hash()) + req.NoError(err) + + req.Equal(baseNonce, mined0.Nonce()) + req.Equal(baseNonce+1, mined1.Nonce()) + req.Equal(baseNonce+2, mined2.Nonce()) +} + +// TestGasRevertSemantics covers: +// - Out-of-gas on a contract call yields status=0 and no state change +// - Revert-with-reason is surfaced via eth_call and trace for a mined tx +func TestGasRevertSemantics(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + // OOG: no state change + // Deploy Counter + parsedCounter, err := abi.JSON(strings.NewReader(contracts.CounterABIJSON())) + req.NoError(err) + creation := common.FromHex(contracts.CounterBinHex()) + + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, nil, big.NewInt(0), creation, 0) + req.NoError(err) + + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + counter := rec.ContractAddress + + // Pre-state: slot0 should be zero + slot0 := common.Hash{} + preRaw, err := chain.EthClient.StorageAt(harness.Ctx, counter, slot0, nil) + req.NoError(err) + req.Len(preRaw, 32) + req.Zero(new(big.Int).SetBytes(preRaw).Cmp(big.NewInt(0))) + + // Prepare set(42) call data + setData, err := parsedCounter.Pack("set", big.NewInt(42)) + req.NoError(err) + + // Estimate and undercut gas to force OOG while still clearing intrinsic gas + from := harness.SenderAddr + est, err := chain.EthClient.EstimateGas(harness.Ctx, ethereum.CallMsg{From: from, To: &counter, Data: setData}) + req.NoError(err) + req.NotZero(est, "eth_estimateGas returned 0 for set(42) โ€” unexpected; investigate RPC/node behavior") + + const outOfGasFraction = 0.66 // Use 66% of estimated gas to force OOG + gas := uint64(float64(est) * outOfGasFraction) + if gas < 30000 { + gas = 30000 // ensure above intrinsic + } + + // Build and send a dynamic-fee tx with limited gas + txHashLimited, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &counter, big.NewInt(0), setData, gas) + req.NoError(err) + + rec2, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHashLimited, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec2) + req.Equal(uint64(0), rec2.Status) + t.Logf("OOG tx=%s gasUsed=%d gasLimit=%d", txHashLimited.Hex(), rec2.GasUsed, gas) + + // Post-state: still zero + postRaw, err := chain.EthClient.StorageAt(harness.Ctx, counter, slot0, nil) + req.NoError(err) + req.Len(postRaw, 32) + req.Zero(new(big.Int).SetBytes(postRaw).Cmp(big.NewInt(0))) + + // Revert with reason visibility + parsedRev, err := abi.JSON(strings.NewReader(contracts.ReverterABIJSON())) + req.NoError(err) + creation2 := common.FromHex(contracts.ReverterBinHex()) + + txHash3, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, nil, big.NewInt(0), creation2, 0) + req.NoError(err) + rec3, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash3, utils.ReceiptTimeout) + req.NoError(err) + req.Equal(uint64(1), rec3.Status) + revAddr := rec3.ContractAddress + + // eth_call should return revert data encoding the reason; decode via rpc error.data + reason := "NOPE" + callData, err := parsedRev.Pack("revertWithReason", reason) + req.NoError(err) + + // Send a tx that reverts with reason + txHashRevert, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &revAddr, big.NewInt(0), callData, 100000) + req.NoError(err) + + rec4, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHashRevert, utils.ReceiptTimeout) + req.NoError(err) + req.Equal(uint64(0), rec4.Status) + + // Assert revert reason strictly by decoding revert data via eth_call at the block of inclusion + got, err := utils.GetRevertReasonViaEthCall(harness.Ctx, chain.RPC, revAddr, callData, rec4.BlockNumber) + req.NoError(err) + req.Equal(reason, got) +} diff --git a/evmd/e2e/fixtures/fixtures.go b/evmd/e2e/fixtures/fixtures.go new file mode 100644 index 000000000..f411f2b2e --- /dev/null +++ b/evmd/e2e/fixtures/fixtures.go @@ -0,0 +1,19 @@ +package fixtures + +import ( + "encoding/json" + "os" +) + +func loadFixture[T any](filename string) (*T, error) { + jsonData, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + + fixture := new(T) + if err := json.Unmarshal(jsonData, fixture); err != nil { + return nil, err + } + return fixture, nil +} diff --git a/evmd/e2e/fixtures/update_client.go b/evmd/e2e/fixtures/update_client.go new file mode 100644 index 000000000..eef28acbd --- /dev/null +++ b/evmd/e2e/fixtures/update_client.go @@ -0,0 +1,85 @@ +package fixtures + +import ( + "encoding/hex" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + + clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" + ibctmtypes "github.com/cosmos/ibc-go/v10/modules/light-clients/07-tendermint" +) + +type UpdateClientFixture struct { + ClientStateHex string `json:"client_state_hex"` + ConsensusStateHex string `json:"consensus_state_hex"` + UpdateClientMessage UpdateClientMessage `json:"update_client_message"` +} + +type UpdateClientMessage struct { + ClientMessageHex string `json:"client_message_hex"` + TypeURL string `json:"type_url"` +} + +func LoadUpdateClientFixture(filename string) (*UpdateClientFixture, error) { + return loadFixture[UpdateClientFixture](filename) +} + +func (f *UpdateClientFixture) ClientState() (*ibctmtypes.ClientState, error) { + clientStateBytes, err := hex.DecodeString(f.ClientStateHex) + if err != nil { + return nil, err + } + clientState := new(ibctmtypes.ClientState) + if err := clientState.Unmarshal(clientStateBytes); err != nil { + return nil, err + } + return clientState, nil +} + +func (f *UpdateClientFixture) ConsensusState() (*ibctmtypes.ConsensusState, error) { + consensusStateBytes, err := hex.DecodeString(f.ConsensusStateHex) + if err != nil { + return nil, err + } + consensusState := new(ibctmtypes.ConsensusState) + if err := consensusState.Unmarshal(consensusStateBytes); err != nil { + return nil, err + } + return consensusState, nil +} + +func (f *UpdateClientFixture) ClientStateAny() (*codectypes.Any, error) { + clientState, err := f.ClientState() + if err != nil { + return nil, err + } + return clienttypes.PackClientState(clientState) +} + +func (f *UpdateClientFixture) ConsensusStateAny() (*codectypes.Any, error) { + consensusState, err := f.ConsensusState() + if err != nil { + return nil, err + } + return clienttypes.PackConsensusState(consensusState) +} + +func (f *UpdateClientFixture) UpdateClientHeader() (*ibctmtypes.Header, error) { + clientMessageBytes, err := hex.DecodeString(f.UpdateClientMessage.ClientMessageHex) + if err != nil { + return nil, err + } + header := new(ibctmtypes.Header) + if err := header.Unmarshal(clientMessageBytes); err != nil { + return nil, err + } + return header, nil +} + +func (f *UpdateClientFixture) UpdateClientMessageAny() (*codectypes.Any, error) { + header, err := f.UpdateClientHeader() + if err != nil { + return nil, err + } + return clienttypes.PackClientMessage(header) +} diff --git a/evmd/e2e/fixtures/update_client.json b/evmd/e2e/fixtures/update_client.json new file mode 100644 index 000000000..3e57bf110 --- /dev/null +++ b/evmd/e2e/fixtures/update_client.json @@ -0,0 +1,20 @@ +{ + "client_state_hex": "0a0673696d642d321204080110031a040880ea4922040880df6e2a02080f32003a040802101642190a090801180120012a0100120c0a02000110211804200c300142190a090801180120012a0100120c0a02000110201801200130014a07757067726164654a1075706772616465644942435374617465", + "consensus_state_hex": "0a0c08c5c4b8c80610f5dfbba50312220a20e20be24d1ad87825d76e89c2168b9fb7e0e44a24cb374cb8a52660d5c8e12ddd1a20fb7457be51f81ff86a4396f43126609c7d2fe6530f24d1a870bba360e9a9b93c", + "metadata": { + "description": "Tendermint light client fixture for scenario: happy_path", + "generated_at": "2025-11-07T16:46:44Z", + "source": "local_cosmos_chain" + }, + "update_client_message": { + "client_message_hex": "0ab2050a8c030a02080b120673696d642d32182a220c08f2c4b8c80610a1a398b7022a480a20ff3857e748f46a2d1f9203ba14fe02f910f9357805cbd55ee91cc7511db737a8122408011220cc2b72b4bff4cbd4f1bea63dc396c3c8c566b39dd3326cae7e19158bc0cc780932201c8778f163d14415b598a12a30adb5183305976b7264e0334e8400854c2870593a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8554220fb7457be51f81ff86a4396f43126609c7d2fe6530f24d1a870bba360e9a9b93c4a20fb7457be51f81ff86a4396f43126609c7d2fe6530f24d1a870bba360e9a9b93c5220048091bc7ddc283f77bfbf91d73c44da58c3df8a9cbc867405d8b7f3daada22f5a20f0759de7debe0bb9af4db771b849e8f20ee87076b9b9f024341a31ea92a8e77e6220e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8556a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214dce4f1c4d44edc542cfd30b5980293005e3e66da12a002082a1a480a20fd5ff6f77583080360801ad3117f34417288c7c170cdcdd53362ce392b67f3781224080112208a9f6f8d699e52fb7cafff7b69a8c117175f02904f0a00e64ec863c1ff03534422680802121416769ac7fff5c1ca68a925edf76310becb2231721a0c08f4c4b8c80610f2a28c9e032240ab709a1462adbb4334d1e807bb1cee77066d936d1f844db976711a60e208642383695c5ea63dfa42db702c4194ebcffcf419519f42c45201b219e6dbeb090f0e226808021214dce4f1c4d44edc542cfd30b5980293005e3e66da1a0c08f4c4b8c80610a9a997c90322406bc4abaa746545c3123408c4e8223c87069cd39841fa07e30af6eadba24ecfcc95d3a0db2d7dea6dbb87e3c7233ad0c0970bbb5ecdd39f99be01329f7624d60612c8010a3f0a1416769ac7fff5c1ca68a925edf76310becb22317212220a203e381687809d38eba6c78da45329fde5fdb8f85d6d89f039f6b0f24f5b89d7c018c096b1020a3f0a14dce4f1c4d44edc542cfd30b5980293005e3e66da12220a20bdab77f5a9de505e41e2d400487ff79ca2795e55dac60773dd3b5f8939ff81a518c096b102123f0a14dce4f1c4d44edc542cfd30b5980293005e3e66da12220a20bdab77f5a9de505e41e2d400487ff79ca2795e55dac60773dd3b5f8939ff81a518c096b1021880ade2041a040802101622c8010a3f0a1416769ac7fff5c1ca68a925edf76310becb22317212220a203e381687809d38eba6c78da45329fde5fdb8f85d6d89f039f6b0f24f5b89d7c018c096b1020a3f0a14dce4f1c4d44edc542cfd30b5980293005e3e66da12220a20bdab77f5a9de505e41e2d400487ff79ca2795e55dac60773dd3b5f8939ff81a518c096b102123f0a14dce4f1c4d44edc542cfd30b5980293005e3e66da12220a20bdab77f5a9de505e41e2d400487ff79ca2795e55dac60773dd3b5f8939ff81a518c096b1021880ade204", + "metadata": { + "description": "Protobuf-encoded Tendermint header for update client", + "generated_at": "2025-11-07T16:46:44Z", + "source": "local_cosmos_chain" + }, + "new_height": 42, + "trusted_height": 22, + "type_url": "/ibc.lightclients.tendermint.v1.Header" + } +} diff --git a/evmd/e2e/go.mod b/evmd/e2e/go.mod new file mode 100644 index 000000000..5b2844051 --- /dev/null +++ b/evmd/e2e/go.mod @@ -0,0 +1,324 @@ +module github.com/cosmos/evm/evmd/e2e + +go 1.25.0 + +replace ( + // use cosmos fork of keyring + github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 + github.com/cosmos/evm/evmd => .. + github.com/ethereum/go-ethereum => github.com/cosmos/go-ethereum v1.16.2-cosmos-1 + // fix upstream GHSA-h395-qcrw-5vmq vulnerability. + github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 + github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + // replace broken goleveldb + github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + // Cosmos fork of tidwall's btree + github.com/tidwall/btree => github.com/cosmos/btree v0.0.0-20250924232609-2c6195d95951 +) + +require ( + cosmossdk.io/log v1.6.1 + cosmossdk.io/math v1.5.3 + github.com/cosmos/cosmos-db v1.1.3 + github.com/cosmos/cosmos-sdk v0.54.0-beta.0 + github.com/cosmos/evm v1.0.0-rc2.0.20251104202834-7998121a9c31 + github.com/cosmos/evm/evmd v0.0.0-00010101000000-000000000000 + github.com/cosmos/ibc-go/v10 v10.0.0-beta.0.0.20251027215440-22f0033d0aee + github.com/ethereum/go-ethereum v1.16.5 + github.com/google/uuid v1.6.0 + github.com/pelletier/go-toml/v2 v2.2.4 + github.com/stretchr/testify v1.11.1 + github.com/testcontainers/testcontainers-go v0.39.0 + google.golang.org/grpc v1.76.0 +) + +require ( + github.com/VictoriaMetrics/fastcache v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2 v1.39.0 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect + github.com/aws/aws-sdk-go-v2/config v1.31.8 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.18.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.80.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 // indirect + github.com/aws/smithy-go v1.23.0 // indirect + github.com/dgraph-io/ristretto/v2 v2.1.0 // indirect + github.com/ferranbt/fastssz v0.1.4 // indirect + github.com/gofrs/flock v0.12.1 // indirect + github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/rivo/uniseg v0.4.3 // indirect + github.com/tidwall/sjson v1.2.5 // indirect + github.com/zondax/golem v0.27.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect +) + +require ( + cel.dev/expr v0.24.0 // indirect + cloud.google.com/go v0.122.0 // indirect + cloud.google.com/go/auth v0.16.5 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.8.0 // indirect + cloud.google.com/go/iam v1.5.2 // indirect + cloud.google.com/go/monitoring v1.24.2 // indirect + cloud.google.com/go/storage v1.56.1 // indirect + cosmossdk.io/api v0.9.2 // indirect + cosmossdk.io/client/v2 v2.0.0-beta.9 // indirect + cosmossdk.io/collections v1.3.1 // indirect + cosmossdk.io/core v0.11.3 // indirect + cosmossdk.io/depinject v1.2.1 // indirect + cosmossdk.io/errors v1.0.2 // indirect + cosmossdk.io/schema v1.1.0 // indirect + cosmossdk.io/store v1.3.0-beta.0 // indirect + cosmossdk.io/x/evidence v0.2.0 // indirect + cosmossdk.io/x/feegrant v0.2.0 // indirect + cosmossdk.io/x/tx v0.14.0 // indirect + cosmossdk.io/x/upgrade v0.2.0 // indirect + dario.cat/mergo v1.0.2 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.2 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/DataDog/datadog-go v4.8.3+incompatible // indirect + github.com/DataDog/zstd v1.5.7 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/bgentry/speakeasy v0.2.0 // indirect + github.com/bits-and-blooms/bitset v1.24.1 // indirect + github.com/btcsuite/btcd v0.24.2 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect + github.com/btcsuite/btcd/btcutil v1.1.6 // indirect + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect + github.com/bytedance/sonic v1.14.0 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/chzyer/readline v1.5.1 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect + github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect + github.com/cockroachdb/errors v1.12.0 // indirect + github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect + github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect + github.com/cockroachdb/pebble v1.1.5 // indirect + github.com/cockroachdb/redact v1.1.6 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb // indirect + github.com/cometbft/cometbft v0.39.0-beta.2 // indirect + github.com/cometbft/cometbft-db v0.14.1 // indirect + github.com/consensys/gnark-crypto v0.18.1 // indirect + github.com/containerd/continuity v0.4.2 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/gogoproto v1.7.2 // indirect + github.com/cosmos/iavl v1.2.6 // indirect + github.com/cosmos/ics23/go v0.11.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.16.0 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect + github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect + github.com/danieljoos/wincred v1.2.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/desertbit/timer v1.0.1 // indirect + github.com/dgraph-io/badger/v4 v4.6.0 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/dlclark/regexp2 v1.7.0 // indirect + github.com/docker/docker v28.3.3+incompatible // indirect + github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.7.0 // indirect + github.com/ebitengine/purego v0.8.4 // indirect + github.com/emicklei/dot v1.8.0 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.0 // indirect + github.com/ethereum/go-verkle v0.2.2 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/getsentry/sentry-go v0.35.0 // indirect + github.com/go-jose/go-jose/v4 v4.1.2 // indirect + github.com/go-kit/kit v0.13.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.1 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v1.0.0 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/flatbuffers v25.2.10+incompatible // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/orderedcode v0.0.1 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/gorilla/handlers v1.5.2 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-getter v1.8.2 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.4 // indirect + github.com/hashicorp/go-plugin v1.7.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/hdevalence/ed25519consensus v0.2.0 // indirect + github.com/holiman/uint256 v1.3.2 // indirect + github.com/huandu/skiplist v1.2.1 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/linxGnu/grocksdb v1.10.1 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/manifoldco/promptui v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/minio/highwayhash v1.0.3 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.1.0 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/mtibben/percent v0.2.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // indirect + github.com/pion/dtls/v2 v2.2.7 // indirect + github.com/pion/logging v0.2.2 // indirect + github.com/pion/stun/v2 v2.0.0 // indirect + github.com/pion/transport/v2 v2.2.1 // indirect + github.com/pion/transport/v3 v3.0.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/rs/cors v1.11.1 // indirect + github.com/rs/zerolog v1.34.0 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sasha-s/go-deadlock v0.3.6 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/shirou/gopsutil/v4 v4.25.6 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/cobra v1.10.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/spf13/viper v1.21.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/supranational/blst v0.3.16 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tendermint/go-amino v0.16.0 // indirect + github.com/tidwall/btree v1.8.1 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tklauser/go-sysconf v0.3.15 // indirect + github.com/tklauser/numcpus v0.10.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/tyler-smith/go-bip39 v1.1.0 // indirect + github.com/ulikunitz/xz v0.5.15 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + github.com/zeebo/errs v1.4.0 // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v1.0.1 // indirect + go.etcd.io/bbolt v1.4.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.1 // indirect + go.uber.org/mock v0.6.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/arch v0.21.0 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect + golang.org/x/net v0.46.0 // indirect + golang.org/x/oauth2 v0.31.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/term v0.36.0 // indirect + golang.org/x/text v0.30.0 // indirect + golang.org/x/time v0.13.0 // indirect + google.golang.org/api v0.247.0 // indirect + google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect + google.golang.org/protobuf v1.36.10 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.5.2 // indirect + nhooyr.io/websocket v1.8.17 // indirect + pgregory.net/rapid v1.2.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect +) diff --git a/evmd/e2e/go.sum b/evmd/e2e/go.sum new file mode 100644 index 000000000..d16440271 --- /dev/null +++ b/evmd/e2e/go.sum @@ -0,0 +1,1411 @@ +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.122.0 h1:0JTLGrcSIs3HIGsgVPvTx3cfyFSP/k9CI8vLPHTd6Wc= +cloud.google.com/go v0.122.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= +cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= +cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= +cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= +cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= +cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= +cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= +cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= +cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= +cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= +cloud.google.com/go/storage v1.56.1 h1:n6gy+yLnHn0hTwBFzNn8zJ1kqWfR91wzdM8hjRF4wP0= +cloud.google.com/go/storage v1.56.1/go.mod h1:C9xuCZgFl3buo2HZU/1FncgvvOgTAs/rnh4gF4lMg0s= +cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= +cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= +cosmossdk.io/api v0.9.2 h1:9i9ptOBdmoIEVEVWLtYYHjxZonlF/aOVODLFaxpmNtg= +cosmossdk.io/api v0.9.2/go.mod h1:CWt31nVohvoPMTlPv+mMNCtC0a7BqRdESjCsstHcTkU= +cosmossdk.io/client/v2 v2.0.0-beta.9 h1:xc06zg4G858/pK5plhf8RCfo+KR2mdDKJNrEkfrVAqc= +cosmossdk.io/client/v2 v2.0.0-beta.9/go.mod h1:pHf3CCHX5gmbL9rDCVbXhGI2+/DdAVTEZSLpdd5V9Zs= +cosmossdk.io/collections v1.3.1 h1:09e+DUId2brWsNOQ4nrk+bprVmMUaDH9xvtZkeqIjVw= +cosmossdk.io/collections v1.3.1/go.mod h1:ynvkP0r5ruAjbmedE+vQ07MT6OtJ0ZIDKrtJHK7Q/4c= +cosmossdk.io/core v0.11.3 h1:mei+MVDJOwIjIniaKelE3jPDqShCc/F4LkNNHh+4yfo= +cosmossdk.io/core v0.11.3/go.mod h1:9rL4RE1uDt5AJ4Tg55sYyHWXA16VmpHgbe0PbJc6N2Y= +cosmossdk.io/depinject v1.2.1 h1:eD6FxkIjlVaNZT+dXTQuwQTKZrFZ4UrfCq1RKgzyhMw= +cosmossdk.io/depinject v1.2.1/go.mod h1:lqQEycz0H2JXqvOgVwTsjEdMI0plswI7p6KX+MVqFOM= +cosmossdk.io/errors v1.0.2 h1:wcYiJz08HThbWxd/L4jObeLaLySopyyuUFB5w4AGpCo= +cosmossdk.io/errors v1.0.2/go.mod h1:0rjgiHkftRYPj//3DrD6y8hcm40HcPv/dR4R/4efr0k= +cosmossdk.io/log v1.6.1 h1:YXNwAgbDwMEKwDlCdH8vPcoggma48MgZrTQXCfmMBeI= +cosmossdk.io/log v1.6.1/go.mod h1:gMwsWyyDBjpdG9u2avCFdysXqxq28WJapJvu+vF1y+E= +cosmossdk.io/math v1.5.3 h1:WH6tu6Z3AUCeHbeOSHg2mt9rnoiUWVWaQ2t6Gkll96U= +cosmossdk.io/math v1.5.3/go.mod h1:uqcZv7vexnhMFJF+6zh9EWdm/+Ylyln34IvPnBauPCQ= +cosmossdk.io/schema v1.1.0 h1:mmpuz3dzouCoyjjcMcA/xHBEmMChN+EHh8EHxHRHhzE= +cosmossdk.io/schema v1.1.0/go.mod h1:Gb7pqO+tpR+jLW5qDcNOSv0KtppYs7881kfzakguhhI= +cosmossdk.io/store v1.3.0-beta.0 h1:jwJvAQkMsCY9xJHU/nz7yOo1WnNRvcI/9yLRSgZoFTk= +cosmossdk.io/store v1.3.0-beta.0/go.mod h1:CMz9JQGEA8eRcZv2pK07NgEbL4NEb9wVgzWK4tNQaPg= +cosmossdk.io/x/evidence v0.2.0 h1:o72zbmgCM7U0v7z7b0XnMB+NqX0tFamqb1HHkQbhrZ0= +cosmossdk.io/x/evidence v0.2.0/go.mod h1:zx/Xqy+hnGVzkqVuVuvmP9KsO6YCl4SfbAetYi+k+sE= +cosmossdk.io/x/feegrant v0.2.0 h1:oq3WVpoJdxko/XgWmpib63V1mYy9ZQN/1qxDajwGzJ8= +cosmossdk.io/x/feegrant v0.2.0/go.mod h1:9CutZbmhulk/Yo6tQSVD5LG8Lk40ZAQ1OX4d1CODWAE= +cosmossdk.io/x/tx v0.14.0 h1:hB3O25kIcyDW/7kMTLMaO8Ripj3yqs5imceVd6c/heA= +cosmossdk.io/x/tx v0.14.0/go.mod h1:Tn30rSRA1PRfdGB3Yz55W4Sn6EIutr9xtMKSHij+9PM= +cosmossdk.io/x/upgrade v0.2.0 h1:ZHy0xny3wBCSLomyhE06+UmQHWO8cYlVYjfFAJxjz5g= +cosmossdk.io/x/upgrade v0.2.0/go.mod h1:DXDtkvi//TrFyHWSOaeCZGBoiGAE6Rs8/0ABt2pcDD0= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= +github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE= +github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/adlio/schema v1.3.9 h1:MLYk1VX1dn7xHW7Kdm1ywKKLjh19DRnrc65axS5xQA8= +github.com/adlio/schema v1.3.9/go.mod h1:GnxXztHzNh6pIc7qm3sw+jsmHrXgBy/x2RBSkKZ3L4w= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/aws/aws-sdk-go-v2 v1.39.0 h1:xm5WV/2L4emMRmMjHFykqiA4M/ra0DJVSWUkDyBjbg4= +github.com/aws/aws-sdk-go-v2 v1.39.0/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1/go.mod h1:ddqbooRZYNoJ2dsTwOty16rM+/Aqmk/GOXrK8cg7V00= +github.com/aws/aws-sdk-go-v2/config v1.31.8 h1:kQjtOLlTU4m4A64TsRcqwNChhGCwaPBt+zCQt/oWsHU= +github.com/aws/aws-sdk-go-v2/config v1.31.8/go.mod h1:QPpc7IgljrKwH0+E6/KolCgr4WPLerURiU592AYzfSY= +github.com/aws/aws-sdk-go-v2/credentials v1.18.12 h1:zmc9e1q90wMn8wQbjryy8IwA6Q4XlaL9Bx2zIqdNNbk= +github.com/aws/aws-sdk-go-v2/credentials v1.18.12/go.mod h1:3VzdRDR5u3sSJRI4kYcOSIBbeYsgtVk7dG5R/U6qLWY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 h1:Is2tPmieqGS2edBnmOJIbdvOA6Op+rRpaYR60iBAwXM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7/go.mod h1:F1i5V5421EGci570yABvpIXgRIBPb5JM+lSkHF6Dq5w= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 h1:UCxq0X9O3xrlENdKf1r9eRJoKz/b0AfGkpp3a7FPlhg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7/go.mod h1:rHRoJUNUASj5Z/0eqI4w32vKvC7atoWR0jC+IkmVH8k= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 h1:Y6DTZUn7ZUC4th9FMBbo8LVE+1fyq3ofw+tRwkUd3PY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7/go.mod h1:x3XE6vMnU9QvHN/Wrx2s44kwzV2o2g5x/siw4ZUJ9g8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.7 h1:BszAktdUo2xlzmYHjWMq70DqJ7cROM8iBd3f6hrpuMQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.7/go.mod h1:XJ1yHki/P7ZPuG4fd3f0Pg/dSGA2cTQBCLw82MH2H48= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2 h1:BCG7DCXEXpNCcpwCxg1oi9pkJWH2+eZzTn9MY56MbVw= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 h1:mLgc5QIgOy26qyh5bvW+nDoAppxgn3J2WV3m9ewq7+8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7/go.mod h1:wXb/eQnqt8mDQIQTTmcw58B5mYGxzLGZGK8PWNFZ0BA= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA= +github.com/aws/aws-sdk-go-v2/service/s3 v1.80.1 h1:xYEAf/6QHiTZDccKnPMbsMwlau13GsDsTgdue3wmHGw= +github.com/aws/aws-sdk-go-v2/service/s3 v1.80.1/go.mod h1:qbn305Je/IofWBJ4bJz/Q7pDEtnnoInw/dGt71v6rHE= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 h1:7PKX3VYsZ8LUWceVRuv0+PU+E7OtQb1lgmi5vmUE9CM= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.3/go.mod h1:Ql6jE9kyyWI5JHn+61UT/Y5Z0oyVJGmgmJbZD5g4unY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 h1:e0XBRn3AptQotkyBFrHAxFB8mDhAIOfsG+7KyJ0dg98= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4/go.mod h1:XclEty74bsGBCr1s0VSaA11hQ4ZidK4viWK7rRfO88I= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 h1:PR00NXRYgY4FWHqOGx3fC3lhVKjsp1GdloDv2ynMSd8= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.4/go.mod h1:Z+Gd23v97pX9zK97+tX4ppAgqCt3Z2dIXB02CtBncK8= +github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= +github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E= +github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.24.1 h1:hqnfFbjjk3pxGa5E9Ho3hjoU7odtUuNmJ9Ao+Bo8s1c= +github.com/bits-and-blooms/bitset v1.24.1/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= +github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU= +github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c= +github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= +github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo= +github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g= +github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 h1:pU88SPhIFid6/k0egdR5V6eALQYq2qbSmukrkgIh/0A= +github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k= +github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo= +github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw= +github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo= +github.com/cockroachdb/redact v1.1.6 h1:zXJBwDZ84xJNlHl1rMyCojqyIxv+7YUpQiJLQ7n4314= +github.com/cockroachdb/redact v1.1.6/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb h1:3bCgBvB8PbJVMX1ouCcSIxvsqKPYM7gs72o0zC76n9g= +github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/cometbft/cometbft v0.39.0-beta.2 h1:OfP4Qlw2L66xqvrNYSlVufYwXXP9frOPBnwYPyDttOk= +github.com/cometbft/cometbft v0.39.0-beta.2/go.mod h1:6Lt9liF9HxSth+zDotxtv94SuGMzRfuHmI287USgjlw= +github.com/cometbft/cometbft-db v0.14.1 h1:SxoamPghqICBAIcGpleHbmoPqy+crij/++eZz3DlerQ= +github.com/cometbft/cometbft-db v0.14.1/go.mod h1:KHP1YghilyGV/xjD5DP3+2hyigWx0WTp9X+0Gnx0RxQ= +github.com/consensys/gnark-crypto v0.18.1 h1:RyLV6UhPRoYYzaFnPQA4qK3DyuDgkTgskDdoGqFt3fI= +github.com/consensys/gnark-crypto v0.18.1/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= +github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/btree v0.0.0-20250924232609-2c6195d95951 h1:dC3GJcS8bJiSEe7VAFDDFgFnVM1G9nBdGOgqJsmsZwM= +github.com/cosmos/btree v0.0.0-20250924232609-2c6195d95951/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= +github.com/cosmos/cosmos-db v1.1.3 h1:7QNT77+vkefostcKkhrzDK9uoIEryzFrU9eoMeaQOPY= +github.com/cosmos/cosmos-db v1.1.3/go.mod h1:kN+wGsnwUJZYn8Sy5Q2O0vCYA99MJllkKASbs6Unb9U= +github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= +github.com/cosmos/cosmos-sdk v0.54.0-beta.0 h1:KOyM1CjuriSxMm2CNhceGfEc2LbRWuImKw7L9yp3+SM= +github.com/cosmos/cosmos-sdk v0.54.0-beta.0/go.mod h1:VBGchDTNoLW3xNy5mPNlnDFZiEjgRN/06vwSkXuBQdg= +github.com/cosmos/evm v1.0.0-rc2.0.20251104202834-7998121a9c31 h1:Dr5xeDMJmQ93otNISK6KPXrmyX+rvSz73Y8+wnJW/RI= +github.com/cosmos/evm v1.0.0-rc2.0.20251104202834-7998121a9c31/go.mod h1:BkRANwouPsrrg1bw+kDQjUL/TuRk+y/UQbpPsqkcgwI= +github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +github.com/cosmos/go-ethereum v1.16.2-cosmos-1 h1:QIaIS6HIdPSBdTvpFhxswhMLUJgcr4irbd2o9ZKldAI= +github.com/cosmos/go-ethereum v1.16.2-cosmos-1/go.mod h1:X5CIOyo8SuK1Q5GnaEizQVLHT/DfsiGWuNeVdQcEMNA= +github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= +github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= +github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= +github.com/cosmos/gogoproto v1.7.2 h1:5G25McIraOC0mRFv9TVO139Uh3OklV2hczr13KKVHCA= +github.com/cosmos/gogoproto v1.7.2/go.mod h1:8S7w53P1Y1cHwND64o0BnArT6RmdgIvsBuco6uTllsk= +github.com/cosmos/iavl v1.2.6 h1:Hs3LndJbkIB+rEvToKJFXZvKo6Vy0Ex1SJ54hhtioIs= +github.com/cosmos/iavl v1.2.6/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw= +github.com/cosmos/ibc-go/v10 v10.0.0-beta.0.0.20251027215440-22f0033d0aee h1:zGsni85/bBwWW6uQh0NEEXaUAliFgo1dBIoHfAqNHRk= +github.com/cosmos/ibc-go/v10 v10.0.0-beta.0.0.20251027215440-22f0033d0aee/go.mod h1:kbhNmgk/OlNfeLUX9ieOvBv1Y0dRa9lUwx+jb4zc7Nc= +github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= +github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0= +github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo= +github.com/cosmos/keyring v1.2.0/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/cosmos/ledger-cosmos-go v0.16.0 h1:YKlWPG9NnGZIEUb2bEfZ6zhON1CHlNTg0QKRRGcNEd0= +github.com/cosmos/ledger-cosmos-go v0.16.0/go.mod h1:WrM2xEa8koYoH2DgeIuZXNarF7FGuZl3mrIOnp3Dp0o= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI= +github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0= +github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0= +github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/desertbit/timer v1.0.1 h1:yRpYNn5Vaaj6QXecdLMPMJsW81JLiI1eokUft5nBmeo= +github.com/desertbit/timer v1.0.1/go.mod h1:htRrYeY5V/t4iu1xCJ5XsQvp4xve8QulXXctAzxqcwE= +github.com/dgraph-io/badger/v4 v4.6.0 h1:acOwfOOZ4p1dPRnYzvkVm7rUk2Y21TgPVepCy5dJdFQ= +github.com/dgraph-io/badger/v4 v4.6.0/go.mod h1:KSJ5VTuZNC3Sd+YhvVjk2nYua9UZnnTr/SkXvdtiPgI= +github.com/dgraph-io/ristretto/v2 v2.1.0 h1:59LjpOJLNDULHh8MC4UaegN52lC4JnO2dITsie/Pa8I= +github.com/dgraph-io/ristretto/v2 v2.1.0/go.mod h1:uejeqfYXpUomfse0+lO+13ATz4TypQYLJZzBSAemuB4= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= +github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 h1:+3HCtB74++ClLy8GgjUQYeC8R4ILzVcIe8+5edAJJnE= +github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= +github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= +github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dvsekhvalnov/jose2go v1.7.0 h1:bnQc8+GMnidJZA8zc6lLEAb4xNrIqHwO+9TzqvtQZPo= +github.com/dvsekhvalnov/jose2go v1.7.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/emicklei/dot v1.8.0 h1:HnD60yAKFAevNeT+TPYr9pb8VB9bqdeSo0nzwIW6IOI= +github.com/emicklei/dot v1.8.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= +github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= +github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= +github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= +github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/getsentry/sentry-go v0.35.0 h1:+FJNlnjJsZMG3g0/rmmP7GiKjQoUF5EXfEtBwtPtkzY= +github.com/getsentry/sentry-go v0.35.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= +github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE= +github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= +github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= +github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= +github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 h1:81+kWbE1yErFBMjME0I5k3x3kojjKsWtPYHEAutoPow= +github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65/go.mod h1:WtMzv9T++tfWVea+qB2MXoaqxw33S8bpJslzUike2mQ= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-getter v1.8.2 h1:CGCK+bZQLl44PYiwJweVzfpjg7bBwtuXu3AGcLiod2o= +github.com/hashicorp/go-getter v1.8.2/go.mod h1:CUTt9x2bCtJ/sV8ihgrITL3IUE+0BE1j/e4n5P/GIM4= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= +github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= +github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/skiplist v1.2.1 h1:dTi93MgjwErA/8idWTzIw4Y1kZsMWx35fmI2c8Rij7w= +github.com/huandu/skiplist v1.2.1/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= +github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= +github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linxGnu/grocksdb v1.10.1 h1:YX6gUcKvSC3d0s9DaqgbU+CRkZHzlELgHu1Z/kmtslg= +github.com/linxGnu/grocksdb v1.10.1/go.mod h1:C3CNe9UYc9hlEM2pC82AqiGS3LRW537u9LFV4wIZuHk= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= +github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a h1:dlRvE5fWabOchtH7znfiFCcOvmIYgOeAS5ifBXBlh9Q= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY= +github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/opencontainers/runc v1.1.14 h1:rgSuzbmgz5DUJjeSnw337TxDbRuqjs6iqQck/2weR6w= +github.com/opencontainers/runc v1.1.14/go.mod h1:E4C2z+7BxR7GHXp0hAY53mek+x49X1LjPNeMTfRGvOA= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe h1:vHpqOnPlnkba8iSxU4j/CvDSS9J4+F4473esQsYLGoE= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= +github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= +github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= +github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= +github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.67.1 h1:OTSON1P4DNxzTg4hmKCc37o4ZAZDv0cfXLkOt0oEowI= +github.com/prometheus/common v0.67.1/go.mod h1:RpmT9v35q2Y+lsieQsdOh5sXZ6ajUGC8NjZAmr8vb0Q= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= +github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= +github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sasha-s/go-deadlock v0.3.6 h1:TR7sfOnZ7x00tWPfD397Peodt57KzMDo+9Ae9rMiUmw= +github.com/sasha-s/go-deadlock v0.3.6/go.mod h1:CUqNyyvMxTyjFqDT7MRg9mb4Dv/btmGTqSR+rky/UXo= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= +github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= +github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= +github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= +github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= +github.com/testcontainers/testcontainers-go v0.39.0 h1:uCUJ5tA+fcxbFAB0uP3pIK3EJ2IjjDUHFSZ1H1UxAts= +github.com/testcontainers/testcontainers-go v0.39.0/go.mod h1:qmHpkG7H5uPf/EvOORKvS6EuDkBUPE3zpVGaH9NL7f8= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= +github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= +github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= +github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +github.com/zondax/golem v0.27.0 h1:IbBjGIXF3SoGOZHsILJvIM/F/ylwJzMcHAcggiqniPw= +github.com/zondax/golem v0.27.0/go.mod h1:AmorCgJPt00L8xN1VrMBe13PSifoZksnQ1Ge906bu4A= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v1.0.1 h1:Ks/2tz/dOF+dbRynfZ0dEhcdL1lqw43Sa0zMXHpQ3aQ= +github.com/zondax/ledger-go v1.0.1/go.mod h1:j7IgMY39f30apthJYMd1YsHZRqdyu4KbVmUp0nU78X0= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= +go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw= +golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= +golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= +golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.247.0 h1:tSd/e0QrUlLsrwMKmkbQhYVa109qIintOls2Wh6bngc= +google.golang.org/api v0.247.0/go.mod h1:r1qZOPmxXffXg6xS5uhx16Fa/UFY8QU/K4bfKrnvovM= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= +google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g= +google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= +nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/evmd/e2e/ibc_ante_test.go b/evmd/e2e/ibc_ante_test.go new file mode 100644 index 000000000..8f0968e9f --- /dev/null +++ b/evmd/e2e/ibc_ante_test.go @@ -0,0 +1,122 @@ +package e2e + +import ( + "testing" + + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/evm/crypto/ethsecp256k1" + eapp "github.com/cosmos/evm/evmd/app" + "github.com/cosmos/evm/evmd/e2e/testharness" + "github.com/cosmos/evm/evmd/e2e/utils" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" + + "cosmossdk.io/log" + sdkmath "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types" +) + +func TestIBCAntePanicsWithoutKeeper(t *testing.T) { + t.Parallel() + harness := testharness.CreateHarness(t) + chain := harness.Chain + + tempApp := eapp.New( + log.NewNopLogger(), + dbm.NewMemDB(), + nil, + true, + sims.EmptyAppOptions{}, + ) + txConfig := tempApp.GetTxConfig() + ifaceRegistry := tempApp.InterfaceRegistry() + + senderBech32 := sdk.MustBech32ifyAddressBytes(utils.TestBech32Prefix, harness.SenderAddr.Bytes()) + + authClient := authtypes.NewQueryClient(chain.GrpcClient) + accountResp, err := authClient.Account(harness.Ctx, &authtypes.QueryAccountRequest{Address: senderBech32}) + require.NoError(t, err) + + var account sdk.AccountI + require.NoError(t, ifaceRegistry.UnpackAny(accountResp.Account, &account)) + + accountNumber := account.GetAccountNumber() + sequence := account.GetSequence() + + packet := channeltypes.NewPacket( + []byte{0x01}, + 1, + "transfer", + "channel-0", + "transfer", + "channel-1", + clienttypes.NewHeight(1, 1), + 1, + ) + msg := channeltypes.NewMsgTimeout( + packet, + 1, + []byte{0x01}, + clienttypes.NewHeight(1, 1), + senderBech32, + ) + + txBuilder := txConfig.NewTxBuilder() + require.NoError(t, txBuilder.SetMsgs(msg)) + txBuilder.SetGasLimit(200_000) + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(utils.TestDenom, sdkmath.NewInt(1_000_000_000_000_000)))) + + privKey := ðsecp256k1.PrivKey{Key: crypto.FromECDSA(harness.SenderKey)} + apiMode := txConfig.SignModeHandler().DefaultMode() + mode, err := authsigning.APISignModeToInternal(apiMode) + require.NoError(t, err) + sigData := &signingtypes.SingleSignatureData{SignMode: mode} + require.NoError(t, txBuilder.SetSignatures(signingtypes.SignatureV2{ + PubKey: privKey.PubKey(), + Data: sigData, + Sequence: sequence, + })) + + signerData := authsigning.SignerData{ + ChainID: utils.TestChainID, + AccountNumber: accountNumber, + Sequence: sequence, + } + signBytes, err := authsigning.GetSignBytesAdapter(harness.Ctx, txConfig.SignModeHandler(), mode, signerData, txBuilder.GetTx()) + require.NoError(t, err) + + signature, err := privKey.Sign(signBytes) + require.NoError(t, err) + sigData.Signature = signature + require.NoError(t, txBuilder.SetSignatures(signingtypes.SignatureV2{ + PubKey: privKey.PubKey(), + Data: sigData, + Sequence: sequence, + })) + + txBytes, err := txConfig.TxEncoder()(txBuilder.GetTx()) + require.NoError(t, err) + + txClient := txtypes.NewServiceClient(chain.GrpcClient) + res, err := txClient.BroadcastTx(harness.Ctx, &txtypes.BroadcastTxRequest{ + Mode: txtypes.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, + }) + require.NoError(t, err) + require.NotNil(t, res) + require.NotNil(t, res.TxResponse) + require.NotEqual(t, uint32(0), res.TxResponse.Code) + + // Make sure the chain is still producing blocks + err = utils.WaitForBlocks(harness.Ctx, harness.Chain.EthClient, 5) + require.NoError(t, err) +} diff --git a/evmd/e2e/jsonrpc_conformance_test.go b/evmd/e2e/jsonrpc_conformance_test.go new file mode 100644 index 000000000..053b1320c --- /dev/null +++ b/evmd/e2e/jsonrpc_conformance_test.go @@ -0,0 +1,787 @@ +package e2e + +import ( + "context" + "errors" + "math/big" + "strings" + "testing" + "time" + + "github.com/cosmos/evm/evmd/e2e/contracts" + "github.com/cosmos/evm/evmd/e2e/testharness" + "github.com/cosmos/evm/evmd/e2e/utils" + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" + "github.com/stretchr/testify/require" +) + +// TestJSONRPCEstimateGasRevert verifies that eth_estimateGas returns an error +// containing revert data when the target call would revert, and that the revert +// reason decodes as expected. +func TestJSONRPCEstimateGasRevert(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + // Deploy Reverter + creation := common.FromHex(contracts.ReverterBinHex()) + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, nil, big.NewInt(0), creation, 0) + req.NoError(err) + + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + revAddr := rec.ContractAddress + + // Build call data that will revert with a known reason + parsed, err := abi.JSON(strings.NewReader(contracts.ReverterABIJSON())) + req.NoError(err) + reason := "ESTIMATE_GAS_REVERT" + callData, err := parsed.Pack("revertWithReason", reason) + req.NoError(err) + + // eth_estimateGas should error and include revert data + _, err = chain.EthClient.EstimateGas(harness.Ctx, ethereum.CallMsg{ + From: harness.SenderAddr, + To: &revAddr, + Data: callData, + }) + req.Error(err) + + // Extract revert data hex from the RPC error + var hexData string + var de rpc.DataError + if errors.As(err, &de) { + switch v := de.ErrorData().(type) { + case string: + hexData = v + case map[string]any: + // Common shapes: + // {"data":"0x..."} OR {"data":{"data":"0x...", ...}} + if d, ok := v["data"].(string); ok { + hexData = d + } else if inner, ok := v["data"].(map[string]any); ok { + if d, ok := inner["data"].(string); ok { + hexData = d + } + } + } + } + req.NotEmpty(hexData, "missing revert data in eth_estimateGas error: %v", err) + + // Decode revert reason from the data + b := common.FromHex(hexData) + msg, derr := abi.UnpackRevert(b) + req.NoError(derr) + req.Equal(reason, msg) + + // Cross-check via eth_call helper against latest state (no specific block) + rr, err := utils.GetRevertReasonViaEthCall(harness.Ctx, chain.RPC, revAddr, callData, nil) + req.NoError(err) + req.Equal(reason, rr) +} + +// TestJSONRPCFilters covers eth_getLogs filtering semantics: +// - blockHash-only filter (with topic0) restricts results to that block and includes our log +// - range filter with address + topic OR across indexed caller includes both expected logs +func TestJSONRPCFilters(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + // Deploy Counter + parsedABI, err := abi.JSON(strings.NewReader(contracts.CounterABIJSON())) + req.NoError(err) + creation := common.FromHex(contracts.CounterBinHex()) + + txHashCreate, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, nil, big.NewInt(0), creation, 0) + req.NoError(err) + recCreate, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHashCreate, utils.ReceiptTimeout) + req.NoError(err) + req.Equal(uint64(1), recCreate.Status) + counter := recCreate.ContractAddress + + // Second caller (B): fund with small amount for gas + bKey, err := crypto.GenerateKey() + req.NoError(err) + bAddr := crypto.PubkeyToAddress(bKey.PublicKey) + fundAmt := big.NewInt(1_000_000_000_000_000) // 1e15 wei + txFund, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &bAddr, fundAmt, nil, utils.BasicTransferGas) + req.NoError(err) + recFund, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txFund, utils.ReceiptTimeout) + req.NoError(err) + req.Equal(uint64(1), recFund.Status) + + // Emit two events from two callers + callSet1, err := parsedABI.Pack("set", big.NewInt(1)) + req.NoError(err) + callSet2, err := parsedABI.Pack("set", big.NewInt(2)) + req.NoError(err) + + txA, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &counter, big.NewInt(0), callSet1, 0) + req.NoError(err) + recA, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txA, utils.ReceiptTimeout) + req.NoError(err) + req.Equal(uint64(1), recA.Status) + + txB, err := utils.SendTx(harness.Ctx, chain.EthClient, bKey, &counter, big.NewInt(0), callSet2, 0) + req.NoError(err) + recB, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txB, utils.ReceiptTimeout) + req.NoError(err) + req.Equal(uint64(1), recB.Status) + + // Topics + topic0 := crypto.Keccak256Hash([]byte("ValueChanged(uint256,address)")) + topicA := common.BytesToHash(common.LeftPadBytes(harness.SenderAddr.Bytes(), 32)) + topicB := common.BytesToHash(common.LeftPadBytes(bAddr.Bytes(), 32)) + + // blockHash-only filter (plus topic0) for the block that included txA + logs1, err := chain.EthClient.FilterLogs(harness.Ctx, ethereum.FilterQuery{BlockHash: &recA.BlockHash, Topics: [][]common.Hash{{topic0}}}) + req.NoError(err) + req.NotEmpty(logs1) + for _, lg := range logs1 { + req.Equal(recA.BlockHash, lg.BlockHash) + } + // Ensure our txA event is present + foundA := false + for i := range logs1 { + lg := logs1[i] + if lg.Address == counter && lg.TxHash == recA.TxHash && len(lg.Topics) > 0 && lg.Topics[0] == topic0 { + foundA = true + break + } + } + req.True(foundA, "expected txA event in blockHash-only filter results") + + // range + address + OR on indexed caller + from := recA.BlockNumber + to := recB.BlockNumber + if to.Cmp(from) < 0 { + from, to = to, from + } + logs2, err := chain.EthClient.FilterLogs(harness.Ctx, ethereum.FilterQuery{ + FromBlock: from, + ToBlock: to, + Addresses: []common.Address{counter}, + Topics: [][]common.Hash{{topic0}, {topicA, topicB}}, + }) + req.NoError(err) + + var seenA, seenB bool + for i := range logs2 { + lg := logs2[i] + if lg.Address != counter || len(lg.Topics) < 2 || lg.Topics[0] != topic0 { + continue + } + if lg.TxHash == recA.TxHash || lg.Topics[1] == topicA { + seenA = true + } + if lg.TxHash == recB.TxHash || lg.Topics[1] == topicB { + seenB = true + } + } + req.True(seenA, "expected A's event present in range+OR filter") + req.True(seenB, "expected B's event present in range+OR filter") +} + +// TestJSONRPCNodeInfoAndFees validates node info & fee tip RPCs: +// - web3_clientVersion is non-empty +// - net_listening is true +// - net_peerCount is 0 or 1 (single-node harness) +// - eth_maxPriorityFeePerGas and SuggestGasTipCap are non-negative and roughly consistent +func TestJSONRPCNodeInfoAndFees(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + rpcClient, err := rpc.DialContext(harness.Ctx, chain.RPC) + req.NoError(err) + defer rpcClient.Close() + + // web3_clientVersion + { + cctx, cancel := context.WithTimeout(harness.Ctx, utils.RevertReasonTimeout) + defer cancel() + var clientVersion string + req.NoError(rpcClient.CallContext(cctx, &clientVersion, "web3_clientVersion")) + req.NotEmpty(clientVersion) + t.Logf("web3_clientVersion: %s", clientVersion) + } + + // net_listening + { + cctx, cancel := context.WithTimeout(harness.Ctx, utils.RevertReasonTimeout) + defer cancel() + var listening bool + req.NoError(rpcClient.CallContext(cctx, &listening, "net_listening")) + req.True(listening) + t.Logf("net_listening: %v", listening) + } + + // net_peerCount (accept hex string or number) + peerCountInt := new(big.Int) + { + cctx, cancel := context.WithTimeout(harness.Ctx, utils.RevertReasonTimeout) + defer cancel() + var peerCountRaw any + req.NoError(rpcClient.CallContext(cctx, &peerCountRaw, "net_peerCount")) + switch v := peerCountRaw.(type) { + case string: + clean := strings.TrimPrefix(v, "0x") + if clean == "" { + clean = "0" + } + _, ok := peerCountInt.SetString(clean, 16) + req.True(ok, "bad hex quantity: %q", v) + case float64: + if v < 0 { + peerCountInt.SetInt64(0) + } else { + peerCountInt.SetInt64(int64(v)) + } + default: + req.Failf("unexpected type", "net_peerCount result has unsupported type %T", v) + } + // Expect 0 or 1 in a single-node harness + zero := big.NewInt(0) + one := big.NewInt(1) + req.GreaterOrEqual(peerCountInt.Cmp(zero), 0) + req.LessOrEqual(peerCountInt.Cmp(one), 0) + t.Logf("net_peerCount: %v (%s)", peerCountRaw, peerCountInt.String()) + } + + // eth_maxPriorityFeePerGas (accept hex string or number) + rpcTip := new(big.Int) + { + cctx, cancel := context.WithTimeout(harness.Ctx, utils.RevertReasonTimeout) + defer cancel() + var tipRaw any + req.NoError(rpcClient.CallContext(cctx, &tipRaw, "eth_maxPriorityFeePerGas")) + switch v := tipRaw.(type) { + case string: + clean := strings.TrimPrefix(v, "0x") + if clean == "" { + clean = "0" + } + _, ok := rpcTip.SetString(clean, 16) + req.True(ok, "bad hex quantity: %q", v) + case float64: + if v < 0 { + rpcTip.SetInt64(0) + } else { + rpcTip.SetInt64(int64(v)) + } + default: + req.Failf("unexpected type", "eth_maxPriorityFeePerGas result has unsupported type %T", v) + } + req.GreaterOrEqual(rpcTip.Sign(), 0) + t.Logf("eth_maxPriorityFeePerGas: %v (%s wei)", tipRaw, rpcTip.String()) + } + + // ethclient SuggestGasTipCap + var tip *big.Int + { + cctx, cancel := context.WithTimeout(harness.Ctx, utils.RevertReasonTimeout) + defer cancel() + var e error + tip, e = chain.EthClient.SuggestGasTipCap(cctx) + req.NoError(e) + req.NotNil(tip) + req.GreaterOrEqual(tip.Sign(), 0) + t.Logf("ethclient.SuggestGasTipCap: %s wei", tip.String()) + } + + // Rough consistency check (within 5x) when both are positive + if rpcTip.Sign() > 0 && tip.Sign() > 0 { + minTip := new(big.Int).Set(rpcTip) + maxTip := new(big.Int).Set(tip) + if minTip.Cmp(maxTip) > 0 { + minTip, maxTip = maxTip, minTip + } + fiveMin := new(big.Int).Mul(minTip, big.NewInt(5)) + req.LessOrEqual(maxTip.Cmp(fiveMin), 0, "maxPriorityFeePerGas (%s) and SuggestGasTipCap (%s) differ by >5x", rpcTip.String(), tip.String()) + } +} + +// TestJSONRPCPendingState validates pending vs latest nonces around a just-submitted tx. +// Pre-inclusion, pending should equal latest+1; after inclusion, latest catches up. +func TestJSONRPCPendingState(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + // Baseline: latest and pending nonces should be consistent + latest0, err := chain.EthClient.NonceAt(harness.Ctx, harness.SenderAddr, nil) + req.NoError(err) + + pending0, err := chain.EthClient.PendingNonceAt(harness.Ctx, harness.SenderAddr) + req.NoError(err) + req.GreaterOrEqual(pending0, latest0, "pending nonce should be >= latest nonce at baseline") + + // Send a minimal transfer from the funded sender to a fresh recipient + recvKey, err := crypto.GenerateKey() + req.NoError(err) + recipient := crypto.PubkeyToAddress(recvKey.PublicKey) + + value := big.NewInt(1) // 1 wei + gas := uint64(21000) // basic transfer + + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &recipient, value, nil, gas) + req.NoError(err) + + // Observe pending vs latest before inclusion. It's possible the tx is mined + // so quickly that we don't catch the pre-inclusion window; that's not a failure. + sawPendingBump := false + deadline := time.Now().Add(1500 * time.Millisecond) + for time.Now().Before(deadline) { + latest, err := chain.EthClient.NonceAt(harness.Ctx, harness.SenderAddr, nil) + req.NoError(err) + pending, err := chain.EthClient.PendingNonceAt(harness.Ctx, harness.SenderAddr) + req.NoError(err) + + if latest == latest0 && pending == latest0+1 { + sawPendingBump = true + t.Log("Observed pending == latest+1 prior to inclusion") + break + } + time.Sleep(100 * time.Millisecond) + } + + // Wait for inclusion and assert success + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + + if !sawPendingBump { + t.Log("Tx mined quickly; did not observe pre-inclusion pending bump; continuing") + } + + // After inclusion: latest nonce should have caught up by +1; pending >= latest + latest1, err := chain.EthClient.NonceAt(harness.Ctx, harness.SenderAddr, nil) + req.NoError(err) + req.Equal(latest0+1, latest1) + + pending1, err := chain.EthClient.PendingNonceAt(harness.Ctx, harness.SenderAddr) + req.NoError(err) + req.GreaterOrEqual(pending1, latest1) +} + +// TestJSONRPCSanity ensures core JSON-RPC calls behave as expected: +// - chainId and net_version match expected (1946 / 0x79a) +// - blockNumber advances over time +// - gasPrice >= baseFee and feeHistory returns sane values +// - estimateGas for a simple transfer is usable and the tx succeeds +func TestJSONRPCSanity(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + // chainId and net_version + cid, err := chain.EthClient.ChainID(harness.Ctx) + req.NoError(err) + req.Equal(utils.TestEVMChainID, cid.Uint64()) + + nid, err := chain.EthClient.NetworkID(harness.Ctx) + req.NoError(err) + req.Equal(utils.TestEVMChainID, nid.Uint64()) + + // Ensure at least one block has been produced + req.NoError(utils.WaitForBlocks(harness.Ctx, chain.EthClient, 1)) + + // gasPrice and fee history sanity + gp, err := chain.EthClient.SuggestGasPrice(harness.Ctx) + req.NoError(err) + req.NotNil(gp) + req.Positive(gp.Sign(), "gasPrice should be > 0") + + hdr, err := chain.EthClient.HeaderByNumber(harness.Ctx, nil) + req.NoError(err) + req.NotNil(hdr) + if hdr.BaseFee != nil { + req.Positive(hdr.BaseFee.Sign(), "baseFee should be > 0") + req.GreaterOrEqual(gp.Cmp(hdr.BaseFee), 0, "gasPrice %s < baseFee %s", gp.String(), hdr.BaseFee.String()) + } + + fh, err := chain.EthClient.FeeHistory(harness.Ctx, 4, nil, []float64{25, 50, 75}) + req.NoError(err) + req.NotNil(fh) + req.NotEmpty(fh.BaseFee) + for _, bf := range fh.BaseFee { + req.NotNil(bf) + req.GreaterOrEqual(bf.Sign(), 0) + } + + // estimateGas usable for a simple transfer, then send using that estimate + recvKey, err := crypto.GenerateKey() + req.NoError(err) + recipient := crypto.PubkeyToAddress(recvKey.PublicKey) + value := big.NewInt(1) // 1 wei + + est, err := chain.EthClient.EstimateGas(harness.Ctx, ethereum.CallMsg{From: harness.SenderAddr, To: &recipient, Value: value}) + req.NoError(err) + req.GreaterOrEqual(int(est), 21000) + req.Less(int(est), 100000) + + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &recipient, value, nil, est) + req.NoError(err) + + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) +} + +// TestJSONRPCSubscriptions_NewHeads subscribes to newHeads and asserts headers arrive. +func TestJSONRPCSubscriptions_NewHeads(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + ws := wsClientOrFail(harness.Ctx, t, chain.WS) + defer ws.Close() + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + headersCh := make(chan *types.Header, 16) + sub, err := ws.SubscribeNewHead(ctx, headersCh) + req.NoError(err) + defer sub.Unsubscribe() + + // Trigger a block by sending a trivial tx + recvKey, err := crypto.GenerateKey() + req.NoError(err) + recipient := crypto.PubkeyToAddress(recvKey.PublicKey) + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &recipient, big.NewInt(1), nil, utils.BasicTransferGas) + req.NoError(err) + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + + select { + case h := <-headersCh: + req.NotNil(h) + // basic sanity: non-nil number + req.NotNil(h.Number) + case err := <-sub.Err(): + req.NoError(err) + case <-ctx.Done(): + req.Fail("timeout waiting for new head via websocket subscription") + } +} + +// TestJSONRPCSubscriptions_Logs subscribes to logs, deploys Counter, emits an event, and asserts a log is received. +func TestJSONRPCSubscriptions_Logs(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + ws := wsClientOrFail(harness.Ctx, t, chain.WS) + defer ws.Close() + + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + parsedABI, err := abi.JSON(strings.NewReader(contracts.CounterABIJSON())) + req.NoError(err) + creation := common.FromHex(contracts.CounterBinHex()) + + // Deploy contract + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, nil, big.NewInt(0), creation, 0) + req.NoError(err) + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + contractAddr := rec.ContractAddress + + logsCh := make(chan types.Log, 16) + sub, err := ws.SubscribeFilterLogs(ctx, ethereum.FilterQuery{Addresses: []common.Address{contractAddr}}, logsCh) + req.NoError(err) + defer sub.Unsubscribe() + + // Call set(7) which emits ValueChanged + callData, err := parsedABI.Pack("set", big.NewInt(7)) + req.NoError(err) + txHash2, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &contractAddr, big.NewInt(0), callData, 0) + req.NoError(err) + rec2, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash2, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec2) + req.Equal(uint64(1), rec2.Status) + + // Wait for a matching log + for { + select { + case lg := <-logsCh: + if lg.Address == contractAddr && lg.TxHash == rec2.TxHash { + req.NotEmpty(lg.Topics) + return + } + // keep waiting until timeout or matching log + case err := <-sub.Err(): + req.NoError(err) + case <-ctx.Done(): + req.Fail("timeout waiting for contract event log via websocket subscription") + } + } +} + +// TestDebugTraceOnRevertedTx implements backlog item #11: +// - Send a tx that reverts +// - Call debug_traceTransaction on the tx hash +// - Assert structLogs contain the REVERT opcode +// - Skip if the debug API is unavailable +func TestDebugTraceOnRevertedTx(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + // Deploy Reverter contract + creation := common.FromHex(contracts.ReverterBinHex()) + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, nil, nil, creation, 0) + req.NoError(err) + + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + reverter := rec.ContractAddress + req.NotEqual(common.Address{}, reverter) + + // Prepare calldata that will revert with a reason + parsed, err := abi.JSON(strings.NewReader(contracts.ReverterABIJSON())) + req.NoError(err) + reason := "NOPE" + callData, err := parsed.Pack("revertWithReason", reason) + req.NoError(err) + + // Send a tx that reverts + txHash2, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &reverter, nil, callData, 100000) + req.NoError(err) + rec2, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash2, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec2) + req.Equal(uint64(0), rec2.Status) + + // Dial RPC and attempt debug_traceTransaction + rpcClient, err := rpc.DialContext(harness.Ctx, chain.RPC) + req.NoError(err, "RPC not reachable for debug endpoint") + defer rpcClient.Close() + + // Short timeout for the trace call + tctx, cancel := context.WithTimeout(harness.Ctx, 10*time.Second) + defer cancel() + + // Minimal trace result shape we care about + type structLog struct { + Op string `json:"op"` + } + type traceResult struct { + Failed bool `json:"failed"` + StructLogs []structLog `json:"structLogs"` + } + + var out traceResult + err = rpcClient.CallContext(tctx, &out, "debug_traceTransaction", rec2.TxHash.Hex(), map[string]any{}) + req.NoError(err, "debug_traceTransaction must be available and succeed") + + req.NotEmpty(out.StructLogs, "structLogs should not be empty for a reverted tx") + + // Find a REVERT opcode in the trace + foundRevert := false + for _, sl := range out.StructLogs { + if strings.EqualFold(sl.Op, "REVERT") { + foundRevert = true + break + } + } + req.True(foundRevert, "expected REVERT opcode in structLogs") +} + +// TestJSONRPCTxReceipts covers JSON-RPC tx and receipt lookups: +// - Send a type-2 transfer and wait for inclusion +// - Verify receipt fields (status, gasUsed, effectiveGasPrice >= baseFee) +// - Lookup via getTransactionByHash, byBlockHash+Index, byBlockNumber+Index +// - Verify from/to/nonce/type across all variants +func TestJSONRPCTxReceipts(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + recvKey, err := crypto.GenerateKey() + req.NoError(err) + recipient := crypto.PubkeyToAddress(recvKey.PublicKey) + + expectedNonce, err := chain.EthClient.PendingNonceAt(harness.Ctx, harness.SenderAddr) + req.NoError(err) + + value := big.NewInt(1_000_000_000_000_000_000) // 1e18 wei + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &recipient, value, nil, utils.BasicTransferGas) + req.NoError(err) + + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + req.GreaterOrEqual(int(rec.GasUsed), utils.BasicTransferGas) + + hdr, err := chain.EthClient.HeaderByNumber(harness.Ctx, rec.BlockNumber) + req.NoError(err) + req.NotNil(hdr) + if hdr.BaseFee != nil { + req.GreaterOrEqual(rec.EffectiveGasPrice.Cmp(hdr.BaseFee), 0) + } + + tx, isPending, err := chain.EthClient.TransactionByHash(harness.Ctx, txHash) + req.NoError(err) + req.False(isPending) + + chainID, err := chain.EthClient.ChainID(harness.Ctx) + req.NoError(err) + from, err := types.Sender(types.LatestSignerForChainID(chainID), tx) + req.NoError(err) + req.Equal(harness.SenderAddr, from) + + to := tx.To() + req.NotNil(to) + req.Equal(recipient, *to) + req.Equal(expectedNonce, tx.Nonce()) + req.Equal(uint8(types.DynamicFeeTxType), tx.Type()) + + rc, err := rpc.DialContext(harness.Ctx, chain.RPC) + req.NoError(err) + defer rc.Close() + + type rpcTx struct { + Hash common.Hash `json:"hash"` + From common.Address `json:"from"` + To *common.Address `json:"to"` + Nonce hexutil.Uint64 `json:"nonce"` + Type hexutil.Uint64 `json:"type"` + TransactionIndex hexutil.Uint64 `json:"transactionIndex"` + } + + idx := hexutil.Uint64(rec.TransactionIndex) + var byHashIdx rpcTx + err = rc.CallContext(harness.Ctx, &byHashIdx, "eth_getTransactionByBlockHashAndIndex", rec.BlockHash, idx) + req.NoError(err) + req.Equal(txHash, byHashIdx.Hash) + req.Equal(harness.SenderAddr, byHashIdx.From) + req.NotNil(byHashIdx.To) + req.Equal(recipient, *byHashIdx.To) + req.Equal(hexutil.Uint64(expectedNonce), byHashIdx.Nonce) + req.Equal(hexutil.Uint64(types.DynamicFeeTxType), byHashIdx.Type) + req.Equal(idx, byHashIdx.TransactionIndex) + + var byNumIdx rpcTx + bn := hexutil.Uint64(rec.BlockNumber.Uint64()) + err = rc.CallContext(harness.Ctx, &byNumIdx, "eth_getTransactionByBlockNumberAndIndex", bn, idx) + req.NoError(err) + req.Equal(txHash, byNumIdx.Hash) + req.Equal(harness.SenderAddr, byNumIdx.From) + req.NotNil(byNumIdx.To) + req.Equal(recipient, *byNumIdx.To) + req.Equal(hexutil.Uint64(expectedNonce), byNumIdx.Nonce) + req.Equal(hexutil.Uint64(types.DynamicFeeTxType), byNumIdx.Type) + req.Equal(idx, byNumIdx.TransactionIndex) + + rcvd, err := chain.EthClient.TransactionReceipt(harness.Ctx, txHash) + req.NoError(err) + req.NotNil(rcvd) + req.Equal(uint64(1), rcvd.Status) + req.Equal(txHash, rec.TxHash) + req.Equal(txHash, rcvd.TxHash) +} + +// TestJSONRPCTxpool submits a tx and verifies it appears in txpool.pending. +// This test fails if the txpool namespace is unavailable or if the tx is mined before inspection. +func TestJSONRPCTxpool(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + // Raw RPC client for txpool_* methods + rc, err := rpc.DialContext(harness.Ctx, chain.RPC) + req.NoError(err) + defer rc.Close() + + // Require txpool_status to be available + var status map[string]any + err = rc.CallContext(harness.Ctx, &status, "txpool_status") + req.NoError(err) + + // Submit a minimal transfer (1 wei) from funded sender to a fresh recipient + recvKey, err := crypto.GenerateKey() + req.NoError(err) + recipient := crypto.PubkeyToAddress(recvKey.PublicKey) + + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &recipient, big.NewInt(1), nil, utils.BasicTransferGas) + req.NoError(err) + t.Logf("submitted tx %s", txHash.Hex()) + + type poolContent struct { + Pending map[string]map[string]any `json:"pending"` + Queued map[string]map[string]any `json:"queued"` + } + + foundPending := false + foundQueued := false + deadline := time.Now().Add(1500 * time.Millisecond) + for time.Now().Before(deadline) { + var pc poolContent + err := rc.CallContext(harness.Ctx, &pc, "txpool_content") + req.NoError(err) + + for addr := range pc.Pending { + if strings.EqualFold(addr, harness.SenderAddr.Hex()) { + foundPending = true + break + } + } + if !foundPending { + for addr := range pc.Queued { + if strings.EqualFold(addr, harness.SenderAddr.Hex()) { + foundQueued = true + break + } + } + } + if foundPending { + break + } + time.Sleep(150 * time.Millisecond) + } + + // Require presence in txpool.pending + req.Truef(foundPending, "expected sender %s in txpool.pending; queuedSeen=%v tx=%s", harness.SenderAddr.Hex(), foundQueued, txHash.Hex()) +} + +// wsClientOrFail dials the websocket endpoint with a short timeout based on the base context. +func wsClientOrFail(ctx context.Context, t *testing.T, wsURL string) *ethclient.Client { + t.Helper() + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + c, err := ethclient.DialContext(ctx, wsURL) + require.NoErrorf(t, err, "failed to dial websocket endpoint at %s", wsURL) + return c +} diff --git a/evmd/e2e/precompile_test.go b/evmd/e2e/precompile_test.go new file mode 100644 index 000000000..c33e6d1ac --- /dev/null +++ b/evmd/e2e/precompile_test.go @@ -0,0 +1,370 @@ +package e2e + +import ( + "context" + "math/big" + "testing" + "time" + + evmcontracts "github.com/cosmos/evm/contracts" + "github.com/cosmos/evm/evmd/e2e/fixtures" + "github.com/cosmos/evm/evmd/e2e/testharness" + "github.com/cosmos/evm/evmd/e2e/utils" + bankpc "github.com/cosmos/evm/precompiles/bank" + bech32pc "github.com/cosmos/evm/precompiles/bech32" + distributionpc "github.com/cosmos/evm/precompiles/distribution" + ics02pc "github.com/cosmos/evm/precompiles/ics02" + stakingpc "github.com/cosmos/evm/precompiles/staking" + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/types/bech32" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v10/testing" +) + +// TestBankPrecompile verifies EVM -> bech32 flow by: +// - deriving a bech32 recipient from a fresh EVM address +// - querying bank precompile balances before and after +// - transferring 1e18 astake via the native ERC-20 mapping (WERC20) +// - asserting the bank balance delta equals 1e18 and receipt.status=1 +func TestBankPrecompile(t *testing.T) { + t.Skip("Skipping bank precompile test (since it is not currently integrated)") + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + const ( + amountWeiStr = "1000000000000000000" // 1e18 + ) + + bankPC := common.HexToAddress(utils.BankPrecompileAddr) + bech32PC := common.HexToAddress(utils.Bech32PrecompileAddr) + werc20 := common.HexToAddress(utils.WERC20Addr) + amount, ok := new(big.Int).SetString(amountWeiStr, 10) + req.Truef(ok, "invalid bank amount constant %s", amountWeiStr) + + // Load ABIs from the evm module without changing that repo + bankP := bankpc.NewPrecompile(nil, nil) + bankABI := bankP.ABI + + bech32P, err := bech32pc.NewPrecompile(1) // any non-zero base gas + req.NoError(err) + bech32ABI := bech32P.ABI + + // Use the compiled ERC20 contract ABI from evm/contracts + erc20ABI := evmcontracts.ERC20MinterBurnerDecimalsContract.ABI + + // Create a fresh EVM recipient and derive its bech32 form + recvKey, err := crypto.GenerateKey() + req.NoError(err) + recipient := crypto.PubkeyToAddress(recvKey.PublicKey) + recipientBech32, err := bech32.ConvertAndEncode(utils.TestBech32Prefix, recipient.Bytes()) + req.NoError(err) + req.NotEmpty(recipientBech32) + + // Sanity: bech32 -> hex via precompile + inB2H, err := bech32ABI.Pack("bech32ToHex", recipientBech32) + req.NoError(err) + resB2H, err := chain.EthClient.CallContract(harness.Ctx, ethereum.CallMsg{From: harness.SenderAddr, To: &bech32PC, Data: inB2H}, nil) + req.NoError(err) + outsB2H, err := bech32ABI.Unpack("bech32ToHex", resB2H) + req.NoError(err) + req.Len(outsB2H, 1) + gotAddr, ok := outsB2H[0].(common.Address) + req.True(ok) + req.Equal(recipient, gotAddr) + + // Helper types and finder + type balance struct { + ContractAddress common.Address + Amount *big.Int + } + findAmt := func(list []balance, token common.Address) *big.Int { + for _, b := range list { + if b.ContractAddress == token { + return new(big.Int).Set(b.Amount) + } + } + return big.NewInt(0) + } + + // Pre-transfer balances(recipient) + callBalances, err := bankABI.Pack("balances", recipient) + req.NoError(err) + outPre, err := chain.EthClient.CallContract(harness.Ctx, ethereum.CallMsg{From: harness.SenderAddr, To: &bankPC, Data: callBalances}, nil) + req.NoError(err) + var pre []balance + err = bankABI.UnpackIntoInterface(&pre, "balances", outPre) + req.NoError(err) + preAmt := findAmt(pre, werc20) + + // Transfer 1e18 astake via WERC20 to recipient + callTransfer, err := erc20ABI.Pack("transfer", recipient, amount) + req.NoError(err) + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &werc20, big.NewInt(0), callTransfer, 0) + req.NoError(err) + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + t.Logf("bank_precompile transfer tx=%s gasUsed=%d", txHash.Hex(), rec.GasUsed) + + // Post-transfer balances(recipient) + outPost, err := chain.EthClient.CallContract(harness.Ctx, ethereum.CallMsg{From: harness.SenderAddr, To: &bankPC, Data: callBalances}, nil) + req.NoError(err) + var post []balance + err = bankABI.UnpackIntoInterface(&post, "balances", outPost) + req.NoError(err) + postAmt := findAmt(post, werc20) + delta := new(big.Int).Sub(postAmt, preAmt) + req.Equalf(0, delta.Cmp(amount), "recipient bank delta %s != %s", delta.String(), amount.String()) +} + +// TestStakingPrecompile delegates and undelegates via the staking precompile, verifying delegation shares +// increase on delegate and return to baseline after the unbonding period completes. +func TestStakingPrecompile(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + const ( + stakingPrecompileAddr = "0x0000000000000000000000000000000000000800" + amountWeiStr = "1000000000000000000" // 1e18 + ) + + stakingAddr := common.HexToAddress(stakingPrecompileAddr) + amount, ok := new(big.Int).SetString(amountWeiStr, 10) + req.Truef(ok, "invalid staking amount constant %s", amountWeiStr) + + // Load staking ABI from the evm module + parsed := stakingpc.ABI + + // Discover bonded validator operator address via evmd CLI inside the validator container + queryClient := stakingtypes.NewQueryClient(chain.GrpcClient) + vr, err := queryClient.Validators(harness.Ctx, &stakingtypes.QueryValidatorsRequest{}) + req.NoError(err) + req.NotEmpty(vr.Validators, "no validators found") + valoper := vr.Validators[0].OperatorAddress + req.NotEmpty(valoper) + + // Helper: query current delegation shares for (delegator, validator) + getShares := func() *big.Int { + in, err := parsed.Pack("delegation", harness.SenderAddr, valoper) + req.NoError(err) + out, err := chain.EthClient.CallContract(harness.Ctx, ethereum.CallMsg{From: harness.SenderAddr, To: &stakingAddr, Data: in}, nil) + req.NoError(err) + vals, err := parsed.Unpack("delegation", out) + req.NoError(err) + req.Len(vals, 2) + // first return value is shares (uint256) + return new(big.Int).Set(vals[0].(*big.Int)) + } + + shares0 := getShares() + + // Delegate amount to validator + callDelegate, err := parsed.Pack("delegate", harness.SenderAddr, valoper, amount) + req.NoError(err) + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &stakingAddr, big.NewInt(0), callDelegate, 0) + req.NoError(err) + rec1, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.StakingReceiptTimeout) + req.NoError(err) + req.NotNil(rec1) + req.Equal(uint64(1), rec1.Status) + t.Logf("delegate tx=%s gasUsed=%d", txHash.Hex(), rec1.GasUsed) + + // Verify shares increased by exactly amount, scaled to staking share precision (1e18) + shares1 := getShares() + delta := new(big.Int).Sub(shares1, shares0) + dec1e18 := new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) + expectedSharesDelta := new(big.Int).Mul(amount, dec1e18) + req.Equalf(0, delta.Cmp(expectedSharesDelta), "shares delta %s != %s", delta.String(), expectedSharesDelta.String()) + + // Undelegate the same amount + callUndelegate, err := parsed.Pack("undelegate", harness.SenderAddr, valoper, amount) + req.NoError(err) + txHash2, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &stakingAddr, big.NewInt(0), callUndelegate, 0) + req.NoError(err) + rec2, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash2, utils.StakingReceiptTimeout) + req.NoError(err) + req.NotNil(rec2) + req.Equal(uint64(1), rec2.Status) + t.Logf("undelegate tx=%s gasUsed=%d", txHash2.Hex(), rec2.GasUsed) + + // Wait for unbonding completion (genesis set to 10s); give buffer and a couple of blocks + time.Sleep(utils.UnbondingWaitTime) + req.NoError(utils.WaitForBlocks(harness.Ctx, chain.EthClient, 2)) + + // Verify shares returned to baseline + shares2 := getShares() + req.Equalf(0, shares2.Cmp(shares0), "shares after unbonding %s != baseline %s", shares2.String(), shares0.String()) +} + +func TestICS02Precompile(t *testing.T) { + ctx := context.Background() + + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + const ( + ics02PrecompileAddr = "0x0000000000000000000000000000000000000807" + ) + + // Parse fixture + fixture, err := fixtures.LoadUpdateClientFixture("fixtures/update_client.json") + req.NoError(err) + + clientStateAny, err := fixture.ClientStateAny() + req.NoError(err) + consensusStateAny, err := fixture.ConsensusStateAny() + req.NoError(err) + + _, err = chain.BroadcastSdkMgs(ctx, harness.SenderKey, 10_000_000, &clienttypes.MsgCreateClient{ + ClientState: clientStateAny, + ConsensusState: consensusStateAny, + Signer: harness.SenderBech32, + }) + req.NoError(err) + + // 11) Wait for a few blocks + time.Sleep(3 * time.Second) + + // GetClientState Call + clientID := ibctesting.FirstClientID + ics02Addr := common.HexToAddress(ics02PrecompileAddr) + calldata, err := ics02pc.ABI.Pack(ics02pc.GetClientStateMethod, clientID) + req.NoError(err) + + // Call ICS02 precompile to get client state + out, err := chain.EthClient.CallContract(ctx, ethereum.CallMsg{From: harness.SenderAddr, To: &ics02Addr, Data: calldata}, nil) + req.NoError(err) + req.NotEmpty(out) + + // Prepare ICS02 precompile call data + updateHeaderAny, err := fixture.UpdateClientMessageAny() + req.NoError(err) + updateBz, err := updateHeaderAny.Marshal() + req.NoError(err) + + calldata, err = ics02pc.ABI.Pack(ics02pc.UpdateClientMethod, clientID, updateBz) + req.NoError(err) + + // Send transaction to ICS02 precompile + txHash, err := utils.SendTx(ctx, chain.EthClient, harness.SenderKey, &ics02Addr, big.NewInt(0), calldata, 0) + req.NoError(err) + rec, err := utils.WaitReceipt(ctx, chain.EthClient, txHash, utils.ICS02ReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + t.Logf("ics02 update_client tx=%s gasUsed=%d", txHash.Hex(), rec.GasUsed) +} + +// TestDistributionPrecompile tests delegating tokens and claiming rewards via the distribution precompile. +// This test verifies that: +// - A user can delegate tokens to a validator via the staking precompile +// - Rewards accumulate over time +// - The user can query their pending rewards +// - The user can successfully claim rewards via the distribution precompile +func TestDistributionPrecompile(t *testing.T) { + t.Parallel() + req := require.New(t) + harness := testharness.CreateHarness(t) + chain := harness.Chain + + const ( + stakingPrecompileAddr = "0x0000000000000000000000000000000000000800" + distributionPrecompileAddr = "0x0000000000000000000000000000000000000801" + amountWeiStr = "1000000000000000000" // 1e18 + ) + + stakingAddr := common.HexToAddress(stakingPrecompileAddr) + distributionAddr := common.HexToAddress(distributionPrecompileAddr) + amount, ok := new(big.Int).SetString(amountWeiStr, 10) + req.Truef(ok, "invalid staking amount constant %s", amountWeiStr) + + // Load ABIs + stakingABI := stakingpc.ABI + distributionABI := distributionpc.ABI + + // Get a bonded validator + queryClient := stakingtypes.NewQueryClient(chain.GrpcClient) + vr, err := queryClient.Validators(harness.Ctx, &stakingtypes.QueryValidatorsRequest{}) + req.NoError(err) + req.NotEmpty(vr.Validators, "no validators found") + valoper := vr.Validators[0].OperatorAddress + req.NotEmpty(valoper) + + // Step 1: Delegate tokens to the validator + callDelegate, err := stakingABI.Pack("delegate", harness.SenderAddr, valoper, amount) + req.NoError(err) + txHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &stakingAddr, big.NewInt(0), callDelegate, 0) + req.NoError(err) + rec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, txHash, utils.StakingReceiptTimeout) + req.NoError(err) + req.NotNil(rec) + req.Equal(uint64(1), rec.Status) + t.Logf("delegate tx=%s gasUsed=%d", txHash.Hex(), rec.GasUsed) + + // Step 2: Wait for some blocks to accumulate rewards + // In a real chain, rewards accumulate each block + time.Sleep(5 * time.Second) + req.NoError(utils.WaitForBlocks(harness.Ctx, chain.EthClient, 5)) + + // Step 3: Query delegation total rewards before claiming + callTotalRewards, err := distributionABI.Pack("delegationTotalRewards", harness.SenderAddr) + req.NoError(err) + rewardsOut, err := chain.EthClient.CallContract(harness.Ctx, ethereum.CallMsg{ + From: harness.SenderAddr, + To: &distributionAddr, + Data: callTotalRewards, + }, nil) + req.NoError(err) + req.NotEmpty(rewardsOut, "expected non-empty rewards response") + + // Unpack and validate rewards are greater than 0 + rewardsVals, err := distributionABI.Unpack("delegationTotalRewards", rewardsOut) + req.NoError(err) + req.NotEmpty(rewardsVals, "expected rewards values") + t.Logf("Rewards response: %+v", rewardsVals) + + // The response returns [rewards_per_validator, total_rewards] + // where total_rewards is the second element - an array of {Denom, Amount, Precision} + req.GreaterOrEqual(len(rewardsVals), 2, "expected at least 2 return values from delegationTotalRewards") + + // Get the total rewards (second return value) + totalRewards, ok := rewardsVals[1].([]struct { + Denom string `json:"denom"` + Amount *big.Int `json:"amount"` + Precision uint8 `json:"precision"` + }) + req.True(ok, "expected total rewards to be array of coins, got %T", rewardsVals[1]) + req.NotEmpty(totalRewards, "expected at least one total reward") + + // Validate all reward amounts are > 0 + for _, reward := range totalRewards { + req.Positive(reward.Amount.Cmp(big.NewInt(0)), + "reward for denom %s should be greater than 0, got %s", + reward.Denom, reward.Amount.String()) + t.Logf("Total reward for %s: %s (precision: %d)", reward.Denom, reward.Amount.String(), reward.Precision) + } + + // Step 4: Claim rewards from all validators (maxRetrieve=1 since we only delegated to one) + callClaimRewards, err := distributionABI.Pack("claimRewards", harness.SenderAddr, uint32(1)) + req.NoError(err) + claimTxHash, err := utils.SendTx(harness.Ctx, chain.EthClient, harness.SenderKey, &distributionAddr, big.NewInt(0), callClaimRewards, 0) + req.NoError(err) + claimRec, err := utils.WaitReceipt(harness.Ctx, chain.EthClient, claimTxHash, utils.StakingReceiptTimeout) + req.NoError(err) + req.NotNil(claimRec) + req.Equal(uint64(1), claimRec.Status, "claim rewards transaction should succeed") + t.Logf("claim_rewards tx=%s gasUsed=%d status=%d", claimTxHash.Hex(), claimRec.GasUsed, claimRec.Status) + t.Logf("Successfully claimed rewards from validator %s", valoper) +} diff --git a/evmd/e2e/testharness/chain.go b/evmd/e2e/testharness/chain.go new file mode 100644 index 000000000..c6b84844e --- /dev/null +++ b/evmd/e2e/testharness/chain.go @@ -0,0 +1,534 @@ +package testharness + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "time" + + "github.com/cosmos/evm/evmd/app/params" + "github.com/cosmos/evm/evmd/e2e/utils" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/google/uuid" + "github.com/pelletier/go-toml/v2" + tc "github.com/testcontainers/testcontainers-go" + tcexec "github.com/testcontainers/testcontainers-go/exec" + tcnetwork "github.com/testcontainers/testcontainers-go/network" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +type Chain struct { + Container tc.Container + Network *tc.DockerNetwork + + RPC string + WS string + EthClient *ethclient.Client + GrpcClient *grpc.ClientConn + EVMEncodingConfig params.EncodingConfig + + HomeDir string + + keepContainers bool +} + +type Options struct { + RepoRoot string + ChainID string + EVMChainID uint64 + Bech32Prefix string + Denom string + DisplayDenom string + SenderHex string + SenderBech32 string + FundSender bool + ConsensusTimeout map[string]string + UnbondingTime time.Duration + EnableRPC bool + RPCAPIs []string + ValidatorAmount string // e.g. "5000000000000000000astake" + SenderAmount string // e.g. "2000000000000000000astake" + SelfDelegation string // e.g. "1000000000000000000astake" +} + +func NewChain() *Chain { + keepContainers := false + if os.Getenv("KEEP_CONTAINERS") == "true" { + keepContainers = true + os.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") // nolint:revive + } + return &Chain{HomeDir: "/data", keepContainers: keepContainers, EVMEncodingConfig: evmdEncodingConfig()} +} + +func (c *Chain) Start(ctx context.Context, opts Options) error { + // Create isolated test network (for future multi-validator support) + net, err := tcnetwork.New(ctx, tcnetwork.WithAttachable()) + if err != nil { + return err + } + c.Network = net + + req := tc.ContainerRequest{ + Name: fmt.Sprintf("evmd-e2e-node-%s", uuid.NewString()), + Image: "evmd:local", + Env: map[string]string{ + "EVMD_HOME": c.HomeDir, + "DAEMON_HOME": c.HomeDir, + }, + Networks: []string{c.Network.Name}, + ExposedPorts: []string{ + // TODO: seems like we could pull out some consts for the ports (used elsewhere too) + "8545/tcp", // HTTP JSON-RPC + "8546/tcp", // WS JSON-RPC + "9090/tcp", // gRPC + }, + Entrypoint: []string{"tail", "-f", "/dev/null"}, // TODO: Is this necessary? + } + container, err := tc.GenericContainer(ctx, tc.GenericContainerRequest{ContainerRequest: req, Started: true}) + if err != nil { + c.cleanup(ctx) + return err + } + c.Container = container + + host, err := container.Host(ctx) + if err != nil { + c.cleanup(ctx) + return err + } + + chainID := opts.ChainID + if chainID == "" { + chainID = utils.TestChainID + } + + // 4) Initialize chain home and keys + if err := c.execArgsOK(ctx, []string{"evmd", "init", "validator", "--chain-id", chainID, "--home", c.HomeDir}); err != nil { + c.cleanup(ctx) + return err + } + if err := c.execArgsOK(ctx, []string{"evmd", "keys", "add", "val", "--keyring-backend", "test", "--home", c.HomeDir, "--output", "json"}); err != nil { + c.cleanup(ctx) + return err + } + + // 5) Add genesis accounts (validator + optional sender) + outShow, _, err := c.execArgsOut(ctx, []string{"evmd", "keys", "show", "val", "-a", "--keyring-backend", "test", "--home", c.HomeDir}) + valAddr := outShow + if err != nil { + c.cleanup(ctx) + return err + } + valAddr = strings.TrimSpace(valAddr) + + valAmt := opts.ValidatorAmount + if valAmt == "" { + valAmt = "5000000000000000000" + opts.Denom // 5e18 + } + if err := c.execArgsOK(ctx, []string{"evmd", "genesis", "add-genesis-account", valAddr, valAmt, "--home", c.HomeDir}); err != nil { + c.cleanup(ctx) + return err + } + + if opts.FundSender && opts.SenderBech32 != "" { + sendAmt := opts.SenderAmount + if sendAmt == "" { + sendAmt = "2000000000000000000" + opts.Denom // 2e18 + } + if err := c.execArgsOK(ctx, []string{"evmd", "genesis", "add-genesis-account", opts.SenderBech32, sendAmt, "--home", c.HomeDir}); err != nil { + c.cleanup(ctx) + return err + } + } + + // 6) Mutate genesis (EVM denom, precompiles, WERC20 mapping, staking unbonding time) + if err := c.mutateGenesis(ctx, opts); err != nil { + c.cleanup(ctx) + return err + } + + // 7) Generate/collect gentx + selfDel := opts.SelfDelegation + if selfDel == "" { + selfDel = "1000000000000000000" + opts.Denom // 1e18 + } + if err := c.execArgsOK(ctx, []string{"evmd", "genesis", "gentx", "val", selfDel, "--chain-id", chainID, "--keyring-backend", "test", "--home", c.HomeDir}); err != nil { + c.cleanup(ctx) + return err + } + if err := c.execArgsOK(ctx, []string{"evmd", "genesis", "collect-gentxs", "--home", c.HomeDir}); err != nil { + c.cleanup(ctx) + return err + } + + // Re-apply genesis mutation after gentx collection to ensure settings persist + if err := c.mutateGenesis(ctx, opts); err != nil { + c.cleanup(ctx) + return err + } + + // 8) Patch app.toml and config.toml + if err := c.patchConfigs(ctx, opts); err != nil { + c.cleanup(ctx) + return err + } + + // 9) Start evmd in background + // include --chain-id so BaseApp matches genesis (avoids handshake chain-id mismatch) + startCmd := fmt.Sprintf( + "evmd start --home %s --chain-id %s --evm.evm-chain-id %d --mempool.max-txs=0 > %s/start.log 2>&1 & echo $! > %s/evmd.pid", + c.HomeDir, + chainID, + opts.EVMChainID, + c.HomeDir, + c.HomeDir, + ) + if err := c.execArgsOK(ctx, []string{"sh", "-c", startCmd}); err != nil { + c.cleanup(ctx) + return err + } + + // 10) Resolve mapped ports and build URLs + rpcMapped, err := container.MappedPort(ctx, "8545/tcp") + if err != nil { + c.cleanup(ctx) + return err + } + wsMapped, err := container.MappedPort(ctx, "8546/tcp") + if err != nil { + c.cleanup(ctx) + return err + } + grpcMapped, err := container.MappedPort(ctx, "9090/tcp") + if err != nil { + c.cleanup(ctx) + return err + } + + grpcCli, err := grpc.NewClient(fmt.Sprintf("%s:%s", host, grpcMapped.Port()), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + c.cleanup(ctx) + return err + } + + c.RPC = fmt.Sprintf("http://%s:%s", host, rpcMapped.Port()) // nolint:revive + c.WS = fmt.Sprintf("ws://%s:%s", host, wsMapped.Port()) // nolint:revive + c.GrpcClient = grpcCli + + // Initialize EthClient + ethClient, err := ethclient.Dial(c.RPC) + if err != nil { + c.cleanup(ctx) + return err + } + c.EthClient = ethClient + + // 11) Wait for JSON-RPC ready then a few blocks + if err := utils.WaitForBlocks(ctx, c.EthClient, 2); err != nil { + c.cleanup(ctx) + return err + } + + return nil +} + +func (c *Chain) Stop(ctx context.Context) { c.cleanup(ctx) } + +// ExecQuery runs `evmd query -o json --home ` inside the container. +func (c *Chain) ExecQuery(ctx context.Context, args ...string) (string, string, error) { + full := append([]string{"evmd", "query"}, append(args, "-o", "json", "--home", c.HomeDir)...) + out, exit, err := c.execArgsOut(ctx, full) + if err != nil { + return "", out, err + } + if exit != 0 { + return "", out, fmt.Errorf("non-zero exit: %d", exit) + } + return out, "", nil +} + +// Exec runs the provided command inside the validator container and returns combined stdout/stderr. +func (c *Chain) Exec(ctx context.Context, args ...string) (string, error) { + out, exit, err := c.execArgsOut(ctx, args) + if err != nil { + return "", err + } + if exit != 0 { + return out, fmt.Errorf("exec failed (%d): %s", exit, strings.TrimSpace(out)) + } + return out, nil +} + +// internals + +func (c *Chain) execArgsOK(ctx context.Context, args []string) error { + exit, reader, err := c.Container.Exec(ctx, args, tcexec.Multiplexed()) + if err != nil { + return err + } + b, _ := io.ReadAll(reader) + if exit != 0 { + return fmt.Errorf("exec failed (%d): %s", exit, string(b)) + } + return nil +} + +func (c *Chain) execArgsOut(ctx context.Context, args []string) (string, int, error) { + exit, reader, err := c.Container.Exec(ctx, args, tcexec.Multiplexed()) + if err != nil { + return "", 0, err + } + b, _ := io.ReadAll(reader) + return string(b), exit, nil +} + +func (c *Chain) copyFileFromContainer(ctx context.Context, src string) ([]byte, error) { + r, err := c.Container.CopyFileFromContainer(ctx, src) + if err != nil { + return nil, err + } + defer r.Close() + b, err := io.ReadAll(r) + if err != nil { + return nil, err + } + return b, nil +} + +// CopyFileFromContainer is an exported wrapper to allow callers outside this package to fetch files. +func (c *Chain) CopyFileFromContainer(ctx context.Context, src string) ([]byte, error) { + return c.copyFileFromContainer(ctx, src) +} + +// dumpStacks sends SIGQUIT to the evmd process to dump goroutine stacks into start.log. +// Errors are ignored to avoid interfering with test shutdown. +func (c *Chain) dumpStacks(ctx context.Context) { + if c.Container == nil { + return + } + _, _, _ = c.execArgsOut(ctx, []string{"sh", "-lc", "kill -QUIT $(cat /data/evmd.pid) 2>/dev/null || true"}) +} + +func (c *Chain) copyFileToContainer(ctx context.Context, dstAbs string, data []byte, mode int64) error { + return c.Container.CopyToContainer(ctx, data, dstAbs, mode) +} + +// Mutate genesis via existing utils.MutateGenesisInMemory +func (c *Chain) mutateGenesis(ctx context.Context, opts Options) error { + path := filepath.Join(c.HomeDir, "config", "genesis.json") + bz, err := c.copyFileFromContainer(ctx, path) + if err != nil { + return err + } + var g map[string]any + if err := json.Unmarshal(bz, &g); err != nil { + return err + } + appState := ensureMap(g, "app_state") + + // EVM params + evm := ensureMap(appState, "evm") + evmParams := ensureMap(evm, "params") + evmParams["evm_denom"] = opts.Denom + // TODO: Why do I need this? + evmParams["active_static_precompiles"] = []any{ + "0x0000000000000000000000000000000000000100", + "0x0000000000000000000000000000000000000400", + "0x0000000000000000000000000000000000000800", + "0x0000000000000000000000000000000000000801", + "0x0000000000000000000000000000000000000802", + "0x0000000000000000000000000000000000000803", + "0x0000000000000000000000000000000000000804", + "0x0000000000000000000000000000000000000805", + "0x0000000000000000000000000000000000000806", + "0x0000000000000000000000000000000000000807", + } + + // Staking params: align bond_denom and shorten unbonding_time for e2e + staking := ensureMap(appState, "staking") + stakingParams := ensureMap(staking, "params") + stakingParams["bond_denom"] = opts.Denom + stakingParams["unbonding_time"] = "10s" + + // Bank genesis: set denom metadata for gas token + bank := ensureMap(appState, "bank") + + // Standard metadata fields + name := "evmd" + symbol := "STAKE" + md := banktypes.Metadata{ + Description: "Native 18-decimal denom metadata for evmd chain", + Base: opts.Denom, + DenomUnits: []*banktypes.DenomUnit{ + {Denom: opts.Denom, Exponent: 0}, + {Denom: opts.DisplayDenom, Exponent: 18}, + }, + Name: name, + Symbol: symbol, + Display: opts.DisplayDenom, + } + bank["denom_metadata"] = []banktypes.Metadata{md} + + out, err := json.MarshalIndent(g, "", " ") + if err != nil { + return err + } + return c.copyFileToContainer(ctx, path, out, 0o644) +} + +func ensureMap(parent map[string]any, key string) map[string]any { + if v, ok := parent[key]; ok { + if mm, ok := v.(map[string]any); ok { + return mm + } + } + child := make(map[string]any) + parent[key] = child + return child +} + +func (c *Chain) patchConfigs(ctx context.Context, opts Options) error { + // app.toml + appPath := filepath.Join(c.HomeDir, "config", "app.toml") + appBz, err := c.copyFileFromContainer(ctx, appPath) + if err != nil { + return err + } + app := map[string]any{} + if err := toml.Unmarshal(appBz, &app); err != nil { + return err + } + + // enable Cosmos API + unsafe CORS (harmless for tests) + if sec, ok := app["api"].(map[string]any); ok { + sec["enable"] = true + sec["enabled-unsafe-cors"] = true + app["api"] = sec + } else { + app["api"] = map[string]any{"enable": true, "enabled-unsafe-cors": true} + } + + // enable JSON-RPC + if opts.EnableRPC { + jr, ok := app["json-rpc"].(map[string]any) + if !ok { + jr = map[string]any{} + } + jr["enable"] = true + jr["address"] = "0.0.0.0:8545" + jr["ws-address"] = "0.0.0.0:8546" + apis := opts.RPCAPIs + if len(apis) == 0 { + apis = []string{"eth", "txpool", "personal", "net", "debug", "web3"} + } + jr["api"] = strings.Join(apis, ",") + app["json-rpc"] = jr + } + + // enable gRPC and bind to all interfaces for host access + if sec, ok := app["grpc"].(map[string]any); ok { + sec["enable"] = true + sec["address"] = "0.0.0.0:9090" + app["grpc"] = sec + } else { + app["grpc"] = map[string]any{"enable": true, "address": "0.0.0.0:9090"} + } + + // minimum-gas-prices for SDK (EVM txs not affected, but safe to set low) + if opts.Denom != "" { + app["minimum-gas-prices"] = "0" + opts.Denom + } + + newAppBz, err := toml.Marshal(app) + if err != nil { + return err + } + if err := c.copyFileToContainer(ctx, appPath, newAppBz, 0o644); err != nil { + return err + } + + // config.toml + cfgPath := filepath.Join(c.HomeDir, "config", "config.toml") + cfgBz, err := c.copyFileFromContainer(ctx, cfgPath) + if err != nil { + return err + } + cfg := map[string]any{} + if err := toml.Unmarshal(cfgBz, &cfg); err != nil { + return err + } + + if tm, ok := cfg["consensus"].(map[string]any); ok { + for k, v := range opts.ConsensusTimeout { + tm[k] = v + } + cfg["consensus"] = tm + } + if rpc, ok := cfg["rpc"].(map[string]any); ok { + rpc["laddr"] = "tcp://0.0.0.0:26657" + cfg["rpc"] = rpc + } + if p2p, ok := cfg["p2p"].(map[string]any); ok { + p2p["laddr"] = "tcp://0.0.0.0:26656" + cfg["p2p"] = p2p + } + + newCfgBz, err := toml.Marshal(cfg) + if err != nil { + return err + } + return c.copyFileToContainer(ctx, cfgPath, newCfgBz, 0o644) +} + +func (c *Chain) cleanup(ctx context.Context) { + if c.keepContainers { + return + } + + // Attempt to copy artifacts before tearing down the container + if c.Container != nil { + c.dumpStacks(ctx) + c.saveArtifacts(ctx) + + _ = c.Container.Terminate(ctx) // errors are ignored: it is likely already terminated, we just do this for good measure + } + + if c.Network != nil { + if err := c.Network.Remove(ctx); err != nil { + fmt.Printf("evmd e2e: failed removing network: %v\n", err) + } + } +} + +// saveArtifacts copies useful runtime artifacts from the container to a host temp dir. +// Best-effort; errors are printed but do not fail cleanup. +func (c *Chain) saveArtifacts(ctx context.Context) { + dir, err := os.MkdirTemp("", "evmd-e2e-*") + if err != nil { + fmt.Printf("evmd e2e: failed to create temp dir for artifacts: %v\n", err) + return + } + + saveArtifact := func(src, dstName string) { + b, err := c.copyFileFromContainer(ctx, src) + if err != nil { + fmt.Printf("evmd e2e: failed copying %s: %v\n", src, err) + return + } + if werr := os.WriteFile(filepath.Join(dir, dstName), b, 0o600); werr != nil { + fmt.Printf("evmd e2e: failed writing %s: %v\n", dstName, werr) + } + } + + saveArtifact(filepath.Join(c.HomeDir, "start.log"), "start.log") + saveArtifact(filepath.Join(c.HomeDir, "config", "genesis.json"), "genesis.json") + + fmt.Printf("evmd e2e artifacts saved: %s\n", dir) +} diff --git a/evmd/e2e/testharness/evmd.go b/evmd/e2e/testharness/evmd.go new file mode 100644 index 000000000..996f97178 --- /dev/null +++ b/evmd/e2e/testharness/evmd.go @@ -0,0 +1,132 @@ +package testharness + +import ( + "context" + "crypto/ecdsa" + "fmt" + + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/evm/crypto/ethsecp256k1" + eapp "github.com/cosmos/evm/evmd/app" + "github.com/cosmos/evm/evmd/app/params" + "github.com/cosmos/evm/evmd/e2e/utils" + "github.com/ethereum/go-ethereum/crypto" + + "cosmossdk.io/log" + + clienttx "github.com/cosmos/cosmos-sdk/client/tx" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +func evmdEncodingConfig() params.EncodingConfig { + // we "pre"-instantiate the application for getting the injected/configured encoding configuration + tempApp := eapp.New( + log.NewNopLogger(), + dbm.NewMemDB(), + nil, + true, + simtestutil.EmptyAppOptions{}, + ) + return params.EncodingConfig{ + InterfaceRegistry: tempApp.InterfaceRegistry(), + Codec: tempApp.AppCodec(), + TxConfig: tempApp.GetTxConfig(), + Amino: tempApp.LegacyAmino(), + } +} + +func (c *Chain) BroadcastSdkMgs(ctx context.Context, key *ecdsa.PrivateKey, gas uint64, msgs ...sdk.Msg) (*sdk.TxResponse, error) { + txBuilder := c.EVMEncodingConfig.TxConfig.NewTxBuilder() + + if err := txBuilder.SetMsgs(msgs...); err != nil { + return nil, err + } + txBuilder.SetGasLimit(gas) + + // TODO: This seems like a very high fee, but otherwise txs get rejected for insufficient fee + // This should be configured better and calculated based on `gas` + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin(utils.TestDenom, 10_000_000_000_000_000))) + + // Sign Tx + pKey := ethsecp256k1.PrivKey{Key: crypto.FromECDSA(key)} + senderAddr := crypto.PubkeyToAddress(key.PublicKey) + senderBech32, err := bech32.ConvertAndEncode(utils.TestBech32Prefix, senderAddr.Bytes()) + if err != nil { + return nil, err + } + + authQueryClient := authtypes.NewQueryClient(c.GrpcClient) + res, err := authQueryClient.AccountInfo(ctx, &authtypes.QueryAccountInfoRequest{Address: senderBech32}) + if err != nil { + return nil, fmt.Errorf("failed to get account info: %w", err) + } + + signingData := authsigning.SignerData{ + Address: senderBech32, + ChainID: utils.TestChainID, + AccountNumber: res.Info.AccountNumber, + Sequence: res.Info.Sequence, + PubKey: pKey.PubKey(), + } + + // First round: we gather all the signer infos. We use the "set empty + // signature" hack to do that. + sigV2Empty := signing.SignatureV2{ + PubKey: pKey.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_DIRECT, + Signature: nil, + }, + Sequence: res.Info.Sequence, + } + if err := txBuilder.SetSignatures(sigV2Empty); err != nil { + return nil, fmt.Errorf("failed to set empty tx signature: %w", err) + } + + sigV2, err := clienttx.SignWithPrivKey( + ctx, signing.SignMode_SIGN_MODE_DIRECT, signingData, + txBuilder, &pKey, c.EVMEncodingConfig.TxConfig, + res.Info.Sequence, + ) + if err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + if err := txBuilder.SetSignatures(sigV2); err != nil { + return nil, fmt.Errorf("failed to set tx signature: %w", err) + } + + // Broadcast Tx + + txJSONBytes, _ := c.EVMEncodingConfig.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + fmt.Println("Broadcasting transaction:", string(txJSONBytes)) + + txBytes, err := c.EVMEncodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return nil, fmt.Errorf("failed to encode tx: %w", err) + } + + txClient := tx.NewServiceClient(c.GrpcClient) + grpcRes, err := txClient.BroadcastTx( + ctx, + &tx.BroadcastTxRequest{ + Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, // Proto-binary of the signed transaction, see previous step. + }, + ) + if err != nil { + return nil, fmt.Errorf("failed to broadcast tx: %w", err) + } + + if grpcRes.TxResponse.Code != 0 { + return nil, fmt.Errorf("tx failed with code %d: %s", grpcRes.TxResponse.Code, grpcRes.TxResponse.RawLog) + } + + return grpcRes.TxResponse, nil +} diff --git a/evmd/e2e/testharness/harness.go b/evmd/e2e/testharness/harness.go new file mode 100644 index 000000000..fa9280b65 --- /dev/null +++ b/evmd/e2e/testharness/harness.go @@ -0,0 +1,97 @@ +package testharness + +import ( + "context" + "crypto/ecdsa" + "testing" + "time" + + "github.com/cosmos/evm/evmd/e2e/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/types/bech32" +) + +// Harness is a per-test environment that mirrors the prior EVMSuite fields. +// Each test should call NewEnv(t) to provision its own isolated chain instance. +// No shared state across tests remains. +type Harness struct { + Chain *Chain + + SenderKey *ecdsa.PrivateKey + SenderAddr common.Address + SenderBech32 string + + Ctx context.Context + Cancel context.CancelFunc +} + +// CreateHarness provisions a fresh harness and JSON-RPC client for a single test. +func CreateHarness(t *testing.T) *Harness { + t.Helper() + req := require.New(t) + + ctx, cancel := context.WithTimeout(context.Background(), utils.TestSetupTimeout) + + // Generate a funded sender (funded via harness genesis mutation) + k, err := crypto.GenerateKey() + req.NoError(err) + senderAddr := crypto.PubkeyToAddress(k.PublicKey) + senderBech32, err := bech32.ConvertAndEncode(utils.TestBech32Prefix, senderAddr.Bytes()) + req.NoError(err) + + c := NewChain() + err = c.Start(ctx, Options{ + RepoRoot: "..", // tests run from e2e/, Dockerfile lives one level up + ChainID: utils.TestChainID, + EVMChainID: utils.TestEVMChainID, + Bech32Prefix: utils.TestBech32Prefix, + Denom: utils.TestDenom, + DisplayDenom: utils.DisplayDenom, + SenderHex: senderAddr.Hex(), + SenderBech32: senderBech32, + FundSender: true, + ValidatorAmount: "5000000000000000000" + utils.TestDenom, // 5e18 + SenderAmount: "2000000000000000000" + utils.TestDenom, // 2e18 + SelfDelegation: "1000000000000000000" + utils.TestDenom, // 1e18 + EnableRPC: true, + RPCAPIs: []string{"eth", "txpool", "personal", "net", "debug", "web3"}, + UnbondingTime: 10 * time.Second, + ConsensusTimeout: map[string]string{ + "timeout_propose": "2s", + "timeout_propose_delta": "200ms", + "timeout_prevote": "500ms", + "timeout_prevote_delta": "200ms", + "timeout_precommit": "500ms", + "timeout_precommit_delta": "200ms", + "timeout_commit": "1s", + "timeout_broadcast_tx_commit": "5s", + }, + }) + req.NoError(err, "failed to start harness") + + h := &Harness{ + Ctx: ctx, + Cancel: cancel, + Chain: c, + SenderKey: k, + SenderAddr: senderAddr, + SenderBech32: senderBech32, + } + + // Ensure teardown happens even if the test fails + t.Cleanup(func() { + // Attempt to capture useful artifacts before stopping the container + if h.Chain != nil { + h.Chain.cleanup(h.Ctx) + } + + if h.Cancel != nil { + h.Cancel() + } + }) + + return h +} diff --git a/evmd/e2e/utils/constants.go b/evmd/e2e/utils/constants.go new file mode 100644 index 000000000..eef204f0e --- /dev/null +++ b/evmd/e2e/utils/constants.go @@ -0,0 +1,39 @@ +package utils + +import "time" + +const ( + // TestSetupTimeout is the maximum time allowed for test environment setup + TestSetupTimeout = 8 * time.Minute + + // ReceiptTimeout is the maximum time to wait for transaction receipts + ReceiptTimeout = 20 * time.Second + + // RevertReasonTimeout is the timeout for RPC calls to extract revert reasons + RevertReasonTimeout = 5 * time.Second + + // WaitReceiptPollInterval is how often to check for transaction receipts + WaitReceiptPollInterval = 200 * time.Millisecond + + // Extended timeouts for special cases + ICS02ReceiptTimeout = 12 * time.Second + StakingReceiptTimeout = 30 * time.Second + UnbondingWaitTime = 12 * time.Second + ShortReceiptTimeout = 5 * time.Second + FailureReceiptTimeout = 4 * time.Second + + // Gas constants + BasicTransferGas = 21000 // Standard gas cost for basic ETH transfers + + // Precompile addresses + BankPrecompileAddr = "0x0000000000000000000000000000000000000804" + Bech32PrecompileAddr = "0x0000000000000000000000000000000000000400" + WERC20Addr = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + + // Test chain configuration (shared across e2e tests) + TestChainID = "evmd-1" + TestEVMChainID uint64 = 19460 + TestBech32Prefix = "evmd" + TestDenom = "astake" + DisplayDenom = "stake" +) diff --git a/evmd/e2e/utils/eth_client.go b/evmd/e2e/utils/eth_client.go new file mode 100644 index 000000000..e67fa29e7 --- /dev/null +++ b/evmd/e2e/utils/eth_client.go @@ -0,0 +1,185 @@ +package utils + +import ( + "context" + "crypto/ecdsa" + "errors" + "math/big" + "strings" + "time" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" +) + +const ( + defaultGasLimit = 300000 + gasPaddingPercent = 20 // 20% padding for gas estimation + gasPaddingFixed = 10000 +) + +// NewClient dials the JSON-RPC endpoint. +func NewClient(ctx context.Context, rpcURL string) (*ethclient.Client, error) { + return ethclient.DialContext(ctx, rpcURL) +} + +// GetBalance returns the latest balance (wei) for the given address. +func GetBalance(ctx context.Context, c *ethclient.Client, addr common.Address) (*big.Int, error) { + return c.BalanceAt(ctx, addr, nil) +} + +// WaitReceipt polls for a transaction receipt until the deadline. +func WaitReceipt(ctx context.Context, c *ethclient.Client, txHash common.Hash, deadline time.Duration) (*types.Receipt, error) { + var receipt *types.Receipt + var receiptErr error + + err := WaitForCondition(ctx, func() (bool, string, error) { + // Try to get the receipt + rec, err := c.TransactionReceipt(ctx, txHash) + if err == nil && rec != nil { + receipt = rec + receiptErr = nil + return true, "receipt found", nil + } + // Check if this is a genuine error (not just "not found"). Some RPC backends may intermittently time out + // individual requests even while the node is healthy. Treat such timeouts as transient and keep polling + // until the overall deadline expires. + if err != nil && !isTransientReceiptError(err) { + receiptErr = err + return true, "", err // Return true to stop polling on genuine errors + } + return false, "receipt not found yet", nil + }, WaitReceiptPollInterval, deadline) + if err != nil { + if receiptErr != nil { + return nil, receiptErr + } + return nil, err + } + + return receipt, nil +} + +func isTransientReceiptError(err error) bool { + if errors.Is(err, ethereum.NotFound) { + return true + } + if errors.Is(err, context.DeadlineExceeded) { + return true + } + msg := err.Error() + return strings.Contains(msg, "request timed out") || + strings.Contains(msg, "context deadline exceeded") || + strings.Contains(msg, "Client.Timeout exceeded") +} + +// BuildSignedTx constructs and signs an EIP-1559 transaction. +// - If explicitNonce == nil, the account's pending nonce is used. +// - If gas == 0, gas is estimated and padded with a safety margin. +// - If to == nil, the tx is treated as contract creation using data as init code. +func BuildSignedTx( + ctx context.Context, + c *ethclient.Client, + priv *ecdsa.PrivateKey, + to *common.Address, + value *big.Int, + data []byte, + gas uint64, + explicitNonce *uint64, +) (*types.Transaction, error) { + from := crypto.PubkeyToAddress(priv.PublicKey) + + chainID, err := c.ChainID(ctx) + if err != nil { + return nil, err + } + + // Resolve nonce + var nonce uint64 + if explicitNonce != nil { + nonce = *explicitNonce + } else { + n, err := c.PendingNonceAt(ctx, from) + if err != nil { + return nil, err + } + nonce = n + } + + // Resolve gas limit + if gas == 0 { + msg := ethereum.CallMsg{From: from, To: to, Value: value, Data: data} + g, err := c.EstimateGas(ctx, msg) + if err != nil || g == 0 { + gas = defaultGasLimit + } else { + gas = g + g*gasPaddingPercent/100 + gasPaddingFixed + } + } + + // Tip cap with fallback + tipCap, err := c.SuggestGasTipCap(ctx) + if err != nil || tipCap == nil || tipCap.Sign() <= 0 { + tipCap = big.NewInt(1_000_000_000) // 1 gwei fallback + } + + // Base fee (tolerate missing header/basefee by treating as zero) + hdr, err := c.HeaderByNumber(ctx, nil) + if err != nil { + return nil, err + } + baseFee := new(big.Int) + if hdr != nil && hdr.BaseFee != nil { + baseFee.Set(hdr.BaseFee) + } + + val := big.NewInt(0) + if value != nil { + val = new(big.Int).Set(value) + } + + // feeCap = 2*baseFee + tipCap (or tipCap if baseFee==0) + feeCap := new(big.Int).Set(tipCap) + if baseFee.Sign() > 0 { + feeCap = new(big.Int).Add(new(big.Int).Mul(baseFee, big.NewInt(2)), tipCap) + } + + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + To: to, + Value: val, + Gas: gas, + GasFeeCap: new(big.Int).Set(feeCap), + GasTipCap: new(big.Int).Set(tipCap), + Data: append([]byte(nil), data...), + }) + + signer := types.LatestSignerForChainID(chainID) + return types.SignTx(tx, signer, priv) +} + +// SendTx sends a transaction using EIP-1559. +// - If to == nil, this performs contract creation using data as init code. +// - If gas == 0, gas is estimated and buffered; otherwise, the provided gas limit is used. +func SendTx( + ctx context.Context, + c *ethclient.Client, + priv *ecdsa.PrivateKey, + to *common.Address, + value *big.Int, + data []byte, + gas uint64, +) (common.Hash, error) { + signed, err := BuildSignedTx(ctx, c, priv, to, value, data, gas, nil) + if err != nil { + return common.Hash{}, err + } + if err := c.SendTransaction(ctx, signed); err != nil { + return common.Hash{}, err + } + return signed.Hash(), nil +} diff --git a/evmd/e2e/utils/revert_reason.go b/evmd/e2e/utils/revert_reason.go new file mode 100644 index 000000000..8a0c07ec0 --- /dev/null +++ b/evmd/e2e/utils/revert_reason.go @@ -0,0 +1,62 @@ +package utils + +import ( + "context" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" +) + +func GetRevertReasonViaEthCall(ctx context.Context, rpcURL string, addr common.Address, data []byte, blockNum *big.Int) (string, error) { + cctx, cancel := context.WithTimeout(ctx, RevertReasonTimeout) + defer cancel() + + rpcClient, err := rpc.DialContext(cctx, rpcURL) + if err != nil { + return "", err + } + defer rpcClient.Close() + + callArg := map[string]any{ + "to": addr.Hex(), + "data": "0x" + common.Bytes2Hex(data), + } + + var blockParam any = "latest" + if blockNum != nil { + blockParam = "0x" + blockNum.Text(16) + } + + var res any + err = rpcClient.CallContext(cctx, &res, "eth_call", callArg, blockParam) + if err == nil { + return "", errors.New("expected revert, got success") + } + + // Extract revert data from rpc error + var hexData string + var rperr rpc.DataError + if errors.As(err, &rperr) { + switch v := rperr.ErrorData().(type) { + case string: + hexData = v + case map[string]any: + if d, ok := v["data"].(string); ok { + hexData = d + } + } + } + if hexData == "" { + return "", errors.New("missing revert data in rpc error") + } + + b := common.FromHex(hexData) + msg, uerr := abi.UnpackRevert(b) + if uerr != nil { + return "", uerr + } + return msg, nil +} diff --git a/evmd/e2e/utils/utils.go b/evmd/e2e/utils/utils.go new file mode 100644 index 000000000..3f3ab5ea9 --- /dev/null +++ b/evmd/e2e/utils/utils.go @@ -0,0 +1,55 @@ +package utils + +import ( + "context" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "google.golang.org/grpc" + + "github.com/cosmos/cosmos-sdk/types/bech32" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func WaitForCondition(ctx context.Context, cond func() (bool, string, error), interval time.Duration, timeout time.Duration) error { + timeoutCtx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + for { + ok, reason, err := cond() + if err != nil { + return err + } + if ok { + return nil + } + select { + case <-timeoutCtx.Done(): + return fmt.Errorf("timeout waiting for condition: %s", reason) + case <-time.After(interval): + } + } +} + +func WaitForBlocks(ctx context.Context, cli *ethclient.Client, n uint64) error { + return WaitForCondition(ctx, func() (bool, string, error) { + block, err := cli.BlockNumber(ctx) + if err != nil { + return false, err.Error(), nil // only return error as reason, so we can use this while chain is starting up + } + return block >= n, fmt.Sprintf("block number %d >= %d", block, n), nil + }, 500*time.Millisecond, 60*time.Second) +} + +func GetBankBalance(ctx context.Context, grpcClient *grpc.ClientConn, address string, denom string) (*banktypes.QueryBalanceResponse, error) { + return banktypes.NewQueryClient(grpcClient).Balance(ctx, &banktypes.QueryBalanceRequest{ + Address: address, + Denom: denom, + }) +} + +func AddressToBech32(address common.Address) (string, error) { + return bech32.ConvertAndEncode(TestBech32Prefix, address.Bytes()) +} diff --git a/evmd/export.go b/evmd/export.go deleted file mode 100644 index 096f5eb2c..000000000 --- a/evmd/export.go +++ /dev/null @@ -1,247 +0,0 @@ -package evmd - -import ( - "encoding/json" - "fmt" - "log" - - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - - storetypes "cosmossdk.io/store/types" - - servertypes "github.com/cosmos/cosmos-sdk/server/types" - sdk "github.com/cosmos/cosmos-sdk/types" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/cosmos-sdk/x/staking" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// ExportAppStateAndValidators exports the state of the application for a genesis -// file. -func (app *EVMD) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs []string, modulesToExport []string) (servertypes.ExportedApp, error) { - // as if they could withdraw from the start of the next block - ctx := app.NewContextLegacy(true, tmproto.Header{Height: app.LastBlockHeight()}) - - // We export at last height + 1, because that's the height at which - // CometBFT will start InitChain. - height := app.LastBlockHeight() + 1 - if forZeroHeight { - height = 0 - if err := app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs); err != nil { - return servertypes.ExportedApp{}, err - } - } - - genState, err := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport) - if err != nil { - return servertypes.ExportedApp{}, err - } - - appState, err := json.MarshalIndent(genState, "", " ") - if err != nil { - return servertypes.ExportedApp{}, err - } - - validators, err := staking.WriteValidators(ctx, app.StakingKeeper) - return servertypes.ExportedApp{ - AppState: appState, - Validators: validators, - Height: height, - ConsensusParams: app.GetConsensusParams(ctx), - }, err -} - -// prepare for fresh start at zero height -// NOTE zero height genesis is a temporary feature which will be deprecated -// -// in favour of export at a block height -func (app *EVMD) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) error { - applyAllowedAddrs := len(jailAllowedAddrs) > 0 - - // check if there is an allowed address list - allowedAddrsMap := make(map[string]bool) - for _, addr := range jailAllowedAddrs { - _, err := sdk.ValAddressFromBech32(addr) - if err != nil { - log.Fatal(err) - } - allowedAddrsMap[addr] = true - } - - /* Handle fee distribution state. */ - - // withdraw all validator commission - if err := app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { - _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, sdk.ValAddress(val.GetOperator())) - return false - }); err != nil { - return err - } - - // withdraw all delegator rewards - dels, err := app.StakingKeeper.GetAllDelegations(ctx) - if err != nil { - return err - } - - for _, delegation := range dels { - valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) - if err != nil { - panic(err) - } - - delAddr := sdk.MustAccAddressFromBech32(delegation.DelegatorAddress) - - _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr) - } - - // reinitialize all delegations - for _, del := range dels { - valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress) - if err != nil { - panic(err) - } - delAddr := sdk.MustAccAddressFromBech32(del.DelegatorAddress) - - if err := app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil { - // never called as BeforeDelegationCreated always returns nil - panic(fmt.Errorf("error while incrementing period: %w", err)) - } - - if err := app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr); err != nil { - // never called as AfterDelegationModified always returns nil - panic(fmt.Errorf("error while creating a new delegation period record: %w", err)) - } - } - - // clear validator slash events - app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx) - - // clear validator historical rewards - app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) - - // set context height to zero - height := ctx.BlockHeight() - ctx = ctx.WithBlockHeight(0) - - // reinitialize all validators - err = app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { - // donate any unwithdrawn outstanding reward fraction tokens to the community pool - scraps, err := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, sdk.ValAddress(val.GetOperator())) - if err != nil { - return true - } - feePool, err := app.DistrKeeper.FeePool.Get(ctx) - if err != nil { - return true - } - feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) - err = app.DistrKeeper.FeePool.Set(ctx, feePool) - if err != nil { - return true - } - - err = app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, sdk.ValAddress(val.GetOperator())) - // this lets us stop in case there's an error - return err != nil - }) - if err != nil { - return err - } - - // reset context height - ctx = ctx.WithBlockHeight(height) - - /* Handle staking state. */ - - // iterate through redelegations, reset creation height - var iterErr error - if err := app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { - for i := range red.Entries { - red.Entries[i].CreationHeight = 0 - } - if iterErr = app.StakingKeeper.SetRedelegation(ctx, red); iterErr != nil { - return true - } - return false - }); err != nil { - return err - } - - if iterErr != nil { - return iterErr - } - - // iterate through unbonding delegations, reset creation height - if err := app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { - for i := range ubd.Entries { - ubd.Entries[i].CreationHeight = 0 - } - if iterErr = app.StakingKeeper.SetUnbondingDelegation(ctx, ubd); iterErr != nil { - return true - } - return false - }); err != nil { - return err - } - - if iterErr != nil { - return iterErr - } - - // Iterate through validators by power descending, reset bond heights, and - // update bond intra-tx counters. - store := ctx.KVStore(app.GetKey(stakingtypes.StoreKey)) - iter := storetypes.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) - counter := int16(0) - - for ; iter.Valid(); iter.Next() { - addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) - validator, err := app.StakingKeeper.GetValidator(ctx, addr) - if err != nil { - return fmt.Errorf("expected validator %s not found. Error: %w", addr, err) - } - - validator.UnbondingHeight = 0 - if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { - validator.Jailed = true - } - - if err = app.StakingKeeper.SetValidator(ctx, validator); err != nil { - return err - } - counter++ - } - - if err := iter.Close(); err != nil { - app.Logger().Error("error while closing the key-value store reverse prefix iterator: ", err) - return nil - } - - _, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - if err != nil { - log.Fatal(err) - } - - /* Handle slashing state. */ - - // reset start height on signing infos - if err := app.SlashingKeeper.IterateValidatorSigningInfos( - ctx, - func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) { - info.StartHeight = 0 - if iterErr = app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info); iterErr != nil { - return true - } - return false - }, - ); err != nil { - return err - } - - if iterErr != nil { - return iterErr - } - - return nil -} diff --git a/evmd/genesis.go b/evmd/genesis.go deleted file mode 100644 index 27cc9837b..000000000 --- a/evmd/genesis.go +++ /dev/null @@ -1,65 +0,0 @@ -package evmd - -import ( - "encoding/json" - - testconstants "github.com/cosmos/evm/testutil/constants" - erc20types "github.com/cosmos/evm/x/erc20/types" - feemarkettypes "github.com/cosmos/evm/x/feemarket/types" - evmtypes "github.com/cosmos/evm/x/vm/types" - - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" -) - -// GenesisState of the blockchain is represented here as a map of raw json -// messages key'd by an 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 - -// NewEVMGenesisState returns the default genesis state for the EVM module. -// -// NOTE: for the example chain implementation we need to set the default EVM denomination, -// enable ALL precompiles, and include default preinstalls. -func NewEVMGenesisState() *evmtypes.GenesisState { - evmGenState := evmtypes.DefaultGenesisState() - evmGenState.Params.ActiveStaticPrecompiles = evmtypes.AvailableStaticPrecompiles - evmGenState.Preinstalls = evmtypes.DefaultPreinstalls - - return evmGenState -} - -// NewErc20GenesisState returns the default genesis state for the ERC20 module. -// -// NOTE: for the example chain implementation we are also adding a default token pair, -// which is the base denomination of the chain (i.e. the WEVMOS contract). -func NewErc20GenesisState() *erc20types.GenesisState { - erc20GenState := erc20types.DefaultGenesisState() - erc20GenState.TokenPairs = testconstants.ExampleTokenPairs - erc20GenState.NativePrecompiles = []string{testconstants.WEVMOSContractMainnet} - - return erc20GenState -} - -// NewMintGenesisState returns the default genesis state for the mint module. -// -// NOTE: for the example chain implementation we are also adding a default minter. -func NewMintGenesisState() *minttypes.GenesisState { - mintGenState := minttypes.DefaultGenesisState() - - mintGenState.Params.MintDenom = testconstants.ExampleAttoDenom - return mintGenState -} - -// NewFeeMarketGenesisState returns the default genesis state for the feemarket module. -// -// NOTE: for the example chain implementation we are disabling the base fee. -func NewFeeMarketGenesisState() *feemarkettypes.GenesisState { - feeMarketGenState := feemarkettypes.DefaultGenesisState() - feeMarketGenState.Params.NoBaseFee = true - - return feeMarketGenState -} diff --git a/evmd/go.sum b/evmd/go.sum index d00ea3ccf..15efdc6dd 100644 --- a/evmd/go.sum +++ b/evmd/go.sum @@ -972,6 +972,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= +github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= diff --git a/evmd/interfaces.go b/evmd/interfaces.go deleted file mode 100644 index 98a22647c..000000000 --- a/evmd/interfaces.go +++ /dev/null @@ -1,11 +0,0 @@ -package evmd - -import ( - cmn "github.com/cosmos/evm/precompiles/common" - evmtypes "github.com/cosmos/evm/x/vm/types" -) - -type BankKeeper interface { - evmtypes.BankKeeper - cmn.BankKeeper -} diff --git a/evmd/tests/ibc/helper.go b/evmd/tests/ibc/helper.go deleted file mode 100644 index b7df90579..000000000 --- a/evmd/tests/ibc/helper.go +++ /dev/null @@ -1,125 +0,0 @@ -package ibc - -import ( - "errors" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - - "github.com/cosmos/evm" - "github.com/cosmos/evm/contracts" - evmibctesting "github.com/cosmos/evm/testutil/ibc" - testutiltypes "github.com/cosmos/evm/testutil/types" - erc20types "github.com/cosmos/evm/x/erc20/types" - ibctesting "github.com/cosmos/ibc-go/v10/testing" - - errorsmod "cosmossdk.io/errors" - - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" -) - -// NativeErc20Info holds details about a deployed ERC20 token. -type NativeErc20Info struct { - Denom string - ContractAbi abi.ABI - ContractAddr common.Address - Account common.Address // The address of the minter on the EVM chain - InitialBal *big.Int -} - -// SetupNativeErc20 deploys, registers, and mints a native ERC20 token on an EVM-based chain. -func SetupNativeErc20(t *testing.T, chain *evmibctesting.TestChain, senderAcc evmibctesting.SenderAccount) *NativeErc20Info { - t.Helper() - - evmCtx := chain.GetContext() - evmApp := chain.App.(evm.EvmApp) - - // Deploy new ERC20 contract with default metadata - contractAddr, err := evmApp.GetErc20Keeper().DeployERC20Contract(evmCtx, banktypes.Metadata{ - DenomUnits: []*banktypes.DenomUnit{ - {Denom: "example", Exponent: 18}, - }, - Name: "Example", - Symbol: "Ex", - }) - if err != nil { - t.Fatalf("ERC20 deployment failed: %v", err) - } - chain.NextBlock() - - // Register the contract - _, err = evmApp.GetErc20Keeper().RegisterERC20(evmCtx, &erc20types.MsgRegisterERC20{ - Signer: authtypes.NewModuleAddress(govtypes.ModuleName).String(), // does not have to be gov - Erc20Addresses: []string{contractAddr.Hex()}, - }) - if err != nil { - t.Fatalf("RegisterERC20 failed: %v", err) - } - - // Mint tokens to default sender - contractAbi := contracts.ERC20MinterBurnerDecimalsContract.ABI - nativeDenom := erc20types.CreateDenom(contractAddr.String()) - sendAmt := ibctesting.DefaultCoinAmount - senderAddr := senderAcc.SenderAccount.GetAddress() - - _, err = evmApp.GetEVMKeeper().CallEVM( - evmCtx, - contractAbi, - erc20types.ModuleAddress, - contractAddr, - true, - nil, - "mint", - common.BytesToAddress(senderAddr), - big.NewInt(sendAmt.Int64()), - ) - if err != nil { - t.Fatalf("mint call failed: %v", err) - } - - // Verify minted balance - bal := evmApp.GetErc20Keeper().BalanceOf(evmCtx, contractAbi, contractAddr, common.BytesToAddress(senderAddr)) - if bal.Cmp(big.NewInt(sendAmt.Int64())) != 0 { - t.Fatalf("unexpected ERC20 balance; got %s, want %s", bal.String(), sendAmt.String()) - } - - return &NativeErc20Info{ - Denom: nativeDenom, - ContractAbi: contractAbi, - ContractAddr: contractAddr, - Account: common.BytesToAddress(senderAddr), - InitialBal: big.NewInt(sendAmt.Int64()), - } -} - -// SetupNativeErc20 deploys, registers, and mints a native ERC20 token on an EVM-based chain. -func DeployContract(t *testing.T, chain *evmibctesting.TestChain, deploymentData testutiltypes.ContractDeploymentData) (common.Address, error) { - t.Helper() - - // Get account's nonce to create contract hash - from := common.BytesToAddress(chain.SenderPrivKey.PubKey().Address().Bytes()) - account := chain.App.(evm.EvmApp).GetEVMKeeper().GetAccount(chain.GetContext(), from) - if account == nil { - return common.Address{}, errors.New("account not found") - } - - ctorArgs, err := deploymentData.Contract.ABI.Pack("", deploymentData.ConstructorArgs...) - if err != nil { - return common.Address{}, errorsmod.Wrap(err, "failed to pack constructor arguments") - } - - data := deploymentData.Contract.Bin - data = append(data, ctorArgs...) - - _, err = chain.App.(evm.EvmApp).GetEVMKeeper().CallEVMWithData(chain.GetContext(), from, nil, data, true, nil) - if err != nil { - return common.Address{}, errorsmod.Wrapf(err, "failed to deploy contract") - } - - return crypto.CreateAddress(from, account.Nonce), nil -} diff --git a/evmd/tests/ibc/ibc_middleware_test.go b/evmd/tests/ibc/ibc_middleware_test.go deleted file mode 100644 index b930641dd..000000000 --- a/evmd/tests/ibc/ibc_middleware_test.go +++ /dev/null @@ -1,2211 +0,0 @@ -package ibc - -import ( - "encoding/json" - "errors" - "fmt" - "math/big" - "strings" - "testing" - - "github.com/ethereum/go-ethereum/common" - testifysuite "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/contracts" - "github.com/cosmos/evm/evmd" - "github.com/cosmos/evm/evmd/tests/integration" - "github.com/cosmos/evm/ibc" - "github.com/cosmos/evm/testutil" - evmibctesting "github.com/cosmos/evm/testutil/ibc" - testutiltypes "github.com/cosmos/evm/testutil/types" - "github.com/cosmos/evm/x/erc20" - erc20Keeper "github.com/cosmos/evm/x/erc20/keeper" - "github.com/cosmos/evm/x/erc20/types" - ibctestutil "github.com/cosmos/evm/x/ibc/callbacks/testutil" - callbacktypes "github.com/cosmos/evm/x/ibc/callbacks/types" - evmtypes "github.com/cosmos/evm/x/vm/types" - ibctransfer "github.com/cosmos/ibc-go/v10/modules/apps/transfer" - transfertypes "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v10/testing" - - "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// MiddlewareTestSuite tests the IBC middleware for the ERC20 module. -type MiddlewareTestSuite struct { - testifysuite.Suite - - coordinator *evmibctesting.Coordinator - - // testing chains used for convenience and readability - evmChainA *evmibctesting.TestChain - chainB *evmibctesting.TestChain - - path *evmibctesting.Path -} - -// SetupTest initializes the coordinator and test chains before each test. -func (suite *MiddlewareTestSuite) SetupTest() { - suite.coordinator = evmibctesting.NewCoordinator(suite.T(), 1, 1, integration.SetupEvmd) - suite.evmChainA = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(1)) - suite.chainB = suite.coordinator.GetChain(evmibctesting.GetChainID(2)) - - // Setup path - suite.path = evmibctesting.NewPath(suite.evmChainA, suite.chainB) - suite.path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort - suite.path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort - suite.path.EndpointA.ChannelConfig.Version = transfertypes.V1 - suite.path.EndpointB.ChannelConfig.Version = transfertypes.V1 - suite.path.Setup() - - // ensure the channel is found to verify proper setup - _, found := suite.evmChainA.App.GetIBCKeeper().ChannelKeeper.GetChannel(suite.evmChainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) - suite.Require().True(found) -} - -func TestMiddlewareTestSuite(t *testing.T) { - testifysuite.Run(t, new(MiddlewareTestSuite)) -} - -// TestOnRecvPacketWithCallback checks the OnRecvPacket logic for ICS-20 with comprehensive callback scenarios. -func (suite *MiddlewareTestSuite) TestOnRecvPacketWithCallback() { - var packet channeltypes.Packet - - var contractData evmtypes.CompiledContract - var contractAddr common.Address - var voucherDenom string - var path *evmibctesting.Path - var data transfertypes.InternalTransferRepresentation - - testCases := []struct { - name string - malleate func() - memo func() string - expError string - }{ - // SUCCESS CASES - { - name: "success - callback to add function with valid parameters", - malleate: nil, - memo: func() string { - // Only the 'add' function properly transfers tokens - amountInt, _ := math.NewIntFromString(ibctesting.DefaultCoinAmount.String()) - voucherDenom := testutil.GetVoucherDenomFromPacketData(data, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - singleTokenRepresentation, _ := types.NewTokenPairSTRv2(voucherDenom) - erc20Contract := singleTokenRepresentation.GetERC20Contract() - packedBytes, _ := contractData.ABI.Pack("add", erc20Contract, amountInt.BigInt()) - - return fmt.Sprintf(`{ - "dest_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 1_000_000, packedBytes) - }, - expError: "", - }, - { - name: "success - callback with maximum gas limit", - malleate: nil, - memo: func() string { - amountInt, _ := math.NewIntFromString(ibctesting.DefaultCoinAmount.String()) - voucherDenom := testutil.GetVoucherDenomFromPacketData(data, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - singleTokenRepresentation, _ := types.NewTokenPairSTRv2(voucherDenom) - erc20Contract := singleTokenRepresentation.GetERC20Contract() - packedBytes, _ := contractData.ABI.Pack("add", erc20Contract, amountInt.BigInt()) - - return fmt.Sprintf(`{ - "dest_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 10_000_000, packedBytes) - }, - expError: "", - }, - - // FAILURE CASES - Invalid Contract - { - name: "failure - callback to non-existent contract", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "dest_callback": { - "address": "0x1234567890123456789012345678901234567890", - "gas_limit": "%d", - "calldata": "" - } - }`, 1_000_000) - }, - expError: "ABCI code: 4", - }, - { - name: "failure - callback to empty address", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "dest_callback": { - "address": "0x0000000000000000000000000000000000000000", - "gas_limit": "%d", - "calldata": "" - } - }`, 1_000_000) - }, - expError: "ABCI code: 4", - }, - - // FAILURE CASES - Invalid Functions - { - name: "failure - calling non-existent function", - malleate: nil, - memo: func() string { - // Invalid function selector - packedBytes := []byte{0xff, 0xff, 0xff, 0xff} - - return fmt.Sprintf(`{ - "dest_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 1_000_000, packedBytes) - }, - expError: "ABCI code: 8", - }, - { - name: "failure - calling getCounter function (doesn't transfer tokens)", - malleate: nil, - memo: func() string { - packedBytes, _ := contractData.ABI.Pack("getCounter") - - return fmt.Sprintf(`{ - "dest_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 1_000_000, packedBytes) - }, - expError: "ABCI code: 12", - }, - { - name: "failure - calling resetCounter function (doesn't transfer tokens)", - malleate: nil, - memo: func() string { - packedBytes, _ := contractData.ABI.Pack("resetCounter") - - return fmt.Sprintf(`{ - "dest_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 1_000_000, packedBytes) - }, - expError: "ABCI code: 12", - }, - { - name: "failure - calling add function with wrong parameters", - malleate: nil, - memo: func() string { - // Invalid calldata for add function - packedBytes := []byte{0x12, 0x34, 0x56, 0x78} - - return fmt.Sprintf(`{ - "dest_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 1_000_000, packedBytes) - }, - expError: "ABCI code: 8", - }, - { - name: "failure - calling add function with zero amount", - malleate: nil, - memo: func() string { - voucherDenom := testutil.GetVoucherDenomFromPacketData(data, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - singleTokenRepresentation, _ := types.NewTokenPairSTRv2(voucherDenom) - erc20Contract := singleTokenRepresentation.GetERC20Contract() - packedBytes, _ := contractData.ABI.Pack("add", erc20Contract, big.NewInt(0)) - - return fmt.Sprintf(`{ - "dest_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 1_000_000, packedBytes) - }, - expError: "ABCI code: 8", - }, - - // FAILURE CASES - Gas Issues - { - name: "failure - insufficient gas limit", - malleate: nil, - memo: func() string { - amountInt, _ := math.NewIntFromString(ibctesting.DefaultCoinAmount.String()) - voucherDenom := testutil.GetVoucherDenomFromPacketData(data, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - singleTokenRepresentation, _ := types.NewTokenPairSTRv2(voucherDenom) - erc20Contract := singleTokenRepresentation.GetERC20Contract() - packedBytes, _ := contractData.ABI.Pack("add", erc20Contract, amountInt.BigInt()) - - return fmt.Sprintf(`{ - "dest_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 1000, packedBytes) // Very low gas - }, - expError: "ABCI code: 6", - }, - { - name: "failure - zero gas limit", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "dest_callback": { - "address": "%s", - "gas_limit": "0", - "calldata": "" - } - }`, contractAddr) - }, - expError: "ABCI code: 8", - }, - - // FAILURE CASES - Invalid Memo Format - { - name: "failure - missing required callback fields", - malleate: nil, - memo: func() string { - return `{"dest_callback": {"address": ""}}` - }, - expError: "a", - }, - { - name: "failure - invalid callback address format", - malleate: nil, - memo: func() string { - return `{"dest_callback": {"address": "not_hex_address", "gas_limit": "1000000", "calldata": ""}}` - }, - expError: "a", - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - // Reset state for each test - suite.SetupTest() - path = suite.path - - ctxB := suite.chainB.GetContext() - evmCtx := suite.evmChainA.GetContext() - bondDenom, err := suite.chainB.GetSimApp().StakingKeeper.BondDenom(ctxB) - suite.Require().NoError(err) - - // Generate the isolated address for the sender - sendAmt := ibctesting.DefaultCoinAmount - isolatedAddr := callbacktypes.GenerateIsolatedAddress(path.EndpointA.ChannelID, suite.chainB.SenderAccount.GetAddress().String()) - - // Get callback tester contract and deploy it - contractData, err = ibctestutil.LoadCounterWithCallbacksContract() - suite.Require().NoError(err) - - deploymentData := testutiltypes.ContractDeploymentData{ - Contract: contractData, - ConstructorArgs: nil, - } - - contractAddr, err = DeployContract(suite.T(), suite.evmChainA, deploymentData) - suite.Require().NoError(err) - - // Generate packet to execute the tester contract using callbacks - packetData := transfertypes.NewFungibleTokenPacketData( - bondDenom, - sendAmt.String(), - suite.chainB.SenderAccount.GetAddress().String(), - isolatedAddr.String(), - "", // Will be set by memo function - ) - - _ = path.EndpointA.GetChannel() - sourceChan := path.EndpointB.GetChannel() - - unmarshalledData, ackErr := transfertypes.UnmarshalPacketData(packetData.GetBytes(), sourceChan.Version, "") - data = unmarshalledData - suite.Require().Nil(ackErr) - - voucherDenom = testutil.GetVoucherDenomFromPacketData(data, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - - // Set the memo from the test case - packetData.Memo = tc.memo() - - // Apply test-specific setup - if tc.malleate != nil { - tc.malleate() - } - - packet = channeltypes.Packet{ - Sequence: 1, - SourcePort: path.EndpointB.ChannelConfig.PortID, - SourceChannel: path.EndpointB.ChannelID, - DestinationPort: path.EndpointA.ChannelConfig.PortID, - DestinationChannel: path.EndpointA.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.evmChainA.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - // Get transfer stack - transferStack, ok := suite.evmChainA.App.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) - suite.Require().True(ok) - - _, found := suite.evmChainA.App.GetIBCKeeper().ChannelKeeper.GetChannel(evmCtx, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - suite.Require().True(found) - - // Execute the packet - ack := transferStack.OnRecvPacket( - evmCtx, - sourceChan.Version, - packet, - suite.evmChainA.SenderAccount.GetAddress(), - ) - - // Validate successful callback - evmApp := suite.evmChainA.App.(*evmd.EVMD) - singleTokenRepresentation, err := types.NewTokenPairSTRv2(voucherDenom) - suite.Require().NoError(err) - erc20Contract := singleTokenRepresentation.GetERC20Contract() - - // Validate results - if tc.expError == "" { - suite.Require().True(ack.Success(), "Expected success but got failure") - - balAfterCallback := evmApp.Erc20Keeper.BalanceOf(evmCtx, contracts.ERC20MinterBurnerDecimalsContract.ABI, erc20Contract, contractAddr) - suite.Require().Equal(sendAmt.String(), balAfterCallback.String()) - - tokenPair, found := evmApp.Erc20Keeper.GetTokenPair(evmCtx, singleTokenRepresentation.GetID()) - suite.Require().True(found) - suite.Require().Equal(voucherDenom, tokenPair.Denom) - - available := evmApp.Erc20Keeper.IsDynamicPrecompileAvailable(evmCtx, common.HexToAddress(tokenPair.Erc20Address)) - suite.Require().True(available) - } else { - suite.Require().False(ack.Success(), "Expected failure but got success") - - balAfterCallback := evmApp.Erc20Keeper.BalanceOf(evmCtx, contracts.ERC20MinterBurnerDecimalsContract.ABI, erc20Contract, contractAddr) - suite.Require().Equal("0", balAfterCallback.String()) - - ackObj, ok := ack.(channeltypes.Acknowledgement) - suite.Require().True(ok) - ackErr, ok := ackObj.Response.(*channeltypes.Acknowledgement_Error) - suite.Require().True(ok) - suite.Require().Contains(ackErr.Error, tc.expError) - } - }) - } -} - -// TestNewIBCMiddleware verifies the middleware instantiation logic. -func (suite *MiddlewareTestSuite) TestNewIBCMiddleware() { - testCases := []struct { - name string - instantiateFn func() - expError error - }{ - { - "success", - func() { - _ = erc20.NewIBCMiddleware(erc20Keeper.Keeper{}, ibctransfer.IBCModule{}) - }, - nil, - }, - { - "panics with nil underlying app", - func() { - _ = erc20.NewIBCMiddleware(erc20Keeper.Keeper{}, nil) - }, - errors.New("underlying application cannot be nil"), - }, - { - "panics with nil erc20 keeper", - func() { - _ = erc20.NewIBCMiddleware(nil, ibc.Module{}) - }, - errors.New("erc20 keeper cannot be nil"), - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - if tc.expError == nil { - suite.Require().NotPanics( - tc.instantiateFn, - "unexpected panic: NewIBCMiddleware", - ) - } else { - suite.Require().PanicsWithError( - tc.expError.Error(), - tc.instantiateFn, - "expected panic with error: ", tc.expError.Error(), - ) - } - }) - } -} - -// TestOnRecvPacket checks the OnRecvPacket logic for ICS-20. -func (suite *MiddlewareTestSuite) TestOnRecvPacket() { - var packet channeltypes.Packet - - testCases := []struct { - name string - malleate func() - expError string - }{ - { - name: "pass", - malleate: nil, - expError: "", - }, - { - name: "fail: malformed packet data", - malleate: func() { - packet.Data = []byte("malformed data") - }, - expError: "handling packet", - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - - ctxB := suite.chainB.GetContext() - bondDenom, err := suite.chainB.GetSimApp().StakingKeeper.BondDenom(ctxB) - suite.Require().NoError(err) - - sendAmt := ibctesting.DefaultCoinAmount - receiver := suite.evmChainA.SenderAccount.GetAddress() - - packetData := transfertypes.NewFungibleTokenPacketData( - bondDenom, - sendAmt.String(), - suite.chainB.SenderAccount.GetAddress().String(), - receiver.String(), - "", - ) - path := suite.path - packet = channeltypes.Packet{ - Sequence: 1, - SourcePort: path.EndpointB.ChannelConfig.PortID, - SourceChannel: path.EndpointB.ChannelID, - DestinationPort: path.EndpointA.ChannelConfig.PortID, - DestinationChannel: path.EndpointA.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.evmChainA.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - if tc.malleate != nil { - tc.malleate() - } - - transferStack, ok := suite.evmChainA.App.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) - suite.Require().True(ok) - - ctxA := suite.evmChainA.GetContext() - sourceChan := path.EndpointB.GetChannel() - - ack := transferStack.OnRecvPacket( - ctxA, - sourceChan.Version, - packet, - suite.evmChainA.SenderAccount.GetAddress(), - ) - - if tc.expError == "" { - suite.Require().True(ack.Success()) - - // Ensure ibc transfer from chainB to evmChainA is successful. - data, ackErr := transfertypes.UnmarshalPacketData(packetData.GetBytes(), sourceChan.Version, "") - suite.Require().Nil(ackErr) - - voucherDenom := testutil.GetVoucherDenomFromPacketData(data, packet.GetDestPort(), packet.GetDestChannel()) - - evmApp := suite.evmChainA.App.(*evmd.EVMD) - voucherCoin := evmApp.BankKeeper.GetBalance(ctxA, receiver, voucherDenom) - suite.Require().Equal(sendAmt.String(), voucherCoin.Amount.String()) - - // Make sure token pair is registered - singleTokenRepresentation, err := types.NewTokenPairSTRv2(voucherDenom) - suite.Require().NoError(err) - tokenPair, found := evmApp.Erc20Keeper.GetTokenPair(ctxA, singleTokenRepresentation.GetID()) - suite.Require().True(found) - suite.Require().Equal(voucherDenom, tokenPair.Denom) - // Make sure dynamic precompile is registered - available := evmApp.Erc20Keeper.IsDynamicPrecompileAvailable(ctxA, common.HexToAddress(tokenPair.Erc20Address)) - suite.Require().True(available) - } else { - suite.Require().False(ack.Success()) - - ackObj, ok := ack.(channeltypes.Acknowledgement) - suite.Require().True(ok) - ackErr, ok := ackObj.Response.(*channeltypes.Acknowledgement_Error) - suite.Require().True(ok) - suite.Require().Contains(ackErr.Error, tc.expError) - } - }) - } -} - -// TestOnRecvPacketNativeErc20 checks receiving a native ERC20 token. -func (suite *MiddlewareTestSuite) TestOnRecvPacketNativeErc20() { - testCases := []struct { - name string - setupRecipient func(suite *MiddlewareTestSuite) (string, common.Address) - withCallback bool - expectedRecipientEVM common.Address - }{ - { - name: "recipient with callback", - setupRecipient: func(suite *MiddlewareTestSuite) (string, common.Address) { - recipient := callbacktypes.GenerateIsolatedAddress( - suite.path.EndpointA.ChannelID, - suite.chainB.SenderAccount.GetAddress().String(), - ).String() - return recipient, common.Address{} - }, - withCallback: true, - expectedRecipientEVM: common.Address{}, - }, - { - name: "hex recipient without callback", - setupRecipient: func(suite *MiddlewareTestSuite) (string, common.Address) { - evmAddr := common.BytesToAddress(suite.evmChainA.SenderAccount.GetAddress().Bytes()) - return evmAddr.Hex(), evmAddr - }, - withCallback: false, - expectedRecipientEVM: common.Address{}, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - nativeErc20 := SetupNativeErc20(suite.T(), suite.evmChainA, suite.evmChainA.SenderAccounts[0]) - - evmCtx := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - - // Scenario: Native ERC20 token transfer from evmChainA to chainB - timeoutHeight := clienttypes.NewHeight(1, 110) - path := suite.path - chainBAccount := suite.chainB.SenderAccount.GetAddress() - - sendAmt := math.NewIntFromBigInt(nativeErc20.InitialBal).Quo(math.NewInt(2)) - senderEthAddr := nativeErc20.Account - sender := sdk.AccAddress(senderEthAddr.Bytes()) - - // Transfer half the initial balance out - // Sender transfers 50 out (escrowed) - msg := transfertypes.NewMsgTransfer( - path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - sdk.NewCoin(nativeErc20.Denom, sendAmt), - sender.String(), chainBAccount.String(), - timeoutHeight, 0, "", - ) - - _, err := suite.evmChainA.SendMsgs(msg) - suite.Require().NoError(err) // message committed - - // Balance after transfer should be initial balance - sendAmt - balAfterTransfer := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal( - new(big.Int).Sub(nativeErc20.InitialBal, sendAmt.BigInt()).String(), - balAfterTransfer.String(), - ) - - // Now try to convert sendAmt to ERC20 - convertMsg := types.MsgConvertERC20{ - ContractAddress: nativeErc20.ContractAddr.String(), - Amount: sendAmt, - Receiver: sender.String(), - Sender: senderEthAddr.String(), - } - - _, err = suite.evmChainA.SendMsgs(&convertMsg) - suite.Require().NoError(err) // message committed - - // Check native erc20 token is escrowed on evmChainA for sending to chainB. - // Conversion of remaining 50 tokens to Bank token - escrowAddr := transfertypes.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - escrowedBal := evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().Equal(sendAmt.String(), escrowedBal.Amount.String()) - - // chainBNativeErc20Denom is the native erc20 token denom on chainB from evmChainA through IBC. - chainBNativeErc20Denom := transfertypes.NewDenom( - nativeErc20.Denom, - transfertypes.NewHop( - suite.path.EndpointB.ChannelConfig.PortID, - suite.path.EndpointB.ChannelID, - ), - ) - - // Setup recipient based on test case - recipient, recipientEVM := tc.setupRecipient(suite) - if !tc.withCallback { - tc.expectedRecipientEVM = recipientEVM - } - - var recvAmt math.Int - var packetData transfertypes.FungibleTokenPacketData - - if tc.withCallback { - // half the send amount should be received since our first call will fail due to send disabled, - // and the second will succeed - recvAmt = sendAmt.Quo(math.NewInt(2)) - - // get callback tester contract and deploy it - contractData, err := ibctestutil.LoadCounterWithCallbacksContract() - suite.Require().NoError(err) - - deploymentData := testutiltypes.ContractDeploymentData{ - Contract: contractData, - ConstructorArgs: nil, - } - - contractAddr, err := DeployContract(suite.T(), suite.evmChainA, deploymentData) - if err != nil { - return - } - - // Each callback gets recvAmt - packedBytes, err := contractData.ABI.Pack("add", nativeErc20.ContractAddr, recvAmt.BigInt()) - suite.Require().NoError(err) - - destCallback := fmt.Sprintf(`{ - "dest_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 1_000_000, packedBytes) - - packetData = transfertypes.NewFungibleTokenPacketData( - chainBNativeErc20Denom.Path(), - recvAmt.String(), - chainBAccount.String(), - recipient, - destCallback, - ) - } else { - recvAmt = math.NewInt(50) - packetData = transfertypes.NewFungibleTokenPacketData( - chainBNativeErc20Denom.Path(), - recvAmt.String(), - chainBAccount.String(), - recipient, - "", - ) - } - - transferStack, ok := suite.evmChainA.App.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) - suite.Require().True(ok) - - sourceChan := path.EndpointB.GetChannel() - - if tc.withCallback { - suite.evmChainA.NextBlock() - - // SendEnabled=false will cause the conversion of bank tokens to erc20 tokens to fail, - // but not send them back to escrow - evmApp.BankKeeper.SetSendEnabled(evmCtx, nativeErc20.Denom, false) - isSendEnabled := evmApp.BankKeeper.IsSendEnabledDenom(evmCtx, nativeErc20.Denom) - suite.Require().False(isSendEnabled) - - packet1 := channeltypes.Packet{ - Sequence: 1, - SourcePort: path.EndpointB.ChannelConfig.PortID, - SourceChannel: path.EndpointB.ChannelID, - DestinationPort: path.EndpointA.ChannelConfig.PortID, - DestinationChannel: path.EndpointA.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.evmChainA.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - errAck := transferStack.OnRecvPacket( - evmCtx, - sourceChan.Version, - packet1, - suite.evmChainA.SenderAccount.GetAddress(), - ) - suite.Require().False(errAck.Success()) - - evmCtx = suite.evmChainA.GetContext() - - // SendEnabled=true causes our callback to succeed - evmApp.BankKeeper.SetSendEnabled(evmCtx, nativeErc20.Denom, true) - isSendEnabled = evmApp.BankKeeper.IsSendEnabledDenom(evmCtx, nativeErc20.Denom) - suite.Require().True(isSendEnabled) - - packet2 := channeltypes.Packet{ - Sequence: 2, - SourcePort: path.EndpointB.ChannelConfig.PortID, - SourceChannel: path.EndpointB.ChannelID, - DestinationPort: path.EndpointA.ChannelConfig.PortID, - DestinationChannel: path.EndpointA.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.evmChainA.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - ack := transferStack.OnRecvPacket( - evmCtx, - sourceChan.Version, - packet2, - suite.evmChainA.SenderAccount.GetAddress(), - ) - suite.Require().True(ack.Success()) - - // Check un-escrowed balance on evmChainA after receiving the packet. - escrowedBal = evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().True(escrowedBal.IsZero(), "escrowed balance should be un-escrowed after receiving the packet") - - // recvAmt should be in the contractAddr upon successful recv callback - contractAddr := common.HexToAddress(packetData.Memo) - // Parse contract address from memo - var memoData map[string]interface{} - err = json.Unmarshal([]byte(packetData.Memo), &memoData) - suite.Require().NoError(err) - destCallback := memoData["dest_callback"].(map[string]interface{}) - contractAddrStr := destCallback["address"].(string) - contractAddr = common.HexToAddress(contractAddrStr) - - balAfterUnescrow := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, contractAddr) - suite.Require().Equal(recvAmt.String(), balAfterUnescrow.String()) - - bankBalAfterUnescrow := evmApp.BankKeeper.GetBalance(evmCtx, sender, nativeErc20.Denom) - // InitialBalance half which was converted but not sent will be in the sending account's balance - suite.Require().Equal(sendAmt.String(), bankBalAfterUnescrow.Amount.String()) - - // the packet that failed conversion due to the minting restriction should instead remain as the bank token - // and will be in the isolated address used to invoke the callback - isolatedAddr := callbacktypes.GenerateIsolatedAddress(path.EndpointA.ChannelID, - suite.chainB.SenderAccount.GetAddress().String()) - trappedBal := evmApp.BankKeeper.GetBalance(evmCtx, isolatedAddr, nativeErc20.Denom) - suite.Require().Equal(recvAmt.String(), trappedBal.Amount.String()) - } else { - // Simple case: no callback, just verify hex recipient receives ERC20 - packet := channeltypes.Packet{ - Sequence: 1, - SourcePort: path.EndpointB.ChannelConfig.PortID, - SourceChannel: path.EndpointB.ChannelID, - DestinationPort: path.EndpointA.ChannelConfig.PortID, - DestinationChannel: path.EndpointA.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.evmChainA.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - ack := transferStack.OnRecvPacket( - evmCtx, - sourceChan.Version, - packet, - suite.evmChainA.SenderAccount.GetAddress(), - ) - suite.Require().True(ack.Success()) - - // Verify ERC20 was minted to the hex recipient - bal := evmApp.Erc20Keeper.BalanceOf( - evmCtx, - nativeErc20.ContractAbi, - nativeErc20.ContractAddr, - tc.expectedRecipientEVM, - ) - suite.Require().Equal(recvAmt.String(), bal.String()) - } - }) - } -} - -// TestOnAcknowledgementPacketWithCallback tests acknowledgement logic with comprehensive callback scenarios. -func (suite *MiddlewareTestSuite) TestOnAcknowledgementPacketWithCallback() { - var ( - packet channeltypes.Packet - ack []byte - contractData evmtypes.CompiledContract - contractAddr common.Address - ) - - testCases := []struct { - name string - malleate func() - memo func() string - ackType string // "success" or "error" - onSendRequired bool - expError string - }{ - // SUCCESS CASES - { - name: "success - callback with successful acknowledgement", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 1_000_000) - }, - ackType: "success", - onSendRequired: true, - expError: "", - }, - { - name: "success - callback with error acknowledgement (refund scenario)", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 1_500_000) - }, - ackType: "error", - onSendRequired: true, - expError: "", - }, - { - name: "success - callback with maximum gas limit", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 10_000_000) - }, - ackType: "success", - onSendRequired: true, - expError: "", - }, - { - name: "success - no callback in memo (regular transfer)", - malleate: nil, - memo: func() string { - return "" - }, - ackType: "success", - onSendRequired: true, - expError: "", - }, - - // FAILURE CASES - Invalid Contract - { - name: "failure - callback to non-existent contract", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "0x1234567890123456789012345678901234567890", - "gas_limit": "%d" - } - }`, 1_000_000) - }, - ackType: "success", - onSendRequired: true, - expError: "ABCI code: 4", - }, - { - name: "failure - callback to empty address", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "0x0000000000000000000000000000000000000000", - "gas_limit": "%d" - } - }`, 1_000_000) - }, - ackType: "success", - onSendRequired: true, - expError: "ABCI code: 4", - }, - - // FAILURE CASES - Invalid Calldata - { - name: "failure - acknowledgement callback with calldata (should be empty)", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 1_000_000, []byte{0x12, 0x34, 0x56, 0x78}) - }, - ackType: "success", - onSendRequired: true, - expError: "ABCI code: 3", - }, - - // FAILURE CASES - Gas Issues - { - name: "failure - insufficient gas limit", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 1000) // Very low gas - }, - ackType: "success", - onSendRequired: true, - expError: "ABCI code: 9", - }, - { - name: "success - zero gas limit (defaults to max)", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "0" - } - }`, contractAddr) - }, - ackType: "success", - onSendRequired: true, - expError: "", - }, - - // FAILURE CASES - Invalid Memo Format - { - name: "failure - malformed JSON memo", - malleate: nil, - memo: func() string { - return `{"src_callback": {"address": "invalid_json"` - }, - ackType: "success", - onSendRequired: true, - expError: "invalid callback data", - }, - { - name: "failure - invalid callback address format", - malleate: nil, - memo: func() string { - return `{"src_callback": {"address": "not_hex_address", "gas_limit": "1000000"}}` - }, - ackType: "success", - onSendRequired: true, - expError: "invalid callback data", - }, - - // FAILURE CASES - Base IBC Failures (should not execute callback) - { - name: "failure - malformed packet data (no callback execution)", - malleate: func() { - packet.Data = []byte("malformed data") - }, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 1_000_000) - }, - ackType: "success", - onSendRequired: false, - expError: "cannot unmarshal ICS-20 transfer packet data", - }, - { - name: "failure - empty acknowledgement (no callback execution)", - malleate: func() { - ack = []byte{} - }, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 1_000_000) - }, - ackType: "success", - onSendRequired: true, - expError: "cannot unmarshal ICS-20 transfer packet acknowledgement", - }, - - // EDGE CASES - { - name: "success - callback with error ack and refund verification", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 2_000_000) - }, - ackType: "error", - onSendRequired: true, - expError: "", - }, - { - name: "success - callback with minimal gas", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 100_000) // Minimal but sufficient - }, - ackType: "success", - onSendRequired: true, - expError: "", - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - - ctxA := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - - bondDenom, err := evmApp.StakingKeeper.BondDenom(ctxA) - suite.Require().NoError(err) - - sendAmt := ibctesting.DefaultCoinAmount - sender := suite.evmChainA.SenderAccount.GetAddress() - receiver := suite.chainB.SenderAccount.GetAddress() - - // Deploy callback contract on source chain (evmChainA) - contractData, err = ibctestutil.LoadCounterWithCallbacksContract() - suite.Require().NoError(err) - - deploymentData := testutiltypes.ContractDeploymentData{ - Contract: contractData, - ConstructorArgs: nil, - } - - contractAddr, err = DeployContract(suite.T(), suite.evmChainA, deploymentData) - suite.Require().NoError(err) - - // Create packet data with memo - packetData := transfertypes.NewFungibleTokenPacketData( - bondDenom, - sendAmt.String(), - sender.String(), - receiver.String(), - tc.memo(), - ) - - path := suite.path - packet = channeltypes.Packet{ - Sequence: 1, - SourcePort: path.EndpointA.ChannelConfig.PortID, - SourceChannel: path.EndpointA.ChannelID, - DestinationPort: path.EndpointB.ChannelConfig.PortID, - DestinationChannel: path.EndpointB.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - // Set acknowledgement based on test case - if tc.ackType == "error" { - ackErr := channeltypes.NewErrorAcknowledgement(errors.New("transfer failed")) - ack = ackErr.Acknowledgement() - } else { - ack = channeltypes.NewResultAcknowledgement([]byte{1}).Acknowledgement() - } - - // Apply test-specific malleate function - if tc.malleate != nil { - tc.malleate() - } - - transferStack, ok := evmApp.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) - suite.Require().True(ok) - - sourceChan := suite.path.EndpointA.GetChannel() - - balBeforeTransfer := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - // Execute send if required (for proper escrow setup) - if tc.onSendRequired { - timeoutHeight := clienttypes.NewHeight(1, 110) - msg := transfertypes.NewMsgTransfer( - path.EndpointA.ChannelConfig.PortID, - path.EndpointA.ChannelID, - sdk.NewCoin(bondDenom, sendAmt), - sender.String(), - receiver.String(), - timeoutHeight, 0, tc.memo(), - ) - err = suite.evmChainA.SenderAccount.SetSequence(suite.evmChainA.SenderAccount.GetSequence() + 1) - suite.Require().NoError(err) - res, err := suite.evmChainA.SendMsgs(msg) - suite.Require().NoError(err) // message committed - - feeAmt := evmibctesting.FeeCoins().AmountOf(bondDenom) - balAfterTransfer := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - suite.Require().Equal( - balBeforeTransfer.Amount.Sub(sendAmt).Sub(feeAmt).String(), - balAfterTransfer.Amount.String(), - ) - - sentPacket, err := ibctesting.ParseV1PacketFromEvents(res.Events) - suite.Require().NoError(err) - - // relay the sent packet - err = path.RelayPacket(sentPacket) - suite.Require().NoError(err) // relay committed - - // Verify escrow for successful sends - if tc.expError == "" || !strings.Contains(tc.expError, "ABCI code") { - // One for UpdateClient() and one for AcknowledgePacket() - relayPacketFeeAmt := feeAmt.Mul(math.NewInt(2)) - - balAfterRelayPacket := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - suite.Require().Equal( - balAfterTransfer.Amount.Sub(relayPacketFeeAmt).String(), - balAfterRelayPacket.Amount.String(), - ) - escrowAddr := transfertypes.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - escrowedBal := evmApp.BankKeeper.GetBalance(ctxA, escrowAddr, bondDenom) - suite.Require().Equal(sendAmt.String(), escrowedBal.Amount.String()) - } - - // Use the actually sent packet for acknowledgement - packet = sentPacket - } - - beforeAckBal := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - // Execute acknowledgement - err = transferStack.OnAcknowledgementPacket( - ctxA, - sourceChan.Version, - packet, - ack, - receiver, - ) - - // Validate results - if tc.expError == "" { - suite.Require().NoError(err, "Expected success but got error") - - // Verify callback execution by checking counter increment - if strings.Contains(tc.memo(), "src_callback") { - counterRes, err := evmApp.EVMKeeper.CallEVM( - ctxA, - contractData.ABI, - common.BytesToAddress(suite.evmChainA.SenderAccount.GetAddress()), - contractAddr, - false, - big.NewInt(100000), - "getCounter", - ) - suite.Require().NoError(err) - - var counter *big.Int - err = contractData.ABI.UnpackIntoInterface(&counter, "getCounter", counterRes.Ret) - suite.Require().NoError(err) - suite.Require().True(counter.Cmp(big.NewInt(1)) >= 0, "Counter should be incremented by callback") - } - - // Verify refund for error acknowledgements - if tc.ackType == "error" && tc.onSendRequired { - escrowAddr := transfertypes.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - escrowedBal := evmApp.BankKeeper.GetBalance(ctxA, escrowAddr, bondDenom) - finalSenderBal := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - - // For error acks, tokens should be refunded - suite.Require().True(escrowedBal.IsZero(), "Escrowed balance should be zero after refund") - suite.Require().Equal(beforeAckBal.Amount.Add(sendAmt).String(), finalSenderBal.Amount.String(), "Sender balance should be refunded") - } - } else if strings.Contains(tc.memo(), "src_callback") && strings.Contains(tc.expError, "ABCI code") { - // For ack failures, verify that counter was NOT incremented - - counterRes, err := evmApp.EVMKeeper.CallEVM( - ctxA, - contractData.ABI, - common.BytesToAddress(suite.evmChainA.SenderAccount.GetAddress()), - contractAddr, - false, - big.NewInt(100000), - "getCounter", - ) - // Counter should remain 0 if callback failed - if err == nil { - var counter *big.Int - err = contractData.ABI.UnpackIntoInterface(&counter, "getCounter", counterRes.Ret) - if err == nil { - suite.Require().Equal(big.NewInt(0).String(), counter.String(), "Counter should not be incremented on callback failure") - } - } - } - }) - } -} - -func (suite *MiddlewareTestSuite) TestOnAcknowledgementPacket() { - var ( - packet channeltypes.Packet - ack []byte - ) - - testCases := []struct { - name string - malleate func() - onSendRequired bool - expError string - }{ - { - name: "pass", - malleate: nil, - onSendRequired: false, - expError: "", - }, - { - name: "pass: refund escrowed token", - malleate: func() { - ackErr := channeltypes.NewErrorAcknowledgement(errors.New("error")) - ack = ackErr.Acknowledgement() - }, - onSendRequired: true, - expError: "", - }, - { - name: "fail: malformed packet data", - malleate: func() { - packet.Data = []byte("malformed data") - }, - onSendRequired: false, - expError: "cannot unmarshal ICS-20 transfer packet data", - }, - { - name: "fail: empty ack", - malleate: func() { - ack = []byte{} - }, - onSendRequired: false, - expError: "cannot unmarshal ICS-20 transfer packet acknowledgement", - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - - ctxA := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - - bondDenom, err := evmApp.StakingKeeper.BondDenom(ctxA) - suite.Require().NoError(err) - - sendAmt := ibctesting.DefaultCoinAmount - sender := suite.evmChainA.SenderAccount.GetAddress() - receiver := suite.chainB.SenderAccount.GetAddress() - - packetData := transfertypes.NewFungibleTokenPacketData( - bondDenom, - sendAmt.String(), - sender.String(), - receiver.String(), - "", - ) - - path := suite.path - packet = channeltypes.Packet{ - Sequence: 1, - SourcePort: path.EndpointA.ChannelConfig.PortID, - SourceChannel: path.EndpointA.ChannelID, - DestinationPort: path.EndpointB.ChannelConfig.PortID, - DestinationChannel: path.EndpointB.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - ack = channeltypes.NewResultAcknowledgement([]byte{1}).Acknowledgement() - if tc.malleate != nil { - tc.malleate() - } - - transferStack, ok := evmApp.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) - suite.Require().True(ok) - - sourceChan := suite.path.EndpointA.GetChannel() - onAck := func() error { - return transferStack.OnAcknowledgementPacket( - ctxA, - sourceChan.Version, - packet, - ack, - receiver, - ) - } - if tc.onSendRequired { - timeoutHeight := clienttypes.NewHeight(1, 110) - msg := transfertypes.NewMsgTransfer( - path.EndpointA.ChannelConfig.PortID, - path.EndpointA.ChannelID, - sdk.NewCoin(bondDenom, sendAmt), - sender.String(), - receiver.String(), - timeoutHeight, 0, "", - ) - balBeforeTransfer := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - res, err := suite.evmChainA.SendMsgs(msg) - suite.Require().NoError(err) // message committed - - feeAmt := evmibctesting.FeeCoins().AmountOf(bondDenom) - balAfterTransfer := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - suite.Require().Equal( - balBeforeTransfer.Amount.Sub(sendAmt).Sub(feeAmt).String(), - balAfterTransfer.Amount.String(), - ) - - packet, err := ibctesting.ParseV1PacketFromEvents(res.Events) - suite.Require().NoError(err) - - // relay the sent packet - err = path.RelayPacket(packet) - suite.Require().NoError(err) // relay committed - - // One for UpdateClient() and one for AcknowledgePacket() - relayPacketFeeAmt := feeAmt.Mul(math.NewInt(2)) - - // ensure the ibc token is escrowed. - balAfterRelayPacket := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - suite.Require().Equal( - balAfterTransfer.Amount.Sub(relayPacketFeeAmt).String(), - balAfterRelayPacket.Amount.String(), - ) - escrowAddr := transfertypes.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - escrowedBal := evmApp.BankKeeper.GetBalance(ctxA, escrowAddr, bondDenom) - suite.Require().Equal(sendAmt.String(), escrowedBal.Amount.String()) - } - - err = onAck() - if tc.expError == "" { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - suite.Require().Contains(err.Error(), tc.expError) - } - }) - } -} - -// TestOnAcknowledgementPacketNativeErc20 tests ack logic when the packet involves a native ERC20. -func (suite *MiddlewareTestSuite) TestOnAcknowledgementPacketNativeErc20() { - var ( - packet channeltypes.Packet - ack []byte - ) - - testCases := []struct { - name string - malleate func() - expError string - expRefund bool - }{ - { - name: "pass", - malleate: nil, - expError: "", - expRefund: false, - }, - { - name: "pass: refund escrowed token", - malleate: func() { - ackErr := channeltypes.NewErrorAcknowledgement(errors.New("error")) - ack = ackErr.Acknowledgement() - }, - expError: "", - expRefund: true, - }, - { - name: "fail: malformed packet data", - malleate: func() { - packet.Data = []byte("malformed data") - }, - expError: "cannot unmarshal ICS-20 transfer packet data", - expRefund: false, - }, - { - name: "fail: empty ack", - malleate: func() { - ack = []byte{} - }, - expError: "cannot unmarshal ICS-20 transfer packet acknowledgement", - expRefund: false, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - nativeErc20 := SetupNativeErc20(suite.T(), suite.evmChainA, suite.evmChainA.SenderAccounts[0]) - - evmCtx := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - - timeoutHeight := clienttypes.NewHeight(1, 110) - path := suite.path - chainBAccount := suite.chainB.SenderAccount.GetAddress() - - sendAmt := math.NewIntFromBigInt(nativeErc20.InitialBal) - senderEthAddr := nativeErc20.Account - sender := sdk.AccAddress(senderEthAddr.Bytes()) - receiver := suite.chainB.SenderAccount.GetAddress() - - // Send the native erc20 token from evmChainA to chainB. - msg := transfertypes.NewMsgTransfer( - path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - sdk.NewCoin(nativeErc20.Denom, sendAmt), sender.String(), receiver.String(), - timeoutHeight, 0, "", - ) - - escrowAddr := transfertypes.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - // checkEscrow is a check function to ensure the native erc20 token is escrowed. - checkEscrow := func() { - erc20BalAfterIbcTransfer := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal( - new(big.Int).Sub(nativeErc20.InitialBal, sendAmt.BigInt()).String(), - erc20BalAfterIbcTransfer.String(), - ) - escrowedBal := evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().Equal(sendAmt.String(), escrowedBal.Amount.String()) - } - - // checkRefund is a check function to ensure refund is processed. - checkRefund := func() { - escrowedBal := evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().True(escrowedBal.IsZero()) - - // Check erc20 balance is same as initial balance after refund. - erc20BalAfterIbcTransfer := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal(nativeErc20.InitialBal.String(), erc20BalAfterIbcTransfer.String()) - } - - _, err := suite.evmChainA.SendMsgs(msg) - suite.Require().NoError(err) // message committed - checkEscrow() - - transferStack, ok := suite.evmChainA.App.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) - suite.Require().True(ok) - - packetData := transfertypes.NewFungibleTokenPacketData( - nativeErc20.Denom, - sendAmt.String(), - sender.String(), - chainBAccount.String(), - "", - ) - packet = channeltypes.Packet{ - Sequence: 1, - SourcePort: path.EndpointA.ChannelConfig.PortID, - SourceChannel: path.EndpointA.ChannelID, - DestinationPort: path.EndpointB.ChannelConfig.PortID, - DestinationChannel: path.EndpointB.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - ack = channeltypes.NewResultAcknowledgement([]byte{1}).Acknowledgement() - if tc.malleate != nil { - tc.malleate() - } - - sourceChan := path.EndpointA.GetChannel() - onAck := func() error { - return transferStack.OnAcknowledgementPacket( - evmCtx, - sourceChan.Version, - packet, - ack, - receiver, - ) - } - - err = onAck() - if tc.expError == "" { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - suite.Require().Contains(err.Error(), tc.expError) - } - - if tc.expRefund { - checkRefund() - } else { - checkEscrow() - } - }) - } -} - -// TestOnTimeoutPacket checks the timeout handling for ICS-20. -func (suite *MiddlewareTestSuite) TestOnTimeoutPacket() { - var packet channeltypes.Packet - - testCases := []struct { - name string - malleate func() - onSendRequired bool - expError string - }{ - { - name: "pass", - malleate: nil, - onSendRequired: true, - expError: "", - }, - { - name: "fail: malformed packet data", - malleate: func() { - packet.Data = []byte("malformed data") - }, - onSendRequired: false, - expError: "cannot unmarshal ICS-20 transfer packet data", - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - - ctxA := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - bondDenom, err := evmApp.StakingKeeper.BondDenom(ctxA) - suite.Require().NoError(err) - - sendAmt := ibctesting.DefaultCoinAmount - sender := suite.evmChainA.SenderAccount.GetAddress() - receiver := suite.chainB.SenderAccount.GetAddress() - - packetData := transfertypes.NewFungibleTokenPacketData( - bondDenom, - sendAmt.String(), - sender.String(), - receiver.String(), - "", - ) - - path := suite.path - packet = channeltypes.Packet{ - Sequence: 1, - SourcePort: path.EndpointA.ChannelConfig.PortID, - SourceChannel: path.EndpointA.ChannelID, - DestinationPort: path.EndpointB.ChannelConfig.PortID, - DestinationChannel: path.EndpointB.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - if tc.malleate != nil { - tc.malleate() - } - - transferStack, ok := evmApp.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) - suite.Require().True(ok) - - sourceChan := suite.path.EndpointA.GetChannel() - onTimeout := func() error { - return transferStack.OnTimeoutPacket( - ctxA, - sourceChan.Version, - packet, - sender, - ) - } - - balBeforeTransfer := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - var balAfterRelayPacket sdk.Coin - feeAmt := evmibctesting.FeeCoins().AmountOf(bondDenom) - if tc.onSendRequired { - timeoutHeight := clienttypes.NewHeight(1, 110) - msg := transfertypes.NewMsgTransfer( - path.EndpointA.ChannelConfig.PortID, - path.EndpointA.ChannelID, - sdk.NewCoin(bondDenom, sendAmt), - sender.String(), - receiver.String(), - timeoutHeight, 0, "", - ) - - res, err := suite.evmChainA.SendMsgs(msg) - suite.Require().NoError(err) // message committed - - balAfterTransfer := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - suite.Require().Equal( - balBeforeTransfer.Amount.Sub(sendAmt).Sub(feeAmt).String(), - balAfterTransfer.Amount.String(), - ) - - packet, err := ibctesting.ParseV1PacketFromEvents(res.Events) - suite.Require().NoError(err) - - err = path.RelayPacket(packet) - suite.Require().NoError(err) // relay committed - - // One for UpdateClient() and one for AcknowledgePacket() - relayPacketFeeAmt := feeAmt.Mul(math.NewInt(2)) - - balAfterRelayPacket = evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - suite.Require().Equal( - balAfterTransfer.Amount.Sub(relayPacketFeeAmt).String(), - balAfterRelayPacket.Amount.String(), - ) - } - err = onTimeout() - - balAfterTimeout := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - if tc.onSendRequired { - suite.Require().Equal( - balAfterRelayPacket.Amount.Add(sendAmt).String(), - balAfterTimeout.Amount.String(), - ) - } else { - suite.Require().Equal( - balBeforeTransfer.Amount.String(), - balAfterTimeout.Amount.String(), - ) - } - - // ensure that the escrowed coins were refunded on timeout. - escrowAddr := transfertypes.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - escrowedBal := evmApp.BankKeeper.GetBalance(ctxA, escrowAddr, bondDenom) - suite.Require().Equal(escrowedBal.Amount.String(), math.ZeroInt().String()) - - if tc.expError == "" { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - suite.Require().Contains(err.Error(), tc.expError) - } - }) - } -} - -// TestOnTimeoutPacketWithCallback tests timeout logic with comprehensive callback scenarios. -func (suite *MiddlewareTestSuite) TestOnTimeoutPacketWithCallback() { - var ( - packet channeltypes.Packet - contractData evmtypes.CompiledContract - contractAddr common.Address - ) - - testCases := []struct { - name string - malleate func() - memo func() string - onSendRequired bool - expError string - }{ - // SUCCESS CASES - { - name: "success - callback with timeout", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 1_000_000) - }, - onSendRequired: true, - expError: "", - }, - { - name: "success - callback with maximum gas limit", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 10_000_000) - }, - onSendRequired: true, - expError: "", - }, - { - name: "success - no callback in memo (regular timeout)", - malleate: nil, - memo: func() string { - return "" - }, - onSendRequired: true, - expError: "", - }, - - // FAILURE CASES - Invalid Contract - { - name: "failure - callback to non-existent contract", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "0x1234567890123456789012345678901234567890", - "gas_limit": "%d" - } - }`, 1_000_000) - }, - onSendRequired: true, - expError: "ABCI code: 4", - }, - { - name: "failure - callback to empty address", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "0x0000000000000000000000000000000000000000", - "gas_limit": "%d" - } - }`, 1_000_000) - }, - onSendRequired: true, - expError: "ABCI code: 4", - }, - - // FAILURE CASES - Invalid Calldata - { - name: "failure - timeout callback with calldata (should be empty)", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d", - "calldata": "%x" - } - }`, contractAddr, 1_000_000, []byte{0xab, 0xcd, 0xef, 0x12}) - }, - onSendRequired: true, - expError: "ABCI code: 3", - }, - - // FAILURE CASES - Gas Issues - { - name: "failure - insufficient gas limit", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 1000) // Very low gas - }, - onSendRequired: true, - expError: "ABCI code: 9", - }, - { - name: "success - zero gas limit (defaults to max)", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "0" - } - }`, contractAddr) - }, - onSendRequired: true, - expError: "", - }, - - // FAILURE CASES - Invalid Memo Format - { - name: "failure - malformed JSON memo", - malleate: nil, - memo: func() string { - return `{"src_callback": {"address": "invalid_json"` - }, - onSendRequired: true, - expError: "invalid callback data", - }, - { - name: "failure - invalid callback address format", - malleate: nil, - memo: func() string { - return `{"src_callback": {"address": "not_hex_address", "gas_limit": "1000000"}}` - }, - onSendRequired: true, - expError: "invalid callback data", - }, - - // FAILURE CASES - Base IBC Failures (should not execute callback) - { - name: "failure - malformed packet data (no callback execution)", - malleate: func() { - packet.Data = []byte("malformed data") - }, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 1_000_000) - }, - onSendRequired: false, - expError: "cannot unmarshal ICS-20 transfer packet data", - }, - - // EDGE CASES - { - name: "failure - callback with minimal gas", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 1000) // Minimal and insufficient - }, - onSendRequired: true, - expError: "out of gas", - }, - { - name: "success - timeout with refund verification", - malleate: nil, - memo: func() string { - return fmt.Sprintf(`{ - "src_callback": { - "address": "%s", - "gas_limit": "%d" - } - }`, contractAddr, 2_000_000) - }, - onSendRequired: true, - expError: "", - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - - ctxA := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - - bondDenom, err := evmApp.StakingKeeper.BondDenom(ctxA) - suite.Require().NoError(err) - - sendAmt := ibctesting.DefaultCoinAmount - sender := suite.evmChainA.SenderAccount.GetAddress() - receiver := suite.chainB.SenderAccount.GetAddress() - - // Deploy callback contract on source chain (evmChainA) - contractData, err = ibctestutil.LoadCounterWithCallbacksContract() - suite.Require().NoError(err) - - deploymentData := testutiltypes.ContractDeploymentData{ - Contract: contractData, - ConstructorArgs: nil, - } - - contractAddr, err = DeployContract(suite.T(), suite.evmChainA, deploymentData) - suite.Require().NoError(err) - - // Create packet data with memo - packetData := transfertypes.NewFungibleTokenPacketData( - bondDenom, - sendAmt.String(), - sender.String(), - receiver.String(), - tc.memo(), - ) - - path := suite.path - packet = channeltypes.Packet{ - Sequence: 1, - SourcePort: path.EndpointA.ChannelConfig.PortID, - SourceChannel: path.EndpointA.ChannelID, - DestinationPort: path.EndpointB.ChannelConfig.PortID, - DestinationChannel: path.EndpointB.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - // Apply test-specific malleate function - if tc.malleate != nil { - tc.malleate() - } - - transferStack, ok := evmApp.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) - suite.Require().True(ok) - - balBeforeTransfer := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - balAfterTransfer := balBeforeTransfer - feeAmt := evmibctesting.FeeCoins().AmountOf(bondDenom) - // Execute send if required (for proper escrow setup) - if tc.onSendRequired { - timeoutHeight := clienttypes.NewHeight(1, 110) - msg := transfertypes.NewMsgTransfer( - path.EndpointA.ChannelConfig.PortID, - path.EndpointA.ChannelID, - sdk.NewCoin(bondDenom, sendAmt), - sender.String(), - receiver.String(), - timeoutHeight, 0, tc.memo(), - ) - err = suite.evmChainA.SenderAccount.SetSequence(suite.evmChainA.SenderAccount.GetSequence() + 1) - suite.Require().NoError(err) - res, err := suite.evmChainA.SendMsgs(msg) - suite.Require().NoError(err) // message committed - - sentPacket, err := ibctesting.ParseV1PacketFromEvents(res.Events) - suite.Require().NoError(err) - - balAfterTransfer = evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - suite.Require().Equal( - balBeforeTransfer.Amount.Sub(sendAmt).Sub(feeAmt).String(), - balAfterTransfer.Amount.String(), - ) - escrowAddr := transfertypes.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - escrowedBal := evmApp.BankKeeper.GetBalance(ctxA, escrowAddr, bondDenom) - suite.Require().Equal(sendAmt.String(), escrowedBal.Amount.String()) - - // Use the actually sent packet for timeout - packet = sentPacket - } - - sourceChan := path.EndpointA.GetChannel() - err = transferStack.OnTimeoutPacket( - ctxA, - sourceChan.Version, - packet, - receiver, - ) - balAfterTimeout := evmApp.BankKeeper.GetBalance(ctxA, sender, bondDenom) - - // Validate results - if tc.expError == "" { - suite.Require().NoError(err, "Expected success but got error") - - // Verify callback execution by checking that counter was NOT decremented - // The onPacketTimeout function in the contract doesn't modify the counter, - // so we verify the callback was executed by checking the counter remains unchanged - if strings.Contains(tc.memo(), "src_callback") { - counterRes, err := evmApp.EVMKeeper.CallEVM( - ctxA, - contractData.ABI, - common.BytesToAddress(suite.evmChainA.SenderAccount.GetAddress()), - contractAddr, - false, - big.NewInt(100000), - "getCounter", - ) - suite.Require().NoError(err) - - var counter *big.Int - err = contractData.ABI.UnpackIntoInterface(&counter, "getCounter", counterRes.Ret) - suite.Require().NoError(err) - - // For timeout callbacks, counter should be -1 - // This verifies the callback was executed without error, but didn't change the counter - suite.Require().Equal(big.NewInt(-1).String(), counter.String(), "Counter should be -1 for timeout callbacks") - } - - // Verify refund for timeouts (tokens should always be refunded on timeout) - if tc.onSendRequired { - escrowAddr := transfertypes.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - escrowedBal := evmApp.BankKeeper.GetBalance(ctxA, escrowAddr, bondDenom) - - // For timeouts, tokens should always be refunded - suite.Require().True(escrowedBal.IsZero(), "Escrowed balance should be zero after timeout refund") - suite.Require().Equal(balAfterTransfer.Amount.Add(sendAmt).String(), balAfterTimeout.Amount.String(), "Sender balance should be refunded on timeout") - } - } else { - // For timeout callback failures, verify that counter was NOT changed - if strings.Contains(tc.memo(), "src_callback") && strings.Contains(tc.expError, "ABCI code") { - counterRes, err := evmApp.EVMKeeper.CallEVM( - ctxA, - contractData.ABI, - common.BytesToAddress(suite.evmChainA.SenderAccount.GetAddress()), - contractAddr, - false, - big.NewInt(100000), - "getCounter", - ) - // Counter should remain 0 if callback failed - if err == nil { - var counter *big.Int - err = contractData.ABI.UnpackIntoInterface(&counter, "getCounter", counterRes.Ret) - if err == nil { - suite.Require().Equal(big.NewInt(0).String(), counter.String(), "Counter should remain 0 on timeout callback failure") - } - } - } - - // For timeout callback failures, the base timeout logic should still work - // unless it's a fundamental packet data issue - if tc.onSendRequired && !strings.Contains(tc.expError, "cannot unmarshal") { - // Even if callback fails, the timeout refund should still happen - escrowAddr := transfertypes.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - escrowedBal := evmApp.BankKeeper.GetBalance(ctxA, escrowAddr, bondDenom) - - suite.Require().True(escrowedBal.IsZero(), "Escrowed balance should be zero after timeout refund even with callback failure") - suite.Require().Equal(balAfterTransfer.Amount.Add(sendAmt).String(), balAfterTimeout.Amount.String(), "Sender balance should be refunded on timeout") - } - } - }) - } -} - -// TestOnTimeoutPacketNativeErc20 tests the OnTimeoutPacket method for native ERC20 tokens. -func (suite *MiddlewareTestSuite) TestOnTimeoutPacketNativeErc20() { - var packet channeltypes.Packet - - testCases := []struct { - name string - malleate func() - expError string - expRefund bool - }{ - { - name: "pass: refund escrowed native erc20 coin", - malleate: nil, - expError: "", - expRefund: true, - }, - { - name: "fail: malformed packet data", - malleate: func() { - packet.Data = []byte("malformed data") - }, - expError: "cannot unmarshal ICS-20 transfer packet data", - expRefund: false, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - nativeErc20 := SetupNativeErc20(suite.T(), suite.evmChainA, suite.evmChainA.SenderAccounts[0]) - - evmCtx := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - - timeoutHeight := clienttypes.NewHeight(1, 110) - path := suite.path - chainBAccount := suite.chainB.SenderAccount.GetAddress() - - sendAmt := math.NewIntFromBigInt(nativeErc20.InitialBal) - senderEthAddr := nativeErc20.Account - sender := sdk.AccAddress(senderEthAddr.Bytes()) - receiver := suite.chainB.SenderAccount.GetAddress() - - msg := transfertypes.NewMsgTransfer( - path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - sdk.NewCoin(nativeErc20.Denom, sendAmt), sender.String(), receiver.String(), - timeoutHeight, 0, "", - ) - - escrowAddr := transfertypes.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - // checkEscrow is a check function to ensure the native erc20 token is escrowed. - checkEscrow := func() { - erc20BalAfterIbcTransfer := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal( - new(big.Int).Sub(nativeErc20.InitialBal, sendAmt.BigInt()).String(), - erc20BalAfterIbcTransfer.String(), - ) - escrowedBal := evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().Equal(sendAmt.String(), escrowedBal.Amount.String()) - } - - // checkRefund is a check function to ensure refund is processed. - checkRefund := func() { - escrowedBal := evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().True(escrowedBal.IsZero()) - - // Check erc20 balance is same as initial balance after refund. - erc20BalAfterIbcTransfer := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal(nativeErc20.InitialBal.String(), erc20BalAfterIbcTransfer.String()) - } - _, err := suite.evmChainA.SendMsgs(msg) - suite.Require().NoError(err) // message committed - checkEscrow() - - transferStack, ok := suite.evmChainA.App.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) - suite.Require().True(ok) - - packetData := transfertypes.NewFungibleTokenPacketData( - nativeErc20.Denom, - sendAmt.String(), - sender.String(), - chainBAccount.String(), - "", - ) - packet = channeltypes.Packet{ - Sequence: 1, - SourcePort: path.EndpointA.ChannelConfig.PortID, - SourceChannel: path.EndpointA.ChannelID, - DestinationPort: path.EndpointB.ChannelConfig.PortID, - DestinationChannel: path.EndpointB.ChannelID, - Data: packetData.GetBytes(), - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - TimeoutTimestamp: 0, - } - - if tc.malleate != nil { - tc.malleate() - } - - sourceChan := path.EndpointA.GetChannel() - err = transferStack.OnTimeoutPacket( - evmCtx, - sourceChan.Version, - packet, - receiver, - ) - - if tc.expError == "" { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - suite.Require().Contains(err.Error(), tc.expError) - } - - if tc.expRefund { - checkRefund() - } else { - checkEscrow() - } - }) - } -} diff --git a/evmd/tests/ibc/ics02_precompile_client_test.go b/evmd/tests/ibc/ics02_precompile_client_test.go deleted file mode 100644 index 4914ffdc6..000000000 --- a/evmd/tests/ibc/ics02_precompile_client_test.go +++ /dev/null @@ -1,902 +0,0 @@ -package ibc - -import ( - "fmt" - "math/big" - "testing" - "time" - - "github.com/stretchr/testify/suite" - - abci "github.com/cometbft/cometbft/abci/types" - - "github.com/cosmos/evm/evmd" - "github.com/cosmos/evm/evmd/tests/integration" - "github.com/cosmos/evm/precompiles/ics02" - evmibctesting "github.com/cosmos/evm/testutil/ibc" - "github.com/cosmos/gogoproto/proto" - clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" - commitmenttypesv2 "github.com/cosmos/ibc-go/v10/modules/core/23-commitment/types/v2" - ibchost "github.com/cosmos/ibc-go/v10/modules/core/24-host" - ibcexported "github.com/cosmos/ibc-go/v10/modules/core/exported" - ibctm "github.com/cosmos/ibc-go/v10/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v10/testing" -) - -type ICS02ClientTestSuite struct { - suite.Suite - - coordinator *evmibctesting.Coordinator - - // testing chains used for convenience and readability - chainA *evmibctesting.TestChain - chainAPrecompile *ics02.Precompile - chainB *evmibctesting.TestChain - chainBPrecompile *ics02.Precompile - - pathBToA *evmibctesting.Path -} - -func (s *ICS02ClientTestSuite) SetupTest() { - s.coordinator = evmibctesting.NewCoordinator(s.T(), 2, 0, integration.SetupEvmd) - s.chainA = s.coordinator.GetChain(evmibctesting.GetEvmChainID(1)) - s.chainB = s.coordinator.GetChain(evmibctesting.GetEvmChainID(2)) - - s.pathBToA = evmibctesting.NewTransferPath(s.chainB, s.chainA) - s.pathBToA.Setup() - - evmAppA := s.chainA.App.(*evmd.EVMD) - s.chainAPrecompile = ics02.NewPrecompile( - evmAppA.AppCodec(), - evmAppA.IBCKeeper.ClientKeeper, - ) - evmAppB := s.chainB.App.(*evmd.EVMD) - s.chainBPrecompile = ics02.NewPrecompile( - evmAppA.AppCodec(), - evmAppB.IBCKeeper.ClientKeeper, - ) -} - -func (s *ICS02ClientTestSuite) TestGetClientState() { - var ( - calldata []byte - expClientState []byte - expErr bool - ) - - testCases := []struct { - name string - malleate func() - }{ - { - name: "success", - malleate: func() { - clientID := ibctesting.FirstClientID - clientState, found := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientState( - s.chainA.GetContext(), - clientID, - ) - s.Require().True(found) - - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.GetClientStateMethod, clientID) - s.Require().NoError(err) - - expClientState, err = proto.Marshal(clientState) - s.Require().NoError(err) - }, - }, - { - name: "failure: invalid client ID", - malleate: func() { - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.GetClientStateMethod, ibctesting.InvalidID) - s.Require().NoError(err) - expErr = true - }, - }, - { - name: "failure: client not found", - malleate: func() { - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.GetClientStateMethod, ibctesting.SecondClientID) - s.Require().NoError(err) - expErr = true - }, - }, - { - name: "failure: invalid calldata", - malleate: func() { - calldata = []byte(ibctesting.InvalidID) - expErr = true - }, - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - // == reset test state == - s.SetupTest() - - expClientState = nil - expErr = false - // ==== - - senderIdx := 1 - senderAccount := s.chainA.SenderAccounts[senderIdx] - - // setup - tc.malleate() - - _, _, resp, err := s.chainA.SendEvmTx(senderAccount, senderIdx, s.chainAPrecompile.Address(), big.NewInt(0), calldata, 100_000) - if expErr { - s.Require().Error(err) - return - } - s.Require().NoError(err) - - // decode result - out, err := s.chainAPrecompile.Unpack(ics02.GetClientStateMethod, resp.Ret) - s.Require().NoError(err) - - clientStateBz, ok := out[0].([]byte) - s.Require().True(ok) - s.Require().Equal(expClientState, clientStateBz) - }) - } -} - -func (s *ICS02ClientTestSuite) TestUpdateClient() { - var ( - expResult uint8 - calldata []byte - expErr bool - ) - - testCases := []struct { - name string - malleate func() - }{ - { - name: "success: update client", - malleate: func() { - clientID := ibctesting.FirstClientID - // == construct update header == - // 1. Update chain B to have new header - s.chainB.Coordinator.CommitBlock(s.chainB, s.chainA) - // 2. Construct update header - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - header, err := s.pathBToA.EndpointA.Chain.IBCClientHeader(s.pathBToA.EndpointA.Chain.LatestCommittedHeader, trustedHeight) - s.Require().NoError(err) - - anyHeader, err := clienttypes.PackClientMessage(header) - s.Require().NoError(err) - - updateBz, err := anyHeader.Marshal() - s.Require().NoError(err) - - calldata, err = s.chainAPrecompile.Pack(ics02.UpdateClientMethod, clientID, updateBz) - s.Require().NoError(err) - // ==== - - expResult = ics02.UpdateResultSuccess - }, - }, - { - name: "success: noop", - malleate: func() { - clientID := ibctesting.FirstClientID - // == construct update header == - // 1. Update chain B to have new header - s.chainB.Coordinator.CommitBlock(s.chainB, s.chainA) - // 2. Construct update header - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - header, err := s.pathBToA.EndpointA.Chain.IBCClientHeader(s.pathBToA.EndpointA.Chain.LatestCommittedHeader, trustedHeight) - s.Require().NoError(err) - - anyHeader, err := clienttypes.PackClientMessage(header) - s.Require().NoError(err) - - updateBz, err := anyHeader.Marshal() - s.Require().NoError(err) - - calldata, err = s.chainAPrecompile.Pack(ics02.UpdateClientMethod, clientID, updateBz) - s.Require().NoError(err) - // ==== - - err = s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.UpdateClient(s.chainA.GetContext(), clientID, header) - s.Require().NoError(err) - - expResult = ics02.UpdateResultSuccess - }, - }, - { - name: "success: valid fork misbehaviour", - malleate: func() { - clientID := ibctesting.FirstClientID - // == construct update header == - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - trustedVals, ok := s.chainB.TrustedValidators[trustedHeight.RevisionHeight] - s.Require().True(ok) - - err := s.pathBToA.EndpointB.UpdateClient() - s.Require().NoError(err) - - height := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - - misbehaviour := &ibctm.Misbehaviour{ - ClientId: clientID, - Header1: s.chainB.CreateTMClientHeader(s.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, s.chainB.ProposedHeader.Time.Add(time.Minute), s.chainB.Vals, s.chainB.NextVals, trustedVals, s.chainB.Signers), - Header2: s.chainB.CreateTMClientHeader(s.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, s.chainB.ProposedHeader.Time, s.chainB.Vals, s.chainB.NextVals, trustedVals, s.chainB.Signers), - } - - anyMisbehavior, err := clienttypes.PackClientMessage(misbehaviour) - s.Require().NoError(err) - - updateBz, err := anyMisbehavior.Marshal() - s.Require().NoError(err) - - calldata, err = s.chainAPrecompile.Pack(ics02.UpdateClientMethod, clientID, updateBz) - s.Require().NoError(err) - // ==== - - expResult = ics02.UpdateResultMisbehaviour - }, - }, - { - name: "failure: invalid client ID", - malleate: func() { - clientID := ibctesting.FirstClientID - // == construct update header == - // 1. Update chain B to have new header - s.chainB.Coordinator.CommitBlock(s.chainB, s.chainA) - // 2. Construct update header - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - header, err := s.pathBToA.EndpointA.Chain.IBCClientHeader(s.pathBToA.EndpointA.Chain.LatestCommittedHeader, trustedHeight) - s.Require().NoError(err) - - anyHeader, err := clienttypes.PackClientMessage(header) - s.Require().NoError(err) - - updateBz, err := anyHeader.Marshal() - s.Require().NoError(err) - - // use invalid client ID - calldata, err = s.chainAPrecompile.Pack(ics02.UpdateClientMethod, ibctesting.InvalidID, updateBz) - s.Require().NoError(err) - // ==== - expErr = true - }, - }, - { - name: "failure: invalid client message", - malleate: func() { - clientID := ibctesting.FirstClientID - - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.UpdateClientMethod, clientID, []byte(ibctesting.InvalidID)) - s.Require().NoError(err) - // ==== - expErr = true - }, - }, - { - name: "failure: invalid calldata", - malleate: func() { - calldata = []byte(ibctesting.InvalidID) - expErr = true - }, - }, - { - name: "failure: invalid header update", - malleate: func() { - clientID := ibctesting.FirstClientID - // == construct update header == - // 1. Update chain B to have new header - s.chainB.Coordinator.CommitBlock(s.chainB, s.chainA) - // 2. Construct update header - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - header, err := s.pathBToA.EndpointA.Chain.IBCClientHeader(s.pathBToA.EndpointA.Chain.LatestCommittedHeader, trustedHeight) - s.Require().NoError(err) - - // modify header to be invalid - header.Header.Time = header.Header.Time.Add(10 * time.Second) - - anyHeader, err := clienttypes.PackClientMessage(header) - s.Require().NoError(err) - - updateBz, err := anyHeader.Marshal() - s.Require().NoError(err) - - calldata, err = s.chainAPrecompile.Pack(ics02.UpdateClientMethod, clientID, updateBz) - s.Require().NoError(err) - // ==== - - expErr = true - }, - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - // == reset test state == - s.SetupTest() - - expResult = 0 - expErr = false - calldata = nil - // ==== - - senderIdx := 1 - senderAccount := s.chainA.SenderAccounts[senderIdx] - - // setup - tc.malleate() - - _, _, resp, err := s.chainA.SendEvmTx(senderAccount, senderIdx, s.chainAPrecompile.Address(), big.NewInt(0), calldata, 200_000) - if expErr { - s.Require().Error(err) - return - } - if err != nil { - s.FailNow("failed to send tx", "error: %v", err, "vmerror: %v", resp.VmError) - } - - // decode result - out, err := s.chainAPrecompile.Unpack(ics02.UpdateClientMethod, resp.Ret) - s.Require().NoError(err) - - res, ok := out[0].(uint8) - s.Require().True(ok) - s.Require().Equal(expResult, res) - }) - } -} - -func (s *ICS02ClientTestSuite) TestVerifyMembership() { - var ( - calldata []byte - expErr bool - expResult *big.Int - ) - - testCases := []struct { - name string - malleate func() - }{ - { - name: "success: prove membership of clientState", - malleate: func() { - clientID := ibctesting.FirstClientID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - - clientKey := ibchost.FullClientStateKey(clientID) - clientProof, proofHeight := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - // get pure value from chain B - res, err := s.chainB.App.Query( - s.chainB.GetContext().Context(), - &abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", ibcexported.StoreKey), - Height: int64(trustedHeight.RevisionHeight - 1), //nolint:gosec - Data: clientKey, - }) - s.Require().NoError(err) - value := res.Value - - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyMembershipMethod, - clientID, - clientProof, - trustedHeight, - pathBz, - value, - ) - s.Require().NoError(err) - - timestampNano, err := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientTimestampAtHeight(s.chainA.GetContext(), clientID, proofHeight) - s.Require().NoError(err) - - expResult = big.NewInt(int64(timestampNano / 1_000_000_000)) //nolint:gosec - - // verify membership on-chain to ensure proof is valid - path := commitmenttypesv2.NewMerklePath(pathBz...) - err = s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.VerifyMembership( - s.chainA.GetContext(), - clientID, - proofHeight, - 0, - 0, - clientProof, - path, - value, - ) - s.Require().NoError(err) - }, - }, - { - name: "failure: pass non-membership proof as membership proof", - malleate: func() { - existingClientID := ibctesting.FirstClientID - missingClientID := ibctesting.SecondClientID // NOTE: use a non-existent client ID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - existingClientID, - ) - - clientKey := ibchost.FullClientStateKey(missingClientID) - clientProof, _ := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - // get pure value from chain B (for the existing client) - res, err := s.chainB.App.Query( - s.chainB.GetContext().Context(), - &abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", ibcexported.StoreKey), - Height: int64(trustedHeight.RevisionHeight - 1), //nolint:gosec - Data: ibchost.FullClientStateKey(existingClientID), - }) - s.Require().NoError(err) - value := res.Value - - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyMembershipMethod, - existingClientID, - clientProof, - trustedHeight, - pathBz, - value, - ) - s.Require().NoError(err) - - expErr = true - }, - }, - { - name: "failure: invalid client ID", - malleate: func() { - clientID := ibctesting.FirstClientID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - - clientKey := ibchost.FullClientStateKey(clientID) - clientProof, _ := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - // get pure value from chain B - res, err := s.chainB.App.Query( - s.chainB.GetContext().Context(), - &abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", ibcexported.StoreKey), - Height: int64(trustedHeight.RevisionHeight - 1), //nolint:gosec - Data: clientKey, - }) - s.Require().NoError(err) - value := res.Value - - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyMembershipMethod, - ibctesting.InvalidID, // use invalid client ID - clientProof, - trustedHeight, - pathBz, - value, - ) - s.Require().NoError(err) - - expErr = true - }, - }, - { - name: "failure: invalid proof", - malleate: func() { - clientID := ibctesting.FirstClientID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - - clientKey := ibchost.FullClientStateKey(clientID) - // get pure value from chain B - res, err := s.chainB.App.Query( - s.chainB.GetContext().Context(), - &abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", ibcexported.StoreKey), - Height: int64(trustedHeight.RevisionHeight - 1), //nolint:gosec - Data: clientKey, - }) - s.Require().NoError(err) - value := res.Value - - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyMembershipMethod, - clientID, - []byte(ibctesting.InvalidID), // use invalid client proof - trustedHeight, - pathBz, - value, - ) - s.Require().NoError(err) - - expErr = true - }, - }, - { - name: "failure: invalid height", - malleate: func() { - clientID := ibctesting.FirstClientID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - - clientKey := ibchost.FullClientStateKey(clientID) - clientProof, _ := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - // get pure value from chain B - res, err := s.chainB.App.Query( - s.chainB.GetContext().Context(), - &abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", ibcexported.StoreKey), - Height: int64(trustedHeight.RevisionHeight - 1), //nolint:gosec - Data: clientKey, - }) - s.Require().NoError(err) - value := res.Value - - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyMembershipMethod, - clientID, - clientProof, - clienttypes.NewHeight(69, 420), // use invalid height - pathBz, - value, - ) - s.Require().NoError(err) - - expErr = true - }, - }, - { - name: "failure: invalid path", - malleate: func() { - clientID := ibctesting.FirstClientID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - - clientKey := ibchost.FullClientStateKey(clientID) - clientProof, _ := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - // get pure value from chain B - res, err := s.chainB.App.Query( - s.chainB.GetContext().Context(), - &abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", ibcexported.StoreKey), - Height: int64(trustedHeight.RevisionHeight - 1), //nolint:gosec - Data: clientKey, - }) - s.Require().NoError(err) - value := res.Value - - pathBz := [][]byte{[]byte(ibctesting.InvalidID), clientKey} // use invalid path - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyMembershipMethod, - clientID, - clientProof, - trustedHeight, - pathBz, - value, - ) - s.Require().NoError(err) - - expErr = true - }, - }, - { - name: "failure: invalid value", - malleate: func() { - clientID := ibctesting.FirstClientID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - - clientKey := ibchost.FullClientStateKey(clientID) - clientProof, _ := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyMembershipMethod, - clientID, - clientProof, - trustedHeight, - pathBz, - []byte(ibctesting.InvalidID), // use invalid value - ) - s.Require().NoError(err) - - expErr = true - }, - }, - { - name: "failure: invalid calldata", - malleate: func() { - calldata = []byte(ibctesting.InvalidID) - expErr = true - }, - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - // == reset test state == - s.SetupTest() - - expErr = false - calldata = nil - // ==== - - senderIdx := 1 - senderAccount := s.chainA.SenderAccounts[senderIdx] - - // setup - tc.malleate() - - _, _, resp, err := s.chainA.SendEvmTx(senderAccount, senderIdx, s.chainAPrecompile.Address(), big.NewInt(0), calldata, 100_000) - if expErr { - s.Require().Error(err) - return - } - s.Require().NoError(err) - - // decode result - out, err := s.chainAPrecompile.Unpack(ics02.VerifyMembershipMethod, resp.Ret) - s.Require().NoError(err) - - res, ok := out[0].(*big.Int) - s.Require().True(ok) - s.Require().Equal(expResult, res) - }) - } -} - -func (s *ICS02ClientTestSuite) TestVerifyNonMembership() { - var ( - calldata []byte - expErr bool - expResult *big.Int - ) - - testCases := []struct { - name string - malleate func() - }{ - { - name: "success: prove non-membership of clientState", - malleate: func() { - existingClientID := ibctesting.FirstClientID - missingClientID := ibctesting.SecondClientID // NOTE: use a non-existent client ID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - existingClientID, - ) - - clientKey := ibchost.FullClientStateKey(missingClientID) - clientProof, proofHeight := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyNonMembershipMethod, - existingClientID, - clientProof, - trustedHeight, - pathBz, - ) - s.Require().NoError(err) - - timestampNano, err := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientTimestampAtHeight(s.chainA.GetContext(), existingClientID, proofHeight) - s.Require().NoError(err) - - expResult = big.NewInt(int64(timestampNano / 1_000_000_000)) //nolint:gosec - - // verify non-membership on-chain to ensure proof is valid - path := commitmenttypesv2.NewMerklePath(pathBz...) - err = s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.VerifyNonMembership( - s.chainA.GetContext(), - existingClientID, - proofHeight, - 0, - 0, - clientProof, - path, - ) - s.Require().NoError(err) - }, - }, - { - name: "failure: pass membership proof as non-membership proof", - malleate: func() { - clientID := ibctesting.FirstClientID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - clientID, - ) - - clientKey := ibchost.FullClientStateKey(clientID) - clientProof, _ := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyNonMembershipMethod, - clientID, - clientProof, - trustedHeight, - pathBz, - ) - s.Require().NoError(err) - - expErr = true - }, - }, - { - name: "failure: invalid client ID", - malleate: func() { - existingClientID := ibctesting.FirstClientID - missingClientID := ibctesting.SecondClientID // NOTE: use a non-existent client ID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - existingClientID, - ) - - clientKey := ibchost.FullClientStateKey(missingClientID) - clientProof, _ := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyNonMembershipMethod, - ibctesting.InvalidID, // use invalid client ID - clientProof, - trustedHeight, - pathBz, - ) - s.Require().NoError(err) - - expErr = true - }, - }, - { - name: "failure: invalid proof", - malleate: func() { - existingClientID := ibctesting.FirstClientID - missingClientID := ibctesting.SecondClientID // NOTE: use a non-existent client ID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - existingClientID, - ) - - clientKey := ibchost.FullClientStateKey(missingClientID) - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyNonMembershipMethod, - existingClientID, - []byte(ibctesting.InvalidID), // use invalid client proof - trustedHeight, - pathBz, - ) - s.Require().NoError(err) - - expErr = true - }, - }, - { - name: "failure: invalid height", - malleate: func() { - existingClientID := ibctesting.FirstClientID - missingClientID := ibctesting.SecondClientID // NOTE: use a non-existent client ID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - existingClientID, - ) - - clientKey := ibchost.FullClientStateKey(missingClientID) - clientProof, _ := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - pathBz := [][]byte{[]byte(ibcexported.StoreKey), clientKey} - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyNonMembershipMethod, - existingClientID, - clientProof, - clienttypes.NewHeight(69, 420), // use invalid height - pathBz, - ) - s.Require().NoError(err) - - expErr = true - }, - }, - { - name: "failure: invalid path", - malleate: func() { - existingClientID := ibctesting.FirstClientID - missingClientID := ibctesting.SecondClientID // NOTE: use a non-existent client ID - trustedHeight := s.chainA.App.(*evmd.EVMD).IBCKeeper.ClientKeeper.GetClientLatestHeight( - s.chainA.GetContext(), - existingClientID, - ) - - clientKey := ibchost.FullClientStateKey(missingClientID) - clientProof, _ := s.pathBToA.EndpointA.QueryProofAtHeight(clientKey, trustedHeight.RevisionHeight) - - pathBz := [][]byte{[]byte(ibctesting.InvalidID), clientKey} // use invalid path - var err error - calldata, err = s.chainAPrecompile.Pack(ics02.VerifyNonMembershipMethod, - existingClientID, - clientProof, - trustedHeight, - pathBz, - ) - s.Require().NoError(err) - - expErr = true - }, - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - // == reset test state == - s.SetupTest() - - expErr = false - calldata = nil - // ==== - - senderIdx := 1 - senderAccount := s.chainA.SenderAccounts[senderIdx] - - // setup - tc.malleate() - - _, _, resp, err := s.chainA.SendEvmTx(senderAccount, senderIdx, s.chainAPrecompile.Address(), big.NewInt(0), calldata, 100_000) - if expErr { - s.Require().Error(err) - return - } - s.Require().NoError(err) - - // decode result - out, err := s.chainAPrecompile.Unpack(ics02.VerifyNonMembershipMethod, resp.Ret) - s.Require().NoError(err) - - res, ok := out[0].(*big.Int) - s.Require().True(ok) - s.Require().Equal(expResult, res) - }) - } -} - -func TestICS02ClientTestSuite(t *testing.T) { - suite.Run(t, new(ICS02ClientTestSuite)) -} diff --git a/evmd/tests/ibc/ics20_precompile_transfer_test.go b/evmd/tests/ibc/ics20_precompile_transfer_test.go deleted file mode 100644 index fcb1df32c..000000000 --- a/evmd/tests/ibc/ics20_precompile_transfer_test.go +++ /dev/null @@ -1,360 +0,0 @@ -// Copied from https://github.com/cosmos/ibc-go/blob/7325bd2b00fd5e33d895770ec31b5be2f497d37a/modules/apps/transfer/transfer_test.go -// Why was this copied? -// This test suite was imported to validate that ExampleChain (an EVM-based chain) -// correctly supports IBC v1 token transfers using ibc-goโ€™s Transfer module logic. -// The test ensures that ics20 precompile transfer (A โ†’ B) behave as expected across channels. -package ibc - -import ( - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd" - "github.com/cosmos/evm/evmd/tests/integration" - "github.com/cosmos/evm/precompiles/ics20" - chainutil "github.com/cosmos/evm/testutil" - evmibctesting "github.com/cosmos/evm/testutil/ibc" - evmante "github.com/cosmos/evm/x/vm/ante" - transfertypes "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/query" -) - -type ICS20TransferTestSuite struct { - suite.Suite - - coordinator *evmibctesting.Coordinator - - // testing chains used for convenience and readability - chainA *evmibctesting.TestChain - chainAPrecompile *ics20.Precompile - chainB *evmibctesting.TestChain - chainBPrecompile *ics20.Precompile -} - -func (suite *ICS20TransferTestSuite) SetupTest() { - suite.coordinator = evmibctesting.NewCoordinator(suite.T(), 2, 0, integration.SetupEvmd) - suite.chainA = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(1)) - suite.chainB = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(2)) - - evmAppA := suite.chainA.App.(*evmd.EVMD) - suite.chainAPrecompile = ics20.NewPrecompile( - evmAppA.BankKeeper, - *evmAppA.StakingKeeper, - evmAppA.TransferKeeper, - evmAppA.IBCKeeper.ChannelKeeper, - ) - evmAppB := suite.chainB.App.(*evmd.EVMD) - suite.chainBPrecompile = ics20.NewPrecompile( - evmAppB.BankKeeper, - *evmAppB.StakingKeeper, - evmAppB.TransferKeeper, - evmAppB.IBCKeeper.ChannelKeeper, - ) -} - -// Constructs the following sends based on the established channels/connections -// 1 - from evmChainA to chainB -func (suite *ICS20TransferTestSuite) TestHandleMsgTransfer() { - var ( - sourceDenomToTransfer string - msgAmount sdkmath.Int - err error - nativeErc20 *NativeErc20Info - erc20 bool - ) - - // originally a basic test case from the IBC testing package, and it has been added as-is to ensure that - // it still works properly when invoked through the ics20 precompile. - testCases := []struct { - name string - malleate func(senderAcc evmibctesting.SenderAccount) - }{ - { - "transfer single denom", - func(_ evmibctesting.SenderAccount) { - evmAppA := suite.chainA.App.(*evmd.EVMD) - sourceDenomToTransfer, err = evmAppA.StakingKeeper.BondDenom(suite.chainA.GetContext()) - msgAmount = evmibctesting.DefaultCoinAmount - }, - }, - { - "transfer amount larger than int64", - func(_ evmibctesting.SenderAccount) { - var ok bool - evmAppA := suite.chainA.App.(*evmd.EVMD) - sourceDenomToTransfer, err = evmAppA.StakingKeeper.BondDenom(suite.chainA.GetContext()) - msgAmount, ok = sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64) - suite.Require().True(ok) - }, - }, - { - "transfer entire balance", - func(_ evmibctesting.SenderAccount) { - evmAppA := suite.chainA.App.(*evmd.EVMD) - sourceDenomToTransfer, err = evmAppA.StakingKeeper.BondDenom(suite.chainA.GetContext()) - msgAmount = transfertypes.UnboundedSpendLimit() - }, - }, - { - "native erc20 case", - func(senderAcc evmibctesting.SenderAccount) { - nativeErc20 = SetupNativeErc20(suite.T(), suite.chainA, senderAcc) - sourceDenomToTransfer = nativeErc20.Denom - msgAmount = sdkmath.NewIntFromBigInt(nativeErc20.InitialBal) - erc20 = true - }, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup between evmChainA and chainB - // NOTE: - // pathAToB.EndpointA = endpoint on evmChainA - // pathAToB.EndpointB = endpoint on chainB - pathAToB := evmibctesting.NewTransferPath(suite.chainA, suite.chainB) - pathAToB.Setup() - traceAToB := transfertypes.NewHop(pathAToB.EndpointB.ChannelConfig.PortID, pathAToB.EndpointB.ChannelID) - - senderIdx := 1 - senderAccount := suite.chainA.SenderAccounts[senderIdx] - senderAddr := senderAccount.SenderAccount.GetAddress() - - tc.malleate(senderAccount) - - evmAppA := suite.chainA.App.(*evmd.EVMD) - - GetBalance := func(addr sdk.AccAddress) sdk.Coin { - ctx := suite.chainA.GetContext() - if erc20 { - balanceAmt := evmAppA.Erc20Keeper.BalanceOf(ctx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, nativeErc20.Account) - return sdk.Coin{ - Denom: nativeErc20.Denom, - Amount: sdkmath.NewIntFromBigInt(balanceAmt), - } - } - return evmAppA.BankKeeper.GetBalance(ctx, addr, sourceDenomToTransfer) - } - - senderBalance := GetBalance(senderAddr) - suite.Require().NoError(err) - - timeoutHeight := clienttypes.NewHeight(1, 110) - originalCoin := sdk.NewCoin(sourceDenomToTransfer, msgAmount) - - data, err := suite.chainAPrecompile.Pack("transfer", - pathAToB.EndpointA.ChannelConfig.PortID, - pathAToB.EndpointA.ChannelID, - originalCoin.Denom, - originalCoin.Amount.BigInt(), - common.BytesToAddress(senderAddr.Bytes()), // source addr should be evm hex addr - suite.chainB.SenderAccount.GetAddress().String(), // receiver should be cosmos bech32 addr - timeoutHeight, - uint64(0), - "", - ) - suite.Require().NoError(err) - - res, _, _, err := suite.chainA.SendEvmTx(senderAccount, senderIdx, suite.chainAPrecompile.Address(), big.NewInt(0), data, 0) - suite.Require().NoError(err) // message committed - packet, err := evmibctesting.ParsePacketFromEvents(res.Events) - suite.Require().NoError(err) - - // Get the packet data to determine the amount of tokens being transferred (needed for sending entire balance) - packetData, err := transfertypes.UnmarshalPacketData(packet.GetData(), pathAToB.EndpointA.GetChannel().Version, "") - suite.Require().NoError(err) - transferAmount, ok := sdkmath.NewIntFromString(packetData.Token.Amount) - suite.Require().True(ok) - - afterSenderBalance := GetBalance(senderAddr) - suite.Require().Equal( - senderBalance.Amount.Sub(transferAmount).String(), - afterSenderBalance.Amount.String(), - ) - if msgAmount == transfertypes.UnboundedSpendLimit() { - suite.Require().Equal("0", afterSenderBalance.Amount.String(), "sender should have no balance left") - } - - relayerAddr := suite.chainA.SenderAccounts[0].SenderAccount.GetAddress() - relayerBalance := GetBalance(relayerAddr) - - // relay send - err = pathAToB.RelayPacket(packet) - suite.Require().NoError(err) // relay committed - - feeAmt := evmibctesting.FeeCoins().AmountOf(sourceDenomToTransfer) - // One for UpdateClient() and one for AcknowledgePacket() - relayPacketFeeAmt := feeAmt.Mul(sdkmath.NewInt(2)) - - afterRelayerBalance := GetBalance(relayerAddr) - suite.Require().Equal( - relayerBalance.Amount.Sub(relayPacketFeeAmt).String(), - afterRelayerBalance.Amount.String(), - ) - - escrowAddress := transfertypes.GetEscrowAddress(packet.GetSourcePort(), packet.GetSourceChannel()) - - // check that module account escrow address has locked the tokens - chainAEscrowBalance := evmAppA.BankKeeper.GetBalance( - suite.chainA.GetContext(), - escrowAddress, - sourceDenomToTransfer, - ) - suite.Require().Equal(transferAmount.String(), chainAEscrowBalance.Amount.String()) - - // check that voucher exists on chain B - evmAppB := suite.chainB.App.(*evmd.EVMD) - chainBDenom := transfertypes.NewDenom(originalCoin.Denom, traceAToB) - chainBBalance := evmAppB.BankKeeper.GetBalance( - suite.chainB.GetContext(), - suite.chainB.SenderAccount.GetAddress(), - chainBDenom.IBCDenom(), - ) - coinSentFromAToB := sdk.NewCoin(chainBDenom.IBCDenom(), transferAmount) - suite.Require().Equal(coinSentFromAToB, chainBBalance) - - // --------------------------------------------- - // Tests for Query endpoints of ICS20 precompile - // denoms query method - chainBAddr := common.BytesToAddress(suite.chainB.SenderAccount.GetAddress().Bytes()) - ctxB := evmante.BuildEvmExecutionCtx(suite.chainB.GetContext()) - evmRes, err := evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomsMethod, - query.PageRequest{ - Key: []byte{}, - Offset: 0, - Limit: 0, - CountTotal: false, - Reverse: false, - }, - ) - suite.Require().NoError(err) - var denomsResponse ics20.DenomsResponse - err = suite.chainBPrecompile.UnpackIntoInterface(&denomsResponse, ics20.DenomsMethod, evmRes.Ret) - suite.Require().NoError(err) - suite.Require().Equal(chainBDenom, denomsResponse.Denoms[0]) - - // denom query method with result - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomMethod, - chainBDenom.Hash().String(), - ) - suite.Require().NoError(err) - var denomResponse ics20.DenomResponse - err = suite.chainBPrecompile.UnpackIntoInterface(&denomResponse, ics20.DenomMethod, evmRes.Ret) - suite.Require().NoError(err) - suite.Require().Equal(chainBDenom, denomResponse.Denom) - - // denom query method not exists case - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomMethod, - "0000000000000000000000000000000000000000000000000000000000000000", - ) - suite.Require().NoError(err) - err = suite.chainBPrecompile.UnpackIntoInterface(&denomResponse, ics20.DenomMethod, evmRes.Ret) - suite.Require().NoError(err) - // ensure empty denom struct when not exist - suite.Require().Equal(denomResponse.Denom, transfertypes.Denom{Base: "", Trace: []transfertypes.Hop{}}) - - // denom query method invalid error case - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomMethod, - "INVALID-DENOM-HASH", - ) - suite.Require().ErrorContains(err, vm.ErrExecutionReverted.Error()) - - revertErr := chainutil.DecodeRevertReason(*evmRes) - suite.Require().Contains(revertErr.Error(), "invalid denom trace hash") - ctxB.GasMeter().RefundGas(ctxB.GasMeter().Limit(), "refund after error") - - // denomHash query method - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomHashMethod, - chainBDenom.Path(), - ) - suite.Require().NoError(err) - var denomHashResponse transfertypes.QueryDenomHashResponse - err = suite.chainBPrecompile.UnpackIntoInterface(&denomHashResponse, ics20.DenomHashMethod, evmRes.Ret) - suite.Require().NoError(err) - suite.Require().Equal(chainBDenom.Hash().String(), denomHashResponse.Hash) - - // denomHash query method not exists case - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomHashMethod, - "transfer/channel-0/erc20:not-exists-case", - ) - suite.Require().NoError(err) - err = suite.chainBPrecompile.UnpackIntoInterface(&denomHashResponse, ics20.DenomHashMethod, evmRes.Ret) - suite.Require().NoError(err) - suite.Require().Equal(denomHashResponse.Hash, "") - - // denomHash query method invalid error case - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomHashMethod, - "", - ) - suite.Require().ErrorContains(err, vm.ErrExecutionReverted.Error()) - - revertErr = chainutil.DecodeRevertReason(*evmRes) - suite.Require().Contains(revertErr.Error(), "invalid denomination for cross-chain transfer") - ctxB.GasMeter().RefundGas(ctxB.GasMeter().Limit(), "refund after error") - }) - } -} - -func TestICS20TransferTestSuite(t *testing.T) { - suite.Run(t, new(ICS20TransferTestSuite)) -} diff --git a/evmd/tests/ibc/ics20_recursive_precompile_calls_test.go b/evmd/tests/ibc/ics20_recursive_precompile_calls_test.go deleted file mode 100644 index a99414d58..000000000 --- a/evmd/tests/ibc/ics20_recursive_precompile_calls_test.go +++ /dev/null @@ -1,496 +0,0 @@ -// Copied from https://github.com/cosmos/ibc-go/blob/7325bd2b00fd5e33d895770ec31b5be2f497d37a/modules/apps/transfer/transfer_test.go -// Why was this copied? -// This test suite was imported to validate that ExampleChain (an EVM-based chain) -// correctly supports IBC v1 token transfers using ibc-goโ€™s Transfer module logic. -// The test ensures that ics20 precompile transfer (A โ†’ B) behave as expected across channels. -package ibc - -import ( - "fmt" - "math/big" - "testing" - - distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/evm/utils" - - "github.com/cosmos/evm/contracts" - testutiltypes "github.com/cosmos/evm/testutil/types" - erc20types "github.com/cosmos/evm/x/erc20/types" - evmtypes "github.com/cosmos/evm/x/vm/types" - - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd" - "github.com/cosmos/evm/evmd/tests/integration" - "github.com/cosmos/evm/precompiles/ics20" - evmibctesting "github.com/cosmos/evm/testutil/ibc" - transfertypes "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Test constants -const ( - // Token amounts - InitialTokenAmount = 1_000_000_000_000_000_000 // 1 token with 18 decimals - DelegationAmount = 1_000_000_000_000_000_000 // 1 token for delegation - RewardAmount = 100 // 100 base units for rewards - ExpectedRewards = "50.000000000000000000" // Expected reward amount after allocation - - // Test configuration - SenderIndex = 1 - TimeoutHeight = 110 -) - -// Test suite for ICS20 recursive precompile calls -// Tests the native balance handler bug where reverted distribution calls -// leave persistent bank events that are incorrectly aggregated - -type ICS20RecursivePrecompileCallsTestSuite struct { - suite.Suite - - coordinator *evmibctesting.Coordinator - - // testing chains used for convenience and readability - chainA *evmibctesting.TestChain - chainAPrecompile *ics20.Precompile - chainB *evmibctesting.TestChain - chainBPrecompile *ics20.Precompile -} - -type stakingRewards struct { - Delegator sdk.AccAddress - Validator stakingtypes.Validator - RewardAmt sdkmath.Int -} - -func (suite *ICS20RecursivePrecompileCallsTestSuite) prepareStakingRewards(ctx sdk.Context, stkRs ...stakingRewards) (sdk.Context, error) { - for _, r := range stkRs { - // set distribution module account balance which pays out the rewards - bondDenom, err := suite.chainA.App.(*evmd.EVMD).StakingKeeper.BondDenom(suite.chainA.GetContext()) - suite.Require().NoError(err) - coins := sdk.NewCoins(sdk.NewCoin(bondDenom, r.RewardAmt)) - if err := suite.mintCoinsForDistrMod(ctx, coins); err != nil { - return ctx, err - } - - // allocate rewards to validator - allocatedRewards := sdk.NewDecCoins(sdk.NewDecCoin(bondDenom, r.RewardAmt)) - if err := suite.chainA.App.(*evmd.EVMD).GetDistrKeeper().AllocateTokensToValidator(ctx, r.Validator, allocatedRewards); err != nil { - return ctx, err - } - } - return ctx, nil -} - -func (suite *ICS20RecursivePrecompileCallsTestSuite) mintCoinsForDistrMod(ctx sdk.Context, amount sdk.Coins) error { - // Mint tokens for the distribution module to simulate fee accrued - if err := suite.chainA.App.(*evmd.EVMD).GetBankKeeper().MintCoins( - ctx, - minttypes.ModuleName, - amount, - ); err != nil { - return err - } - - return suite.chainA.App.(*evmd.EVMD).GetBankKeeper().SendCoinsFromModuleToModule( - ctx, - minttypes.ModuleName, - distrtypes.ModuleName, - amount, - ) -} - -// setupRevertingContractForTesting configures the contract for delegation and reward testing -func (suite *ICS20RecursivePrecompileCallsTestSuite) setupContractForTesting( - contractAddr common.Address, - contractData evmtypes.CompiledContract, - senderAcc evmibctesting.SenderAccount, -) { - evmAppA := suite.chainA.App.(*evmd.EVMD) - ctxA := suite.chainA.GetContext() - senderAddr := senderAcc.SenderAccount.GetAddress() - senderEVMAddr := common.BytesToAddress(senderAddr.Bytes()) - deployerAddr := common.BytesToAddress(suite.chainA.SenderPrivKey.PubKey().Address().Bytes()) - - // Register ERC20 contract - _, err := evmAppA.Erc20Keeper.RegisterERC20(ctxA, &erc20types.MsgRegisterERC20{ - Signer: evmAppA.AccountKeeper.GetModuleAddress("gov").String(), - Erc20Addresses: []string{contractAddr.Hex()}, - }) - suite.Require().NoError(err, "registering ERC20 token should succeed") - suite.chainA.NextBlock() - - // Send native tokens to contract for delegation - bondDenom, err := evmAppA.StakingKeeper.BondDenom(ctxA) - suite.Require().NoError(err) - - contractAddrBech32, err := sdk.AccAddressFromHexUnsafe(contractAddr.Hex()[2:]) - suite.Require().NoError(err) - - deployerAddrBech32 := sdk.AccAddress(deployerAddr.Bytes()) - deployerBalance := evmAppA.BankKeeper.GetBalance(ctxA, deployerAddrBech32, bondDenom) - - // Send delegation amount to contract - sendAmount := sdkmath.NewInt(DelegationAmount) - if deployerBalance.Amount.LT(sendAmount) { - sendAmount = deployerBalance.Amount.Quo(sdkmath.NewInt(2)) - } - - err = evmAppA.BankKeeper.SendCoins( - ctxA, - deployerAddrBech32, - contractAddrBech32, - sdk.NewCoins(sdk.NewCoin(bondDenom, sendAmount)), - ) - suite.Require().NoError(err, "sending native tokens to contract should succeed") - - // Mint ERC20 tokens - _, err = evmAppA.GetEVMKeeper().CallEVM( - suite.chainA.GetContext(), - contractData.ABI, - deployerAddr, - contractAddr, - true, - nil, - "mint", - senderEVMAddr, - big.NewInt(InitialTokenAmount), - ) - suite.Require().NoError(err, "mint call failed") - suite.chainA.NextBlock() - - // Delegate tokens - vals, err := evmAppA.StakingKeeper.GetAllValidators(suite.chainA.GetContext()) - suite.Require().NoError(err) - - _, err = evmAppA.GetEVMKeeper().CallEVM( - ctxA, - contractData.ABI, - deployerAddr, - contractAddr, - true, - nil, - "delegate", - vals[0].OperatorAddress, - big.NewInt(DelegationAmount), - ) - suite.Require().NoError(err) - - // Verify delegation - valAddr, err := sdk.ValAddressFromBech32(vals[0].OperatorAddress) - suite.Require().NoError(err) - - amt, err := evmAppA.StakingKeeper.GetDelegation(suite.chainA.GetContext(), contractAddrBech32, valAddr) - suite.Require().NoError(err) - suite.Require().Equal(sendAmount.BigInt(), amt.Shares.BigInt()) - - // Setup rewards for testing - _, err = suite.prepareStakingRewards( - suite.chainA.GetContext(), - stakingRewards{ - Delegator: contractAddrBech32, - Validator: vals[0], - RewardAmt: sdkmath.NewInt(RewardAmount), - }, - ) - suite.Require().NoError(err) - suite.chainA.NextBlock() - - // Verify minted balance - bal := evmAppA.GetErc20Keeper().BalanceOf(ctxA, contractData.ABI, contractAddr, common.BytesToAddress(senderAddr)) - suite.Require().Equal(big.NewInt(InitialTokenAmount), bal, "unexpected ERC20 balance") -} - -func (suite *ICS20RecursivePrecompileCallsTestSuite) SetupTest() { - suite.coordinator = evmibctesting.NewCoordinator(suite.T(), 2, 0, integration.SetupEvmd) - suite.chainA = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(1)) - suite.chainB = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(2)) - - evmAppA := suite.chainA.App.(*evmd.EVMD) - suite.chainAPrecompile = ics20.NewPrecompile( - evmAppA.BankKeeper, - *evmAppA.StakingKeeper, - evmAppA.TransferKeeper, - evmAppA.IBCKeeper.ChannelKeeper, - ) - bondDenom, err := evmAppA.StakingKeeper.BondDenom(suite.chainA.GetContext()) - suite.Require().NoError(err) - - evmAppA.Erc20Keeper.GetTokenPair(suite.chainA.GetContext(), evmAppA.Erc20Keeper.GetTokenPairID(suite.chainA.GetContext(), bondDenom)) - // evmAppA.Erc20Keeper.SetNativePrecompile(suite.chainA.GetContext(), werc20.Address()) - - avail := evmAppA.Erc20Keeper.IsNativePrecompileAvailable(suite.chainA.GetContext(), common.HexToAddress("0xD4949664cD82660AaE99bEdc034a0deA8A0bd517")) - suite.Require().True(avail) - - evmAppB := suite.chainB.App.(*evmd.EVMD) - suite.chainBPrecompile = ics20.NewPrecompile( - evmAppB.BankKeeper, - *evmAppB.StakingKeeper, - evmAppB.TransferKeeper, - evmAppB.IBCKeeper.ChannelKeeper, - ) -} - -// Constructs the following sends based on the established channels/connections -// 1 - from evmChainA to chainB -func (suite *ICS20RecursivePrecompileCallsTestSuite) TestHandleMsgTransfer() { - var ( - sourceDenomToTransfer string - msgAmount sdkmath.Int - err error - nativeErc20 *NativeErc20Info - erc20 bool - ) - - // originally a basic test case from the IBC testing package, and it has been added as-is to ensure that - // it still works properly when invoked through the ics20 precompile. - testCases := []struct { - name string - malleate func(senderAcc evmibctesting.SenderAccount) - postCheck func(querier distributionkeeper.Querier, valAddr string, eventAmount int) - }{ - { - "test recursive precompile call with reverts", - func(senderAcc evmibctesting.SenderAccount) { - // Deploy recursive ERC20 contract with _beforeTokenTransfer override - contractData, err := contracts.LoadERC20RecursiveReverting() - suite.Require().NoError(err) - - deploymentData := testutiltypes.ContractDeploymentData{ - Contract: contractData, - ConstructorArgs: []interface{}{"RecursiveRevertingToken", "RRCT", uint8(18)}, - } - - contractAddr, err := DeployContract(suite.T(), suite.chainA, deploymentData) - suite.chainA.NextBlock() - suite.Require().NoError(err) - - // Setup contract info and test parameters - nativeErc20 = &NativeErc20Info{ - ContractAddr: contractAddr, - ContractAbi: contractData.ABI, - Denom: "erc20:" + contractAddr.Hex(), - InitialBal: big.NewInt(InitialTokenAmount), - Account: common.BytesToAddress(senderAcc.SenderAccount.GetAddress().Bytes()), - } - - sourceDenomToTransfer = nativeErc20.Denom - msgAmount = sdkmath.NewIntFromBigInt(nativeErc20.InitialBal) - erc20 = true - - // Setup contract for testing - suite.setupContractForTesting(contractAddr, contractData, senderAcc) - }, - func(querier distributionkeeper.Querier, valAddr string, eventAmount int) { - evmAppA := suite.chainA.App.(*evmd.EVMD) - bondDenom, err := evmAppA.StakingKeeper.BondDenom(suite.chainA.GetContext()) - suite.Require().NoError(err) - contractBondDenomBalance := evmAppA.BankKeeper.GetBalance(suite.chainA.GetContext(), nativeErc20.ContractAddr.Bytes(), bondDenom) - suite.Require().Equal(contractBondDenomBalance.Amount, sdkmath.NewInt(0)) - // Check distribution rewards after transfer - afterRewards, err := querier.DelegationRewards(suite.chainA.GetContext(), &distrtypes.QueryDelegationRewardsRequest{ - DelegatorAddress: utils.Bech32StringFromHexAddress(nativeErc20.ContractAddr.String()), - ValidatorAddress: valAddr, - }) - suite.Require().NoError(err) - suite.Require().Equal(afterRewards.Rewards[0].Amount.String(), ExpectedRewards) - suite.Require().Equal(eventAmount, 20) - }, - }, - { - "test recursive precompile call without reverts", - func(senderAcc evmibctesting.SenderAccount) { - // Deploy recursive ERC20 contract with _beforeTokenTransfer override - contractData, err := contracts.LoadERC20RecursiveNonReverting() - suite.Require().NoError(err) - - deploymentData := testutiltypes.ContractDeploymentData{ - Contract: contractData, - ConstructorArgs: []interface{}{"RecursiveNonRevertingToken", "RNRCT", uint8(18)}, - } - - contractAddr, err := DeployContract(suite.T(), suite.chainA, deploymentData) - suite.chainA.NextBlock() - suite.Require().NoError(err) - - // Setup contract info and test parameters - nativeErc20 = &NativeErc20Info{ - ContractAddr: contractAddr, - ContractAbi: contractData.ABI, - Denom: "erc20:" + contractAddr.Hex(), - InitialBal: big.NewInt(InitialTokenAmount), - Account: common.BytesToAddress(senderAcc.SenderAccount.GetAddress().Bytes()), - } - - sourceDenomToTransfer = nativeErc20.Denom - msgAmount = sdkmath.NewIntFromBigInt(nativeErc20.InitialBal) - erc20 = true - - // Setup contract for testing - suite.setupContractForTesting(contractAddr, contractData, senderAcc) - }, - func(querier distributionkeeper.Querier, valAddr string, eventAmount int) { - evmAppA := suite.chainA.App.(*evmd.EVMD) - bondDenom, err := evmAppA.StakingKeeper.BondDenom(suite.chainA.GetContext()) - suite.Require().NoError(err) - contractBondDenomBalance := evmAppA.BankKeeper.GetBalance(suite.chainA.GetContext(), nativeErc20.ContractAddr.Bytes(), bondDenom) - - suite.Require().Equal(contractBondDenomBalance.Amount, sdkmath.NewInt(50)) - - // Check distribution rewards after transfer - afterRewards, err := querier.DelegationRewards(suite.chainA.GetContext(), &distrtypes.QueryDelegationRewardsRequest{ - DelegatorAddress: utils.Bech32StringFromHexAddress(nativeErc20.ContractAddr.String()), - ValidatorAddress: valAddr, - }) - suite.Require().NoError(err) - suite.Require().Nil(afterRewards.Rewards) - suite.Require().Equal(eventAmount, 29) // 20 base events + (1 successful reward claim + 1 send + 1 receive + 1 message + 1 transfer) + 4 empty reward claims - }, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - pathAToB := evmibctesting.NewTransferPath(suite.chainA, suite.chainB) - pathAToB.Setup() - traceAToB := transfertypes.NewHop(pathAToB.EndpointB.ChannelConfig.PortID, pathAToB.EndpointB.ChannelID) - - senderAccount := suite.chainA.SenderAccounts[SenderIndex] - senderAddr := senderAccount.SenderAccount.GetAddress() - - tc.malleate(senderAccount) - - evmAppA := suite.chainA.App.(*evmd.EVMD) - - // Get balance helper function - GetBalance := func(addr sdk.AccAddress) sdk.Coin { - ctx := suite.chainA.GetContext() - if erc20 { - balanceAmt := evmAppA.Erc20Keeper.BalanceOf(ctx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, nativeErc20.Account) - return sdk.Coin{ - Denom: nativeErc20.Denom, - Amount: sdkmath.NewIntFromBigInt(balanceAmt), - } - } - return evmAppA.BankKeeper.GetBalance(ctx, addr, sourceDenomToTransfer) - } - - // Verify initial state - senderBalance := GetBalance(nativeErc20.ContractAddr.Bytes()) - suite.Require().NoError(err) - bondDenom, err := evmAppA.StakingKeeper.BondDenom(suite.chainA.GetContext()) - suite.Require().NoError(err) - contractBondDenomBalance := evmAppA.BankKeeper.GetBalance(suite.chainA.GetContext(), nativeErc20.ContractAddr.Bytes(), bondDenom) - suite.Require().Equal(contractBondDenomBalance.Amount, sdkmath.NewInt(0)) - - // Setup transfer parameters - timeoutHeight := clienttypes.NewHeight(1, TimeoutHeight) - originalCoin := sdk.NewCoin(sourceDenomToTransfer, msgAmount) - - // Check distribution rewards before transfer - querier := distributionkeeper.NewQuerier(evmAppA.DistrKeeper) - vals, err := evmAppA.StakingKeeper.GetAllValidators(suite.chainA.GetContext()) - suite.Require().NoError(err) - - beforeRewards, err := querier.DelegationRewards(suite.chainA.GetContext(), &distrtypes.QueryDelegationRewardsRequest{ - DelegatorAddress: utils.Bech32StringFromHexAddress(nativeErc20.ContractAddr.String()), - ValidatorAddress: vals[0].OperatorAddress, - }) - suite.Require().NoError(err) - suite.Require().Equal(beforeRewards.Rewards[0].Amount.String(), ExpectedRewards) - - // Execute ICS20 transfer (this triggers the bug) - data, err := suite.chainAPrecompile.Pack("transfer", - pathAToB.EndpointA.ChannelConfig.PortID, - pathAToB.EndpointA.ChannelID, - originalCoin.Denom, - originalCoin.Amount.BigInt(), - common.BytesToAddress(senderAddr.Bytes()), // source addr should be evm hex addr - suite.chainB.SenderAccount.GetAddress().String(), // receiver should be cosmos bech32 addr - timeoutHeight, - uint64(0), - "", - ) - suite.Require().NoError(err) - - res, _, _, err := suite.chainA.SendEvmTx(senderAccount, SenderIndex, suite.chainAPrecompile.Address(), big.NewInt(0), data, 0) - suite.Require().NoError(err) // message committed - packet, err := evmibctesting.ParsePacketFromEvents(res.Events) - suite.Require().NoError(err) - - eventAmount := len(res.Events) - fmt.Println(res.Events) - - tc.postCheck(querier, vals[0].OperatorAddress, eventAmount) - - // Get the packet data to determine the amount of tokens being transferred (needed for sending entire balance) - packetData, err := transfertypes.UnmarshalPacketData(packet.GetData(), pathAToB.EndpointA.GetChannel().Version, "") - suite.Require().NoError(err) - transferAmount, ok := sdkmath.NewIntFromString(packetData.Token.Amount) - suite.Require().True(ok) - - afterSenderBalance := GetBalance(senderAddr) - suite.Require().Equal( - senderBalance.Amount.Sub(transferAmount).String(), - afterSenderBalance.Amount.String(), - ) - if msgAmount == transfertypes.UnboundedSpendLimit() { - suite.Require().Equal("0", afterSenderBalance.Amount.String(), "sender should have no balance left") - } - - relayerAddr := suite.chainA.SenderAccounts[0].SenderAccount.GetAddress() - relayerBalance := GetBalance(relayerAddr) - - // relay send - pathAToB.EndpointA.Chain.SenderAccount = evmAppA.AccountKeeper.GetAccount(suite.chainA.GetContext(), relayerAddr) // update account in the path as the sequence recorded in that object is out of date - err = pathAToB.RelayPacket(packet) - suite.Require().NoError(err) // relay committed - - feeAmt := evmibctesting.FeeCoins().AmountOf(sourceDenomToTransfer) - - // One for UpdateClient() and one for AcknowledgePacket() - relayPacketFeeAmt := feeAmt.Mul(sdkmath.NewInt(2)) - - afterRelayerBalance := GetBalance(relayerAddr) - suite.Require().Equal( - relayerBalance.Amount.Sub(relayPacketFeeAmt).String(), - afterRelayerBalance.Amount.String(), - ) - - escrowAddress := transfertypes.GetEscrowAddress(packet.GetSourcePort(), packet.GetSourceChannel()) - - // check that module account escrow address has locked the tokens - chainAEscrowBalance := evmAppA.BankKeeper.GetBalance( - suite.chainA.GetContext(), - escrowAddress, - sourceDenomToTransfer, - ) - suite.Require().Equal(transferAmount.String(), chainAEscrowBalance.Amount.String()) - - // check that voucher exists on chain B - evmAppB := suite.chainB.App.(*evmd.EVMD) - chainBDenom := transfertypes.NewDenom(originalCoin.Denom, traceAToB) - chainBBalance := evmAppB.BankKeeper.GetBalance( - suite.chainB.GetContext(), - suite.chainB.SenderAccount.GetAddress(), - chainBDenom.IBCDenom(), - ) - coinSentFromAToB := sdk.NewCoin(chainBDenom.IBCDenom(), transferAmount) - suite.Require().Equal(coinSentFromAToB, chainBBalance) - }) - } -} - -func TestICS20RecursivePrecompileCallsTestSuite(t *testing.T) { - suite.Run(t, new(ICS20RecursivePrecompileCallsTestSuite)) -} diff --git a/evmd/tests/ibc/transfer_test.go b/evmd/tests/ibc/transfer_test.go deleted file mode 100644 index a386e1e68..000000000 --- a/evmd/tests/ibc/transfer_test.go +++ /dev/null @@ -1,288 +0,0 @@ -// Copied from https://github.com/cosmos/ibc-go/blob/7325bd2b00fd5e33d895770ec31b5be2f497d37a/modules/apps/transfer/transfer_test.go -// Why was this copied? -// This test suite was imported to validate that ExampleChain (an EVM-based chain) -// correctly supports IBC v1 token transfers using ibc-goโ€™s Transfer module logic. -// The test ensures that multi-hop transfers (A โ†’ B โ†’ C โ†’ B) behave as expected across channels. -package ibc - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd" - "github.com/cosmos/evm/evmd/tests/integration" - evmibctesting "github.com/cosmos/evm/testutil/ibc" - "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type TransferTestSuite struct { - suite.Suite - - coordinator *evmibctesting.Coordinator - - // testing chains used for convenience and readability - evmChainA *evmibctesting.TestChain - chainB *evmibctesting.TestChain - chainC *evmibctesting.TestChain -} - -func (suite *TransferTestSuite) SetupTest() { - suite.coordinator = evmibctesting.NewCoordinator(suite.T(), 1, 2, integration.SetupEvmd) - suite.evmChainA = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(1)) - suite.chainB = suite.coordinator.GetChain(evmibctesting.GetChainID(2)) - suite.chainC = suite.coordinator.GetChain(evmibctesting.GetChainID(3)) -} - -// Constructs the following sends based on the established channels/connections -// 1 - from evmChainA to chainB -// 2 - from chainB to chainC -// 3 - from chainC to chainB -func (suite *TransferTestSuite) TestHandleMsgTransfer() { - var ( - sourceDenomToTransfer string - msgAmount sdkmath.Int - err error - ) - - // originally a basic test case from the IBC testing package, and it has been added as-is to ensure that - // it still works properly when invoked by evmd app. - testCases := []struct { - name string - malleate func() - }{ - { - "transfer single denom", - func() { - msgAmount = evmibctesting.DefaultCoinAmount - }, - }, - { - "transfer amount larger than int64", - func() { - var ok bool - msgAmount, ok = sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64) - suite.Require().True(ok) - }, - }, - { - "transfer entire balance", - func() { - msgAmount = types.UnboundedSpendLimit() - }, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup between evmChainA and chainB - // NOTE: - // pathAToB.EndpointA = endpoint on evmChainA - // pathAToB.EndpointB = endpoint on chainB - pathAToB := evmibctesting.NewTransferPath(suite.evmChainA, suite.chainB) - pathAToB.Setup() - traceAToB := types.NewHop(pathAToB.EndpointB.ChannelConfig.PortID, pathAToB.EndpointB.ChannelID) - - senderIdx := 1 - senderAccount := suite.evmChainA.SenderAccounts[senderIdx] - senderAddr := senderAccount.SenderAccount.GetAddress() - tc.malleate() - - evmApp := suite.evmChainA.App.(*evmd.EVMD) - sourceDenomToTransfer, err = evmApp.StakingKeeper.BondDenom(suite.evmChainA.GetContext()) - suite.Require().NoError(err) - senderBalance := evmApp.BankKeeper.GetBalance(suite.evmChainA.GetContext(), senderAddr, sourceDenomToTransfer) - - timeoutHeight := clienttypes.NewHeight(1, 110) - - originalCoin := sdk.NewCoin(sourceDenomToTransfer, msgAmount) - - // send from evmChainA to chainB - msg := types.NewMsgTransfer( - pathAToB.EndpointA.ChannelConfig.PortID, - pathAToB.EndpointA.ChannelID, - originalCoin, - senderAddr.String(), - suite.chainB.SenderAccount.GetAddress().String(), - timeoutHeight, 0, "", - ) - fee := evmibctesting.FeeCoins().AmountOf(sourceDenomToTransfer) - res, err := suite.evmChainA.SendMsgsWithSender(senderAccount, msg) - suite.Require().NoError(err) // message committed - - packet, err := evmibctesting.ParsePacketFromEvents(res.Events) - suite.Require().NoError(err) - - // Get the packet data to determine the amount of tokens being transferred (needed for sending entire balance) - packetData, err := types.UnmarshalPacketData(packet.GetData(), pathAToB.EndpointA.GetChannel().Version, "") - suite.Require().NoError(err) - transferAmount, ok := sdkmath.NewIntFromString(packetData.Token.Amount) - suite.Require().True(ok) - - afterSenderBalance := evmApp.BankKeeper.GetBalance(suite.evmChainA.GetContext(), senderAddr, sourceDenomToTransfer) - suite.Require().Equal( - senderBalance.Amount.Sub(fee).Sub(transferAmount).String(), - afterSenderBalance.Amount.String(), - ) - if msgAmount == types.UnboundedSpendLimit() { - suite.Require().Equal("0", afterSenderBalance.Amount.String(), "sender should have no balance left") - } - - relayerAddr := suite.evmChainA.SenderAccounts[0].SenderAccount.GetAddress() - relayerBalance := evmApp.BankKeeper.GetBalance(suite.evmChainA.GetContext(), relayerAddr, originalCoin.Denom) - - // relay send - err = pathAToB.RelayPacket(packet) - suite.Require().NoError(err) // relay committed - - // One for UpdateClient() and one for AcknowledgePacket() - relayPacketFeeAmt := fee.Mul(sdkmath.NewInt(2)) - - afterRelayerBalance := evmApp.BankKeeper.GetBalance(suite.evmChainA.GetContext(), relayerAddr, originalCoin.Denom) - suite.Require().Equal( - relayerBalance.Amount.Sub(relayPacketFeeAmt).String(), - afterRelayerBalance.Amount.String(), - ) - - escrowAddress := types.GetEscrowAddress(packet.GetSourcePort(), packet.GetSourceChannel()) - - // check that module account escrow address has locked the tokens - chainAEscrowBalance := evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - escrowAddress, - originalCoin.Denom, - ) - suite.Require().True(transferAmount.Equal(chainAEscrowBalance.Amount)) - - // check that voucher exists on chain B - chainBApp := suite.chainB.GetSimApp() - chainBDenom := types.NewDenom(originalCoin.Denom, traceAToB) - chainBBalance := chainBApp.BankKeeper.GetBalance( - suite.chainB.GetContext(), - suite.chainB.SenderAccount.GetAddress(), - chainBDenom.IBCDenom(), - ) - coinSentFromAToB := sdk.NewCoin(chainBDenom.IBCDenom(), transferAmount) - suite.Require().Equal(coinSentFromAToB, chainBBalance) - - // setup between chainB to chainC - // NOTE: - // pathBToC.EndpointA = endpoint on chainB - // pathBToC.EndpointB = endpoint on chainC - pathBToC := evmibctesting.NewTransferPath(suite.chainB, suite.chainC) - pathBToC.Setup() - traceBToC := types.NewHop(pathBToC.EndpointB.ChannelConfig.PortID, pathBToC.EndpointB.ChannelID) - - // send from chainB to chainC - msg = types.NewMsgTransfer( - pathBToC.EndpointA.ChannelConfig.PortID, - pathBToC.EndpointA.ChannelID, - coinSentFromAToB, - suite.chainB.SenderAccount.GetAddress().String(), - suite.chainC.SenderAccount.GetAddress().String(), - timeoutHeight, 0, "", - ) - res, err = suite.chainB.SendMsgs(msg) - suite.Require().NoError(err) // message committed - - packet, err = evmibctesting.ParsePacketFromEvents(res.Events) - suite.Require().NoError(err) - - err = pathBToC.RelayPacket(packet) - suite.Require().NoError(err) // relay committed - - coinsSentFromBToC := sdk.NewCoins() - // check balances for chainB and chainC after transfer from chainB to chainC - // NOTE: fungible token is prefixed with the full trace in order to verify the packet commitment - chainCDenom := types.NewDenom(originalCoin.Denom, traceBToC, traceAToB) - - // check that the balance is updated on chainC - chainCApp := suite.chainC.GetSimApp() - coinSentFromBToC := sdk.NewCoin(chainCDenom.IBCDenom(), transferAmount) - chainCBalance := chainCApp.BankKeeper.GetBalance( - suite.chainC.GetContext(), - suite.chainC.SenderAccount.GetAddress(), - coinSentFromBToC.Denom, - ) - suite.Require().Equal(coinSentFromBToC, chainCBalance) - - // check that balance on chain B is empty - chainBBalance = chainBApp.BankKeeper.GetBalance( - suite.chainB.GetContext(), - suite.chainB.SenderAccount.GetAddress(), - coinSentFromBToC.Denom, - ) - suite.Require().Zero(chainBBalance.Amount.Int64()) - - // send from chainC back to chainB - msg = types.NewMsgTransfer( - pathBToC.EndpointB.ChannelConfig.PortID, - pathBToC.EndpointB.ChannelID, coinSentFromBToC, - suite.chainC.SenderAccount.GetAddress().String(), - suite.chainB.SenderAccount.GetAddress().String(), - timeoutHeight, 0, "", - ) - res, err = suite.chainC.SendMsgs(msg) - suite.Require().NoError(err) // message committed - - packet, err = evmibctesting.ParsePacketFromEvents(res.Events) - suite.Require().NoError(err) - - err = pathBToC.RelayPacket(packet) - suite.Require().NoError(err) // relay committed - - // check balances for chainC are empty after transfer from chainC to chainB - for _, coin := range coinsSentFromBToC { - // check that balance on chain C is empty - chainCBalance := chainCApp.BankKeeper.GetBalance( - suite.chainC.GetContext(), - suite.chainC.SenderAccount.GetAddress(), - coin.Denom, - ) - suite.Require().Zero(chainCBalance.Amount.Int64()) - } - - // check balances for chainB after transfer from chainC to chainB - // check that balance on chain B has the transferred amount - chainBBalance = chainBApp.BankKeeper.GetBalance( - suite.chainB.GetContext(), - suite.chainB.SenderAccount.GetAddress(), - coinSentFromAToB.Denom, - ) - suite.Require().Equal(coinSentFromAToB, chainBBalance) - - // check that module account escrow address is empty - escrowAddress = types.GetEscrowAddress(traceBToC.PortId, traceBToC.ChannelId) - chainBEscrowBalance := chainBApp.BankKeeper.GetBalance( - suite.chainB.GetContext(), - escrowAddress, - coinSentFromAToB.Denom, - ) - suite.Require().Zero(chainBEscrowBalance.Amount.Int64()) - - // check balances for evmChainA after transfer from chainC to chainB - // check that the balance is unchanged - chainASenderBalance := evmApp.BankKeeper.GetBalance(suite.evmChainA.GetContext(), senderAddr, originalCoin.Denom) - suite.Require().Equal( - afterSenderBalance.Amount.String(), - chainASenderBalance.Amount.String(), - ) - - // check that module account escrow address is unchanged - escrowAddress = types.GetEscrowAddress(pathAToB.EndpointA.ChannelConfig.PortID, pathAToB.EndpointA.ChannelID) - chainAEscrowBalance = evmApp.BankKeeper.GetBalance(suite.evmChainA.GetContext(), escrowAddress, originalCoin.Denom) - suite.Require().True(transferAmount.Equal(chainAEscrowBalance.Amount)) - }) - } -} - -func TestTransferTestSuite(t *testing.T) { - suite.Run(t, new(TransferTestSuite)) -} diff --git a/evmd/tests/ibc/v2_ibc_middleware_test.go b/evmd/tests/ibc/v2_ibc_middleware_test.go deleted file mode 100644 index 23f25c557..000000000 --- a/evmd/tests/ibc/v2_ibc_middleware_test.go +++ /dev/null @@ -1,944 +0,0 @@ -package ibc - -import ( - "bytes" - "errors" - "math/big" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - testifysuite "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd" - "github.com/cosmos/evm/evmd/tests/integration" - "github.com/cosmos/evm/testutil" - evmibctesting "github.com/cosmos/evm/testutil/ibc" - erc20Keeper "github.com/cosmos/evm/x/erc20/keeper" - "github.com/cosmos/evm/x/erc20/types" - v2 "github.com/cosmos/evm/x/erc20/v2" - transfertypes "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types" - channeltypesv2 "github.com/cosmos/ibc-go/v10/modules/core/04-channel/v2/types" - ibctesting "github.com/cosmos/ibc-go/v10/testing" - ibcmockv2 "github.com/cosmos/ibc-go/v10/testing/mock/v2" - - "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// MiddlewareTestSuite tests the v2 IBC middleware for the ERC20 module. -type MiddlewareV2TestSuite struct { - testifysuite.Suite - - coordinator *evmibctesting.Coordinator - - // testing chains used for convenience and readability - evmChainA *evmibctesting.TestChain - chainB *evmibctesting.TestChain - - // evmChainA to chainB for testing OnSendPacket, OnAckPacket, and OnTimeoutPacket - pathAToB *evmibctesting.Path - // chainB to evmChainA for testing OnRecvPacket - pathBToA *evmibctesting.Path -} - -func (suite *MiddlewareV2TestSuite) SetupTest() { - suite.coordinator = evmibctesting.NewCoordinator(suite.T(), 1, 1, integration.SetupEvmd) - suite.evmChainA = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(1)) - suite.chainB = suite.coordinator.GetChain(evmibctesting.GetChainID(2)) - - // setup between evmChainA and chainB - // pathAToB.EndpointA = endpoint on evmChainA - // pathAToB.EndpointB = endpoint on chainB - suite.pathAToB = evmibctesting.NewPath(suite.evmChainA, suite.chainB) - // setup between chainB and evmChainA - // path.EndpointA = endpoint on chainB - // path.EndpointB = endpoint on evmChainA - suite.pathBToA = evmibctesting.NewPath(suite.chainB, suite.evmChainA) - - // setup IBC v2 paths between the chains - suite.pathAToB.SetupV2() - suite.pathBToA.SetupV2() -} - -func TestMiddlewareV2TestSuite(t *testing.T) { - testifysuite.Run(t, new(MiddlewareV2TestSuite)) -} - -func (suite *MiddlewareV2TestSuite) TestNewIBCMiddleware() { - testCases := []struct { - name string - instantiateFn func() - expError error - }{ - { - "success", - func() { - _ = v2.NewIBCMiddleware(ibcmockv2.IBCModule{}, erc20Keeper.Keeper{}) - }, - nil, - }, - { - "panics with nil underlying app", - func() { - _ = v2.NewIBCMiddleware(nil, erc20Keeper.Keeper{}) - }, - errors.New("underlying application cannot be nil"), - }, - { - "panics with nil erc20 keeper", - func() { - _ = v2.NewIBCMiddleware(ibcmockv2.IBCModule{}, nil) - }, - errors.New("erc20 keeper cannot be nil"), - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - if tc.expError == nil { - suite.Require().NotPanics( - tc.instantiateFn, - "unexpected panic: NewIBCMiddleware", - ) - } else { - suite.Require().PanicsWithError( - tc.expError.Error(), - tc.instantiateFn, - "expected panic with error: ", tc.expError.Error(), - ) - } - }) - } -} - -func (suite *MiddlewareV2TestSuite) TestOnSendPacket() { - var ( - ctx sdk.Context - packetData transfertypes.FungibleTokenPacketData - payload channeltypesv2.Payload - ) - - testCases := []struct { - name string - malleate func() - expError string - }{ - { - name: "pass", - malleate: nil, - expError: "", - }, - { - name: "fail: malformed packet data", - malleate: func() { - payload.Value = []byte("malformed") - }, - expError: "cannot unmarshal ICS20-V1 transfer packet data", - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - ctx = suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - bondDenom, err := evmApp.StakingKeeper.BondDenom(ctx) - suite.Require().NoError(err) - packetData = transfertypes.NewFungibleTokenPacketData( - bondDenom, - ibctesting.DefaultCoinAmount.String(), - suite.evmChainA.SenderAccount.GetAddress().String(), - suite.chainB.SenderAccount.GetAddress().String(), - "", - ) - - payload = channeltypesv2.NewPayload( - transfertypes.PortID, transfertypes.PortID, - transfertypes.V1, transfertypes.EncodingJSON, - packetData.GetBytes(), - ) - - if tc.malleate != nil { - tc.malleate() - } - - onSendPacket := func() error { - return evmApp.GetIBCKeeper().ChannelKeeperV2.Router.Route(ibctesting.TransferPort).OnSendPacket( - ctx, - suite.pathAToB.EndpointA.ClientID, - suite.pathAToB.EndpointB.ClientID, - 1, - payload, - suite.evmChainA.SenderAccount.GetAddress(), - ) - } - - err = onSendPacket() - if tc.expError != "" { - suite.Require().Error(err) - suite.Require().ErrorContains(err, tc.expError) - } else { - suite.Require().NoError(err) - // check that the escrowed coins are in the escrow account - escrowAddress := transfertypes.GetEscrowAddress( - transfertypes.PortID, - suite.pathAToB.EndpointA.ClientID, - ) - escrowedCoins := evmApp.BankKeeper.GetAllBalances(ctx, escrowAddress) - suite.Require().Equal(1, len(escrowedCoins)) - suite.Require().Equal(ibctesting.DefaultCoinAmount.String(), escrowedCoins[0].Amount.String()) - suite.Require().Equal(bondDenom, escrowedCoins[0].Denom) - } - }) - } -} - -func (suite *MiddlewareV2TestSuite) TestOnRecvPacket() { - var ( - ctx sdk.Context - packetData transfertypes.FungibleTokenPacketData - payload channeltypesv2.Payload - ) - - testCases := []struct { - name string - malleate func() - expResult channeltypesv2.PacketStatus - }{ - { - name: "pass", - malleate: nil, - expResult: channeltypesv2.PacketStatus_Success, - }, - { - name: "fail: malformed packet data", - malleate: func() { - payload.Value = []byte("malformed") - }, - expResult: channeltypesv2.PacketStatus_Failure, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - ctx = suite.chainB.GetContext() - bondDenom, err := suite.chainB.GetSimApp().StakingKeeper.BondDenom(ctx) - suite.Require().NoError(err) - receiver := suite.evmChainA.SenderAccount.GetAddress() - sendAmt := ibctesting.DefaultCoinAmount - packetData = transfertypes.NewFungibleTokenPacketData( - bondDenom, - sendAmt.String(), - suite.chainB.SenderAccount.GetAddress().String(), - receiver.String(), - "", - ) - - payload = channeltypesv2.NewPayload( - transfertypes.PortID, transfertypes.PortID, - transfertypes.V1, transfertypes.EncodingJSON, - packetData.GetBytes(), - ) - - if tc.malleate != nil { - tc.malleate() - } - - evmApp := suite.evmChainA.App.(*evmd.EVMD) - // erc20 module is routed as top level middleware - transferStack := evmApp.GetIBCKeeper().ChannelKeeperV2.Router.Route(ibctesting.TransferPort) - sourceClient := suite.pathBToA.EndpointB.ClientID - onRecvPacket := func() channeltypesv2.RecvPacketResult { - ctx = suite.evmChainA.GetContext() - return transferStack.OnRecvPacket( - ctx, - sourceClient, - suite.pathBToA.EndpointA.ClientID, - 1, - payload, - receiver, - ) - } - - recvResult := onRecvPacket() - suite.Require().Equal(tc.expResult, recvResult.Status) - if recvResult.Status == channeltypesv2.PacketStatus_Success { - // make sure voucher coins are sent to the receiver - data, ackErr := transfertypes.UnmarshalPacketData(packetData.GetBytes(), transfertypes.V1, "") - suite.Require().Nil(ackErr) - voucherDenom := testutil.GetVoucherDenomFromPacketData(data, payload.GetSourcePort(), sourceClient) - voucherCoin := evmApp.BankKeeper.GetBalance(ctx, receiver, voucherDenom) - suite.Require().Equal(sendAmt.String(), voucherCoin.Amount.String()) - // make sure token pair is registered - singleTokenRepresentation, err := types.NewTokenPairSTRv2(voucherDenom) - suite.Require().NoError(err) - tokenPair, found := evmApp.Erc20Keeper.GetTokenPair(ctx, singleTokenRepresentation.GetID()) - suite.Require().True(found) - suite.Require().Equal(voucherDenom, tokenPair.Denom) - - // Make sure dynamic precompile is registered - available := evmApp.Erc20Keeper.IsDynamicPrecompileAvailable(ctx, common.HexToAddress(tokenPair.Erc20Address)) - suite.Require().True(available) - } - }) - } -} - -// TestOnRecvPacketNativeERC20 tests the OnRecvPacket logic when the packet involves a native ERC20. -func (suite *MiddlewareV2TestSuite) TestOnRecvPacketNativeERC20() { - var ( - packetData transfertypes.FungibleTokenPacketData - payload channeltypesv2.Payload - ) - - testCases := []struct { - name string - malleate func() - expResult channeltypesv2.PacketStatus - }{ - { - name: "pass", - malleate: nil, - expResult: channeltypesv2.PacketStatus_Success, - }, - { - name: "fail: malformed packet data", - malleate: func() { - payload.Value = []byte("malformed") - }, - expResult: channeltypesv2.PacketStatus_Failure, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - nativeErc20 := SetupNativeErc20(suite.T(), suite.evmChainA, suite.evmChainA.SenderAccounts[0]) - senderEthAddr := nativeErc20.Account - sender := sdk.AccAddress(senderEthAddr.Bytes()) - sendAmt := math.NewIntFromBigInt(nativeErc20.InitialBal) - - evmCtx := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - // MOCK erc20 native coin transfer from chainA to chainB - // 1: Convert erc20 tokens to native erc20 coins for sending through IBC. - _, err := evmApp.Erc20Keeper.ConvertERC20( - evmCtx, - types.NewMsgConvertERC20( - sendAmt, - sender, - nativeErc20.ContractAddr, - senderEthAddr, - ), - ) - suite.Require().NoError(err) - // 1-1: Check native erc20 token is converted to native erc20 coin on chainA. - erc20BalAfterConvert := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal( - new(big.Int).Sub(nativeErc20.InitialBal, sendAmt.BigInt()).String(), - erc20BalAfterConvert.String(), - ) - balAfterConvert := evmApp.BankKeeper.GetBalance(evmCtx, sender, nativeErc20.Denom) - suite.Require().Equal(sendAmt.String(), balAfterConvert.Amount.String()) - - // 2: Transfer erc20 native coin to chainB through IBC. - path := suite.pathAToB - chainBAcc := suite.chainB.SenderAccount.GetAddress() - packetData = transfertypes.NewFungibleTokenPacketData( - nativeErc20.Denom, sendAmt.String(), - sender.String(), chainBAcc.String(), - "", - ) - payload = channeltypesv2.NewPayload( - transfertypes.PortID, transfertypes.PortID, - transfertypes.V1, transfertypes.EncodingJSON, - packetData.GetBytes(), - ) - timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().Add(time.Hour).Unix()) //nolint:gosec // G115 - channelKeeperV2 := evmApp.GetIBCKeeper().ChannelKeeperV2 - _, err = channelKeeperV2.SendPacket(evmCtx, channeltypesv2.NewMsgSendPacket( - path.EndpointA.ClientID, - timeoutTimestamp, - sender.String(), - payload, - )) - suite.Require().NoError(err) - // 2-1: Check native erc20 token is escrowed on evmChainA for sending to chainB. - escrowAddr := transfertypes.GetEscrowAddress(transfertypes.PortID, path.EndpointA.ClientID) - escrowedBal := evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().Equal(sendAmt.String(), escrowedBal.Amount.String()) - - // 3: Assume chainB received native erc20 coin from evmChain A - // 3-1: Mock sending back from chainB to chainA - // chainBNativeErc20Denom is the native erc20 token denom on chainB from evmChainA through IBC. - chainBNativeErc20Denom := transfertypes.NewDenom( - nativeErc20.Denom, - transfertypes.NewHop( - transfertypes.PortID, - path.EndpointB.ClientID, - ), - ) - receiver := suite.evmChainA.SenderAccount.GetAddress() - packetData = transfertypes.NewFungibleTokenPacketData( - chainBNativeErc20Denom.Path(), sendAmt.String(), - chainBAcc.String(), receiver.String(), "", - ) - payload = channeltypesv2.NewPayload( - transfertypes.PortID, transfertypes.PortID, - transfertypes.V1, transfertypes.EncodingJSON, - packetData.GetBytes(), - ) - - if tc.malleate != nil { - tc.malleate() - } - - onRecvPacket := func() channeltypesv2.RecvPacketResult { - return channelKeeperV2.Router.Route(ibctesting.TransferPort).OnRecvPacket( - evmCtx, - path.EndpointB.ClientID, - path.EndpointA.ClientID, - 1, - payload, - receiver, - ) - } - // 4: Packet is received on evmChainA from chainB - recvResult := onRecvPacket() - suite.Require().Equal(tc.expResult, recvResult.Status) - if recvResult.Status == channeltypesv2.PacketStatus_Success { - // Check un-escrowed balance on evmChainA after receiving the packet. - escrowedBal = evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().True(escrowedBal.IsZero(), "escrowed balance should be un-escrowed after receiving the packet") - balAfterUnescrow := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal(nativeErc20.InitialBal.String(), balAfterUnescrow.String()) - bankBalAfterUnescrow := evmApp.BankKeeper.GetBalance(evmCtx, sender, nativeErc20.Denom) - suite.Require().True(bankBalAfterUnescrow.IsZero(), "no duplicate state in the bank balance") - } - }) - } -} - -func (suite *MiddlewareV2TestSuite) TestOnAcknowledgementPacket() { - var ( - ctx sdk.Context - packetData transfertypes.FungibleTokenPacketData - ack []byte - payload channeltypesv2.Payload - ) - - testCases := []struct { - name string - malleate func() - onSendRequired bool - expError string - }{ - { - name: "pass", - malleate: nil, - onSendRequired: false, - expError: "", - }, - { - name: "pass: refund escrowed token because ack err(UNIVERSAL_ERROR_ACKNOWLEDGEMENT)", - malleate: func() { - ack = channeltypesv2.ErrorAcknowledgement[:] - }, - onSendRequired: true, // this test case handles the refund of the escrowed token, so we need to call OnSendPacket. - expError: "", - }, - { - name: "fail: malformed packet data", - malleate: func() { - payload.Value = []byte("malformed") - }, - onSendRequired: false, - expError: "cannot unmarshal ICS20-V1 transfer packet data", - }, - { - name: "fail: empty ack", - malleate: func() { - ack = []byte{} - }, - onSendRequired: false, - expError: "cannot unmarshal ICS-20 transfer packet acknowledgement", - }, - { - name: "fail: ack error", - malleate: func() { - ackErr := channeltypes.NewErrorAcknowledgement(errors.New("error")) - ack = ackErr.Acknowledgement() - }, - onSendRequired: false, - expError: "cannot pass in a custom error acknowledgement with IBC v2", - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - ctx = suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - bondDenom, err := evmApp.StakingKeeper.BondDenom(ctx) - suite.Require().NoError(err) - sendAmt := ibctesting.DefaultCoinAmount - escrowAddress := transfertypes.GetEscrowAddress( - transfertypes.PortID, - suite.pathAToB.EndpointA.ClientID, - ) - packetData = transfertypes.NewFungibleTokenPacketData( - bondDenom, - sendAmt.String(), - suite.evmChainA.SenderAccount.GetAddress().String(), - suite.chainB.SenderAccount.GetAddress().String(), - "", - ) - - ack = channeltypes.NewResultAcknowledgement([]byte{1}).Acknowledgement() - - payload = channeltypesv2.NewPayload( - transfertypes.PortID, transfertypes.PortID, - transfertypes.V1, transfertypes.EncodingJSON, - packetData.GetBytes(), - ) - - if tc.malleate != nil { - tc.malleate() - } - - // erc20 module is routed as top level middleware - transferStack := suite.evmChainA.App.GetIBCKeeper().ChannelKeeperV2.Router.Route(ibctesting.TransferPort) - if tc.onSendRequired { - suite.NoError(transferStack.OnSendPacket( - ctx, - suite.pathAToB.EndpointA.ClientID, - suite.pathAToB.EndpointB.ClientID, - 1, - payload, - suite.evmChainA.SenderAccount.GetAddress(), - )) - // check that the escrowed coin is escrowed - escrowedCoin := evmApp.BankKeeper.GetBalance(ctx, escrowAddress, bondDenom) - suite.Require().Equal(escrowedCoin.Amount, sendAmt) - } - onAckPacket := func() error { - return transferStack.OnAcknowledgementPacket( - ctx, - suite.pathAToB.EndpointA.ClientID, - suite.pathAToB.EndpointB.ClientID, - 1, - ack, - payload, - suite.evmChainA.SenderAccount.GetAddress(), - ) - } - - err = onAckPacket() - if tc.expError != "" { - suite.Require().Error(err) - suite.Require().ErrorContains(err, tc.expError) - } else { - suite.Require().NoError(err) - } - // check that the escrowed coins are un-escrowed - if tc.onSendRequired && bytes.Equal(ack, channeltypesv2.ErrorAcknowledgement[:]) { - escrowedCoins := evmApp.BankKeeper.GetAllBalances(ctx, escrowAddress) - suite.Require().Equal(0, len(escrowedCoins)) - } - }) - } -} - -// TestOnAcknowledgementPacketNativeErc20 tests ack logic when the packet involves a native ERC20. -func (suite *MiddlewareV2TestSuite) TestOnAcknowledgementPacketNativeErc20() { - var ( - payload channeltypesv2.Payload - ack []byte - ) - - testCases := []struct { - name string - malleate func() - expError string - expRefund bool - }{ - { - name: "pass", - malleate: nil, - expError: "", - expRefund: false, - }, - { - name: "pass: refund escrowed token because ack err(UNIVERSAL_ERROR_ACKNOWLEDGEMENT)", - malleate: func() { - ack = channeltypesv2.ErrorAcknowledgement[:] - }, - expError: "", - expRefund: true, - }, - { - name: "fail: malformed packet data", - malleate: func() { - payload.Value = []byte("malformed data") - }, - expError: "cannot unmarshal ICS20-V1 transfer packet data", - expRefund: false, - }, - { - name: "fail: empty ack", - malleate: func() { - ack = []byte{} - }, - expError: "cannot unmarshal ICS-20 transfer packet acknowledgement", - expRefund: false, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - nativeErc20 := SetupNativeErc20(suite.T(), suite.evmChainA, suite.evmChainA.SenderAccounts[0]) - senderEthAddr := nativeErc20.Account - sender := sdk.AccAddress(senderEthAddr.Bytes()) - sendAmt := math.NewIntFromBigInt(nativeErc20.InitialBal) - - evmCtx := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - - // MOCK erc20 native coin transfer from chainA to chainB - // 1: Convert erc20 tokens to native erc20 coins for sending through IBC. - _, err := evmApp.Erc20Keeper.ConvertERC20( - evmCtx, - types.NewMsgConvertERC20( - sendAmt, - sender, - nativeErc20.ContractAddr, - senderEthAddr, - ), - ) - suite.Require().NoError(err) - // 1-1: Check native erc20 token is converted to native erc20 coin on chainA. - erc20BalAfterConvert := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal( - new(big.Int).Sub(nativeErc20.InitialBal, sendAmt.BigInt()).String(), - erc20BalAfterConvert.String(), - ) - balAfterConvert := evmApp.BankKeeper.GetBalance(evmCtx, sender, nativeErc20.Denom) - suite.Require().Equal(sendAmt.String(), balAfterConvert.Amount.String()) - - path := suite.pathAToB - escrowAddr := transfertypes.GetEscrowAddress(transfertypes.PortID, path.EndpointA.ClientID) - // checkEscrow is a check function to ensure the native erc20 token is escrowed. - checkEscrow := func() { - erc20BalAfterIbcTransfer := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal( - new(big.Int).Sub(nativeErc20.InitialBal, sendAmt.BigInt()).String(), - erc20BalAfterIbcTransfer.String(), - ) - escrowedBal := evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().Equal(sendAmt.String(), escrowedBal.Amount.String()) - } - - // checkRefund is a check function to ensure refund is processed. - checkRefund := func() { - escrowedBal := evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().True(escrowedBal.IsZero()) - - // Check erc20 balance is same as initial balance after refund. - erc20BalAfterIbcTransfer := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal(nativeErc20.InitialBal.String(), erc20BalAfterIbcTransfer.String()) - } - - // 2: Transfer erc20 native coin to chainB through IBC. - chainBAcc := suite.chainB.SenderAccount.GetAddress() - packetData := transfertypes.NewFungibleTokenPacketData( - nativeErc20.Denom, sendAmt.String(), - sender.String(), chainBAcc.String(), - "", - ) - payload = channeltypesv2.NewPayload( - transfertypes.PortID, transfertypes.PortID, - transfertypes.V1, transfertypes.EncodingJSON, - packetData.GetBytes(), - ) - timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().Add(time.Hour).Unix()) //nolint:gosec // G115 - channelKeeperV2 := evmApp.GetIBCKeeper().ChannelKeeperV2 - _, err = channelKeeperV2.SendPacket(evmCtx, channeltypesv2.NewMsgSendPacket( - path.EndpointA.ClientID, - timeoutTimestamp, - sender.String(), - payload, - )) - suite.Require().NoError(err) - checkEscrow() - - ack = channeltypes.NewResultAcknowledgement([]byte{1}).Acknowledgement() - if tc.malleate != nil { - tc.malleate() - } - - // 3: Mock sending back from chainB to chainA - onAck := func() error { - return channelKeeperV2.Router.Route(transfertypes.PortID).OnAcknowledgementPacket( - evmCtx, - path.EndpointA.ClientID, - path.EndpointB.ClientID, - 0, - ack, - payload, - sender, - ) - } - - err = onAck() - if tc.expError == "" { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - suite.Require().Contains(err.Error(), tc.expError) - } - - if tc.expRefund { - checkRefund() - } else { - checkEscrow() - } - }) - } -} - -func (suite *MiddlewareV2TestSuite) TestOnTimeoutPacket() { - var ( - ctx sdk.Context - packetData transfertypes.FungibleTokenPacketData - payload channeltypesv2.Payload - ) - - testCases := []struct { - name string - malleate func() - onSendRequired bool - expError string - }{ - { - name: "pass", - malleate: nil, - onSendRequired: true, - expError: "", - }, - { - name: "fail: malformed packet data", - malleate: func() { - payload.Value = []byte("malformed") - }, - onSendRequired: false, // malformed packet data cannot be sent - expError: "cannot unmarshal ICS20-V1 transfer packet data", - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - ctx = suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - bondDenom, err := evmApp.StakingKeeper.BondDenom(ctx) - suite.Require().NoError(err) - packetData = transfertypes.NewFungibleTokenPacketData( - bondDenom, - ibctesting.DefaultCoinAmount.String(), - suite.evmChainA.SenderAccount.GetAddress().String(), - suite.chainB.SenderAccount.GetAddress().String(), - "", - ) - - payload = channeltypesv2.NewPayload( - transfertypes.PortID, transfertypes.PortID, - transfertypes.V1, transfertypes.EncodingJSON, - packetData.GetBytes(), - ) - - if tc.malleate != nil { - tc.malleate() - } - - transferStack := suite.evmChainA.App.GetIBCKeeper().ChannelKeeperV2.Router.Route(ibctesting.TransferPort) - if tc.onSendRequired { - suite.NoError(transferStack.OnSendPacket( - ctx, - suite.pathAToB.EndpointA.ClientID, - suite.pathAToB.EndpointB.ClientID, - 1, - payload, - suite.evmChainA.SenderAccount.GetAddress(), - )) - } - - onTimeoutPacket := func() error { - return transferStack.OnTimeoutPacket( - ctx, - suite.pathAToB.EndpointA.ClientID, - suite.pathAToB.EndpointB.ClientID, - 1, - payload, - suite.evmChainA.SenderAccount.GetAddress(), - ) - } - - err = onTimeoutPacket() - if tc.expError != "" { - suite.Require().Error(err) - suite.Require().ErrorContains(err, tc.expError) - } else { - suite.Require().NoError(err) - // check that the escrowed coins are un-escrowed - escrowAddress := transfertypes.GetEscrowAddress( - transfertypes.PortID, - suite.pathAToB.EndpointA.ClientID, - ) - escrowedCoins := evmApp.BankKeeper.GetAllBalances(ctx, escrowAddress) - suite.Require().Equal(0, len(escrowedCoins)) - } - }) - } -} - -// TestOnTimeoutPacketNativeErc20 tests the OnTimeoutPacket logic when the packet involves a native ERC20. -func (suite *MiddlewareV2TestSuite) TestOnTimeoutPacketNativeErc20() { - var payload channeltypesv2.Payload - - testCases := []struct { - name string - malleate func() - expError string - expRefund bool - }{ - { - name: "pass: refund escrowed native erc20 coin", - malleate: nil, - expError: "", - expRefund: true, - }, - { - name: "fail: malformed packet data", - malleate: func() { - payload.Value = []byte("malformed data") - }, - expError: "cannot unmarshal ICS20-V1 transfer packet data", - expRefund: false, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - nativeErc20 := SetupNativeErc20(suite.T(), suite.evmChainA, suite.evmChainA.SenderAccounts[0]) - senderEthAddr := nativeErc20.Account - sender := sdk.AccAddress(senderEthAddr.Bytes()) - sendAmt := math.NewIntFromBigInt(nativeErc20.InitialBal) - - evmCtx := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - - // MOCK erc20 native coin transfer from chainA to chainB - // 1: Convert erc20 tokens to native erc20 coins for sending through IBC. - _, err := evmApp.Erc20Keeper.ConvertERC20( - evmCtx, - types.NewMsgConvertERC20( - sendAmt, - sender, - nativeErc20.ContractAddr, - senderEthAddr, - ), - ) - suite.Require().NoError(err) - // 1-1: Check native erc20 token is converted to native erc20 coin on chainA. - erc20BalAfterConvert := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal( - new(big.Int).Sub(nativeErc20.InitialBal, sendAmt.BigInt()).String(), - erc20BalAfterConvert.String(), - ) - balAfterConvert := evmApp.BankKeeper.GetBalance(evmCtx, sender, nativeErc20.Denom) - suite.Require().Equal(sendAmt.String(), balAfterConvert.Amount.String()) - - path := suite.pathAToB - escrowAddr := transfertypes.GetEscrowAddress(transfertypes.PortID, path.EndpointA.ClientID) - // checkEscrow is a check function to ensure the native erc20 token is escrowed. - checkEscrow := func() { - erc20BalAfterIbcTransfer := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal( - new(big.Int).Sub(nativeErc20.InitialBal, sendAmt.BigInt()).String(), - erc20BalAfterIbcTransfer.String(), - ) - escrowedBal := evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().Equal(sendAmt.String(), escrowedBal.Amount.String()) - } - - // checkRefund is a check function to ensure refund is processed. - checkRefund := func() { - escrowedBal := evmApp.BankKeeper.GetBalance(evmCtx, escrowAddr, nativeErc20.Denom) - suite.Require().True(escrowedBal.IsZero()) - - // Check erc20 balance is same as initial balance after refund. - erc20BalAfterIbcTransfer := evmApp.Erc20Keeper.BalanceOf(evmCtx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, senderEthAddr) - suite.Require().Equal(nativeErc20.InitialBal.String(), erc20BalAfterIbcTransfer.String()) - } - - // 2: Transfer erc20 native coin to chainB through IBC. - chainBAcc := suite.chainB.SenderAccount.GetAddress() - packetData := transfertypes.NewFungibleTokenPacketData( - nativeErc20.Denom, sendAmt.String(), - sender.String(), chainBAcc.String(), - "", - ) - payload = channeltypesv2.NewPayload( - transfertypes.PortID, transfertypes.PortID, - transfertypes.V1, transfertypes.EncodingJSON, - packetData.GetBytes(), - ) - timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().Add(time.Hour).Unix()) //nolint:gosec // G115 - channelKeeperV2 := evmApp.GetIBCKeeper().ChannelKeeperV2 - _, err = channelKeeperV2.SendPacket(evmCtx, channeltypesv2.NewMsgSendPacket( - path.EndpointA.ClientID, - timeoutTimestamp, - sender.String(), - payload, - )) - suite.Require().NoError(err) - checkEscrow() - - if tc.malleate != nil { - tc.malleate() - } - - // 3: Trigger timeout on chainA - onTimeout := func() error { - return channelKeeperV2.Router.Route(transfertypes.PortID).OnTimeoutPacket( - evmCtx, - path.EndpointA.ClientID, - path.EndpointB.ClientID, - 0, - payload, - sender, - ) - } - - err = onTimeout() - if tc.expError == "" { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - suite.Require().Contains(err.Error(), tc.expError) - } - - if tc.expRefund { - checkRefund() - } else { - checkEscrow() - } - }) - } -} diff --git a/evmd/tests/ibc/v2_ics20_precompile_transfer_test.go b/evmd/tests/ibc/v2_ics20_precompile_transfer_test.go deleted file mode 100644 index 27c7c4565..000000000 --- a/evmd/tests/ibc/v2_ics20_precompile_transfer_test.go +++ /dev/null @@ -1,364 +0,0 @@ -// Copied from https://github.com/cosmos/ibc-go/blob/7325bd2b00fd5e33d895770ec31b5be2f497d37a/modules/apps/transfer/transfer_test.go -// Why was this copied? -// This test suite was imported to validate that ExampleChain (an EVM-based chain) -// correctly supports IBC v1 token transfers using ibc-goโ€™s Transfer module logic. -// The test ensures that ics20 precompile transfer (A โ†’ B) behave as expected across channels. -package ibc - -import ( - "math/big" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd" - "github.com/cosmos/evm/evmd/tests/integration" - "github.com/cosmos/evm/precompiles/ics20" - chainutil "github.com/cosmos/evm/testutil" - evmibctesting "github.com/cosmos/evm/testutil/ibc" - evmante "github.com/cosmos/evm/x/vm/ante" - transfertypes "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/query" -) - -type ICS20TransferV2TestSuite struct { - suite.Suite - - coordinator *evmibctesting.Coordinator - - // testing chains used for convenience and readability - chainA *evmibctesting.TestChain - chainAPrecompile *ics20.Precompile - chainB *evmibctesting.TestChain - chainBPrecompile *ics20.Precompile -} - -func (suite *ICS20TransferV2TestSuite) SetupTest() { - suite.coordinator = evmibctesting.NewCoordinator(suite.T(), 2, 0, integration.SetupEvmd) - suite.chainA = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(1)) - suite.chainB = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(2)) - - evmAppA := suite.chainA.App.(*evmd.EVMD) - suite.chainAPrecompile = ics20.NewPrecompile( - evmAppA.BankKeeper, - *evmAppA.StakingKeeper, - evmAppA.TransferKeeper, - evmAppA.IBCKeeper.ChannelKeeper, - ) - evmAppB := suite.chainB.App.(*evmd.EVMD) - suite.chainBPrecompile = ics20.NewPrecompile( - evmAppB.BankKeeper, - *evmAppB.StakingKeeper, - evmAppB.TransferKeeper, - evmAppB.IBCKeeper.ChannelKeeper, - ) -} - -// Constructs the following sends based on the established channels/connections -// 1 - from chainA to chainB -func (suite *ICS20TransferV2TestSuite) TestHandleMsgTransfer() { - var ( - sourceDenomToTransfer string - msgAmount sdkmath.Int - err error - nativeErc20 *NativeErc20Info - erc20 bool - ) - // originally a basic test case from the IBC testing package, and it has been added as-is to ensure that - // it still works properly when invoked through the ics20 precompile with ibc v2 packet. - testCases := []struct { - name string - malleate func(senderAcc evmibctesting.SenderAccount) - }{ - { - "transfer single denom", - func(_ evmibctesting.SenderAccount) { - evmAppA := suite.chainA.App.(*evmd.EVMD) - sourceDenomToTransfer, err = evmAppA.StakingKeeper.BondDenom(suite.chainA.GetContext()) - msgAmount = evmibctesting.DefaultCoinAmount - }, - }, - { - "transfer amount larger than int64", - func(_ evmibctesting.SenderAccount) { - var ok bool - evmAppA := suite.chainA.App.(*evmd.EVMD) - sourceDenomToTransfer, err = evmAppA.StakingKeeper.BondDenom(suite.chainA.GetContext()) - msgAmount, ok = sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64) - suite.Require().True(ok) - }, - }, - { - "transfer entire balance", - func(_ evmibctesting.SenderAccount) { - evmAppA := suite.chainA.App.(*evmd.EVMD) - sourceDenomToTransfer, err = evmAppA.StakingKeeper.BondDenom(suite.chainA.GetContext()) - msgAmount = transfertypes.UnboundedSpendLimit() - }, - }, - { - "native erc20 case", - func(senderAcc evmibctesting.SenderAccount) { - nativeErc20 = SetupNativeErc20(suite.T(), suite.chainA, senderAcc) - sourceDenomToTransfer = nativeErc20.Denom - msgAmount = sdkmath.NewIntFromBigInt(nativeErc20.InitialBal) - erc20 = true - }, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup between chainA and chainB - // NOTE: - // pathAToB.EndpointA = endpoint on chainA - // pathAToB.EndpointB = endpoint on chainB - // pathAToB := evmibctesting.NewTransferPath(suite.chainA, suite.chainB) - pathAToB := evmibctesting.NewPath(suite.chainA, suite.chainB) - pathAToB.SetupV2() - traceAToB := transfertypes.NewHop(transfertypes.PortID, pathAToB.EndpointB.ClientID) - - senderIdx := 1 - senderAccount := suite.chainA.SenderAccounts[senderIdx] - senderAddr := senderAccount.SenderAccount.GetAddress() - - tc.malleate(senderAccount) - - evmAppA := suite.chainA.App.(*evmd.EVMD) - - GetBalance := func(addr sdk.AccAddress) sdk.Coin { - ctx := suite.chainA.GetContext() - if erc20 { - balanceAmt := evmAppA.Erc20Keeper.BalanceOf(ctx, nativeErc20.ContractAbi, nativeErc20.ContractAddr, nativeErc20.Account) - return sdk.Coin{ - Denom: nativeErc20.Denom, - Amount: sdkmath.NewIntFromBigInt(balanceAmt), - } - } - return evmAppA.BankKeeper.GetBalance(ctx, addr, sourceDenomToTransfer) - } - - senderBalance := GetBalance(senderAddr) - suite.Require().NoError(err) - - timeoutHeight := clienttypes.NewHeight(1, 110) - timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().Add(time.Hour).Unix()) //nolint:gosec // G115 - originalCoin := sdk.NewCoin(sourceDenomToTransfer, msgAmount) - - data, err := suite.chainAPrecompile.Pack("transfer", - transfertypes.PortID, - pathAToB.EndpointA.ClientID, // Note: should be client id on v2 packet - originalCoin.Denom, - originalCoin.Amount.BigInt(), - common.BytesToAddress(senderAddr.Bytes()), // Note: source addr should be evm hex addr - suite.chainB.SenderAccount.GetAddress().String(), // Note: receiver should be cosmos bech32 addr - timeoutHeight, - timeoutTimestamp, - "", - ) - suite.Require().NoError(err) - - res, _, _, err := suite.chainA.SendEvmTx(senderAccount, senderIdx, suite.chainAPrecompile.Address(), big.NewInt(0), data, 0) - suite.Require().NoError(err) // message committed - packets, err := pathAToB.EndpointA.ParseV2PacketFromEvent(res.Events) - suite.Require().NoError(err) - transferAmount := msgAmount - - // check that the balance for chainA is updated - afterSenderBalance := evmAppA.BankKeeper.GetBalance(suite.chainA.GetContext(), senderAddr, originalCoin.Denom) - // Note: When an UnboundedSpendLimit value is sent, the spendable amount is used. - if msgAmount.Equal(transfertypes.UnboundedSpendLimit()) { - transferAmount = senderBalance.Amount - } - suite.Require().Equal( - senderBalance.Amount.Sub(transferAmount).String(), - afterSenderBalance.Amount.String(), - ) - if msgAmount.Equal(transfertypes.UnboundedSpendLimit()) { - suite.Require().True(afterSenderBalance.IsZero()) - } - - relayerAddr := suite.chainA.SenderAccounts[0].SenderAccount.GetAddress() - relayerBalance := GetBalance(relayerAddr) - - // relay send - err = pathAToB.RelayPacketV2(packets[0]) - suite.Require().NoError(err) // relay committed - - // There are two msgs that are sent in the relay: - // 1. MsgAcknowledgePacket to acknowledge the packet - // 2. Counterparty.UpdateClient to update the client - // Both of these msgs incur a fee, so we need to account for that. - relayPacketV2Fee := evmibctesting.FeeCoins().AmountOf(originalCoin.Denom).Mul(sdkmath.NewInt(2)) - afterRelayerBalance := GetBalance(relayerAddr) - suite.Require().Equal( - relayerBalance.Amount.Sub(relayPacketV2Fee).String(), - afterRelayerBalance.Amount.String(), - ) - - escrowAddress := transfertypes.GetEscrowAddress( - transfertypes.PortID, - pathAToB.EndpointA.ClientID, - ) - - // check that module account escrow address has locked the tokens - chainAEscrowBalance := evmAppA.BankKeeper.GetBalance( - suite.chainA.GetContext(), - escrowAddress, - originalCoin.Denom, - ) - suite.Require().True(transferAmount.Equal(chainAEscrowBalance.Amount)) - - // check that voucher exists on chain B - evmAppB := suite.chainB.App.(*evmd.EVMD) - chainBDenom := transfertypes.NewDenom(originalCoin.Denom, traceAToB) - chainBBalance := evmAppB.BankKeeper.GetBalance( - suite.chainB.GetContext(), - suite.chainB.SenderAccount.GetAddress(), - chainBDenom.IBCDenom(), - ) - coinSentFromAToB := sdk.NewCoin(chainBDenom.IBCDenom(), transferAmount) - suite.Require().Equal(coinSentFromAToB, chainBBalance) - - // --------------------------------------------- - // Tests for Query endpoints of ICS20 precompile - // denoms query method - chainBAddr := common.BytesToAddress(suite.chainB.SenderAccount.GetAddress().Bytes()) - ctxB := evmante.BuildEvmExecutionCtx(suite.chainB.GetContext()) - evmRes, err := evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomsMethod, - query.PageRequest{ - Key: []byte{}, - Offset: 0, - Limit: 0, - CountTotal: false, - Reverse: false, - }, - ) - suite.Require().NoError(err) - var denomsResponse ics20.DenomsResponse - err = suite.chainBPrecompile.UnpackIntoInterface(&denomsResponse, ics20.DenomsMethod, evmRes.Ret) - suite.Require().NoError(err) - suite.Require().Equal(chainBDenom, denomsResponse.Denoms[0]) - - // denom query method - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomMethod, - chainBDenom.Hash().String(), - ) - suite.Require().NoError(err) - var denomResponse ics20.DenomResponse - err = suite.chainBPrecompile.UnpackIntoInterface(&denomResponse, ics20.DenomMethod, evmRes.Ret) - suite.Require().NoError(err) - suite.Require().Equal(chainBDenom, denomResponse.Denom) - - // denom query method not exists case - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomMethod, - "0000000000000000000000000000000000000000000000000000000000000000", - ) - suite.Require().NoError(err) - err = suite.chainBPrecompile.UnpackIntoInterface(&denomResponse, ics20.DenomMethod, evmRes.Ret) - suite.Require().NoError(err) - // ensure empty denom struct when not exist - suite.Require().Equal(denomResponse.Denom, transfertypes.Denom{Base: "", Trace: []transfertypes.Hop{}}) - - // denom query method invalid error case - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomMethod, - "INVALID-DENOM-HASH", - ) - suite.Require().ErrorContains(err, vm.ErrExecutionReverted.Error()) - revertErr := chainutil.DecodeRevertReason(*evmRes) - suite.Require().Contains(revertErr.Error(), "invalid denom trace hash") - ctxB.GasMeter().RefundGas(ctxB.GasMeter().Limit(), "refund after error") - - // denomHash query method - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomHashMethod, - chainBDenom.Path(), - ) - suite.Require().NoError(err) - var denomHashResponse transfertypes.QueryDenomHashResponse - err = suite.chainBPrecompile.UnpackIntoInterface(&denomHashResponse, ics20.DenomHashMethod, evmRes.Ret) - suite.Require().NoError(err) - suite.Require().Equal(chainBDenom.Hash().String(), denomHashResponse.Hash) - - // denomHash query method not exists case - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomHashMethod, - "transfer/channel-0/erc20:not-exists-case", - ) - suite.Require().NoError(err) - err = suite.chainBPrecompile.UnpackIntoInterface(&denomHashResponse, ics20.DenomHashMethod, evmRes.Ret) - suite.Require().NoError(err) - suite.Require().Equal(denomHashResponse.Hash, "") - - // denomHash query method invalid error case - evmRes, err = evmAppB.EVMKeeper.CallEVM( - ctxB, - suite.chainBPrecompile.ABI, - chainBAddr, - suite.chainBPrecompile.Address(), - false, - nil, - ics20.DenomHashMethod, - "", - ) - suite.Require().ErrorContains(err, vm.ErrExecutionReverted.Error()) - revertErr = chainutil.DecodeRevertReason(*evmRes) - suite.Require().Contains(revertErr.Error(), "invalid denomination for cross-chain transfer") - ctxB.GasMeter().RefundGas(ctxB.GasMeter().Limit(), "refund after error") - }) - } -} - -func TestICS20TransferV2TestSuite(t *testing.T) { - suite.Run(t, new(ICS20TransferV2TestSuite)) -} diff --git a/evmd/tests/ibc/v2_transfer_test.go b/evmd/tests/ibc/v2_transfer_test.go deleted file mode 100644 index 626428377..000000000 --- a/evmd/tests/ibc/v2_transfer_test.go +++ /dev/null @@ -1,571 +0,0 @@ -// Copied from https://github.com/cosmos/ibc-go/blob/e5baeaea3e549f64055f0a60f2617a3404ea777d/modules/apps/transfer/v2/ibc_module_test.go -// -// Why was this copied? -// This test suite was copied to verify that ExampleChain (EVM-based chain) -// correctly implements IBC v2 packet handling logic, including send, receive, -// acknowledgement, and timeout flows. -// Additionally, we made slight modifications to confirm ExampleChain's behavior -// as a receiving chain (see TestOnRecvPacket), ensuring that the EVM-based chain -// correctly mints/burns/escrows tokens according to ICS-20 standards. -// - -package ibc - -import ( - "crypto/sha256" - "fmt" - "testing" - "time" - - testifysuite "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd" - "github.com/cosmos/evm/evmd/tests/integration" - evmibctesting "github.com/cosmos/evm/testutil/ibc" - "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types" - channeltypesv2 "github.com/cosmos/ibc-go/v10/modules/core/04-channel/v2/types" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const testclientid = "testclientid" - -type TransferTestSuiteV2 struct { - testifysuite.Suite - - coordinator *evmibctesting.Coordinator - - // testing chains used for convenience and readability - evmChainA *evmibctesting.TestChain - chainB *evmibctesting.TestChain - chainC *evmibctesting.TestChain - - pathAToB *evmibctesting.Path - pathBToC *evmibctesting.Path - - // chainB to evmChainA for testing OnRecvPacket, OnAckPacket, and OnTimeoutPacket - pathBToA *evmibctesting.Path -} - -const invalidPortID = "invalidportid" - -func (suite *TransferTestSuiteV2) SetupTest() { - suite.coordinator = evmibctesting.NewCoordinator(suite.T(), 1, 2, integration.SetupEvmd) - suite.evmChainA = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(1)) - suite.chainB = suite.coordinator.GetChain(evmibctesting.GetChainID(2)) - suite.chainC = suite.coordinator.GetChain(evmibctesting.GetChainID(3)) - - // setup between evmChainA and chainB - // NOTE: - // pathAToB.EndpointA = endpoint on evmChainA - // pathAToB.EndpointB = endpoint on chainB - suite.pathAToB = evmibctesting.NewPath(suite.evmChainA, suite.chainB) - - // setup between chainB and chainC - // pathBToC.EndpointA = endpoint on chainB - // pathBToC.EndpointB = endpoint on chainC - suite.pathBToC = evmibctesting.NewPath(suite.chainB, suite.chainC) - - // setup between chainB and evmChainA - // path.EndpointA = endpoint on chainB - // path.EndpointB = endpoint on evmChainA - suite.pathBToA = evmibctesting.NewPath(suite.chainB, suite.evmChainA) - - // setup IBC v2 paths between the chains - suite.pathAToB.SetupV2() - suite.pathBToC.SetupV2() - suite.pathBToA.SetupV2() -} - -func TestTransferTestSuiteV2(t *testing.T) { - testifysuite.Run(t, new(TransferTestSuiteV2)) -} - -// TestOnSendPacket tests ExampleChain's implementation of the OnSendPacket callback. -func (suite *TransferTestSuiteV2) TestOnSendPacket() { - var payload channeltypesv2.Payload - testCases := []struct { - name string - sourceDenomToTransfer string - malleate func() - expError error - }{ - { - "transfer single denom", - sdk.DefaultBondDenom, - func() {}, - nil, - }, - { - "transfer with invalid source port", - sdk.DefaultBondDenom, - func() { - payload.SourcePort = invalidPortID - }, - channeltypesv2.ErrInvalidPacket, - }, - { - "transfer with invalid destination port", - sdk.DefaultBondDenom, - func() { - payload.DestinationPort = invalidPortID - }, - channeltypesv2.ErrInvalidPacket, - }, - { - "transfer with invalid source client", - sdk.DefaultBondDenom, - func() { - suite.pathAToB.EndpointA.ClientID = testclientid - }, - channeltypesv2.ErrInvalidPacket, - }, - { - "transfer with invalid destination client", - sdk.DefaultBondDenom, - func() { - suite.pathAToB.EndpointB.ClientID = testclientid - }, - channeltypesv2.ErrInvalidPacket, - }, - { - "transfer with slashes in base denom", - "base/coin", - func() {}, - types.ErrInvalidDenomForTransfer, - }, - { - "transfer with slashes in ibc denom", - fmt.Sprintf("ibc/%x", sha256.Sum256([]byte("coin"))), - func() {}, - types.ErrInvalidDenomForTransfer, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - evmApp := suite.evmChainA.App.(*evmd.EVMD) - originalBalance := evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - suite.evmChainA.SenderAccount.GetAddress(), - tc.sourceDenomToTransfer, - ) - - amount, ok := sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64) - suite.Require().True(ok) - originalCoin := sdk.NewCoin(tc.sourceDenomToTransfer, amount) - - token := types.Token{ - Denom: types.Denom{Base: originalCoin.Denom}, - Amount: originalCoin.Amount.String(), - } - - transferData := types.NewFungibleTokenPacketData( - token.Denom.Path(), - token.Amount, - suite.evmChainA.SenderAccount.GetAddress().String(), - suite.chainB.SenderAccount.GetAddress().String(), - "", - ) - bz := suite.evmChainA.Codec.MustMarshal(&transferData) - payload = channeltypesv2.NewPayload( - types.PortID, types.PortID, types.V1, - types.EncodingProtobuf, bz, - ) - - // malleate payload - tc.malleate() - - ctx := suite.evmChainA.GetContext() - cbs := suite.evmChainA.App.GetIBCKeeper().ChannelKeeperV2.Router.Route(evmibctesting.TransferPort) - - err := cbs.OnSendPacket( - ctx, - suite.pathAToB.EndpointA.ClientID, - suite.pathAToB.EndpointB.ClientID, - 1, - payload, - suite.evmChainA.SenderAccount.GetAddress(), - ) - - if tc.expError != nil { - suite.Require().Contains(err.Error(), tc.expError.Error()) - } else { - suite.Require().NoError(err) - - escrowAddress := types.GetEscrowAddress(types.PortID, suite.pathAToB.EndpointA.ClientID) - // check that the balance for evmChainA is updated - chainABalance := evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - suite.evmChainA.SenderAccount.GetAddress(), - originalCoin.Denom, - ) - suite.Require().Equal(originalBalance.Amount.Sub(amount).Int64(), chainABalance.Amount.Int64()) - - // check that module account escrow address has locked the tokens - chainAEscrowBalance := evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - escrowAddress, - originalCoin.Denom, - ) - suite.Require().Equal(originalCoin, chainAEscrowBalance) - } - }) - } -} - -// TestOnRecvPacket tests ExampleChain's implementation of the OnRecvPacket callback. -func (suite *TransferTestSuiteV2) TestOnRecvPacket() { - var payload channeltypesv2.Payload - testCases := []struct { - name string - sourceDenomToTransfer string - malleate func() - expErr bool - }{ - { - "transfer single denom", - sdk.DefaultBondDenom, - func() {}, - false, - }, - { - "transfer with invalid source port", - sdk.DefaultBondDenom, - func() { - payload.SourcePort = invalidPortID - }, - true, - }, - { - "transfer with invalid dest port", - sdk.DefaultBondDenom, - func() { - payload.DestinationPort = invalidPortID - }, - true, - }, - { - "transfer with invalid source client", - sdk.DefaultBondDenom, - func() { - suite.pathBToA.EndpointB.ClientID = testclientid - }, - true, - }, - { - "transfer with invalid destination client", - sdk.DefaultBondDenom, - func() { - suite.pathBToA.EndpointA.ClientID = testclientid - }, - true, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - simAppB := suite.chainB.GetSimApp() - originalBalance := simAppB.BankKeeper.GetBalance( - suite.chainB.GetContext(), - suite.chainB.SenderAccount.GetAddress(), - tc.sourceDenomToTransfer, - ) - - timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().Add(time.Hour).Unix()) //nolint:gosec // G115 - - amount, ok := sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64) - suite.Require().True(ok) - originalCoin := sdk.NewCoin(tc.sourceDenomToTransfer, amount) - - msg := types.NewMsgTransferWithEncoding( - types.PortID, suite.pathBToA.EndpointB.ClientID, - originalCoin, suite.chainB.SenderAccount.GetAddress().String(), - suite.evmChainA.SenderAccount.GetAddress().String(), clienttypes.Height{}, - timeoutTimestamp, "", types.EncodingProtobuf, - false, - ) - _, err := suite.chainB.SendMsgs(msg) - suite.Require().NoError(err) // message committed - - token, err := simAppB.TransferKeeper.TokenFromCoin(suite.chainB.GetContext(), originalCoin) - suite.Require().NoError(err) - - transferData := types.NewFungibleTokenPacketData( - token.Denom.Path(), - token.Amount, - suite.chainB.SenderAccount.GetAddress().String(), - suite.evmChainA.SenderAccount.GetAddress().String(), - "", - ) - bz := suite.chainB.Codec.MustMarshal(&transferData) - payload = channeltypesv2.NewPayload( - types.PortID, types.PortID, types.V1, - types.EncodingProtobuf, bz, - ) - - ctx := suite.evmChainA.GetContext() - evmApp := suite.evmChainA.App.(*evmd.EVMD) - cbs := evmApp.GetIBCKeeper().ChannelKeeperV2.Router.Route(evmibctesting.TransferPort) - - // malleate payload after it has been sent but before OnRecvPacket callback is called - tc.malleate() - - recvResult := cbs.OnRecvPacket( - ctx, suite.pathBToA.EndpointB.ClientID, suite.pathBToA.EndpointA.ClientID, - 1, payload, suite.evmChainA.SenderAccount.GetAddress(), - ) - - if !tc.expErr { - - suite.Require().Equal(channeltypesv2.PacketStatus_Success, recvResult.Status) - suite.Require().Equal(channeltypes.NewResultAcknowledgement([]byte{byte(1)}).Acknowledgement(), recvResult.Acknowledgement) - - escrowAddress := types.GetEscrowAddress(types.PortID, suite.pathBToA.EndpointB.ClientID) - // check that the balance for evmChainA is updated - chainBBalance := simAppB.BankKeeper.GetBalance( - suite.chainB.GetContext(), - suite.chainB.SenderAccount.GetAddress(), - originalCoin.Denom, - ) - suite.Require().Equal(originalBalance.Amount.Sub(amount).Int64(), chainBBalance.Amount.Int64()) - - // check that module account escrow address has locked the tokens - chainBEscrowBalance := simAppB.BankKeeper.GetBalance( - suite.chainB.GetContext(), - escrowAddress, - originalCoin.Denom, - ) - suite.Require().Equal(originalCoin, chainBEscrowBalance) - - traceBToA := types.NewHop(types.PortID, suite.pathBToA.EndpointA.ClientID) - - // check that voucher exists on chain B - chainADenom := types.NewDenom(originalCoin.Denom, traceBToA) - chainABalance := evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - suite.evmChainA.SenderAccount.GetAddress(), - chainADenom.IBCDenom(), - ) - coinSentFromBToA := sdk.NewCoin(chainADenom.IBCDenom(), amount) - suite.Require().Equal(coinSentFromBToA, chainABalance) - } else { - suite.Require().Equal(channeltypesv2.PacketStatus_Failure, recvResult.Status) - } - }) - } -} - -// TestOnAckPacket tests ExampleChain's implementation of the OnAckPacket callback. -func (suite *TransferTestSuiteV2) TestOnAckPacket() { - testCases := []struct { - name string - sourceDenomToTransfer string - }{ - { - "transfer single denom", - sdk.DefaultBondDenom, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - evmApp := suite.evmChainA.App.(*evmd.EVMD) - originalBalance := evmApp.BankKeeper.GetBalance(suite.evmChainA.GetContext(), suite.evmChainA.SenderAccount.GetAddress(), tc.sourceDenomToTransfer) - - timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().Add(time.Hour).Unix()) //nolint:gosec // G115 - - amount, ok := sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64) - suite.Require().True(ok) - originalCoin := sdk.NewCoin(tc.sourceDenomToTransfer, amount) - - msg := types.NewMsgTransferWithEncoding( - types.PortID, suite.pathAToB.EndpointA.ClientID, - originalCoin, suite.evmChainA.SenderAccount.GetAddress().String(), - suite.chainB.SenderAccount.GetAddress().String(), clienttypes.Height{}, - timeoutTimestamp, "", types.EncodingProtobuf, - false, - ) - _, err := suite.evmChainA.SendMsgs(msg) - suite.Require().NoError(err) // message committed - - token, err := evmApp.TransferKeeper.TokenFromCoin(suite.evmChainA.GetContext(), originalCoin) - suite.Require().NoError(err) - - transferData := types.NewFungibleTokenPacketData( - token.Denom.Path(), - token.Amount, - suite.evmChainA.SenderAccount.GetAddress().String(), - suite.chainB.SenderAccount.GetAddress().String(), - "", - ) - bz := suite.evmChainA.Codec.MustMarshal(&transferData) - payload := channeltypesv2.NewPayload( - types.PortID, types.PortID, types.V1, - types.EncodingProtobuf, bz, - ) - - ctx := suite.evmChainA.GetContext() - cbs := evmApp.GetIBCKeeper().ChannelKeeperV2.Router.Route(evmibctesting.TransferPort) - - ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) - - err = cbs.OnAcknowledgementPacket( - ctx, suite.pathAToB.EndpointA.ClientID, suite.pathAToB.EndpointB.ClientID, - 1, ack.Acknowledgement(), payload, suite.evmChainA.SenderAccount.GetAddress(), - ) - suite.Require().NoError(err) - - // on successful ack, the tokens sent in packets should still be in escrow - escrowAddress := types.GetEscrowAddress(types.PortID, suite.pathAToB.EndpointA.ClientID) - // check that the balance for evmChainA is updated - chainABalance := evmApp.BankKeeper.GetBalance(suite.evmChainA.GetContext(), suite.evmChainA.SenderAccount.GetAddress(), originalCoin.Denom) - suite.Require().Equal(originalBalance.Amount.Sub(amount).Int64(), chainABalance.Amount.Int64()) - - // check that module account escrow address has locked the tokens - chainAEscrowBalance := evmApp.BankKeeper.GetBalance(suite.evmChainA.GetContext(), escrowAddress, originalCoin.Denom) - suite.Require().Equal(originalCoin, chainAEscrowBalance) - - // create a custom error ack and replay the callback to ensure it fails with IBC v2 callbacks - errAck := channeltypes.NewErrorAcknowledgement(types.ErrInvalidAmount) - err = cbs.OnAcknowledgementPacket( - ctx, suite.pathAToB.EndpointA.ClientID, suite.pathAToB.EndpointB.ClientID, - 1, errAck.Acknowledgement(), payload, suite.evmChainA.SenderAccount.GetAddress(), - ) - suite.Require().Error(err) - - // create the sentinel error ack and replay the callback to ensure the tokens are correctly refunded - // we can replay the callback here because the replay protection is handled in the IBC handler - err = cbs.OnAcknowledgementPacket( - ctx, suite.pathAToB.EndpointA.ClientID, suite.pathAToB.EndpointB.ClientID, - 1, channeltypesv2.ErrorAcknowledgement[:], payload, suite.evmChainA.SenderAccount.GetAddress(), - ) - suite.Require().NoError(err) - - // on error ack, the tokens sent in packets should be returned to sender - // check that the balance for evmChainA is refunded - chainABalance = evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - suite.evmChainA.SenderAccount.GetAddress(), - originalCoin.Denom, - ) - suite.Require().Equal(originalBalance.Amount, chainABalance.Amount) - - // check that module account escrow address has no tokens - chainAEscrowBalance = evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - escrowAddress, - originalCoin.Denom, - ) - suite.Require().Equal(sdk.NewCoin(originalCoin.Denom, sdkmath.ZeroInt()), chainAEscrowBalance) - }) - } -} - -// TestOnTimeoutPacket tests ExampleChain's implementation of the OnTimeoutPacket callback. -func (suite *TransferTestSuiteV2) TestOnTimeoutPacket() { - testCases := []struct { - name string - sourceDenomToTransfer string - }{ - { - "transfer single denom", - sdk.DefaultBondDenom, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - evmApp := suite.evmChainA.App.(*evmd.EVMD) - originalBalance := evmApp.BankKeeper.GetBalance(suite.evmChainA.GetContext(), suite.evmChainA.SenderAccount.GetAddress(), tc.sourceDenomToTransfer) - - timeoutTimestamp := uint64(suite.chainB.GetContext().BlockTime().Add(time.Hour).Unix()) //nolint:gosec // G115 - - amount, ok := sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64) - suite.Require().True(ok) - originalCoin := sdk.NewCoin(tc.sourceDenomToTransfer, amount) - - msg := types.NewMsgTransferWithEncoding( - types.PortID, suite.pathAToB.EndpointA.ClientID, - originalCoin, suite.evmChainA.SenderAccount.GetAddress().String(), - suite.chainB.SenderAccount.GetAddress().String(), clienttypes.Height{}, - timeoutTimestamp, "", types.EncodingProtobuf, - false, - ) - _, err := suite.evmChainA.SendMsgs(msg) - suite.Require().NoError(err) // message committed - - token, err := evmApp.TransferKeeper.TokenFromCoin(suite.evmChainA.GetContext(), originalCoin) - suite.Require().NoError(err) - - transferData := types.NewFungibleTokenPacketData( - token.Denom.Path(), - token.Amount, - suite.evmChainA.SenderAccount.GetAddress().String(), - suite.chainB.SenderAccount.GetAddress().String(), - "", - ) - bz := suite.evmChainA.Codec.MustMarshal(&transferData) - payload := channeltypesv2.NewPayload( - types.PortID, types.PortID, types.V1, - types.EncodingProtobuf, bz, - ) - - // on successful send, the tokens sent in packets should be in escrow - escrowAddress := types.GetEscrowAddress(types.PortID, suite.pathAToB.EndpointA.ClientID) - // check that the balance for evmChainA is updated - chainABalance := evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - suite.evmChainA.SenderAccount.GetAddress(), - originalCoin.Denom, - ) - suite.Require().Equal(originalBalance.Amount.Sub(amount).Int64(), chainABalance.Amount.Int64()) - - // check that module account escrow address has locked the tokens - chainAEscrowBalance := evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - escrowAddress, - originalCoin.Denom, - ) - suite.Require().Equal(originalCoin, chainAEscrowBalance) - - ctx := suite.evmChainA.GetContext() - cbs := suite.evmChainA.App.GetIBCKeeper().ChannelKeeperV2.Router.Route(evmibctesting.TransferPort) - - err = cbs.OnTimeoutPacket( - ctx, suite.pathAToB.EndpointA.ClientID, suite.pathAToB.EndpointB.ClientID, - 1, payload, suite.evmChainA.SenderAccount.GetAddress(), - ) - suite.Require().NoError(err) - - // on timeout, the tokens sent in packets should be returned to sender - // check that the balance for evmChainA is refunded - chainABalance = evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - suite.evmChainA.SenderAccount.GetAddress(), - originalCoin.Denom, - ) - suite.Require().Equal(originalBalance.Amount, chainABalance.Amount) - - // check that module account escrow address has no tokens - chainAEscrowBalance = evmApp.BankKeeper.GetBalance( - suite.evmChainA.GetContext(), - escrowAddress, - originalCoin.Denom, - ) - suite.Require().Equal(sdk.NewCoin(originalCoin.Denom, sdkmath.ZeroInt()), chainAEscrowBalance) - }) - } -} diff --git a/evmd/tests/integration/create_app.go b/evmd/tests/integration/create_app.go index f1adb4854..d20a2c930 100644 --- a/evmd/tests/integration/create_app.go +++ b/evmd/tests/integration/create_app.go @@ -2,6 +2,8 @@ package integration import ( "encoding/json" + "github.com/cosmos/evm" + eapp "github.com/cosmos/evm/evmd/app" "os" "github.com/cosmos/cosmos-sdk/client/flags" @@ -9,8 +11,6 @@ import ( dbm "github.com/cosmos/cosmos-db" ibctesting "github.com/cosmos/ibc-go/v10/testing" - "github.com/cosmos/evm" - "github.com/cosmos/evm/evmd" srvflags "github.com/cosmos/evm/server/flags" "github.com/cosmos/evm/testutil/constants" feemarkettypes "github.com/cosmos/evm/x/feemarket/types" @@ -40,7 +40,7 @@ func CreateEvmd(chainID string, evmChainID uint64, customBaseAppOptions ...func( baseAppOptions := append(customBaseAppOptions, baseapp.SetChainID(chainID)) - return evmd.NewExampleApp( + return eapp.New( logger, db, nil, @@ -58,7 +58,7 @@ func SetupEvmd() (ibctesting.TestingApp, map[string]json.RawMessage) { panic(err) } - app := evmd.NewExampleApp( + app := eapp.New( log.NewNopLogger(), dbm.NewMemDB(), nil, diff --git a/evmd/tests/integration/ibc_callbacks_test.go b/evmd/tests/integration/ibc_callbacks_test.go deleted file mode 100644 index 725e883d5..000000000 --- a/evmd/tests/integration/ibc_callbacks_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package integration - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/tests/integration/x/ibc/callbacks" -) - -func TestIBCCallback(t *testing.T) { - suite.Run(t, callbacks.NewKeeperTestSuite(CreateEvmd)) -} diff --git a/evmd/tests/integration/ibc_test.go b/evmd/tests/integration/ibc_test.go deleted file mode 100644 index 75048733a..000000000 --- a/evmd/tests/integration/ibc_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package integration - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/tests/integration/x/ibc" -) - -func TestIBCKeeperTestSuite(t *testing.T) { - s := ibc.NewKeeperTestSuite(CreateEvmd) - suite.Run(t, s) -} diff --git a/evmd/tests/integration/precompiles/distribution/precompile_distribution_test.go b/evmd/tests/integration/precompiles/distribution/precompile_distribution_test.go deleted file mode 100644 index 0b81df7d6..000000000 --- a/evmd/tests/integration/precompiles/distribution/precompile_distribution_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package distribution - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd/tests/integration" - "github.com/cosmos/evm/tests/integration/precompiles/distribution" -) - -func TestDistributionPrecompileTestSuite(t *testing.T) { - s := distribution.NewPrecompileTestSuite(integration.CreateEvmd) - suite.Run(t, s) -} - -func TestDistributionPrecompileIntegrationTestSuite(t *testing.T) { - distribution.TestPrecompileIntegrationTestSuite(t, integration.CreateEvmd) -} diff --git a/evmd/tests/integration/precompiles/erc20/precompile_erc20_test.go b/evmd/tests/integration/precompiles/erc20/precompile_erc20_test.go deleted file mode 100644 index ba27ed043..000000000 --- a/evmd/tests/integration/precompiles/erc20/precompile_erc20_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package erc20 - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd/tests/integration" - erc21 "github.com/cosmos/evm/tests/integration/precompiles/erc20" -) - -func TestErc20PrecompileTestSuite(t *testing.T) { - s := erc21.NewPrecompileTestSuite(integration.CreateEvmd) - suite.Run(t, s) -} - -func TestErc20IntegrationTestSuite(t *testing.T) { - erc21.TestIntegrationTestSuite(t, integration.CreateEvmd) -} diff --git a/evmd/tests/integration/precompiles/ics20/precompile_ics20_test.go b/evmd/tests/integration/precompiles/ics20/precompile_ics20_test.go deleted file mode 100644 index 98a3583a1..000000000 --- a/evmd/tests/integration/precompiles/ics20/precompile_ics20_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package ics20 - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd/tests/integration" - "github.com/cosmos/evm/tests/integration/precompiles/ics20" -) - -func TestICS20PrecompileTestSuite(t *testing.T) { - s := ics20.NewPrecompileTestSuite(t, integration.SetupEvmd) - suite.Run(t, s) -} - -func TestICS20PrecompileIntegrationTestSuite(t *testing.T) { - ics20.TestPrecompileIntegrationTestSuite(t, integration.SetupEvmd) -} diff --git a/evmd/tests/integration/precompiles/slashing/precompile_slashing_test.go b/evmd/tests/integration/precompiles/slashing/precompile_slashing_test.go deleted file mode 100644 index c4c47e268..000000000 --- a/evmd/tests/integration/precompiles/slashing/precompile_slashing_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package slashing - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd/tests/integration" - "github.com/cosmos/evm/tests/integration/precompiles/slashing" -) - -func TestSlashingPrecompileTestSuite(t *testing.T) { - s := slashing.NewPrecompileTestSuite(integration.CreateEvmd) - suite.Run(t, s) -} - -func TestStakingPrecompileIntegrationTestSuite(t *testing.T) { - slashing.TestPrecompileIntegrationTestSuite(t, integration.CreateEvmd) -} diff --git a/evmd/tests/integration/precompiles/werc20/precompile_werc20_test.go b/evmd/tests/integration/precompiles/werc20/precompile_werc20_test.go deleted file mode 100644 index 011e605fb..000000000 --- a/evmd/tests/integration/precompiles/werc20/precompile_werc20_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package werc20 - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/evmd/tests/integration" - "github.com/cosmos/evm/tests/integration/precompiles/werc20" -) - -func TestWERC20PrecompileUnitTestSuite(t *testing.T) { - s := werc20.NewPrecompileUnitTestSuite(integration.CreateEvmd) - suite.Run(t, s) -} - -func TestWERC20PrecompileIntegrationTestSuite(t *testing.T) { - werc20.TestPrecompileIntegrationTestSuite(t, integration.CreateEvmd) -} diff --git a/evmd/tests/integration/x_erc20_test.go b/evmd/tests/integration/x_erc20_test.go deleted file mode 100644 index 1a1e46b01..000000000 --- a/evmd/tests/integration/x_erc20_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package integration - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/tests/integration/x/erc20" -) - -func TestERC20GenesisTestSuite(t *testing.T) { - suite.Run(t, erc20.NewGenesisTestSuite(CreateEvmd)) -} - -func TestERC20KeeperTestSuite(t *testing.T) { - s := erc20.NewKeeperTestSuite(CreateEvmd) - suite.Run(t, s) -} - -func TestERC20PrecompileIntegrationTestSuite(t *testing.T) { - erc20.TestPrecompileIntegrationTestSuite(t, CreateEvmd) -} diff --git a/evmd/tests/integration/x_precisebank_test.go b/evmd/tests/integration/x_precisebank_test.go deleted file mode 100644 index 950b473f4..000000000 --- a/evmd/tests/integration/x_precisebank_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package integration - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/evm/tests/integration/x/precisebank" -) - -func TestPreciseBankGenesis(t *testing.T) { - s := precisebank.NewGenesisTestSuite(CreateEvmd) - suite.Run(t, s) -} - -func TestPreciseBankKeeper(t *testing.T) { - s := precisebank.NewKeeperIntegrationTestSuite(CreateEvmd) - suite.Run(t, s) -} diff --git a/evmd/tests/ledger/evmosd_suite_test.go b/evmd/tests/ledger/evmosd_suite_test.go index 200a8a5da..66e7f2e15 100644 --- a/evmd/tests/ledger/evmosd_suite_test.go +++ b/evmd/tests/ledger/evmosd_suite_test.go @@ -4,6 +4,8 @@ import ( "bufio" "context" "fmt" + "github.com/cosmos/evm/evmd/app" + "github.com/cosmos/evm/evmd/testutil" "io" "testing" "time" @@ -27,7 +29,6 @@ import ( "github.com/cosmos/evm/crypto/hd" cosmosevmkeyring "github.com/cosmos/evm/crypto/keyring" "github.com/cosmos/evm/encoding" - "github.com/cosmos/evm/evmd" "github.com/cosmos/evm/evmd/tests/ledger/mocks" "github.com/cosmos/evm/testutil/constants" utiltx "github.com/cosmos/evm/testutil/tx" @@ -47,7 +48,7 @@ var s *LedgerTestSuite type LedgerTestSuite struct { suite.Suite - app *evmd.EVMD + app *app.App ctx sdk.Context ledger *mocks.SECP256K1 @@ -88,7 +89,7 @@ func (suite *LedgerTestSuite) SetupEvmosApp() { // init app chainID := constants.ExampleChainID - suite.app = evmd.Setup(suite.T(), chainID.ChainID, chainID.EVMChainID) + suite.app = testutil.Setup(suite.T(), chainID.ChainID, chainID.EVMChainID) suite.ctx = suite.app.NewContextLegacy(false, tmproto.Header{ Height: 1, ChainID: chainID.ChainID, diff --git a/evmd/tests/network/network.go b/evmd/tests/network/network.go index 76904f726..eec4a2724 100644 --- a/evmd/tests/network/network.go +++ b/evmd/tests/network/network.go @@ -6,6 +6,11 @@ import ( "encoding/json" "errors" "fmt" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/evm/evmd/app" + evmconfig "github.com/cosmos/evm/evmd/cmd/evmd/config" + "github.com/cosmos/evm/testutil" "net/http" "net/url" "os" @@ -29,10 +34,7 @@ import ( dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/evm/crypto/hd" - "github.com/cosmos/evm/evmd" - evmconfig "github.com/cosmos/evm/evmd/config" "github.com/cosmos/evm/server/config" - evmtestutil "github.com/cosmos/evm/testutil" testconstants "github.com/cosmos/evm/testutil/constants" "cosmossdk.io/log" @@ -42,8 +44,6 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keyring" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/server" @@ -75,18 +75,18 @@ type Config struct { KeyringOptions []keyring.Option // keyring configuration options Codec codec.Codec LegacyAmino *codec.LegacyAmino // TODO: Remove! - InterfaceRegistry codectypes.InterfaceRegistry + InterfaceRegistry types.InterfaceRegistry TxConfig client.TxConfig AccountRetriever client.AccountRetriever - AppConstructor AppConstructor // the ABCI application constructor - GenesisState evmtestutil.GenesisState // custom gensis state to provide - TimeoutCommit time.Duration // the consensus commitment timeout - AccountTokens math.Int // the amount of unique validator tokens (e.g. 1000node0) - StakingTokens math.Int // the amount of tokens each validator has available to stake - BondedTokens math.Int // the amount of tokens each validator stakes (used if BondedTokensPerValidator is nil) - BondedTokensPerValidator []math.Int // optional per-validator bonded tokens (overrides BondedTokens if set) - NumValidators int // the total number of validators to create and bond - ChainID string // the network chain-id + AppConstructor AppConstructor // the ABCI application constructor + GenesisState testutil.GenesisState // custom gensis state to provide + TimeoutCommit time.Duration // the consensus commitment timeout + AccountTokens math.Int // the amount of unique validator tokens (e.g. 1000node0) + StakingTokens math.Int // the amount of tokens each validator has available to stake + BondedTokens math.Int // the amount of tokens each validator stakes (used if BondedTokensPerValidator is nil) + BondedTokensPerValidator []math.Int // optional per-validator bonded tokens (overrides BondedTokens if set) + NumValidators int // the total number of validators to create and bond + ChainID string // the network chain-id EVMChainID uint64 BondDenom string // the staking bond denomination MinGasPrices string // the minimum gas prices each validator will accept @@ -110,11 +110,11 @@ func DefaultConfig() Config { panic(fmt.Sprintf("failed creating temporary directory: %v", err)) } defer os.RemoveAll(dir) - tempApp := evmd.NewExampleApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, simutils.NewAppOptionsWithFlagHome(dir), baseapp.SetChainID(chainID)) + tempApp := app.New(log.NewNopLogger(), dbm.NewMemDB(), nil, true, simutils.NewAppOptionsWithFlagHome(dir), baseapp.SetChainID(chainID)) cfg := Config{ Codec: tempApp.AppCodec(), - TxConfig: tempApp.TxConfig(), + TxConfig: tempApp.GetTxConfig(), LegacyAmino: tempApp.LegacyAmino(), InterfaceRegistry: tempApp.InterfaceRegistry(), AccountRetriever: authtypes.AccountRetriever{}, @@ -140,7 +140,7 @@ func DefaultConfig() Config { // NewAppConstructor returns a new Cosmos EVM AppConstructor func NewAppConstructor(chainID string) AppConstructor { return func(val Validator) servertypes.Application { - return evmd.NewExampleApp( + return app.New( val.Ctx.Logger, dbm.NewMemDB(), nil, true, simutils.NewAppOptionsWithFlagHome(val.Ctx.Config.RootDir), baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), @@ -202,8 +202,8 @@ type ( // Logger is a network logger interface that exposes testnet-level Log() methods for an in-process testing network // This is not to be confused with logging that may happen at an individual node or validator level type Logger interface { - Log(args ...interface{}) - Logf(format string, args ...interface{}) + Log(args ...any) + Logf(format string, args ...any) } var ( @@ -215,11 +215,11 @@ type CLILogger struct { cmd *cobra.Command } -func (s CLILogger) Log(args ...interface{}) { +func (s CLILogger) Log(args ...any) { s.cmd.Println(args...) } -func (s CLILogger) Logf(format string, args ...interface{}) { +func (s CLILogger) Logf(format string, args ...any) { s.cmd.Printf(format, args...) } @@ -249,13 +249,13 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) { var ( genAccounts []authtypes.GenesisAccount genBalances []banktypes.Balance - genFiles []string + genFiles []string //nolint: prealloc ) buf := bufio.NewReader(os.Stdin) // generate private keys, node IDs, and initial transactions - for i := 0; i < cfg.NumValidators; i++ { + for i := range cfg.NumValidators { appCfg := config.DefaultConfig() appCfg.Pruning = cfg.PruningStrategy appCfg.MinGasPrices = cfg.MinGasPrices @@ -275,13 +275,13 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) { appCfg.GRPC.Enable = false appCfg.GRPCWeb.Enable = false appCfg.JSONRPC.Enable = false - apiListenAddr := "" + var apiListenAddr string if i == 0 { if cfg.APIAddress != "" { apiListenAddr = cfg.APIAddress } else { if len(portPool) == 0 { - return nil, fmt.Errorf("failed to get port for API server") + return nil, errors.New("failed to get port for API server") } port := <-portPool apiListenAddr = fmt.Sprintf("tcp://0.0.0.0:%s", port) @@ -292,13 +292,13 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) { if err != nil { return nil, err } - apiAddr = fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port()) + apiAddr = fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port()) //nolint: revive if cfg.RPCAddress != "" { cmtCfg.RPC.ListenAddress = cfg.RPCAddress } else { if len(portPool) == 0 { - return nil, fmt.Errorf("failed to get port for RPC server") + return nil, errors.New("failed to get port for RPC server") } port := <-portPool cmtCfg.RPC.ListenAddress = fmt.Sprintf("tcp://0.0.0.0:%s", port) @@ -308,7 +308,7 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) { appCfg.GRPC.Address = cfg.GRPCAddress } else { if len(portPool) == 0 { - return nil, fmt.Errorf("failed to get port for GRPC server") + return nil, errors.New("failed to get port for GRPC server") } port := <-portPool appCfg.GRPC.Address = fmt.Sprintf("0.0.0.0:%s", port) @@ -320,7 +320,7 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) { appCfg.JSONRPC.Address = cfg.JSONRPCAddress } else { if len(portPool) == 0 { - return nil, fmt.Errorf("failed to get port for JSON-RPC server") + return nil, errors.New("failed to get port for JSON-RPC server") } port := <-portPool appCfg.JSONRPC.Address = fmt.Sprintf("0.0.0.0:%s", port) @@ -356,14 +356,14 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) { monikers[i] = nodeDirName if len(portPool) == 0 { - return nil, fmt.Errorf("failed to get port for Proxy server") + return nil, errors.New("failed to get port for Proxy server") } port := <-portPool proxyAddr := fmt.Sprintf("tcp://0.0.0.0:%s", port) cmtCfg.ProxyApp = proxyAddr if len(portPool) == 0 { - return nil, fmt.Errorf("failed to get port for Proxy server") + return nil, errors.New("failed to get port for Proxy server") } port = <-portPool p2pAddr := fmt.Sprintf("tcp://0.0.0.0:%s", port) @@ -717,6 +717,8 @@ func trapSignal(cleanupFunc func()) { exitCode += int(syscall.SIGINT) case syscall.SIGTERM: exitCode += int(syscall.SIGTERM) + default: + exitCode += int(syscall.SIGKILL) } os.Exit(exitCode) diff --git a/evmd/test_helpers.go b/evmd/testutil/test_helpers.go similarity index 90% rename from evmd/test_helpers.go rename to evmd/testutil/test_helpers.go index a8e355b9b..43bfd706d 100644 --- a/evmd/test_helpers.go +++ b/evmd/testutil/test_helpers.go @@ -1,11 +1,12 @@ -package evmd +package testutil import ( "encoding/json" "fmt" + eapp "github.com/cosmos/evm/evmd/app" + "github.com/cosmos/evm/evmd/cmd/evmd/config" "testing" - "github.com/cosmos/evm/evmd/config" testconstants "github.com/cosmos/evm/testutil/constants" "github.com/cosmos/evm/testutil/integration/evm/network" "github.com/cosmos/evm/x/vm/types" @@ -51,23 +52,23 @@ func init() { config.SetBip44CoinType(cfg) } -func setup(withGenesis bool, invCheckPeriod uint, chainID string, evmChainID uint64) (*EVMD, GenesisState) { +func setup(withGenesis bool, invCheckPeriod uint, chainID string, evmChainID uint64) (*eapp.App, eapp.GenesisState) { db := dbm.NewMemDB() appOptions := make(simtestutil.AppOptionsMap, 0) - appOptions[flags.FlagHome] = defaultNodeHome + appOptions[flags.FlagHome] = eapp.DefaultNodeHome appOptions[server.FlagInvCheckPeriod] = invCheckPeriod - app := NewExampleApp(log.NewNopLogger(), db, nil, true, appOptions, baseapp.SetChainID(chainID)) + app := eapp.New(log.NewNopLogger(), db, nil, true, appOptions, baseapp.SetChainID(chainID)) if withGenesis { return app, app.DefaultGenesis() } - return app, GenesisState{} + return app, eapp.GenesisState{} } // Setup initializes a new EVMD. A Nop logger is set in EVMD. -func Setup(t *testing.T, chainID string, evmChainID uint64) *EVMD { +func Setup(t *testing.T, chainID string, evmChainID uint64) *eapp.App { t.Helper() privVal := mock.NewPV() @@ -95,7 +96,7 @@ func Setup(t *testing.T, chainID string, evmChainID uint64) *EVMD { // that also act as delegators. For simplicity, each validator is bonded with a delegation // of one consensus engine unit in the default token of the simapp from first genesis // account. A Nop logger is set in EVMD. -func SetupWithGenesisValSet(t *testing.T, chainID string, evmChainID uint64, valSet *cmttypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *EVMD { +func SetupWithGenesisValSet(t *testing.T, chainID string, evmChainID uint64, valSet *cmttypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *eapp.App { t.Helper() app, genesisState := setup(true, 5, chainID, evmChainID) @@ -138,10 +139,10 @@ func SetupWithGenesisValSet(t *testing.T, chainID string, evmChainID uint64, val func SetupTestingApp(chainID string) func() (ibctesting.TestingApp, map[string]json.RawMessage) { return func() (ibctesting.TestingApp, map[string]json.RawMessage) { db := dbm.NewMemDB() - app := NewExampleApp( + app := eapp.New( log.NewNopLogger(), db, nil, true, - simtestutil.NewAppOptionsWithFlagHome(defaultNodeHome), + simtestutil.NewAppOptionsWithFlagHome(eapp.DefaultNodeHome), baseapp.SetChainID(chainID), ) return app, app.DefaultGenesis() diff --git a/evmd/upgrades.go b/evmd/upgrades.go deleted file mode 100644 index 074d6114d..000000000 --- a/evmd/upgrades.go +++ /dev/null @@ -1,85 +0,0 @@ -package evmd - -import ( - "context" - - "github.com/cosmos/evm/x/vm/types" - - storetypes "cosmossdk.io/store/types" - upgradetypes "cosmossdk.io/x/upgrade/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" -) - -// UpgradeName defines the on-chain upgrade name for the sample EVMD upgrade -// from v0.5.0 to v0.6.0. -// -// NOTE: This upgrade defines a reference implementation of what an upgrade -// could look like when an application is migrating from EVMD version -// v0.4.0 to v0.5.x -const UpgradeName = "v0.5.0-to-v0.6.0" - -func (app EVMD) RegisterUpgradeHandlers() { - app.UpgradeKeeper.SetUpgradeHandler( - UpgradeName, - func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - sdkCtx := sdk.UnwrapSDKContext(ctx) - sdkCtx.Logger().Debug("this is a debug level message to test that verbose logging mode has properly been enabled during a chain upgrade") - - app.BankKeeper.SetDenomMetaData(ctx, banktypes.Metadata{ - Description: "Example description", - DenomUnits: []*banktypes.DenomUnit{ - { - Denom: "atest", - Exponent: 0, - Aliases: nil, - }, - { - Denom: "test", - Exponent: 18, - Aliases: nil, - }, - }, - Base: "atest", - Display: "test", - Name: "Test Token", - Symbol: "TEST", - URI: "example_uri", - URIHash: "example_uri_hash", - }) - - // (Required for NON-18 denom chains *only) - // Update EVM params to add Extended denom options - // Ensure that this corresponds to the EVM denom - // (tyically the bond denom) - evmParams := app.EVMKeeper.GetParams(sdkCtx) - evmParams.ExtendedDenomOptions = &types.ExtendedDenomOptions{ExtendedDenom: "atest"} - err := app.EVMKeeper.SetParams(sdkCtx, evmParams) - if err != nil { - return nil, err - } - // Initialize EvmCoinInfo in the module store. Chains bootstrapped before v0.5.0 - // binaries never stored this information (it lived only in process globals), - // so migrating nodes would otherwise see an empty EvmCoinInfo on upgrade. - if err := app.EVMKeeper.InitEvmCoinInfo(sdkCtx); err != nil { - return nil, err - } - return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM) - }, - ) - - upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() - if err != nil { - panic(err) - } - - if upgradeInfo.Name == UpgradeName && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { - storeUpgrades := storetypes.StoreUpgrades{ - Added: []string{}, - } - // configure store loader that checks if version == upgradeHeight and applies store upgrades - app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) - } -} diff --git a/go.mod b/go.mod index 58bf5a228..2ee49aecc 100644 --- a/go.mod +++ b/go.mod @@ -109,7 +109,6 @@ require ( github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/chigopher/pathlib v0.19.1 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cloudwego/base64x v0.1.5 // indirect github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect @@ -184,12 +183,10 @@ require ( github.com/hdevalence/ed25519consensus v0.2.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/huandu/skiplist v1.2.1 // indirect - github.com/huandu/xstrings v1.4.0 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect - github.com/jinzhu/copier v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect @@ -246,7 +243,6 @@ require ( github.com/tklauser/numcpus v0.10.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ulikunitz/xz v0.5.15 // indirect - github.com/vektra/mockery/v2 v2.53.5 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zeebo/errs v1.4.0 // indirect github.com/zondax/golem v0.27.0 // indirect @@ -268,7 +264,6 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/arch v0.21.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/mod v0.28.0 // indirect golang.org/x/oauth2 v0.31.0 // indirect golang.org/x/sys v0.37.0 // indirect golang.org/x/term v0.36.0 // indirect diff --git a/go.sum b/go.sum index 2a25ff548..bdba4d1c6 100644 --- a/go.sum +++ b/go.sum @@ -204,8 +204,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chigopher/pathlib v0.19.1 h1:RoLlUJc0CqBGwq239cilyhxPNLXTK+HXoASGyGznx5A= -github.com/chigopher/pathlib v0.19.1/go.mod h1:tzC1dZLW8o33UQpWkNkhvPwL5n4yyFRFm/jL1YGWFvY= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= @@ -602,8 +600,6 @@ github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3 github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= github.com/huandu/skiplist v1.2.1 h1:dTi93MgjwErA/8idWTzIw4Y1kZsMWx35fmI2c8Rij7w= github.com/huandu/skiplist v1.2.1/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= @@ -627,8 +623,6 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= -github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= -github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= @@ -985,8 +979,6 @@ github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/vektra/mockery/v2 v2.53.5 h1:iktAY68pNiMvLoHxKqlSNSv/1py0QF/17UGrrAMYDI8= -github.com/vektra/mockery/v2 v2.53.5/go.mod h1:hIFFb3CvzPdDJJiU7J4zLRblUMv7OuezWsHPmswriwo= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= @@ -1097,8 +1089,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=