diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eff69e..4fc4561 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ --- +## 1.9.1 + +Changes included since `v1.9.0` (range: `v1.9.0..v1.9.1`). + +.- Action/ICA: persist `app_pubkey` on new actions, expose `app_pubkey` in action query responses, and regenerate action protobufs. +- Action/crypto: refreshed signature verification paths (ADR-36 fallback, DERβ†’RS64) and added coverage for app_pubkey validation/caching + query output. +- Devnet/Hermes: added ICA cascade flow tests and IBC helpers; updated Hermes configs/scripts and devnet setup scripts; removed legacy `devnet/tests/test-channel.sh`. +- Dependencies/docs: updated devnet and root Go module files and refreshed `readme.md`. + +--- + ## 1.9.0 Changes included since `v1.8.5` (range: `v1.8.5..v1.9.0`). @@ -18,7 +29,7 @@ Changes included since `v1.8.5` (range: `v1.8.5..v1.9.0`). ## 1.8.5 -Changes included since `v1.8.4` (range: `v1.8.4..HEAD`). +Changes included since `v1.8.4` (range: `v1.8.4..v1.8.5`). - Register every upgrade handler at startup (before Load) so state-sync nodes always have handlers available, even without an on-disk plan. - Fixed x/upgrade downgrade verification panics on state-synced nodes that already applied v1.8.4 but lacked a registered handler. @@ -30,7 +41,7 @@ Changes included since `v1.8.4` (range: `v1.8.4..HEAD`). ## 1.8.4 -Changes included since `v1.8.0` (range: `v1.8.0..HEAD`). +Changes included since `v1.8.0` (range: `v1.8.0..v1.8.4`). - Added a legacy type URL aliasing framework (`internal/legacyalias`) and wired it into module registration so pre-versioned Action/Supernode messages stored on-chain continue to decode after the versioned protobuf migration. AutoCLI now wraps the codec resolvers with the legacy-aware resolver to keep CLI and REST queries seamless. - Introduced a protobuf enum bridge (`internal/protobridge`) that double-registers enum descriptors with both gogoproto and the standard protobuf runtime, eliminating REST/GRPC mismatches when the gateway still consults the legacy registry. @@ -47,7 +58,7 @@ Changes included since `v1.8.0` (range: `v1.8.0..HEAD`). ## 1.8.0 -Changes included since `v1.7.2` (range: `v1.7.2..HEAD`). +Changes included since `v1.7.2` (range: `v1.7.2..v1.8.0`). πŸš€ This release delivers major upgrades across Lumera’s blockchain core, IBC, CosmWasm, Ignite CLI, governance automation, and devnet infrastructure β€” improving performance, reliability, and developer experience. @@ -148,12 +159,12 @@ IBC v2.0 brings improved cross‑chain routing and middleware support, laying th - `start.sh` supports`auto`,`bootstrap`,`run`, and`wait` modes. - Auto-installs binaries, monitors height, coordinates services. -* Enable/disable each component via flags or environment variables when bringing up the devnet (kept generic here to avoid locking to specific flag names). -* **Optional service installers**: add-on installation toggles for**Supernode**,**Network‑Maker**, and**SN Client** (enable via flags/env when bringing up the devnet). Network‑Maker installation on a selected validator is driven by the`network-maker` flag in`validators.json`. +- Enable/disable each component via flags or environment variables when bringing up the devnet (kept generic here to avoid locking to specific flag names). +- **Optional service installers**: add-on installation toggles for**Supernode**,**Network‑Maker**, and**SN Client** (enable via flags/env when bringing up the devnet). Network‑Maker installation on a selected validator is driven by the`network-maker` flag in`validators.json`. #### πŸ“‹ Devnet Architecture Overview -``` +```go β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Lumera Devnet Architecture β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ @@ -182,7 +193,7 @@ IBC v2.0 brings improved cross‑chain routing and middleware support, laying th ## 1.7.2 -Changes included since `v1.7.0` (range: `v1.7.0..HEAD`). +Changes included since `v1.7.0` (range: `v1.7.0..v1.7.2`). Added diff --git a/Makefile.devnet b/Makefile.devnet index e21f80b..cc8cbf7 100644 --- a/Makefile.devnet +++ b/Makefile.devnet @@ -1,4 +1,4 @@ -.PHONY: devnet-build devnet-up devnet-reset devnet-up-detach devnet-down devnet-stop devnet-clean devnet-deploy-tar devnet-upgrade devnet-new devnet-start +.PHONY: devnet-build devnet-tests-build devnet-up devnet-reset devnet-up-detach devnet-down devnet-stop devnet-clean devnet-deploy-tar devnet-upgrade devnet-new devnet-start .PHONY: devnet-build-default _check-devnet-default-cfg devnet-upgrade-binaries devnet-upgrade-binaries-default devnet-update-scripts ##### Devnet Makefile ######################################## @@ -31,6 +31,7 @@ COMPOSE_FILE := devnet/docker-compose.yml DEVNET_BUILD_LUMERA ?= 1 # 1 = build lumerad for devnet setup, 0 = skip # directory to take lumerad/supernode/network-maker/sncli binaries from DEVNET_BIN_DIR ?= devnet/bin +DEVNET_BIN_DIR_ABS := $(abspath $(DEVNET_BIN_DIR)) # Default paths for configuration files DEFAULT_CONFIG_JSON := config/config.json @@ -41,6 +42,14 @@ DEFAULT_GENESIS_FILE := devnet/default-config/devnet-genesis.json DEFAULT_CLAIMS_FILE := claims.csv # relative to devnet ORIG_GENESIS_FILE := devnet/default-config/devnet-genesis-orig.json +devnet-tests-build: + @mkdir -p "${DEVNET_BIN_DIR_ABS}" + @echo "Building devnet test binaries..." + @cd devnet && \ + $(GO) test -c -o "${DEVNET_BIN_DIR_ABS}/ibc_validator" ./tests/validator && \ + $(GO) test -c -o "${DEVNET_BIN_DIR_ABS}/ibc_hermes" ./tests/hermes + @echo "Devnet test binaries built successfully" + devnet-build: @mkdir -p "$(SHARED_RELEASE_DIR)"; \ if [ -n "$(EXTERNAL_GENESIS_FILE)" ] && [ -f "$(EXTERNAL_GENESIS_FILE)" ]; then \ @@ -78,6 +87,7 @@ devnet-build: echo "${DEVNET_BIN_DIR}/libwasmvm.x86_64.so library not found..."; \ exit 1; \ fi; \ + $(MAKE) devnet-tests-build DEVNET_BIN_DIR="${DEVNET_BIN_DIR}"; \ cp -f "${DEVNET_BIN_DIR}/lumerad" "${SHARED_RELEASE_DIR}/"; \ cp -f "${DEVNET_BIN_DIR}/libwasmvm.x86_64.so" "${SHARED_RELEASE_DIR}/"; \ cd devnet && \ @@ -205,6 +215,7 @@ devnet-upgrade: echo "${DEVNET_BIN_DIR}/libwasmvm.x86_64.so library not found..."; \ exit 1; \ fi; \ + $(MAKE) devnet-tests-build DEVNET_BIN_DIR="${DEVNET_BIN_DIR}"; \ cp -f "${DEVNET_BIN_DIR}/lumerad" "${SHARED_RELEASE_DIR}/"; \ cp -f "${DEVNET_BIN_DIR}/libwasmvm.x86_64.so" "${SHARED_RELEASE_DIR}/"; \ cd devnet && \ diff --git a/app/upgrades/upgrades.go b/app/upgrades/upgrades.go index 6afa739..363ba60 100644 --- a/app/upgrades/upgrades.go +++ b/app/upgrades/upgrades.go @@ -28,6 +28,7 @@ import ( // | v1.8.4 | standard | mainnet: add PFM, drop NFT | Store upgrade gated to mainnet; handler is migrations only // | v1.8.5 | standard | none | Migrations only // | v1.9.0 | custom | none | Backfills action/supernode secondary indices +// | v1.9.1 | standard | none | Migrations only // ====================================================================================================================== type UpgradeConfig struct { @@ -40,6 +41,7 @@ const ( upgradeNameV170 = "v1.7.0" upgradeNameV172 = "v1.7.2" upgradeNameV185 = "v1.8.5" + upgradeNameV191 = "v1.9.1" ) var upgradeNames = []string{ @@ -50,6 +52,7 @@ var upgradeNames = []string{ upgrade_v1_8_4.UpgradeName, upgradeNameV185, upgrade_v1_9_0.UpgradeName, + upgradeNameV191, } var NoUpgradeConfig = UpgradeConfig{ @@ -100,6 +103,10 @@ func SetupUpgrades(upgradeName string, params appParams.AppUpgradeParams) (Upgra return UpgradeConfig{ Handler: upgrade_v1_9_0.CreateUpgradeHandler(params), }, true + case upgradeNameV191: + return UpgradeConfig{ + Handler: standardUpgradeHandler(upgradeNameV191, params), + }, true // add future upgrades here default: diff --git a/app/upgrades/upgrades_test.go b/app/upgrades/upgrades_test.go index 4fc6461..9231d7a 100644 --- a/app/upgrades/upgrades_test.go +++ b/app/upgrades/upgrades_test.go @@ -27,6 +27,7 @@ func TestUpgradeNamesOrder(t *testing.T) { upgrade_v1_8_4.UpgradeName, upgradeNameV185, upgrade_v1_9_0.UpgradeName, + upgradeNameV191, } require.Equal(t, expected, upgradeNames, "upgradeNames should stay in ascending order") } diff --git a/devnet/go.mod b/devnet/go.mod index 4d60994..02bfc79 100644 --- a/devnet/go.mod +++ b/devnet/go.mod @@ -1,10 +1,190 @@ module gen -go 1.24.4 +go 1.25.5 -require gopkg.in/yaml.v2 v2.4.0 +require ( + cosmossdk.io/api v0.9.2 + cosmossdk.io/math v1.5.3 + github.com/LumeraProtocol/lumera v1.9.0 + github.com/LumeraProtocol/sdk-go v1.0.3 + github.com/cosmos/cosmos-sdk v0.53.0 + github.com/cosmos/gogoproto v1.7.0 + github.com/cosmos/ibc-go/v10 v10.3.0 + github.com/stretchr/testify v1.11.1 + gopkg.in/yaml.v2 v2.4.0 +) + +replace ( + github.com/LumeraProtocol/lumera => .. + github.com/LumeraProtocol/sdk-go => ../../sdk-go + github.com/LumeraProtocol/supernode/v2 => ../../supernode + github.com/cosmos/cosmos-sdk => github.com/cosmos/cosmos-sdk v0.50.14 + github.com/envoyproxy/protoc-gen-validate => github.com/bufbuild/protoc-gen-validate v1.3.0 + github.com/lyft/protoc-gen-validate => github.com/envoyproxy/protoc-gen-validate v1.3.0 + github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + nhooyr.io/websocket => github.com/coder/websocket v1.8.7 +) require ( + cosmossdk.io/collections v1.3.0 // indirect + cosmossdk.io/core v0.11.3 // indirect + cosmossdk.io/depinject v1.2.0 // indirect + cosmossdk.io/errors v1.0.2 // indirect + cosmossdk.io/log v1.6.0 // indirect + cosmossdk.io/schema v1.1.0 // indirect + cosmossdk.io/store v1.1.2 // indirect + cosmossdk.io/x/tx v0.14.0 // indirect + cosmossdk.io/x/upgrade v0.2.0 // 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/DataDog/datadog-go v4.8.3+incompatible // indirect + github.com/DataDog/zstd v1.5.7 // indirect + github.com/LumeraProtocol/rq-go v0.2.1 // indirect + github.com/LumeraProtocol/supernode/v2 v2.4.10 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/speakeasy v0.2.0 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.14.1 // 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/cloudwego/base64x v0.1.6 // indirect + github.com/cockroachdb/errors v1.12.0 // indirect + github.com/cockroachdb/fifo v0.0.0-20240616162244-4768e80dfb9a // 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-20230807174530-cc333fc44b06 // indirect + github.com/cometbft/cometbft v0.38.18 // indirect + github.com/cometbft/cometbft-db v0.14.1 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-db v1.1.2 // 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/iavl v1.2.4 // indirect + github.com/cosmos/ics23/go v0.11.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.14.0 // indirect + github.com/danieljoos/wincred v1.2.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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.2.0 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.7.0 // indirect + github.com/emicklei/dot v1.6.2 // indirect + github.com/ethereum/go-ethereum v1.15.11 // 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.32.0 // indirect + github.com/go-errors/errors v1.5.1 // 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.0 // 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.2 // indirect + github.com/golang/glog v1.2.5 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/flatbuffers v24.3.25+incompatible // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/orderedcode v0.0.1 // indirect + github.com/google/uuid v1.6.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/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // 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.6.3 // 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/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/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 - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/linxGnu/grocksdb v1.9.8 // 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/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/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.63.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // 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.5 // 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/subosito/gotenv v1.6.0 // 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.7.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + go.etcd.io/bbolt v1.4.0-alpha.1 // indirect + go.opencensus.io v0.24.0 // indirect + go.uber.org/mock v0.6.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/arch v0.15.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.1-0.20251013234738-63d1a5100f82 // 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 + google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect + google.golang.org/grpc v1.77.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.5.2 // indirect + lukechampine.com/blake3 v1.4.1 // indirect + nhooyr.io/websocket v1.8.17 // indirect + pgregory.net/rapid v1.2.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/devnet/go.sum b/devnet/go.sum index 91524ed..7cd180c 100644 --- a/devnet/go.sum +++ b/devnet/go.sum @@ -1,13 +1,1466 @@ +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.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= +cloud.google.com/go/auth v0.16.0 h1:Pd8P1s9WkcrBE2n/PhAwKsdrR35V3Sg2II9B+ndM3CU= +cloud.google.com/go/auth v0.16.0/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= +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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.37.0 h1:XxtZlXYkZXub3LNaLu90TTemcFqIU1yZ4E4q9VlR39A= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +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/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/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs= +cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY= +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.8.0.20250402172810-41e3e9d004a1 h1:nlMUeKu6CGrO7Gxt5S31qT3g27CHmBJHsZPjqHApVTI= +cosmossdk.io/client/v2 v2.0.0-beta.8.0.20250402172810-41e3e9d004a1/go.mod h1:xgv0ejeOk5yeDraPW5tv+PfBkCDt4yYa/+u45MyP+bM= +cosmossdk.io/collections v1.3.0 h1:RUY23xXBy/bu5oSHZ5y+mkJRyA4ZboKDO4Yvx4+g2uc= +cosmossdk.io/collections v1.3.0/go.mod h1:cqVpBMDGEYhuNmNSXIOmqpnQ7Eav43hpJIetzLuEkns= +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.0 h1:6NW/FSK1IkWTrX7XxUpBmX1QMBozpEI9SsWkKTBc5zw= +cosmossdk.io/depinject v1.2.0/go.mod h1:pvitjtUxZZZTQESKNS9KhGjWVslJZxtO9VooRJYyPjk= +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.0 h1:SJIOmJ059wi1piyRgNRXKXhlDXGqnB5eQwhcZKv2tOk= +cosmossdk.io/log v1.6.0/go.mod h1:5cXXBvfBkR2/BcXmosdCSLXllvgSjphrrDVdfVRmBGM= +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.1.2 h1:3HOZG8+CuThREKv6cn3WSohAc6yccxO3hLzwK6rBC7o= +cosmossdk.io/store v1.1.2/go.mod h1:60rAGzTHevGm592kFhiUVkNC9w7gooSEn5iUBPzHQ6A= +cosmossdk.io/x/circuit v0.1.1 h1:KPJCnLChWrxD4jLwUiuQaf5mFD/1m7Omyo7oooefBVQ= +cosmossdk.io/x/circuit v0.1.1/go.mod h1:B6f/urRuQH8gjt4eLIXfZJucrbreuYrKh5CSjaOxr+Q= +cosmossdk.io/x/evidence v0.1.1 h1:Ks+BLTa3uftFpElLTDp9L76t2b58htjVbSZ86aoK/E4= +cosmossdk.io/x/evidence v0.1.1/go.mod h1:OoDsWlbtuyqS70LY51aX8FBTvguQqvFrt78qL7UzeNc= +cosmossdk.io/x/feegrant v0.1.1 h1:EKFWOeo/pup0yF0svDisWWKAA9Zags6Zd0P3nRvVvw8= +cosmossdk.io/x/feegrant v0.1.1/go.mod h1:2GjVVxX6G2fta8LWj7pC/ytHjryA6MHAJroBWHFNiEQ= +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= +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/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= +github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= +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/CosmWasm/wasmd v0.55.0-ibc2.0 h1:9bH+QDnSGxmZhjSykLYGtW4sltzGFFVm10Awk683q2Y= +github.com/CosmWasm/wasmd v0.55.0-ibc2.0/go.mod h1:c9l+eycjUB2zNVLIGjAXd7QrFEbxVTEa1Fh1Mx74VwQ= +github.com/CosmWasm/wasmvm/v3 v3.0.0-ibc2.0 h1:QoagSm5iYuRSPYDxgRxsa6hVfDppUp4+bOwY7bDuMO0= +github.com/CosmWasm/wasmvm/v3 v3.0.0-ibc2.0/go.mod h1:oknpb1bFERvvKcY7vHRp1F/Y/z66xVrsl7n9uWkOAlM= +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.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 h1:5IT7xOdq17MtcdtL/vtl6mGfzhaq4m4vpollPRmlsBQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0/go.mod h1:ZV4VOm0/eHR06JLrXWe09068dHpr3TRpY9Uo7T+anuA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 h1:ig/FpDD2JofP/NExKQUbn7uOSZzJAQqogfqluZK4ed4= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/LumeraProtocol/rq-go v0.2.1 h1:8B3UzRChLsGMmvZ+UVbJsJj6JZzL9P9iYxbdUwGsQI4= +github.com/LumeraProtocol/rq-go v0.2.1/go.mod h1:APnKCZRh1Es2Vtrd2w4kCLgAyaL5Bqrkz/BURoRJ+O8= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +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/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.6 h1:k1/zc2jNfeiZBA5aFTRy37jlBIuCkXCm0XmvpzCKI9I= +github.com/adlio/schema v1.3.6/go.mod h1:qkxwLgPBd1FgLRHYVCmQT/rrBr3JH38J9LjmVzWNudg= +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/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 v1.49.0 h1:g9BkW1fo9GqKfwg2+zCD+TW/D36Ux+vtfJ8guF4AYmY= +github.com/aws/aws-sdk-go v1.49.0/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +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.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4= +github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +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/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= +github.com/bufbuild/protoc-gen-validate v1.3.0 h1:0lq2b9qA1uzfVnMW6oFJepiVVihDOOzj+VuTGSX4EgE= +github.com/bufbuild/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= +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/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w= +github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc= +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/xxhash/v2 v2.1.1/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/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +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/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.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +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-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= +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-20240616162244-4768e80dfb9a h1:f52TdbU4D5nozMAhO9TvTJ2ZMCXtN4VIAmfrrZ0JXQ4= +github.com/cockroachdb/fifo v0.0.0-20240616162244-4768e80dfb9a/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-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coder/websocket v1.8.7 h1:jiep6gmlfP/yq2w1gBoubJEXL9gf8x3bp6lzzX8nJxE= +github.com/coder/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +github.com/cometbft/cometbft v0.38.18 h1:1ZHYMdu0S75YxFM13LlPXnOwiIpUW5z9TKMQtTIALpw= +github.com/cometbft/cometbft v0.38.18/go.mod h1:PlOQgf3jQorep+g6oVnJgtP65TJvBJoLiXjGaMdNxBE= +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/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +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/cosmos-db v1.1.2 h1:KZm4xLlPp6rLkyIOmPOhh+XDK9oH1++pNH/csLdX0Dk= +github.com/cosmos/cosmos-db v1.1.2/go.mod h1:dMg2gav979Ig2N076POEw4CEKbCsieaOfDWSfFZxs8M= +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.50.14 h1:G8CtGHFWbExa+ZpVOVAb4kFmko/R30igsYOwyzRMtgY= +github.com/cosmos/cosmos-sdk v0.50.14/go.mod h1:hrWEFMU1eoXqLJeE6VVESpJDQH67FS1nnMrQIjO2daw= +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/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.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= +github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= +github.com/cosmos/iavl v1.2.4 h1:IHUrG8dkyueKEY72y92jajrizbkZKPZbMmG14QzsEkw= +github.com/cosmos/iavl v1.2.4/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v10 v10.1.0 h1:epKcbFAeWRRw1i1jZnYzLIEm9sgUPaL1RftuRjjUKGw= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v10 v10.1.0/go.mod h1:S4ZQwf5/LhpOi8JXSAese/6QQDk87nTdicJPlZ5q9UQ= +github.com/cosmos/ibc-go/v10 v10.3.0 h1:w5DkHih8qn15deAeFoTk778WJU+xC1krJ5kDnicfUBc= +github.com/cosmos/ibc-go/v10 v10.3.0/go.mod h1:CthaR7n4d23PJJ7wZHegmNgbVcLXCQql7EwHrAXnMtw= +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/ledger-cosmos-go v0.14.0 h1:WfCHricT3rPbkPSVKRH+L4fQGKYHuGOK9Edpel8TYpE= +github.com/cosmos/ledger-cosmos-go v0.14.0/go.mod h1:E07xCWSBl3mTGofZ2QnL4cIUzMbbGVyik84QYKbX3RA= +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/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +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/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 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/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.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +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.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs= +github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= +github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/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/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/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/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/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/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.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +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.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= +github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= +github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= +github.com/ethereum/go-ethereum v1.15.11 h1:JK73WKeu0WC0O1eyX+mdQAVHUV+UR1a9VB/domDngBU= +github.com/ethereum/go-ethereum v1.15.11/go.mod h1:mf8YiHIb0GR4x4TipcvBUPxJLw1mFdmxzoDi11sDRoI= +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/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/getsentry/sentry-go v0.32.0 h1:YKs+//QmwE3DcYtfKRH8/KyOOF/I6Qnx7qYGNHCGmCY= +github.com/getsentry/sentry-go v0.32.0/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG72XcGN/p71BAY= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +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.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +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.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +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-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +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 h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +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/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +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/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/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +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/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +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.4/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 v0.0.5-0.20231225225746-43d5d4cd4e0e h1:4bw4WeyTYPp0smaXiJZCNnLrvVBqirQVreixayXezGc= +github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e/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 v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= +github.com/google/flatbuffers v24.3.25+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.4.1/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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/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.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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 v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +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-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +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.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= +github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +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.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= +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/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-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.7.8 h1:mshVHx1Fto0/MydBekWan5zUipGq7jO0novchgMmSiY= +github.com/hashicorp/go-getter v1.7.8/go.mod h1:2c6CboOEb9jG6YvmC9xdD+tyAFsrUaJPedwXDGr0TM4= +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.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= +github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= +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-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= +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/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/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= +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-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +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/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +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/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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +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.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +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/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.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +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.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/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +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.9.8 h1:vOIKv9/+HKiqJAElJIEYv3ZLcihRxyP7Suu/Mu8Dxjs= +github.com/linxGnu/grocksdb v1.9.8/go.mod h1:C3CNe9UYc9hlEM2pC82AqiGS3LRW537u9LFV4wIZuHk= +github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/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/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/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/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +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/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +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/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +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/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.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.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= +github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +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.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= +github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= +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/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/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +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/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +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/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.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +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.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= +github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= +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.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +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.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/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shamaton/msgpack/v2 v2.2.0 h1:IP1m01pHwCrMa6ZccP9B3bqxEMKMSmMVAVKk54g3L/Y= +github.com/shamaton/msgpack/v2 v2.2.0/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= +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.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +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.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +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.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/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/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +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/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= +github.com/ulikunitz/xz v0.5.11/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/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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 v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.4.0-alpha.1 h1:3yrqQzbRRPFPdOMWS/QQIVxVnzSkAZQYeWlZFv1kbj4= +go.etcd.io/bbolt v1.4.0-alpha.1/go.mod h1:S/Z/Nm3iuOnyO1W4XuFfPci51Gj6F1Hv0z8hisyYYOw= +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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs= +go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= +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/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.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/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.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw= +golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= +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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +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-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +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.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/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.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/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-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/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-20200520182314-0ba52f642ac2/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-20200707034311-ab3426394381/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-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +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-20211112202133-69e39bad7dc2/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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo= +golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= +golang.org/x/oauth2 v0.32.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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/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-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-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/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-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/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-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/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-20210225134936-a50acf3fe073/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-20210423185535-09eb48e85fd7/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-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-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-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.0.0-20221010170243-090e33056c14/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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.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.30.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/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +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.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +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.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.4/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.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.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/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.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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-20181030221726-6c7e314b6563/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-20190312151545-0bb0c0a6e846/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-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +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-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/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-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +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/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +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.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.229.0 h1:p98ymMtqeJ5i3lIBMj5MpR9kzIIgzpHHh8vQ+vgAzx8= +google.golang.org/api v0.229.0/go.mod h1:wyDfmq5g1wYJWn29O22FDWN48P7Xcz0xz+LBpptYvB0= +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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +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-20190418145605-e7d98fc518a7/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-20190502173448-54afdca5d873/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-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/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-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= +google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= +google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4= +google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +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.21.1/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.27.1/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.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +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.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +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.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= +google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +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.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +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/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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/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= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= +lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= +lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/devnet/hermes/config.toml b/devnet/hermes/config.toml index a9f68d1..d0a117a 100644 --- a/devnet/hermes/config.toml +++ b/devnet/hermes/config.toml @@ -21,11 +21,11 @@ misbehaviour = true [mode.connections] # Whether or not to enable the connection workers for handshake completion. [Required] -enabled = false +enabled = true [mode.channels] # Whether or not to enable the channel workers for handshake completion. [Required] -enabled = false +enabled = true [mode.packets] # Whether or not to enable the packet workers. [Required] @@ -100,4 +100,4 @@ enabled = true host = '127.0.0.1' # Specify the port over which the built-in HTTP server will serve the metrics gathered # by the telemetry service. Default: 3001 -port = 3001 \ No newline at end of file +port = 3001 diff --git a/devnet/hermes/scripts/hermes-channel.sh b/devnet/hermes/scripts/hermes-channel.sh index afc5a97..a476b01 100644 --- a/devnet/hermes/scripts/hermes-channel.sh +++ b/devnet/hermes/scripts/hermes-channel.sh @@ -5,9 +5,11 @@ set -euo pipefail : "${LUMERA_CHAIN_ID:=lumera-devnet-1}" : "${SIMD_CHAIN_ID:=hermes-simd-1}" : "${HERMES_KEY_NAME:=hermes-relayer}" -: "${RELAYER_MNEMONIC_FILE:=/shared/hermes/hermes-relayer.mnemonic}" +: "${RELAYER_MNEMONIC_FILE:=/shared/hermes/lumera-hermes-relayer.mnemonic}" : "${LUMERA_MNEMONIC_FILE:=${RELAYER_MNEMONIC_FILE}}" : "${SIMD_MNEMONIC_FILE:=${RELAYER_MNEMONIC_FILE}}" +: "${LUMERA_REST_ADDR:=}" +: "${SIMD_REST_ADDR:=}" : "${HERMES_STATUS_DIR:=/shared/status/hermes}" ENTRY_LOG_FILE="${ENTRY_LOG_FILE:-/root/logs/entrypoint.log}" @@ -180,6 +182,88 @@ require_jq() { fi } +parse_key_address() { + local key_name="$1" + local json_payload="$2" + printf '%s\n' "${json_payload}" \ + | extract_json \ + | jq -r --arg name "${key_name}" ' + .result as $r | + if ($r|type)=="array" then + ($r | map(select(.name==$name)) | .[0].address // empty) + elif ($r|type)=="object" then + ($r[$name].account // $r[$name].address // empty) + else + empty + end + ' 2>/dev/null || true +} + +check_account_rest() { + local rest_addr="$1" + local addr="$2" + local url + + if [ -z "${rest_addr}" ]; then + return 2 + fi + + url="${rest_addr%/}/cosmos/auth/v1beta1/accounts/${addr}" + local out + out="$(run_capture curl -s "${url}" || true)" + if [ -z "${out}" ]; then + return 2 + fi + if printf '%s\n' "${out}" | jq -e '.account' >/dev/null 2>&1; then + return 0 + fi + if printf '%s\n' "${out}" | jq -e '.code' >/dev/null 2>&1; then + printf '%s\n' "${out}" >&2 + return 1 + fi + return 2 +} + +ensure_chain_account() { + local chain_id="$1" + local addr="$2" + local label="$3" + + if [ -z "${addr}" ] || [ "${addr}" = "null" ]; then + log "Missing ${label} address; cannot verify account on ${chain_id}" + exit 1 + fi + + log "Checking ${label} account on ${chain_id} (${addr})" + local rest_addr="" + if [ "${chain_id}" = "${LUMERA_CHAIN_ID}" ] && [ -n "${LUMERA_REST_ADDR}" ]; then + rest_addr="${LUMERA_REST_ADDR}" + elif [ "${chain_id}" = "${SIMD_CHAIN_ID}" ] && [ -n "${SIMD_REST_ADDR}" ]; then + rest_addr="${SIMD_REST_ADDR}" + fi + + if [ -z "${rest_addr}" ]; then + log "No REST address configured for ${chain_id}; skipping ${label} account check" + return 0 + fi + + log "Checking ${label} account via REST (${rest_addr})" + if check_account_rest "${rest_addr}" "${addr}"; then + return 0 + fi + + case $? in + 1) + log "${label} account ${addr} not found on ${chain_id}" + log "Ensure the relayer account is funded in genesis or re-run devnet build." + exit 1 + ;; + *) + log "REST account check unavailable for ${chain_id}; skipping ${label} account check" + ;; + esac +} + ensure_client() { local host_chain="$1" local reference_chain="$2" @@ -243,18 +327,23 @@ ensure_connection() { --a-chain "${a_chain}" \ --b-chain "${b_chain}" \ --delay 0)" - local conn_result_line - conn_result_line="$(printf '%s\n' "${create_conn_json}")" - connection_id="$(printf '%s' "${conn_result_line}" \ - | first_result_object \ - | jq -r --arg a_client "${a_client}" --arg b_client "${b_client}" \ - 'select(.result.a_side.client_id==$a_client and .result.b_side.client_id==$b_client) - | (.result.a_side.connection_id // .result.b_side.connection_id // empty)' \ - 2>/dev/null || true)" + connection_id="$(printf '%s\n' "${create_conn_json}" \ + | result_items \ + | jq -r '.[0].a_side.connection_id // .[0].b_side.connection_id // .[0].connection_id // empty' \ + 2>/dev/null || true)" + + if [ -z "${connection_id}" ] || [ "${connection_id}" = "null" ]; then + log "Connection id not found in create-connection output; re-querying existing connections" + connections_json="$(run_capture hermes --json query connections --chain "${a_chain}" --counterparty-chain "${b_chain}" || true)" + log_query_empty "connections (${a_chain})" "${connections_json}" + connection_id="$(printf '%s\n' "${connections_json}" \ + | result_items \ + | jq -r '.[0] // empty' || true)" + fi if [ -z "${connection_id}" ] || [ "${connection_id}" = "null" ]; then local conn_err - conn_err="$(printf '%s\n' "${conn_result_line}" \ + conn_err="$(printf '%s\n' "${create_conn_json}" \ | first_result_object \ | jq -r '.result // empty' || true)" log "Failed to create connection between ${a_chain} and ${b_chain}" @@ -300,11 +389,11 @@ if ! OUT="$(run_capture hermes keys add \ exit 1 fi log "Imported Lumera key ${HERMES_KEY_NAME}" -lumera_relayer_addr="$(run_capture hermes --json keys list --chain "${LUMERA_CHAIN_ID}" \ - | jq -r ".result[]? | select(.name==\"${HERMES_KEY_NAME}\") | .address" || true)" +lumera_relayer_addr="$(parse_key_address "${HERMES_KEY_NAME}" "$(run_capture hermes --json keys list --chain "${LUMERA_CHAIN_ID}")")" if [ -n "${lumera_relayer_addr}" ] && [ "${lumera_relayer_addr}" != "null" ]; then log "Lumera relayer address: ${lumera_relayer_addr}" fi +ensure_chain_account "${LUMERA_CHAIN_ID}" "${lumera_relayer_addr}" "Lumera relayer" if ! OUT="$(run_capture hermes keys add \ --chain "${SIMD_CHAIN_ID}" \ @@ -315,11 +404,11 @@ if ! OUT="$(run_capture hermes keys add \ exit 1 fi log "Imported SIMD key ${HERMES_KEY_NAME}" -simd_relayer_addr="$(run_capture hermes --json keys list --chain "${SIMD_CHAIN_ID}" \ - | jq -r ".result[]? | select(.name==\"${HERMES_KEY_NAME}\") | .address" || true)" +simd_relayer_addr="$(parse_key_address "${HERMES_KEY_NAME}" "$(run_capture hermes --json keys list --chain "${SIMD_CHAIN_ID}")")" if [ -n "${simd_relayer_addr}" ] && [ "${simd_relayer_addr}" != "null" ]; then log "SIMD relayer address: ${simd_relayer_addr}" fi +ensure_chain_account "${SIMD_CHAIN_ID}" "${simd_relayer_addr}" "SIMD relayer" existing="" log "Querying existing transfer channels on ${LUMERA_CHAIN_ID}" @@ -418,6 +507,7 @@ cat <"${CHANNEL_INFO_FILE}" "b_client_id": "${b_client_id}", "channel_id": "${new_channel_id}", "port_id": "transfer", + "counterparty_chain_id": "${SIMD_CHAIN_ID}", "last_updated": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" } EOF diff --git a/devnet/hermes/scripts/hermes-configure.sh b/devnet/hermes/scripts/hermes-configure.sh index 655a74e..50a6279 100644 --- a/devnet/hermes/scripts/hermes-configure.sh +++ b/devnet/hermes/scripts/hermes-configure.sh @@ -93,6 +93,7 @@ ran_capture() { : "${SIMD_RPC_PORT:=26657}" : "${SIMD_GRPC_PORT:=9090}" : "${HERMES_KEY_NAME:=relayer}" +: "${HERMES_MAX_GAS:=1000000}" CONFIG_DIR="$(dirname "${HERMES_CONFIG_PATH}")" ran mkdir -p "${CONFIG_DIR}" @@ -101,6 +102,52 @@ if [ ! -f "${HERMES_CONFIG_PATH}" ]; then ran cp "${HERMES_TEMPLATE_PATH}" "${HERMES_CONFIG_PATH}" fi +ensure_mode_enabled() { + local section="$1" + local value="$2" + if ! ran python3 - "$HERMES_CONFIG_PATH" "$section" "$value" <<'PY' +import pathlib +import re +import sys + +path = pathlib.Path(sys.argv[1]) +section = sys.argv[2] +value = sys.argv[3] + +lines = path.read_text().splitlines() +out = [] +in_section = False +replaced = False + +for line in lines: + if re.match(r'^\s*\[', line): + if in_section and not replaced: + out.append(f'enabled = {value}') + replaced = True + in_section = line.strip() == f'[{section}]' + out.append(line) + continue + if in_section and re.match(r'^\s*enabled\s*=', line): + out.append(f'enabled = {value}') + replaced = True + continue + out.append(line) + +if in_section and not replaced: + out.append(f'enabled = {value}') + +path.write_text('\n'.join(out) + '\n') +PY + then + log_info "Failed to enforce ${section}.enabled=${value}" + return 1 + fi + log_info "Ensured ${section}.enabled=${value}" +} + +ensure_mode_enabled "mode.channels" "true" +ensure_mode_enabled "mode.connections" "true" + append_chain() { local chain_id="$1" local block="$2" @@ -144,6 +191,7 @@ key_name = '${HERMES_KEY_NAME}' store_prefix = 'ibc' memo_prefix = '' gas_price = { price = 0.025, denom = '${LUMERA_BOND_DENOM}' } +max_gas = ${HERMES_MAX_GAS} clock_drift = '5s' trusting_period = '14days' trust_threshold = '1/3' @@ -163,6 +211,7 @@ key_name = '${HERMES_KEY_NAME}' store_prefix = 'ibc' memo_prefix = '' gas_price = { price = 0.025, denom = '${SIMD_DENOM}' } +max_gas = ${HERMES_MAX_GAS} clock_drift = '5s' trusting_period = '14days' trust_threshold = '1/3' diff --git a/devnet/hermes/scripts/hermes-start.sh b/devnet/hermes/scripts/hermes-start.sh index a933ba7..e09b92a 100644 --- a/devnet/hermes/scripts/hermes-start.sh +++ b/devnet/hermes/scripts/hermes-start.sh @@ -15,8 +15,12 @@ SIMD_KEYRING="${SIMD_KEYRING:-test}" SIMD_DENOM="${SIMD_DENOM:-stake}" SIMD_GENESIS_BALANCE="${SIMD_GENESIS_BALANCE:-100000000000${SIMD_DENOM}}" SIMD_STAKE_AMOUNT="${SIMD_STAKE_AMOUNT:-50000000000${SIMD_DENOM}}" +SIMD_TEST_KEY_NAME="${SIMD_TEST_KEY_NAME:-simd-test}" +SIMD_TEST_ACCOUNT_BALANCE="${SIMD_TEST_ACCOUNT_BALANCE:-100000000${SIMD_DENOM}}" +SIMD_RELAYER_ACCOUNT_BALANCE="${SIMD_RELAYER_ACCOUNT_BALANCE:-100000000${SIMD_DENOM}}" RELAYER_KEY_NAME="${RELAYER_KEY_NAME:-relayer}" DEFAULT_SIMD_RELAYER_MNEMONIC="" +MINIMUM_GAS_PRICES="${MINIMUM_GAS_PRICES:-${SIMD_MINIMUM_GAS_PRICES:-0.0025${SIMD_DENOM}}}" SIMAPP_KEY_RELAYER_MNEMONIC="${SIMAPP_KEY_RELAYER_MNEMONIC:-${DEFAULT_SIMD_RELAYER_MNEMONIC}}" export SIMAPP_KEY_RELAYER_MNEMONIC @@ -122,10 +126,24 @@ CONFIG_JSON="${SHARED_DIR}/config/config.json" VALIDATORS_JSON="${SHARED_DIR}/config/validators.json" mkdir -p "${HERMES_SHARED_DIR}" -HERMES_RELAYER_MNEMONIC_FILE="${HERMES_SHARED_DIR}/hermes-relayer.mnemonic" +HERMES_RELAYER_MNEMONIC_FILE="${HERMES_SHARED_DIR}/lumera-hermes-relayer.mnemonic" +LEGACY_RELAYER_MNEMONIC_FILE="${HERMES_SHARED_DIR}/hermes-relayer.mnemonic" -if [ -z "${SIMAPP_KEY_RELAYER_MNEMONIC:-}" ] && [ -s "${HERMES_RELAYER_MNEMONIC_FILE}" ]; then - SIMAPP_KEY_RELAYER_MNEMONIC="$(cat "${HERMES_RELAYER_MNEMONIC_FILE}")" +if [ ! -s "${HERMES_RELAYER_MNEMONIC_FILE}" ] && [ -s "${LEGACY_RELAYER_MNEMONIC_FILE}" ]; then + log_info "Legacy relayer mnemonic file detected; copying to ${HERMES_RELAYER_MNEMONIC_FILE}" + cp "${LEGACY_RELAYER_MNEMONIC_FILE}" "${HERMES_RELAYER_MNEMONIC_FILE}" +fi + +if [ -s "${HERMES_RELAYER_MNEMONIC_FILE}" ]; then + if [ -z "${SIMAPP_KEY_RELAYER_MNEMONIC:-}" ]; then + SIMAPP_KEY_RELAYER_MNEMONIC="$(cat "${HERMES_RELAYER_MNEMONIC_FILE}")" + else + existing_relayer_mnemonic="$(cat "${HERMES_RELAYER_MNEMONIC_FILE}")" + if [ "${SIMAPP_KEY_RELAYER_MNEMONIC}" != "${existing_relayer_mnemonic}" ]; then + log_info "Relayer mnemonic file ${HERMES_RELAYER_MNEMONIC_FILE} differs from SIMAPP_KEY_RELAYER_MNEMONIC; using file to keep Hermes/simd aligned" + SIMAPP_KEY_RELAYER_MNEMONIC="${existing_relayer_mnemonic}" + fi + fi fi if command -v jq >/dev/null 2>&1 && [ -f "${CONFIG_JSON}" ]; then @@ -155,10 +173,13 @@ fi # Inside the compose network every validator exposes the default container ports. LUMERA_RPC_PORT="${LUMERA_RPC_PORT:-26657}" LUMERA_GRPC_PORT="${LUMERA_GRPC_PORT:-9090}" +LUMERA_API_PORT="${LUMERA_API_PORT:-1317}" LUMERA_RPC_ADDR="http://${FIRST_VALIDATOR_SERVICE}:${LUMERA_RPC_PORT}" LUMERA_GRPC_ADDR="http://${FIRST_VALIDATOR_SERVICE}:${LUMERA_GRPC_PORT}" LUMERA_WS_ADDR="ws://${FIRST_VALIDATOR_SERVICE}:${LUMERA_RPC_PORT}/websocket" +LUMERA_REST_ADDR="http://${FIRST_VALIDATOR_SERVICE}:${LUMERA_API_PORT}" +SIMD_REST_ADDR="http://127.0.0.1:${SIMD_API_PORT}" LUMERA_ACCOUNT_PREFIX="${LUMERA_ACCOUNT_PREFIX:-lumera}" HERMES_KEY_NAME="${HERMES_KEY_NAME:-${RELAYER_KEY_NAME}}" @@ -169,7 +190,8 @@ HERMES_TEMPLATE_PATH="${HERMES_TEMPLATE_PATH:-/root/scripts/hermes-config-templa export HERMES_CONFIG_PATH export HERMES_TEMPLATE_PATH -export LUMERA_CHAIN_ID LUMERA_BOND_DENOM LUMERA_RPC_ADDR LUMERA_GRPC_ADDR LUMERA_WS_ADDR LUMERA_ACCOUNT_PREFIX +export LUMERA_CHAIN_ID LUMERA_BOND_DENOM LUMERA_RPC_ADDR LUMERA_GRPC_ADDR LUMERA_WS_ADDR LUMERA_REST_ADDR LUMERA_ACCOUNT_PREFIX +export SIMD_REST_ADDR export SIMD_CHAIN_ID SIMD_DENOM SIMD_RPC_PORT SIMD_GRPC_PORT export HERMES_KEY_NAME LUMERA_MNEMONIC_FILE SIMD_MNEMONIC_FILE @@ -193,6 +215,8 @@ init_simd_home() { export SIMD_DENOM DENOM="${SIMD_DENOM}" STAKE_DENOM="${SIMD_DENOM}" export SIMD_GENESIS_BALANCE ACCOUNT_BALANCE="${SIMD_GENESIS_BALANCE}" export SIMD_STAKE_AMOUNT STAKING_AMOUNT="${SIMD_STAKE_AMOUNT}" + export SIMD_TEST_KEY_NAME SIMD_TEST_ACCOUNT_BALANCE + export MINIMUM_GAS_PRICES export SIMAPP_KEY_RELAYER_MNEMONIC export SIMAPP_SHARED_DIR="${SHARED_DIR}" @@ -220,32 +244,46 @@ wait_for_lumera_rpc() { return 1 } -current_lumera_height() { - local url="${LUMERA_RPC_ADDR}/status" +current_height() { + local url="$1" curl -sf "${url}" 2>/dev/null \ | jq -r '.result.sync_info.latest_block_height // "0"' 2>/dev/null \ | awk '($1 ~ /^[0-9]+$/) { print $1; next } { print 0 }' } -wait_for_lumera_height() { - local target="$1" - local retries="${2:-180}" - local delay="${3:-2}" +wait_for_height() { + local label="$1" + local url="$2" + local target="$3" + local retries="${4:-180}" + local delay="${5:-2}" - log_info "Waiting for Lumera height to reach at least ${target}..." + log_info "Waiting for ${label} height to reach at least ${target}..." for _ in $(seq 1 "${retries}"); do local height - height="$(current_lumera_height)" + height="$(current_height "${url}")" if (( height >= target )); then - log_info "Lumera height is ${height} (>= ${target})." + log_info "${label} height is ${height} (>= ${target})." return 0 fi sleep "${delay}" done - log_info "Timed out waiting for Lumera height ${target}; continuing anyway." + log_info "Timed out waiting for ${label} height ${target}; continuing anyway." return 1 } +current_lumera_height() { + current_height "${LUMERA_RPC_ADDR}/status" +} + +wait_for_lumera_height() { + local target="$1" + local retries="${2:-180}" + local delay="${3:-2}" + + wait_for_height "Lumera" "${LUMERA_RPC_ADDR}/status" "${target}" "${retries}" "${delay}" +} + wait_for_lumera_blocks() { local blocks="${1:-5}" local start @@ -288,6 +326,129 @@ wait_for_simd() { return 1 } +current_simd_height() { + current_height "http://127.0.0.1:${SIMD_RPC_PORT}/status" +} + +wait_for_simd_height() { + local target="$1" + local retries="${2:-180}" + local delay="${3:-1}" + + wait_for_height "simd" "http://127.0.0.1:${SIMD_RPC_PORT}/status" "${target}" "${retries}" "${delay}" +} + +fund_simd_test_account() { + local key_name="${SIMD_TEST_KEY_NAME}" + if [ -z "${key_name}" ]; then + log_info "SIMD test key name is empty; skipping funding" + return 0 + fi + + local addr + addr="$(simd --home "${SIMD_HOME}" keys show "${key_name}" -a --keyring-backend "${SIMD_KEYRING}" 2>/dev/null || true)" + addr="$(printf '%s' "${addr}" | tr -d '\r\n')" + if [ -z "${addr}" ]; then + log_info "SIMD test key ${key_name} not found; skipping funding" + return 0 + fi + + local target amount current + target="${SIMD_TEST_ACCOUNT_BALANCE}" + amount="${target%${SIMD_DENOM}}" + if [ "${amount}" = "${target}" ]; then + log_info "SIMD test account balance ${target} missing denom ${SIMD_DENOM}; skipping funding" + return 0 + fi + + current="$(simd --home "${SIMD_HOME}" query bank balances "${addr}" --node "http://127.0.0.1:${SIMD_RPC_PORT}" --output json 2>/dev/null \ + | jq -r --arg denom "${SIMD_DENOM}" '.balances[]? | select(.denom==$denom) | .amount' 2>/dev/null || echo 0)" + if ! [[ "${current}" =~ ^[0-9]+$ ]]; then + current=0 + fi + + if (( current >= amount )); then + log_info "SIMD test account already funded (${current}${SIMD_DENOM} >= ${target})" + return 0 + fi + + local topup + topup=$(( amount - current )) + log_info "Funding SIMD test account ${addr} with ${topup}${SIMD_DENOM}" + local tx_output + if tx_output=$(simd --home "${SIMD_HOME}" tx bank send "${SIMD_KEY_NAME}" "${addr}" "${topup}${SIMD_DENOM}" \ + --keyring-backend "${SIMD_KEYRING}" \ + --chain-id "${SIMD_CHAIN_ID}" \ + --node "http://127.0.0.1:${SIMD_RPC_PORT}" \ + --gas auto \ + --gas-adjustment 1.3 \ + --gas-prices "${MINIMUM_GAS_PRICES}" \ + -y 2>&1); then + log_info "Funding transaction for SIMD test account succeeded" + else + log_info "Funding transaction for SIMD test account failed: ${tx_output}" + fi +} + +fund_simd_relayer_account() { + local key_name="${RELAYER_KEY_NAME}" + if [ -z "${key_name}" ]; then + log_info "SIMD relayer key name is empty; skipping funding" + return 0 + fi + if [ "${key_name}" = "${SIMD_KEY_NAME}" ]; then + log_info "SIMD relayer key matches validator key; skipping funding" + return 0 + fi + + log_info "Resolving SIMD relayer key ${key_name} for funding" + local addr + addr="$(simd --home "${SIMD_HOME}" keys show "${key_name}" -a --keyring-backend "${SIMD_KEYRING}" 2>/dev/null || true)" + addr="$(printf '%s' "${addr}" | tr -d '\r\n')" + if [ -z "${addr}" ]; then + log_info "SIMD relayer key ${key_name} not found; skipping funding" + return 0 + fi + log_info "SIMD relayer address: ${addr}" + + local target amount current + target="${SIMD_RELAYER_ACCOUNT_BALANCE}" + amount="${target%${SIMD_DENOM}}" + if [ "${amount}" = "${target}" ]; then + log_info "SIMD relayer balance ${target} missing denom ${SIMD_DENOM}; skipping funding" + return 0 + fi + + current="$(simd --home "${SIMD_HOME}" query bank balances "${addr}" --node "http://127.0.0.1:${SIMD_RPC_PORT}" --output json 2>/dev/null \ + | jq -r --arg denom "${SIMD_DENOM}" '.balances[]? | select(.denom==$denom) | .amount' 2>/dev/null || echo 0)" + if ! [[ "${current}" =~ ^[0-9]+$ ]]; then + current=0 + fi + log_info "SIMD relayer current balance: ${current}${SIMD_DENOM}" + + if (( current >= amount )); then + log_info "SIMD relayer account already funded (${current}${SIMD_DENOM} >= ${target})" + return 0 + fi + + local topup + topup=$(( amount - current )) + log_info "Funding SIMD relayer account ${addr} with ${topup}${SIMD_DENOM}" + local tx_output + if tx_output=$(simd --home "${SIMD_HOME}" tx bank send "${SIMD_KEY_NAME}" "${addr}" "${topup}${SIMD_DENOM}" \ + --keyring-backend "${SIMD_KEYRING}" \ + --chain-id "${SIMD_CHAIN_ID}" \ + --node "http://127.0.0.1:${SIMD_RPC_PORT}" \ + --gas auto \ + --gas-adjustment 1.3 \ + --gas-prices "${MINIMUM_GAS_PRICES}" \ + -y 2>&1); then + log_info "Funding transaction for SIMD relayer account succeeded" + else + log_info "Funding transaction for SIMD relayer account failed: ${tx_output}" + fi +} + start_hermes() { log_info "Starting Hermes relayer..." hermes --config "${HERMES_CONFIG_PATH}" start >"${HERMES_LOG_FILE}" 2>&1 & @@ -313,7 +474,9 @@ else fi if start_simd; then if wait_for_simd; then - : + wait_for_simd_height 1 || true + fund_simd_relayer_account || true + fund_simd_test_account || true else log_info "wait_for_simd timed out; continuing to keep container alive" fi diff --git a/devnet/hermes/scripts/init-simapp.sh b/devnet/hermes/scripts/init-simapp.sh index 31cb063..19a769f 100644 --- a/devnet/hermes/scripts/init-simapp.sh +++ b/devnet/hermes/scripts/init-simapp.sh @@ -31,15 +31,48 @@ RELAYER_KEY_NAME="${RELAYER_KEY_NAME:-${KEY_NAME}}" KEYRING="${KEYRING:-${SIMD_KEYRING:-test}}" DENOM="${DENOM:-${SIMD_DENOM:-stake}}" STAKE_DENOM="${STAKE_DENOM:-${DENOM}}" +MINIMUM_GAS_PRICES="${MINIMUM_GAS_PRICES:-${SIMD_MINIMUM_GAS_PRICES:-0.0025${DENOM}}}" ACCOUNT_BALANCE="${ACCOUNT_BALANCE:-${SIMD_GENESIS_BALANCE:-100000000000${DENOM}}}" STAKING_AMOUNT="${STAKING_AMOUNT:-${SIMD_STAKE_AMOUNT:-50000000000${DENOM}}}" +TEST_KEY_NAME="${TEST_KEY_NAME:-${SIMD_TEST_KEY_NAME:-simd-test}}" +TEST_ACCOUNT_BALANCE="${TEST_ACCOUNT_BALANCE:-${SIMD_TEST_ACCOUNT_BALANCE:-100000000${DENOM}}}" SIMAPP_SHARED_DIR="${SIMAPP_SHARED_DIR:-/shared}" GENESIS_FILE="${SIMAPP_HOME}/config/genesis.json" +prefixed_name() { + local prefix="$1" + local name="$2" + case "${name}" in + "${prefix}"*) printf '%s' "${name}" ;; + *) printf '%s%s' "${prefix}" "${name}" ;; + esac +} + +wait_for_file() { + local file="$1" + local timeout="${2:-60}" + local elapsed=0 + while [ ! -s "${file}" ]; do + if [ "${elapsed}" -ge "${timeout}" ]; then + return 1 + fi + sleep 1 + elapsed=$((elapsed + 1)) + done + return 0 +} + key_mnemonic_file() { local name="$1" - printf '%s/hermes/simd-%s.mnemonic' "${SIMAPP_SHARED_DIR}" "${name}" + name="$(prefixed_name "simd-" "${name}")" + printf '%s/hermes/%s.mnemonic' "${SIMAPP_SHARED_DIR}" "${name}" +} + +key_address_file() { + local name="$1" + name="$(prefixed_name "simd-" "${name}")" + printf '%s/hermes/%s.address' "${SIMAPP_SHARED_DIR}" "${name}" } record_key_mnemonic() { @@ -52,46 +85,83 @@ record_key_mnemonic() { printf '%s\n' "${mnemonic}" > "${file}" } -RELAYER_MNEMONIC_FILE="${SIMAPP_SHARED_DIR}/hermes/hermes-relayer.mnemonic" +record_key_address() { + local name="$1" + local addr file + addr="$("${SIMD_BIN}" --home "${SIMAPP_HOME}" keys show "${name}" -a --keyring-backend "${KEYRING}" 2>/dev/null || true)" + addr="$(printf '%s' "${addr}" | tr -d '\r\n')" + if [ -n "${addr}" ]; then + log "Recorded ${name} address: ${addr}" + file="$(key_address_file "${name}")" + mkdir -p "$(dirname "${file}")" + printf '%s\n' "${addr}" > "${file}" + else + log "Failed to resolve address for key ${name}" + fi +} + +LUMERA_RELAYER_MNEMONIC_FILE="${SIMAPP_SHARED_DIR}/hermes/lumera-hermes-relayer.mnemonic" record_relayer_mnemonic() { local mnemonic="$1" [ -z "${mnemonic}" ] && return 0 + log "Recording relayer mnemonic for key ${RELAYER_KEY_NAME}" record_key_mnemonic "${RELAYER_KEY_NAME}" "${mnemonic}" - mkdir -p "$(dirname "${RELAYER_MNEMONIC_FILE}")" - printf '%s\n' "${mnemonic}" > "${RELAYER_MNEMONIC_FILE}" } -if [ -z "${SIMAPP_KEY_RELAYER_MNEMONIC:-}" ] && [ -s "${RELAYER_MNEMONIC_FILE}" ]; then - SIMAPP_KEY_RELAYER_MNEMONIC="$(cat "${RELAYER_MNEMONIC_FILE}")" - export SIMAPP_KEY_RELAYER_MNEMONIC +if [ -s "${LUMERA_RELAYER_MNEMONIC_FILE}" ]; then + if [ -z "${SIMAPP_KEY_RELAYER_MNEMONIC:-}" ]; then + log "Using relayer mnemonic from ${LUMERA_RELAYER_MNEMONIC_FILE}" + SIMAPP_KEY_RELAYER_MNEMONIC="$(cat "${LUMERA_RELAYER_MNEMONIC_FILE}")" + export SIMAPP_KEY_RELAYER_MNEMONIC + else + existing_relayer_mnemonic="$(cat "${LUMERA_RELAYER_MNEMONIC_FILE}")" + if [ "${SIMAPP_KEY_RELAYER_MNEMONIC}" != "${existing_relayer_mnemonic}" ]; then + SIMAPP_KEY_RELAYER_MNEMONIC="${existing_relayer_mnemonic}" + export SIMAPP_KEY_RELAYER_MNEMONIC + fi + fi fi +already_init=0 if [ -f "${GENESIS_FILE}" ]; then + already_init=1 if [ -n "${SIMAPP_KEY_RELAYER_MNEMONIC:-}" ]; then record_relayer_mnemonic "${SIMAPP_KEY_RELAYER_MNEMONIC}" fi log "simd already initialised at ${GENESIS_FILE}" - exit 0 -fi +else + log "Initialising simd home at ${SIMAPP_HOME}" + + if [ -d "${SIMAPP_HOME}" ]; then + if findmnt -rn -T "${SIMAPP_HOME}" >/dev/null 2>&1; then + log "${SIMAPP_HOME} is a mount point; clearing existing contents" + find "${SIMAPP_HOME}" -mindepth 1 -maxdepth 1 -exec rm -rf {} + + else + run rm -rf "${SIMAPP_HOME}" + fi + fi -log "Initialising simd home at ${SIMAPP_HOME}" + mkdir -p "${SIMAPP_HOME}" -if [ -d "${SIMAPP_HOME}" ]; then - if findmnt -rn -T "${SIMAPP_HOME}" >/dev/null 2>&1; then - log "${SIMAPP_HOME} is a mount point; clearing existing contents" - find "${SIMAPP_HOME}" -mindepth 1 -maxdepth 1 -exec rm -rf {} + - else - run rm -rf "${SIMAPP_HOME}" - fi + run "${SIMD_BIN}" --home "${SIMAPP_HOME}" init "${MONIKER}" --chain-id "${CHAIN_ID}" fi -mkdir -p "${SIMAPP_HOME}" - -run "${SIMD_BIN}" --home "${SIMAPP_HOME}" init "${MONIKER}" --chain-id "${CHAIN_ID}" run "${SIMD_BIN}" --home "${SIMAPP_HOME}" config set client chain-id "${CHAIN_ID}" run "${SIMD_BIN}" --home "${SIMAPP_HOME}" config set client keyring-backend "${KEYRING}" run "${SIMD_BIN}" --home "${SIMAPP_HOME}" config set app api.enable true || true +run "${SIMD_BIN}" --home "${SIMAPP_HOME}" config set app minimum-gas-prices "${MINIMUM_GAS_PRICES}" + +if [ "${already_init}" -eq 1 ]; then + record_key_address "${KEY_NAME}" + if [ "${RELAYER_KEY_NAME}" != "${KEY_NAME}" ]; then + record_key_address "${RELAYER_KEY_NAME}" + fi + if [ -n "${TEST_KEY_NAME}" ]; then + record_key_address "${TEST_KEY_NAME}" + fi + exit 0 +fi ensure_key() { local name="$1" @@ -101,13 +171,26 @@ ensure_key() { mnemonic="${!var:-}" mnemonic_file="$(key_mnemonic_file "${name}")" + if [ "${name}" = "${RELAYER_KEY_NAME}" ] && [ -n "${LUMERA_RELAYER_MNEMONIC_FILE:-}" ]; then + log "Waiting for Lumera relayer mnemonic ${LUMERA_RELAYER_MNEMONIC_FILE}" + if wait_for_file "${LUMERA_RELAYER_MNEMONIC_FILE}" "${SIMAPP_RELAYER_MNEMONIC_WAIT_SECONDS:-60}"; then + mnemonic="$(cat "${LUMERA_RELAYER_MNEMONIC_FILE}")" + printf -v "${var}" '%s' "${mnemonic}" + export "${var}" + else + log "Lumera relayer mnemonic ${LUMERA_RELAYER_MNEMONIC_FILE} not available; proceeding without it" + fi + fi + if [ -z "${mnemonic}" ] && [ -s "${mnemonic_file}" ]; then + log "Loading mnemonic for key ${name} from ${mnemonic_file}" mnemonic="$(cat "${mnemonic_file}")" printf -v "${var}" '%s' "${mnemonic}" export "${var}" fi if "${SIMD_BIN}" --home "${SIMAPP_HOME}" keys show "${name}" --keyring-backend "${KEYRING}" >/dev/null 2>&1; then + log "Key ${name} already exists in keyring" record_key_mnemonic "${name}" "${mnemonic}" if [ "${name}" = "${RELAYER_KEY_NAME}" ]; then record_relayer_mnemonic "${mnemonic}" @@ -131,6 +214,7 @@ ensure_key() { fi record_key_mnemonic "${name}" "${mnemonic}" + record_key_address "${name}" if [ "${name}" = "${RELAYER_KEY_NAME}" ]; then record_relayer_mnemonic "${mnemonic}" @@ -141,11 +225,17 @@ ensure_key "${KEY_NAME}" if [ "${RELAYER_KEY_NAME}" != "${KEY_NAME}" ]; then ensure_key "${RELAYER_KEY_NAME}" fi +if [ -n "${TEST_KEY_NAME}" ]; then + ensure_key "${TEST_KEY_NAME}" +fi run "${SIMD_BIN}" --home "${SIMAPP_HOME}" genesis add-genesis-account "${KEY_NAME}" "${ACCOUNT_BALANCE}" --keyring-backend "${KEYRING}" if [ "${RELAYER_KEY_NAME}" != "${KEY_NAME}" ]; then run "${SIMD_BIN}" --home "${SIMAPP_HOME}" genesis add-genesis-account "${RELAYER_KEY_NAME}" "${ACCOUNT_BALANCE}" --keyring-backend "${KEYRING}" fi +if [ -n "${TEST_KEY_NAME}" ]; then + run "${SIMD_BIN}" --home "${SIMAPP_HOME}" genesis add-genesis-account "${TEST_KEY_NAME}" "${TEST_ACCOUNT_BALANCE}" --keyring-backend "${KEYRING}" +fi run "${SIMD_BIN}" --home "${SIMAPP_HOME}" genesis gentx "${KEY_NAME}" "${STAKING_AMOUNT}" --chain-id "${CHAIN_ID}" --keyring-backend "${KEYRING}" run "${SIMD_BIN}" --home "${SIMAPP_HOME}" genesis collect-gentxs diff --git a/devnet/scripts/configure.sh b/devnet/scripts/configure.sh index 8038b6b..2645146 100755 --- a/devnet/scripts/configure.sh +++ b/devnet/scripts/configure.sh @@ -140,6 +140,18 @@ install_sncli() { fi } +install_ibc_tests() { + local test_bins=("ibc_validator" "ibc_hermes") + local bin + for bin in "${test_bins[@]}"; do + if [ -n "${BIN_DIR}" ] && [ -f "${BIN_DIR}/${bin}" ]; then + echo "[CONFIGURE] Copying ${bin} binary from ${BIN_DIR} to ${RELEASE_DIR}" + cp -f "${BIN_DIR}/${bin}" "${RELEASE_DIR}/" + chmod 755 "${RELEASE_DIR}/${bin}" + fi + done +} + mkdir -p "${CFG_DIR}" "${RELEASE_DIR}" cp -f "${CONFIG_JSON}" "${VALIDATORS_JSON}" "${CFG_DIR}/" echo "[CONFIGURE] Configuration files copied to ${CFG_DIR}" @@ -147,4 +159,5 @@ echo "[CONFIGURE] Configuration files copied to ${CFG_DIR}" install_supernode install_sncli install_nm +install_ibc_tests echo "[CONFIGURE] Lumera configuration completed successfully." diff --git a/devnet/scripts/validator-setup.sh b/devnet/scripts/validator-setup.sh index 46ed1aa..5189628 100755 --- a/devnet/scripts/validator-setup.sh +++ b/devnet/scripts/validator-setup.sh @@ -30,8 +30,12 @@ LOCKS_DIR="${STATUS_DIR}/locks" HERMES_SHARED_DIR="${SHARED_DIR}/hermes" HERMES_STATUS_DIR="${STATUS_DIR}/hermes" HERMES_RELAYER_KEY="${HERMES_RELAYER_KEY:-hermes-relayer}" -HERMES_RELAYER_MNEMONIC_FILE="${HERMES_SHARED_DIR}/hermes-relayer.mnemonic" -HERMES_RELAYER_ADDR_FILE="${HERMES_SHARED_DIR}/hermes-relayer.address" +HERMES_RELAYER_FILE_NAME="${HERMES_RELAYER_KEY}" +if [[ "${HERMES_RELAYER_FILE_NAME}" != lumera-* ]]; then + HERMES_RELAYER_FILE_NAME="lumera-${HERMES_RELAYER_FILE_NAME}" +fi +HERMES_RELAYER_MNEMONIC_FILE="${HERMES_SHARED_DIR}/${HERMES_RELAYER_FILE_NAME}.mnemonic" +HERMES_RELAYER_ADDR_FILE="${HERMES_SHARED_DIR}/${HERMES_RELAYER_FILE_NAME}.address" HERMES_RELAYER_GENESIS_AMOUNT="${HERMES_RELAYER_GENESIS_AMOUNT:-10000000}" # in bond denom units # ----- read config from config.json ----- diff --git a/devnet/tests/hermes/ibc_hermes_ica_test.go b/devnet/tests/hermes/ibc_hermes_ica_test.go new file mode 100644 index 0000000..8827ffc --- /dev/null +++ b/devnet/tests/hermes/ibc_hermes_ica_test.go @@ -0,0 +1,1401 @@ +package hermes + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "io" + stdlog "log" + "net/http" + "net/url" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "time" + + txtypes "cosmossdk.io/api/cosmos/tx/v1beta1" + sdkmath "cosmossdk.io/math" + "gen/tests/ibcutil" + actiontypes "github.com/LumeraProtocol/lumera/x/action/v1/types" + "github.com/LumeraProtocol/sdk-go/blockchain" + "github.com/LumeraProtocol/sdk-go/cascade" + "github.com/LumeraProtocol/sdk-go/types" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkbech32 "github.com/cosmos/cosmos-sdk/types/bech32" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + gogoproto "github.com/cosmos/gogoproto/proto" + icatypes "github.com/cosmos/ibc-go/v10/modules/apps/27-interchain-accounts/types" +) + +const icaAckRetries = 120 + +type supernodeInfoLogger struct { + logger *stdlog.Logger +} + +func newSupernodeInfoLogger() *supernodeInfoLogger { + return &supernodeInfoLogger{logger: stdlog.New(os.Stdout, "", stdlog.LstdFlags)} +} + +func (l *supernodeInfoLogger) Printf(format string, v ...interface{}) { + l.logf(format, v...) +} + +func (l *supernodeInfoLogger) Infof(format string, v ...interface{}) { + l.logf(format, v...) +} + +func (l *supernodeInfoLogger) Warnf(format string, v ...interface{}) { + l.logf(format, v...) +} + +func (l *supernodeInfoLogger) logf(format string, v ...interface{}) { + msg := fmt.Sprintf(format, v...) + if strings.Contains(msg, "[supernode] DEBUG") { + return + } + l.logger.Printf("%s", msg) +} + +func (s *ibcSimdSuite) TestICARequestActionAppPubkeyRequired() { + ctx, cancel := context.WithTimeout(context.Background(), icaTestTimeout) + defer cancel() + + s.T().Log("ica: resolve simd owner address") + ownerAddr, err := s.resolveSimdOwnerAddress(ctx) + s.Require().NoError(err, "resolve simd owner address") + + s.T().Log("ica: ensure ICA address") + icaAddr, err := s.ensureICAAddress(ctx, ownerAddr) + s.Require().NoError(err, "ensure ICA address") + s.Require().NotEmpty(icaAddr, "ICA address is empty") + + s.T().Log("ica: load lumera keyring") + kr, _, lumeraAddr, err := loadKeyringFromMnemonic(s.lumera.KeyName, s.lumera.MnemonicFile) + s.Require().NoError(err, "load lumera keyring") + s.Require().NotEmpty(lumeraAddr, "lumera address is empty") + + s.T().Log("ica: load simd key for app pubkey") + simdPubkey, simdAddr, err := importKeyFromMnemonic(kr, s.simd.KeyName, s.simd.MnemonicFile, "cosmos") + s.Require().NoError(err, "load simd key") + if ownerAddr != "" { + s.Require().Equal(ownerAddr, simdAddr, "simd owner address mismatch") + } + + s.T().Log("ica: create lumera blockchain client") + lumeraClient, err := newLumeraBlockchainClient(ctx, s.lumera.ChainID, s.lumera.GRPC, kr, s.lumera.KeyName) + s.Require().NoError(err, "create lumera client") + defer lumeraClient.Close() + actionClient := lumeraClient.Action + + s.T().Log("ica: ensure ICA account funded") + err = s.ensureICAFunded(ctx, lumeraClient, lumeraAddr, icaAddr) + s.Require().NoError(err, "fund ICA account") + + s.T().Log("ica: create cascade client") + cascadeClient, err := cascade.New(ctx, cascade.Config{ + ChainID: s.lumera.ChainID, + GRPCAddr: s.lumera.GRPC, + Address: lumeraAddr, + KeyName: s.lumera.KeyName, + ICAOwnerKeyName: s.simd.KeyName, + ICAOwnerHRP: "cosmos", + Timeout: 30 * time.Second, + }, kr) + s.Require().NoError(err, "create cascade client") + cascadeClient.SetLogger(newSupernodeInfoLogger()) + defer cascadeClient.Close() + + files, err := createICATestFiles(s.T().TempDir()) + s.Require().NoError(err, "create test files") + s.Require().NotEmpty(files, "no test files created") + + options := &cascade.UploadOptions{ICACreatorAddress: icaAddr} + msg, _, err := cascadeClient.CreateRequestActionMessage(ctx, icaAddr, files[0].path, options) + s.Require().NoError(err, "build request action without app_pubkey") + + _, err = s.sendICARequestTx(ctx, []*actiontypes.MsgRequestAction{msg}) + s.Require().Error(err, "expected app_pubkey requirement failure") + if err != nil { + s.Contains(err.Error(), "app_pubkey") + } + + options.AppPubkey = simdPubkey + msg, _, err = cascadeClient.CreateRequestActionMessage(ctx, icaAddr, files[0].path, options) + s.Require().NoError(err, "build request action with app_pubkey") + + actionIDs, err := s.sendICARequestTx(ctx, []*actiontypes.MsgRequestAction{msg}) + s.Require().NoError(err, "send request with app_pubkey") + s.Require().NotEmpty(actionIDs, "no action ids returned") + s.Require().NotEmpty(actionIDs[0], "empty action id response") + s.Require().NoError(waitForActionState(ctx, actionClient, actionIDs[0], types.ActionStatePending)) +} + +func (s *ibcSimdSuite) TestICACascadeFlow() { + ctx, cancel := context.WithTimeout(context.Background(), icaTestTimeout) + defer cancel() + + s.T().Log("ica: resolve simd owner address") + ownerAddr, err := s.resolveSimdOwnerAddress(ctx) + s.Require().NoError(err, "resolve simd owner address") + s.T().Logf("ica: simd owner address=%s", ownerAddr) + + s.T().Log("ica: ensure ICA address") + icaAddr, err := s.ensureICAAddress(ctx, ownerAddr) + s.Require().NoError(err, "ensure ICA address") + s.Require().NotEmpty(icaAddr, "ICA address is empty") + s.T().Logf("ica: interchain account address=%s", icaAddr) + + s.T().Log("ica: load lumera keyring") + kr, _, lumeraAddr, err := loadKeyringFromMnemonic(s.lumera.KeyName, s.lumera.MnemonicFile) + s.Require().NoError(err, "load lumera keyring") + s.Require().NotEmpty(lumeraAddr, "lumera address is empty") + s.T().Logf("ica: lumera address=%s", lumeraAddr) + + s.T().Log("ica: load simd key for app pubkey") + simdPubkey, simdAddr, err := importKeyFromMnemonic(kr, s.simd.KeyName, s.simd.MnemonicFile, "cosmos") + s.Require().NoError(err, "load simd key") + s.T().Logf("ica: simd key address=%s app_pubkey_len=%d", simdAddr, len(simdPubkey)) + if ownerAddr != "" { + s.Require().Equal(ownerAddr, simdAddr, "simd owner address mismatch") + } + + s.T().Log("ica: create lumera blockchain client") + lumeraClient, err := newLumeraBlockchainClient(ctx, s.lumera.ChainID, s.lumera.GRPC, kr, s.lumera.KeyName) + s.Require().NoError(err, "create lumera client") + defer lumeraClient.Close() + actionClient := lumeraClient.Action + + s.T().Log("ica: ensure ICA account funded") + err = s.ensureICAFunded(ctx, lumeraClient, lumeraAddr, icaAddr) + s.Require().NoError(err, "fund ICA account") + + s.T().Log("ica: create cascade client") + // Create cascade client for metadata, upload, and download helpers. + cascadeClient, err := cascade.New(ctx, cascade.Config{ + ChainID: s.lumera.ChainID, + GRPCAddr: s.lumera.GRPC, + Address: lumeraAddr, + KeyName: s.lumera.KeyName, + ICAOwnerKeyName: s.simd.KeyName, + ICAOwnerHRP: "cosmos", + Timeout: 30 * time.Second, + }, kr) + s.Require().NoError(err, "create cascade client") + cascadeClient.SetLogger(newSupernodeInfoLogger()) + defer cascadeClient.Close() + + s.T().Log("ica: create test files") + // Prepare local test files of varying sizes. + files, err := createICATestFiles(s.T().TempDir()) + s.Require().NoError(err, "create test files") + + s.T().Log("ica: register actions via ICA") + // ICA send hook: build packet, submit via simd controller, and resolve action ID from ack. + sendFunc := func(ctx context.Context, msg *actiontypes.MsgRequestAction, _ []byte, filePath string, _ *cascade.UploadOptions) (*types.ActionResult, error) { + s.T().Logf("ica: send request for %s", filepath.Base(filePath)) + actionIDs, err := s.sendICARequestTx(ctx, []*actiontypes.MsgRequestAction{msg}) + if err != nil { + return nil, err + } + if len(actionIDs) == 0 { + return nil, fmt.Errorf("no action ids returned for %s", filepath.Base(filePath)) + } + return &types.ActionResult{ActionID: actionIDs[0]}, nil + } + + // Register actions over ICA and track action IDs per file. + actionIDs := make(map[string]string) + for _, f := range files { + s.T().Logf("ica: upload %s", filepath.Base(f.path)) + res, err := cascadeClient.Upload(ctx, icaAddr, nil, f.path, + cascade.WithICACreatorAddress(icaAddr), + cascade.WithAppPubkey(simdPubkey), + cascade.WithICASendFunc(sendFunc), + ) + s.Require().NoError(err, "upload via ICA for %s", f.path) + s.Require().NotEmpty(res.ActionID, "missing action id for %s", f.path) + actionIDs[f.path] = res.ActionID + s.T().Logf("ica: action id for %s -> %s", filepath.Base(f.path), res.ActionID) + } + + s.T().Log("ica: download and verify files") + // Download each action payload and compare to the original. + downloadDir := s.T().TempDir() + for _, f := range files { + actionID := actionIDs[f.path] + _, err := cascadeClient.Download(ctx, actionID, downloadDir, cascade.WithDownloadSignerAddress(ownerAddr)) + s.Require().NoError(err, "download action %s", actionID) + + downloadedPath := filepath.Join(downloadDir, actionID, filepath.Base(f.path)) + downloaded, err := os.ReadFile(downloadedPath) + s.Require().NoError(err, "read downloaded file %s", downloadedPath) + s.True(bytes.Equal(f.content, downloaded), "downloaded content mismatch for %s", downloadedPath) + } + + s.T().Log("ica: wait for DONE and build approve messages") + for _, f := range files { + actionID := actionIDs[f.path] + err := waitForActionState(ctx, actionClient, actionID, types.ActionStateDone) + s.Require().NoError(err, "wait for action done %s", actionID) + s.T().Logf("ica: action %s is DONE", actionID) + + msg, err := cascade.CreateApproveActionMessage(ctx, actionID, cascade.WithApproveCreator(icaAddr)) + s.Require().NoError(err, "build approve message for %s", actionID) + s.T().Logf("ica: send approve tx via ICA (action_id=%s)", actionID) + err = s.sendICAApproveTx(ctx, []*actiontypes.MsgApproveAction{msg}) + s.Require().NoError(err, "send ICA approve tx for %s", actionID) + } + + s.T().Log("ica: wait for APPROVED") + // Confirm actions reach APPROVED on the host chain. + for _, f := range files { + actionID := actionIDs[f.path] + err := waitForActionState(ctx, actionClient, actionID, types.ActionStateApproved) + s.Require().NoError(err, "wait for action approved %s", actionID) + } +} + +type icaTestFile struct { + path string + content []byte +} + +func newLumeraBlockchainClient(ctx context.Context, chainID, grpcAddr string, kr keyring.Keyring, keyName string) (*blockchain.Client, error) { + if grpcAddr == "" { + return nil, fmt.Errorf("grpc address is required") + } + cfg := blockchain.Config{ + ChainID: chainID, + GRPCAddr: grpcAddr, + Timeout: 30 * time.Second, + MaxRecvMsgSize: 10 * 1024 * 1024, + MaxSendMsgSize: 10 * 1024 * 1024, + InsecureGRPC: true, + } + return blockchain.New(ctx, cfg, kr, keyName) +} + +func createICATestFiles(dir string) ([]icaTestFile, error) { + base := time.Now().UnixNano() + sizes := []int{128, 2048, 8192} + files := make([]icaTestFile, 0, len(sizes)) + + for i, size := range sizes { + name := fmt.Sprintf("ica-test-%d-%d.txt", base, i) + path := filepath.Join(dir, name) + content := bytes.Repeat([]byte{byte('a' + i)}, size) + if err := os.WriteFile(path, content, 0o600); err != nil { + return nil, err + } + files = append(files, icaTestFile{path: path, content: content}) + } + + return files, nil +} + +func (s *ibcSimdSuite) ensureICAFunded(ctx context.Context, client *blockchain.Client, fromAddr, icaAddr string) error { + if client == nil { + return fmt.Errorf("lumera client is nil") + } + if fromAddr == "" || icaAddr == "" { + return fmt.Errorf("from/ICA address is empty") + } + if s.lumera.Denom == "" { + return fmt.Errorf("lumera denom is empty") + } + if s.lumeraICAFund == "" { + return fmt.Errorf("lumera ICA fund amount is empty") + } + amount, ok := sdkmath.NewIntFromString(s.lumeraICAFund) + if !ok { + return fmt.Errorf("invalid lumera ICA fund amount: %s", s.lumeraICAFund) + } + if amount.IsZero() { + s.T().Log("ica: skip funding (amount=0)") + return nil + } + if s.lumera.REST != "" && amount.IsInt64() { + bal, err := ibcutil.QueryBalanceREST(s.lumera.REST, icaAddr, s.lumera.Denom) + if err == nil && bal >= amount.Int64() { + s.T().Logf("ica: ICA already funded (balance=%d%s)", bal, s.lumera.Denom) + return nil + } + if err != nil { + s.T().Logf("ica: ICA balance query failed: %s", err) + } + } + feeBuffer := int64(0) + if s.lumeraICAFeeBuffer != "" { + buf, err := strconv.ParseInt(s.lumeraICAFeeBuffer, 10, 64) + if err != nil { + return fmt.Errorf("invalid lumera ICA fee buffer: %s", s.lumeraICAFeeBuffer) + } + if buf > 0 { + feeBuffer = buf + } + } + if s.lumera.REST != "" && amount.IsInt64() { + fromBal, err := ibcutil.QueryBalanceREST(s.lumera.REST, fromAddr, s.lumera.Denom) + if err != nil { + s.T().Logf("ica: funding source balance query failed: %s", err) + } else { + maxSend := fromBal - feeBuffer + if maxSend <= 0 { + return fmt.Errorf("insufficient funds for ICA funding: balance=%d%s buffer=%d", fromBal, s.lumera.Denom, feeBuffer) + } + requested := amount.Int64() + if requested > maxSend { + s.T().Logf("ica: funding amount reduced to %d%s (requested=%d%s balance=%d%s)", + maxSend, s.lumera.Denom, requested, s.lumera.Denom, fromBal, s.lumera.Denom) + amount = sdkmath.NewInt(maxSend) + } + } + } + + fromAcc, err := sdk.AccAddressFromBech32(fromAddr) + if err != nil { + return fmt.Errorf("parse funding address: %w", err) + } + icaAcc, err := sdk.AccAddressFromBech32(icaAddr) + if err != nil { + return fmt.Errorf("parse ICA address: %w", err) + } + coin := sdk.NewCoin(s.lumera.Denom, amount) + msg := banktypes.NewMsgSend(fromAcc, icaAcc, sdk.NewCoins(coin)) + txBytes, err := client.BuildAndSignTxWithGasAdjustment(ctx, msg, "fund ica account", 2.0) + if err != nil { + return fmt.Errorf("build ICA fund tx: %w", err) + } + txHash, err := client.Broadcast(ctx, txBytes, txtypes.BroadcastMode_BROADCAST_MODE_SYNC) + if err != nil { + return fmt.Errorf("broadcast ICA fund tx: %w", err) + } + resp, err := client.WaitForTxInclusion(ctx, txHash) + if err != nil { + return fmt.Errorf("wait for ICA fund tx: %w", err) + } + if resp.TxResponse == nil { + return fmt.Errorf("ICA fund tx response is empty") + } + if resp.TxResponse.Code != 0 { + return fmt.Errorf("ICA fund tx failed: code=%d log=%s", resp.TxResponse.Code, resp.TxResponse.RawLog) + } + s.T().Logf("ica: ICA funded (tx=%s amount=%s%s)", txHash, amount.String(), s.lumera.Denom) + return nil +} + +func loadKeyringFromMnemonic(keyName, mnemonicFile string) (keyring.Keyring, []byte, string, error) { + if keyName == "" { + return nil, nil, "", fmt.Errorf("key name is required") + } + mnemonicRaw, err := os.ReadFile(mnemonicFile) + if err != nil { + return nil, nil, "", fmt.Errorf("read mnemonic file: %w", err) + } + mnemonic := strings.TrimSpace(string(mnemonicRaw)) + if mnemonic == "" { + return nil, nil, "", fmt.Errorf("mnemonic file is empty") + } + + krDir, err := os.MkdirTemp("", "lumera-keyring-*") + if err != nil { + return nil, nil, "", fmt.Errorf("create keyring dir: %w", err) + } + + registry := codectypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + krCodec := codec.NewProtoCodec(registry) + kr, err := keyring.New("lumera", "test", krDir, strings.NewReader(""), krCodec) + if err != nil { + return nil, nil, "", fmt.Errorf("create keyring: %w", err) + } + if _, err := kr.NewAccount(keyName, mnemonic, "", sdk.FullFundraiserPath, hd.Secp256k1); err != nil { + return nil, nil, "", fmt.Errorf("import key: %w", err) + } + + addr, err := addressFromKey(kr, keyName, "lumera") + if err != nil { + return nil, nil, "", fmt.Errorf("derive address: %w", err) + } + + rec, err := kr.Key(keyName) + if err != nil { + return nil, nil, "", fmt.Errorf("load key: %w", err) + } + pub, err := rec.GetPubKey() + if err != nil { + return nil, nil, "", fmt.Errorf("get pubkey: %w", err) + } + if pub == nil { + return nil, nil, "", fmt.Errorf("pubkey is nil") + } + + return kr, pub.Bytes(), addr, nil +} + +func importKeyFromMnemonic(kr keyring.Keyring, keyName, mnemonicFile, hrp string) ([]byte, string, error) { + if kr == nil { + return nil, "", fmt.Errorf("keyring is nil") + } + if keyName == "" { + return nil, "", fmt.Errorf("key name is required") + } + mnemonicRaw, err := os.ReadFile(mnemonicFile) + if err != nil { + return nil, "", fmt.Errorf("read mnemonic file: %w", err) + } + mnemonic := strings.TrimSpace(string(mnemonicRaw)) + if mnemonic == "" { + return nil, "", fmt.Errorf("mnemonic file is empty") + } + + if _, err := kr.Key(keyName); err != nil { + if _, err := kr.NewAccount(keyName, mnemonic, "", sdk.FullFundraiserPath, hd.Secp256k1); err != nil { + return nil, "", fmt.Errorf("import key: %w", err) + } + } + + addr, err := addressFromKey(kr, keyName, hrp) + if err != nil { + return nil, "", fmt.Errorf("derive address: %w", err) + } + rec, err := kr.Key(keyName) + if err != nil { + return nil, "", fmt.Errorf("load key: %w", err) + } + pub, err := rec.GetPubKey() + if err != nil { + return nil, "", fmt.Errorf("get pubkey: %w", err) + } + if pub == nil { + return nil, "", fmt.Errorf("pubkey is nil") + } + return pub.Bytes(), addr, nil +} + +func addressFromKey(kr keyring.Keyring, keyName, hrp string) (string, error) { + if kr == nil { + return "", fmt.Errorf("keyring is required") + } + if keyName == "" { + return "", fmt.Errorf("key name is required") + } + rec, err := kr.Key(keyName) + if err != nil { + return "", fmt.Errorf("key %s not found: %w", keyName, err) + } + pub, err := rec.GetPubKey() + if err != nil { + return "", fmt.Errorf("get pubkey: %w", err) + } + if pub == nil { + return "", fmt.Errorf("pubkey is nil") + } + addrBz := pub.Address() + bech, err := sdkbech32.ConvertAndEncode(hrp, addrBz) + if err != nil { + return "", fmt.Errorf("bech32 encode: %w", err) + } + return bech, nil +} + +func waitForActionState(ctx context.Context, client *blockchain.ActionClient, actionID string, state types.ActionState) error { + for i := 0; i < actionPollRetries; i++ { + action, err := client.GetAction(ctx, actionID) + if err == nil && action != nil && action.State == state { + return nil + } + time.Sleep(actionPollDelay) + } + return fmt.Errorf("action %s did not reach state %s", actionID, state) +} + +func (s *ibcSimdSuite) resolveSimdOwnerAddress(ctx context.Context) (string, error) { + if s.simdAddrFile != "" { + if addr, err := ibcutil.ReadAddress(s.simdAddrFile); err == nil { + return addr, nil + } + } + out, err := s.runSimdCmd(ctx, simdQueryTimeout, "keys", "show", s.simd.KeyName, "-a", "--keyring-backend", s.simdKeyring) + if err != nil { + return "", err + } + return strings.TrimSpace(out), nil +} + +func (s *ibcSimdSuite) ensureICAAddress(ctx context.Context, ownerAddr string) (string, error) { + addr, err := s.queryICAAddress(ctx, ownerAddr) + if err == nil && addr != "" { + s.T().Logf("ica: existing ICA address found=%s", addr) + return addr, nil + } + s.T().Log("ica: ICA address not found; registering controller") + if err := s.registerICA(ctx); err != nil { + return "", err + } + if err := s.waitForICAChannel(ctx, ownerAddr); err != nil { + s.T().Logf("ica: channel not open yet: %s", err) + } + for i := 0; i < actionPollRetries; i++ { + addr, err = s.queryICAAddress(ctx, ownerAddr) + if err == nil && addr != "" { + s.T().Logf("ica: ICA address registered=%s", addr) + return addr, nil + } + if i%10 == 0 { + s.T().Logf("ica: waiting for ICA address (attempt %d/%d)", i+1, actionPollRetries) + } + time.Sleep(actionPollDelay) + } + if err != nil { + return "", err + } + return "", fmt.Errorf("ICA address not found") +} + +func (s *ibcSimdSuite) waitForICAChannel(_ context.Context, ownerAddr string) error { + portID := fmt.Sprintf("icacontroller-%s", ownerAddr) + for i := 0; i < actionPollRetries; i++ { + channels, err := ibcutil.QueryChannels(s.simdBin, s.simd.RPC) + if err != nil { + return err + } + if i%10 == 0 { + s.T().Logf("ica: waiting for controller channel open (attempt %d/%d channels=%d)", i+1, actionPollRetries, len(channels)) + } + for _, ch := range channels { + if ch.PortID == portID && ibcutil.IsOpenState(ch.State) { + s.T().Logf("ica: controller channel open (port=%s channel=%s)", ch.PortID, ch.ChannelID) + return nil + } + if ch.PortID == portID && !ibcutil.IsOpenState(ch.State) && i%10 == 0 { + s.T().Logf("ica: controller channel not open yet (port=%s channel=%s state=%s)", ch.PortID, ch.ChannelID, ch.State) + } + } + time.Sleep(actionPollDelay) + } + return fmt.Errorf("controller channel %s not open after %d retries", portID, actionPollRetries) +} + +func (s *ibcSimdSuite) queryICAAddress(ctx context.Context, ownerAddr string) (string, error) { + args := []string{ + "q", "interchain-accounts", "controller", "interchain-account", + ownerAddr, s.connection.ID, + "--output", "json", + } + if s.simd.RPC != "" { + args = append(args, "--node", s.simd.RPC) + } + out, err := s.runSimdCmd(ctx, simdQueryTimeout, args...) + if err != nil { + msg := err.Error() + if strings.Contains(msg, "failed to retrieve account address") || strings.Contains(msg, "key not found") { + s.T().Logf("ica: ICA address not found yet (%s)", ownerAddr) + return "", nil + } + return "", err + } + var resp struct { + Address string `json:"address"` + } + if err := json.Unmarshal([]byte(out), &resp); err != nil { + return "", err + } + return strings.TrimSpace(resp.Address), nil +} + +func (s *ibcSimdSuite) registerICA(ctx context.Context) error { + version := icatypes.NewDefaultMetadataString(s.connection.ID, s.connection.Counterparty.ConnectionID) + s.T().Logf("ica: register controller (connection=%s counterparty=%s version=%s)", s.connection.ID, s.connection.Counterparty.ConnectionID, version) + s.logICAConnectionDetails(ctx) + args := []string{ + "tx", "interchain-accounts", "controller", "register", s.connection.ID, + "--version", version, + "--from", s.simd.KeyName, + "--chain-id", s.simd.ChainID, + "--keyring-backend", s.simdKeyring, + "--gas", "auto", + "--gas-adjustment", "1.3", + "--broadcast-mode", "sync", + "--yes", + } + if s.simdGasPrices != "" { + args = append(args, "--gas-prices", s.simdGasPrices) + } + if s.simd.RPC != "" { + args = append(args, "--node", s.simd.RPC) + } + out, err := s.runSimdCmd(ctx, simdTxTimeout, args...) + if err != nil { + return err + } + s.T().Logf("ica: register controller response: %s", trimLog(out)) + if resp, ok := parseTxResponse(out); ok { + if resp.Code != 0 { + return fmt.Errorf("ica register failed (code=%d codespace=%s tx=%s log=%s)", resp.Code, resp.Codespace, resp.TxHash, resp.RawLog) + } + if resp.TxHash != "" { + s.T().Logf("ica: register controller tx hash=%s", resp.TxHash) + } + } + return nil +} + +func (s *ibcSimdSuite) logICAConnectionDetails(ctx context.Context) { + if s.connection == nil { + return + } + + s.T().Logf("ica: simd connection detail (id=%s client_id=%s counterparty_client_id=%s counterparty_connection_id=%s)", + s.connection.ID, s.connection.ClientID, s.connection.Counterparty.ClientID, s.connection.Counterparty.ConnectionID) + simdClientChainID := s.querySimdClientChainID(ctx, s.connection.ClientID) + if simdClientChainID != "" { + s.T().Logf("ica: simd client-state chain_id=%s (client_id=%s)", simdClientChainID, s.connection.ClientID) + if s.lumera.ChainID != "" && simdClientChainID != s.lumera.ChainID { + s.T().Logf("ica: WARNING simd client chain_id mismatch (expected %s)", s.lumera.ChainID) + } + } + + // Query the lumera side for the counterparty connection via REST, if available. + if s.connection.Counterparty.ConnectionID == "" || s.lumera.REST == "" { + return + } + + url := strings.TrimSuffix(s.lumera.REST, "/") + "/ibc/core/connection/v1/connections/" + s.connection.Counterparty.ConnectionID + reqCtx, cancel := context.WithTimeout(ctx, simdQueryTimeout) + defer cancel() + req, err := http.NewRequestWithContext(reqCtx, http.MethodGet, url, nil) + if err != nil { + s.T().Logf("ica: lumera connection request build failed: %s", err) + return + } + httpResp, err := http.DefaultClient.Do(req) + if err != nil { + s.T().Logf("ica: lumera connection request failed: %s", err) + return + } + defer httpResp.Body.Close() + body, err := io.ReadAll(httpResp.Body) + if err != nil { + s.T().Logf("ica: lumera connection read failed: %s", err) + return + } + + var connResp struct { + Connection struct { + ClientID string `json:"client_id"` + State string `json:"state"` + Counterparty struct { + ClientID string `json:"client_id"` + ConnectionID string `json:"connection_id"` + } `json:"counterparty"` + } `json:"connection"` + } + if err := json.Unmarshal(body, &connResp); err != nil { + s.T().Logf("ica: lumera connection parse failed: %s", err) + return + } + s.T().Logf("ica: lumera connection detail (id=%s client_id=%s state=%s counterparty_client_id=%s counterparty_connection_id=%s)", + s.connection.Counterparty.ConnectionID, connResp.Connection.ClientID, connResp.Connection.State, connResp.Connection.Counterparty.ClientID, connResp.Connection.Counterparty.ConnectionID) + lumeraClientChainID := s.queryLumeraClientChainID(ctx, connResp.Connection.ClientID) + if lumeraClientChainID != "" { + s.T().Logf("ica: lumera client-state chain_id=%s (client_id=%s)", lumeraClientChainID, connResp.Connection.ClientID) + if s.simd.ChainID != "" && lumeraClientChainID != s.simd.ChainID { + s.T().Logf("ica: WARNING lumera client chain_id mismatch (expected %s)", s.simd.ChainID) + } + } +} + +func (s *ibcSimdSuite) querySimdClientChainID(ctx context.Context, clientID string) string { + if clientID == "" { + return "" + } + args := []string{"q", "ibc", "client", "state", clientID, "--output", "json"} + if s.simd.RPC != "" { + args = append(args, "--node", s.simd.RPC) + } + out, err := s.runSimdCmd(ctx, simdQueryTimeout, args...) + if err != nil { + s.T().Logf("ica: simd client-state query failed (client_id=%s): %s", clientID, err) + return "" + } + chainID := extractClientChainID([]byte(out)) + if chainID == "" { + s.T().Logf("ica: simd client-state missing chain_id (client_id=%s)", clientID) + return "" + } + return chainID +} + +func (s *ibcSimdSuite) queryLumeraClientChainID(ctx context.Context, clientID string) string { + if clientID == "" || s.lumera.REST == "" { + return "" + } + url := strings.TrimSuffix(s.lumera.REST, "/") + "/ibc/core/client/v1/client_states/" + clientID + reqCtx, cancel := context.WithTimeout(ctx, simdQueryTimeout) + defer cancel() + req, err := http.NewRequestWithContext(reqCtx, http.MethodGet, url, nil) + if err != nil { + s.T().Logf("ica: lumera client-state request build failed (client_id=%s): %s", clientID, err) + return "" + } + httpResp, err := http.DefaultClient.Do(req) + if err != nil { + s.T().Logf("ica: lumera client-state request failed (client_id=%s): %s", clientID, err) + return "" + } + defer httpResp.Body.Close() + body, err := io.ReadAll(httpResp.Body) + if err != nil { + s.T().Logf("ica: lumera client-state read failed (client_id=%s): %s", clientID, err) + return "" + } + chainID := extractClientChainID(body) + if chainID == "" { + s.T().Logf("ica: lumera client-state missing chain_id (client_id=%s)", clientID) + return "" + } + return chainID +} + +func extractClientChainID(raw []byte) string { + var resp map[string]any + if err := json.Unmarshal(raw, &resp); err != nil { + return "" + } + clientState := mapPath(resp, "identified_client_state", "client_state") + if len(clientState) == 0 { + clientState = mapPath(resp, "client_state") + } + if len(clientState) == 0 { + return "" + } + return stringPath(clientState, "chain_id") +} + +func mapPath(m map[string]any, path ...string) map[string]any { + var cur any = m + for _, p := range path { + next, ok := cur.(map[string]any) + if !ok { + return nil + } + cur = next[p] + } + if out, ok := cur.(map[string]any); ok { + return out + } + return nil +} + +func stringPath(m map[string]any, path ...string) string { + var cur any = m + for _, p := range path { + next, ok := cur.(map[string]any) + if !ok { + return "" + } + cur = next[p] + } + return stringFromAny(cur) +} + +func stringFromAny(v any) string { + switch val := v.(type) { + case string: + return val + case float64: + return strconv.FormatInt(int64(val), 10) + case json.Number: + return val.String() + default: + return "" + } +} + +func (s *ibcSimdSuite) waitForPacketInfo(ctx context.Context, txHash string) (cascade.PacketInfo, error) { + for i := 0; i < actionPollRetries; i++ { + info, err := s.queryPacketInfo(ctx, txHash) + if err == nil { + s.T().Logf("ica: packet info found (tx=%s port=%s channel=%s seq=%d)", txHash, info.Port, info.Channel, info.Sequence) + return info, nil + } + time.Sleep(actionPollDelay) + } + return cascade.PacketInfo{}, fmt.Errorf("packet info not found for tx %s", txHash) +} + +func (s *ibcSimdSuite) queryPacketInfo(ctx context.Context, txHash string) (cascade.PacketInfo, error) { + args := []string{"q", "tx", txHash, "--output", "json"} + if s.simd.RPC != "" { + args = append(args, "--node", s.simd.RPC) + } + out, err := s.runSimdCmd(ctx, simdQueryTimeout, args...) + if err != nil { + return cascade.PacketInfo{}, err + } + return cascade.ExtractPacketInfoFromTxJSON([]byte(out)) +} + +func (s *ibcSimdSuite) waitForPacketAcknowledgement(ctx context.Context, info cascade.PacketInfo) ([]byte, error) { + hostPort, hostChannel := s.resolveHostPacketRoute(ctx, info) + s.T().Logf("ica: wait for host ack (port=%s channel=%s seq=%d)", hostPort, hostChannel, info.Sequence) + var lastErr error + for i := 0; i < icaAckRetries; i++ { + ack, err := s.queryLumeraPacketAcknowledgement(ctx, hostPort, hostChannel, info.Sequence) + if err == nil { + s.T().Logf("ica: packet ack received (port=%s channel=%s seq=%d len=%d)", info.Port, info.Channel, info.Sequence, len(ack)) + return ack, nil + } + lastErr = err + if !isRetryableAckErr(err) { + return nil, err + } + if i%10 == 0 { + s.T().Logf("ica: ack not found yet (port=%s channel=%s seq=%d err=%s)", info.Port, info.Channel, info.Sequence, err) + s.logAckDebug(ctx, info) + } + time.Sleep(actionPollDelay) + } + if lastErr != nil { + return nil, fmt.Errorf("acknowledgement not found for %s/%s/%d: %v", info.Port, info.Channel, info.Sequence, lastErr) + } + return nil, fmt.Errorf("acknowledgement not found for %s/%s/%d", info.Port, info.Channel, info.Sequence) +} + +func isRetryableAckErr(err error) bool { + msg := err.Error() + return strings.Contains(msg, "not found") || + strings.Contains(msg, "NotFound") || + strings.Contains(msg, "invalid acknowledgement") +} + +func (s *ibcSimdSuite) logAckDebug(ctx context.Context, info cascade.PacketInfo) { + s.logSimdPacketQuery(ctx, "packet-commitment", info) + s.logSimdPacketQuery(ctx, "packet-receipt", info) + + state, cpPort, cpChannel, err := s.querySimdChannelEnd(ctx, info.Port, info.Channel) + if err != nil { + s.T().Logf("ica: simd channel end query failed (port=%s channel=%s): %s", info.Port, info.Channel, err) + } else { + s.T().Logf("ica: simd channel end state=%s counterparty_port=%s counterparty_channel=%s", state, cpPort, cpChannel) + } + + lumeraPort, lumeraChannel := s.resolveHostPacketRoute(ctx, info) + s.logLumeraPacketQuery(ctx, "packet_receipts", lumeraPort, lumeraChannel, info.Sequence) + s.logLumeraPacketQuery(ctx, "packet_acks", lumeraPort, lumeraChannel, info.Sequence) +} + +func (s *ibcSimdSuite) logSimdPacketQuery(ctx context.Context, subcommand string, info cascade.PacketInfo) { + args := []string{ + "q", "ibc", "channel", subcommand, + info.Port, info.Channel, strconv.FormatUint(info.Sequence, 10), + "--output", "json", + } + if s.simd.RPC != "" { + args = append(args, "--node", s.simd.RPC) + } + out, err := s.runSimdCmd(ctx, simdQueryTimeout, args...) + if err != nil { + s.T().Logf("ica: simd %s error: %s", subcommand, err) + return + } + s.T().Logf("ica: simd %s response: %s", subcommand, trimLog(out)) +} + +func (s *ibcSimdSuite) querySimdChannelEnd(ctx context.Context, port, channel string) (string, string, string, error) { + args := []string{ + "q", "ibc", "channel", "end", + port, channel, + "--output", "json", + } + if s.simd.RPC != "" { + args = append(args, "--node", s.simd.RPC) + } + out, err := s.runSimdCmd(ctx, simdQueryTimeout, args...) + if err != nil { + return "", "", "", err + } + var resp struct { + Channel struct { + State string `json:"state"` + Counterparty struct { + PortID string `json:"port_id"` + ChannelID string `json:"channel_id"` + } `json:"counterparty"` + } `json:"channel"` + } + if err := json.Unmarshal([]byte(out), &resp); err != nil { + return "", "", "", fmt.Errorf("parse channel end: %w", err) + } + return resp.Channel.State, resp.Channel.Counterparty.PortID, resp.Channel.Counterparty.ChannelID, nil +} + +func (s *ibcSimdSuite) logLumeraPacketQuery(ctx context.Context, endpoint, port, channel string, sequence uint64) { + if s.lumera.REST == "" { + return + } + url := fmt.Sprintf("%s/ibc/core/channel/v1/channels/%s/ports/%s/%s/%d", + strings.TrimSuffix(s.lumera.REST, "/"), channel, port, endpoint, sequence) + reqCtx, cancel := context.WithTimeout(ctx, simdQueryTimeout) + defer cancel() + req, err := http.NewRequestWithContext(reqCtx, http.MethodGet, url, nil) + if err != nil { + s.T().Logf("ica: lumera %s request build failed: %s", endpoint, err) + return + } + httpResp, err := http.DefaultClient.Do(req) + if err != nil { + s.T().Logf("ica: lumera %s request failed: %s", endpoint, err) + return + } + defer httpResp.Body.Close() + body, err := io.ReadAll(httpResp.Body) + if err != nil { + s.T().Logf("ica: lumera %s read failed: %s", endpoint, err) + return + } + s.T().Logf("ica: lumera %s response (status=%d): %s", endpoint, httpResp.StatusCode, trimLog(string(body))) +} + +func (s *ibcSimdSuite) resolveHostPacketRoute(ctx context.Context, info cascade.PacketInfo) (string, string) { + hostPort := info.Port + hostChannel := info.Channel + if _, cpPort, cpChannel, err := s.querySimdChannelEnd(ctx, info.Port, info.Channel); err == nil { + if cpPort != "" { + hostPort = cpPort + } + if cpChannel != "" { + hostChannel = cpChannel + } + } + if hostPort == info.Port && strings.HasPrefix(info.Port, "icacontroller-") { + hostPort = "icahost" + } + return hostPort, hostChannel +} + +func (s *ibcSimdSuite) queryLumeraPacketAcknowledgement(ctx context.Context, port, channel string, sequence uint64) ([]byte, error) { + return s.queryLumeraAckHex(ctx, port, channel, sequence) +} + +func (s *ibcSimdSuite) queryLumeraAckHex(ctx context.Context, port, channel string, sequence uint64) ([]byte, error) { + if s.lumera.RPC == "" { + return nil, fmt.Errorf("lumera RPC address is empty") + } + base := strings.TrimSuffix(s.lumera.RPC, "/") + "/tx_search" + values := url.Values{} + queryExpr := fmt.Sprintf("write_acknowledgement.packet_dst_port='%s' AND write_acknowledgement.packet_dst_channel='%s' AND write_acknowledgement.packet_sequence='%d'", port, channel, sequence) + // CometBFT expects JSON-string encoded query params. + values.Add("query", fmt.Sprintf("%q", queryExpr)) + values.Add("prove", "false") + values.Add("page", "1") + values.Add("per_page", "5") + reqURL := base + "?" + values.Encode() + + reqCtx, cancel := context.WithTimeout(ctx, simdQueryTimeout) + defer cancel() + req, err := http.NewRequestWithContext(reqCtx, http.MethodGet, reqURL, nil) + if err != nil { + return nil, fmt.Errorf("build lumera ack events request: %w", err) + } + httpResp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("lumera ack events request failed: %w", err) + } + defer httpResp.Body.Close() + body, err := io.ReadAll(httpResp.Body) + if err != nil { + return nil, fmt.Errorf("read lumera ack events response: %w", err) + } + if httpResp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("lumera ack events query failed (status=%d): %s", httpResp.StatusCode, strings.TrimSpace(string(body))) + } + type eventAttr struct { + Key string `json:"key"` + Value string `json:"value"` + } + type event struct { + Type string `json:"type"` + Attributes []eventAttr `json:"attributes"` + } + var resp struct { + Result struct { + Txs []struct { + TxResult struct { + Events []event `json:"events"` + } `json:"tx_result"` + } `json:"txs"` + } `json:"result"` + } + if err := json.Unmarshal(body, &resp); err != nil { + return nil, fmt.Errorf("parse lumera ack events response: %w", err) + } + + for _, tx := range resp.Result.Txs { + var ackHex string + var ackErr string + var ackSuccess *bool + for _, evt := range tx.TxResult.Events { + evtType := strings.TrimSpace(decodeEventValue(evt.Type)) + if evtType == "" { + evtType = evt.Type + } + if evtType != "write_acknowledgement" { + if strings.Contains(evtType, "ics27_packet") { + attr := make(map[string]string) + for _, a := range evt.Attributes { + key := strings.TrimSpace(decodeEventValue(a.Key)) + val := strings.TrimSpace(decodeEventValue(a.Value)) + if key != "" { + attr[key] = val + } + } + if v, ok := attr["success"]; ok { + success := strings.EqualFold(v, "true") + ackSuccess = &success + } + if v, ok := attr["ibccallbackerror-success"]; ok { + success := strings.EqualFold(v, "true") + ackSuccess = &success + } + if v := attr["error"]; v != "" { + ackErr = v + } + if v := attr["ibccallbackerror-error"]; v != "" { + ackErr = v + } + } + continue + } + attr := make(map[string]string) + for _, a := range evt.Attributes { + key := strings.TrimSpace(decodeEventValue(a.Key)) + val := strings.TrimSpace(decodeEventValue(a.Value)) + if key != "" { + attr[key] = val + } + } + if attr["packet_dst_port"] != port || + attr["packet_dst_channel"] != channel || + attr["packet_sequence"] != strconv.FormatUint(sequence, 10) { + continue + } + ackHex = attr["packet_ack_hex"] + } + if ackHex == "" { + continue + } + if ackSuccess != nil && !*ackSuccess { + if ackErr != "" { + return nil, fmt.Errorf("ica host ack error: %s", ackErr) + } + return nil, fmt.Errorf("ica host ack error: unknown failure") + } + ack, err := hex.DecodeString(ackHex) + if err != nil { + return nil, fmt.Errorf("decode acknowledgement hex: %w", err) + } + return ack, nil + } + + return nil, fmt.Errorf("acknowledgement event not found for %s/%s/%d", port, channel, sequence) +} + +func decodeEventValue(raw string) string { + if raw == "" { + return "" + } + decoded, err := base64.StdEncoding.DecodeString(raw) + if err != nil { + return raw + } + if !isMostlyPrintableASCII(decoded) { + return raw + } + return string(decoded) +} + +func isMostlyPrintableASCII(data []byte) bool { + if len(data) == 0 { + return false + } + printable := 0 + for _, b := range data { + if b == '\n' || b == '\r' || b == '\t' || (b >= 32 && b <= 126) { + printable++ + } + } + return printable*100/len(data) >= 90 +} + +func trimLog(payload string) string { + out := strings.TrimSpace(payload) + const maxLen = 600 + if len(out) <= maxLen { + return out + } + return out[:maxLen] + "…" +} + +func parseTxHash(output string) (string, error) { + if hash, err := cascade.ParseTxHashJSON([]byte(output)); err == nil { + return hash, nil + } + if hash, err := parseTxHashFromLines(output); err == nil { + return hash, nil + } + return "", fmt.Errorf("unable to parse tx hash from output") +} + +type txResponse struct { + Code int64 `json:"code"` + Codespace string `json:"codespace"` + RawLog string `json:"raw_log"` + TxHash string `json:"txhash"` +} + +func parseTxResponse(output string) (txResponse, bool) { + if resp, ok := parseTxResponseJSON([]byte(strings.TrimSpace(output))); ok { + return resp, true + } + for _, line := range strings.Split(output, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + if strings.HasPrefix(line, "{") || strings.HasPrefix(line, "[{") { + if resp, ok := parseTxResponseJSON([]byte(line)); ok { + return resp, true + } + } + } + start := strings.Index(output, "{") + if start >= 0 { + if resp, ok := parseTxResponseJSON([]byte(output[start:])); ok { + return resp, true + } + } + return txResponse{}, false +} + +func parseTxResponseJSON(data []byte) (txResponse, bool) { + var resp txResponse + if err := json.Unmarshal(data, &resp); err != nil { + return txResponse{}, false + } + return resp, true +} + +func parseTxHashFromLines(output string) (string, error) { + for _, line := range strings.Split(output, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + if strings.HasPrefix(line, "{") || strings.HasPrefix(line, "[{") { + if hash, err := cascade.ParseTxHashJSON([]byte(line)); err == nil { + return hash, nil + } + } + } + start := strings.Index(output, "{") + if start >= 0 { + if hash, err := cascade.ParseTxHashJSON([]byte(output[start:])); err == nil { + return hash, nil + } + } + return "", fmt.Errorf("tx hash not found in output") +} + +func (s *ibcSimdSuite) sendICARequestTx(ctx context.Context, msgs []*actiontypes.MsgRequestAction) ([]string, error) { + var anys []*codectypes.Any + for _, msg := range msgs { + any, err := packRequestAny(msg) + if err != nil { + return nil, err + } + anys = append(anys, any) + } + _, ackBytes, err := s.sendICAAnysWithAck(ctx, anys) + if err != nil { + return nil, err + } + actionIDs, err := cascade.ExtractRequestActionIDsFromAck(ackBytes) + if err != nil { + return nil, err + } + return actionIDs, nil +} + +func (s *ibcSimdSuite) sendICAApproveTx(ctx context.Context, msgs []*actiontypes.MsgApproveAction) error { + s.T().Logf("ica: build approve ICA packet (msgs=%d)", len(msgs)) + var anys []*codectypes.Any + for _, msg := range msgs { + s.T().Logf("ica: approve msg action_id=%s creator=%s", msg.ActionId, msg.Creator) + any, err := packApproveAny(msg) + if err != nil { + return err + } + anys = append(anys, any) + } + info, ackBytes, err := s.sendICAAnysWithAck(ctx, anys) + if err != nil { + return err + } + s.T().Logf("ica: approve ack received (port=%s channel=%s seq=%d len=%d)", info.Port, info.Channel, info.Sequence, len(ackBytes)) + return nil +} + +func (s *ibcSimdSuite) sendICAAnysWithAck(ctx context.Context, anys []*codectypes.Any) (cascade.PacketInfo, []byte, error) { + s.T().Logf("ica: build ICA packet data (msgs=%d connection=%s)", len(anys), s.connection.ID) + packet, err := cascade.BuildICAPacketData(anys) + if err != nil { + return cascade.PacketInfo{}, nil, err + } + packetJSON, err := marshalICAPacketJSON(packet) + if err != nil { + return cascade.PacketInfo{}, nil, err + } + + tmpFile, err := os.CreateTemp("", "ica-packet-*.json") + if err != nil { + return cascade.PacketInfo{}, nil, err + } + if _, err := tmpFile.Write(packetJSON); err != nil { + _ = tmpFile.Close() + return cascade.PacketInfo{}, nil, err + } + if err := tmpFile.Close(); err != nil { + return cascade.PacketInfo{}, nil, err + } + + args := []string{ + "tx", "interchain-accounts", "controller", "send-tx", s.connection.ID, tmpFile.Name(), + "--from", s.simd.KeyName, + "--chain-id", s.simd.ChainID, + "--keyring-backend", s.simdKeyring, + "--gas", "auto", + "--gas-adjustment", "1.3", + "--broadcast-mode", "sync", + "--output", "json", + "--yes", + } + if s.simdGasPrices != "" { + args = append(args, "--gas-prices", s.simdGasPrices) + } + if s.simd.RPC != "" { + args = append(args, "--node", s.simd.RPC) + } + + out, err := s.runSimdCmd(ctx, simdTxTimeout, args...) + if err != nil { + return cascade.PacketInfo{}, nil, err + } + + txHash, err := parseTxHash(out) + if err != nil { + return cascade.PacketInfo{}, nil, err + } + + packetInfo, err := s.waitForPacketInfo(ctx, txHash) + if err != nil { + return cascade.PacketInfo{}, nil, err + } + + ackBytes, err := s.waitForPacketAcknowledgement(ctx, packetInfo) + if err != nil { + return cascade.PacketInfo{}, nil, err + } + return packetInfo, ackBytes, nil +} + +func packRequestAny(msg *actiontypes.MsgRequestAction) (*codectypes.Any, error) { + anyBytes, err := cascade.PackRequestForICA(msg) + if err != nil { + return nil, err + } + var any codectypes.Any + if err := gogoproto.Unmarshal(anyBytes, &any); err != nil { + return nil, err + } + return &any, nil +} + +func packApproveAny(msg *actiontypes.MsgApproveAction) (*codectypes.Any, error) { + anyBytes, err := cascade.PackApproveForICA(msg) + if err != nil { + return nil, err + } + var any codectypes.Any + if err := gogoproto.Unmarshal(anyBytes, &any); err != nil { + return nil, err + } + return &any, nil +} + +func marshalICAPacketJSON(packet icatypes.InterchainAccountPacketData) ([]byte, error) { + cdc := codec.NewProtoCodec(codectypes.NewInterfaceRegistry()) + return cdc.MarshalJSON(&packet) +} + +func (s *ibcSimdSuite) runSimdCmd(ctx context.Context, timeout time.Duration, args ...string) (string, error) { + cmdArgs := append([]string{}, args...) + if s.simdHome != "" { + cmdArgs = append([]string{"--home", s.simdHome}, cmdArgs...) + } + s.T().Logf("ica: simd cmd: %s", formatCmd(s.simdBin, cmdArgs)) + cmdCtx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + cmd := exec.CommandContext(cmdCtx, s.simdBin, cmdArgs...) + out, err := cmd.CombinedOutput() + if cmdCtx.Err() == context.DeadlineExceeded { + return "", fmt.Errorf("simd command timed out: %s", strings.TrimSpace(string(out))) + } + if err != nil { + return "", fmt.Errorf("simd command failed: %w: %s", err, strings.TrimSpace(string(out))) + } + return string(out), nil +} + +func formatCmd(bin string, args []string) string { + parts := make([]string, 0, len(args)+1) + parts = append(parts, shellQuote(bin)) + for _, arg := range args { + parts = append(parts, shellQuote(arg)) + } + return strings.Join(parts, " ") +} + +func shellQuote(value string) string { + if value == "" { + return "''" + } + safe := true + for _, r := range value { + if (r >= 'a' && r <= 'z') || + (r >= 'A' && r <= 'Z') || + (r >= '0' && r <= '9') || + r == '_' || r == '-' || r == '.' || r == '/' || r == ':' { + continue + } + safe = false + break + } + if safe { + return value + } + return "'" + strings.ReplaceAll(value, "'", "'\"'\"'") + "'" +} diff --git a/devnet/tests/hermes/ibc_hermes_test.go b/devnet/tests/hermes/ibc_hermes_test.go new file mode 100644 index 0000000..a181808 --- /dev/null +++ b/devnet/tests/hermes/ibc_hermes_test.go @@ -0,0 +1,271 @@ +package hermes + +import ( + "os" + "strings" + "testing" + "time" + + "gen/tests/ibcutil" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" +) + +const ( + defaultChannelInfoPath = "/shared/status/hermes/channel_transfer.json" + defaultSimdBin = "simd" + defaultSimdRPC = "http://127.0.0.1:26657" + defaultSimdChainID = "hermes-simd-1" + defaultSimdHome = "/root/.simd" + defaultSimdKeyName = "simd-test" + defaultSimdGasPrices = "0.025stake" + defaultSimdDenom = "stake" + defaultSimdKeyring = "test" + defaultSimdAddrFile = "/shared/hermes/simd-test.address" + defaultSimdMnemonic = "/shared/hermes/simd-test.mnemonic" + defaultLumeraChainID = "lumera-devnet-1" + defaultLumeraDenom = "ulume" + defaultLumeraGRPCAddr = "http://supernova_validator_1:9090" + defaultLumeraRPCAddr = "http://supernova_validator_1:26657" + defaultLumeraKeyName = "hermes-relayer" + defaultLumeraMnemonic = "/shared/hermes/lumera-hermes-relayer.mnemonic" + defaultLumeraAddrFile = "/shared/hermes/lumera-hermes-relayer.address" + defaultLumeraREST = "http://supernova_validator_1:1317" + defaultLumeraICAFund = "1000000" + defaultLumeraICAFeeBuf = "10000" + actionPollRetries = 40 + actionPollDelay = 3 * time.Second + simdQueryTimeout = 20 * time.Second + simdTxTimeout = 2 * time.Minute + icaTestTimeout = 20 * time.Minute +) + +type ibcSimdSuite struct { + suite.Suite + channelInfoPath string + simdBin string + portID string + counterpartyChannel string + simdKeyring string + simdHome string + simdGasPrices string + simdAddrFile string + lumeraICAFund string + lumeraICAFeeBuffer string + lumeraRecipient string + + simd ChainInfo + lumera ChainInfo + + info ibcutil.ChannelInfo + channels []ibcutil.Channel + channel *ibcutil.Channel + connections []ibcutil.Connection + connection *ibcutil.Connection + clientStatus string + csClientID string + csHeight int64 + csType string +} + +type ChainInfo struct { + ChainID string + RPC string + GRPC string + REST string + Denom string + KeyName string + MnemonicFile string +} + +func (s *ibcSimdSuite) SetupSuite() { + // Load environment-driven configuration and shared chain metadata. + s.channelInfoPath = getenv("CHANNEL_INFO_FILE", defaultChannelInfoPath) + s.simdBin = getenv("SIMD_BIN", defaultSimdBin) + s.simd = ChainInfo{ + ChainID: getenv("SIMD_CHAIN_ID", defaultSimdChainID), + RPC: getenv("SIMD_RPC_ADDR", defaultSimdRPC), + Denom: getenv("SIMD_DENOM", defaultSimdDenom), + KeyName: getenv("SIMD_KEY_NAME", defaultSimdKeyName), + MnemonicFile: getenv("SIMD_KEY_MNEMONIC_FILE", defaultSimdMnemonic), + } + s.simdKeyring = getenv("SIMD_KEYRING", defaultSimdKeyring) + s.simdHome = getenv("SIMD_HOME", defaultSimdHome) + s.simdGasPrices = getenv("SIMD_GAS_PRICES", defaultSimdGasPrices) + s.simdAddrFile = getenv("SIMD_OWNER_ADDR_FILE", defaultSimdAddrFile) + s.lumera = ChainInfo{ + ChainID: getenv("LUMERA_CHAIN_ID", defaultLumeraChainID), + GRPC: normalizeGRPCAddr(getenv("LUMERA_GRPC_ADDR", defaultLumeraGRPCAddr)), + RPC: getenv("LUMERA_RPC_ADDR", defaultLumeraRPCAddr), + REST: getenv("LUMERA_REST_ADDR", defaultLumeraREST), + Denom: getenv("LUMERA_DENOM", defaultLumeraDenom), + KeyName: getenv("LUMERA_KEY_NAME", defaultLumeraKeyName), + MnemonicFile: getenv("LUMERA_KEY_MNEMONIC_FILE", defaultLumeraMnemonic), + } + s.lumeraICAFund = getenv("LUMERA_ICA_FUND_AMOUNT", defaultLumeraICAFund) + s.lumeraICAFeeBuffer = getenv("LUMERA_ICA_FUND_FEE_BUFFER", defaultLumeraICAFeeBuf) + + ensureLumeraBech32Prefixes() + + info, err := ibcutil.LoadChannelInfo(s.channelInfoPath) + s.Require().NoError(err, "load channel info") + s.info = info + counterpartyChain := info.CounterpartyChainID + if counterpartyChain == "" && info.AChainID != "" && info.BChainID != "" { + switch s.simd.ChainID { + case info.AChainID: + counterpartyChain = info.BChainID + case info.BChainID: + counterpartyChain = info.AChainID + } + } + info.CounterpartyChainID = counterpartyChain + s.T().Logf("Loaded channel info: port=%s channel=%s counterparty_chain=%s a_chain=%s b_chain=%s", + info.PortID, info.ChannelID, info.CounterpartyChainID, info.AChainID, info.BChainID) + + // Resolve port/channel IDs from env or the generated channel info file. + portID := getenv("PORT_ID", "") + if portID == "" { + portID = info.PortID + } + if portID == "" { + portID = ibcutil.DefaultPortID + } + s.portID = portID + + s.counterpartyChannel = getenv("LUMERA_CHANNEL_ID", info.ChannelID) + s.Require().NotEmpty(s.counterpartyChannel, "channel_id missing in %s", s.channelInfoPath) + + // Load the lumera recipient for transfer tests. + lumeraAddrFile := getenv("LUMERA_RECIPIENT_ADDR_FILE", defaultLumeraAddrFile) + addr, err := ibcutil.ReadAddress(lumeraAddrFile) + s.Require().NoError(err, "read lumera recipient address") + s.lumeraRecipient = addr + + s.T().Logf("Testing IBC on simd (port=%s counterparty_channel=%s rpc=%s)", s.portID, s.counterpartyChannel, s.simd.RPC) + + // Discover channel/connection/client on simd and cache it for the suite. + channels, err := ibcutil.QueryChannels(s.simdBin, s.simd.RPC) + s.Require().NoError(err, "query channels") + s.channels = channels + s.T().Logf("Discovered %d simd channels", len(channels)) + for _, ch := range channels { + s.T().Logf("simd channel: port=%s channel=%s state=%s counterparty_port=%s counterparty_channel=%s conn_hops=%v", + ch.PortID, ch.ChannelID, ch.State, ch.Counterparty.PortID, ch.Counterparty.ChannelID, ch.ConnectionHops) + } + + channel := ibcutil.FindChannelByCounterparty(channels, s.portID, s.counterpartyChannel) + if channel == nil { + channel = ibcutil.FirstChannelByPort(channels, s.portID) + s.Require().NotNil(channel, "no channel found for port %s on simd", s.portID) + s.T().Logf("channel with counterparty %s not found; using channel %s", s.counterpartyChannel, channel.ChannelID) + } + s.channel = channel + + s.Require().NotEmpty(channel.ConnectionHops, "channel %s/%s missing connection hop", channel.PortID, channel.ChannelID) + connectionID := channel.ConnectionHops[0] + s.T().Logf("Channel open; connection=%s counterparty_channel=%s", connectionID, channel.Counterparty.ChannelID) + + connections, err := ibcutil.QueryConnections(s.simdBin, s.simd.RPC) + s.Require().NoError(err, "query connections") + s.connections = connections + s.T().Logf("Discovered %d simd connections", len(connections)) + for _, conn := range connections { + s.T().Logf("simd connection: id=%s state=%s client_id=%s counterparty_client_id=%s counterparty_connection_id=%s", + conn.ID, conn.State, conn.ClientID, conn.Counterparty.ClientID, conn.Counterparty.ConnectionID) + } + + connection := ibcutil.FindConnectionByID(connections, connectionID) + if connection == nil { + connection = ibcutil.FirstOpenConnection(connections) + s.Require().NotNil(connection, "connection %s not found and no open connections", connectionID) + s.T().Logf("connection %s not found; using open connection %s", connectionID, connection.ID) + } + s.connection = connection + s.Require().NotEmpty(connection.ClientID, "connection %s missing client_id", connection.ID) + + // Capture client status and channel client-state for dedicated assertions. + status, err := ibcutil.QueryClientStatus(s.simdBin, s.simd.RPC, connection.ClientID) + s.Require().NoError(err, "query client status") + s.clientStatus = status + + csClientID, csHeight, csType, err := ibcutil.QueryChannelClientState(s.simdBin, s.simd.RPC, channel.PortID, channel.ChannelID) + s.Require().NoError(err, "query channel client-state") + s.csClientID = csClientID + s.csHeight = csHeight + s.csType = csType +} + +func (s *ibcSimdSuite) TestChannelOpen() { + s.Require().NotNil(s.channel, "channel is nil") + s.True(ibcutil.IsOpenState(s.channel.State), "channel %s/%s not open: %s", s.channel.PortID, s.channel.ChannelID, s.channel.State) + if s.channel.Counterparty.ChannelID != "" { + s.Equal(s.counterpartyChannel, s.channel.Counterparty.ChannelID, "counterparty channel mismatch") + } +} + +func (s *ibcSimdSuite) TestConnectionOpen() { + s.Require().NotNil(s.connection, "connection is nil") + s.True(ibcutil.IsOpenState(s.connection.State), "connection %s not open: %s", s.connection.ID, s.connection.State) +} + +func (s *ibcSimdSuite) TestClientActive() { + s.True(ibcutil.IsActiveStatus(s.clientStatus), "client %s not active: %s", s.connection.ClientID, s.clientStatus) +} + +func (s *ibcSimdSuite) TestChannelClientState() { + if s.csClientID != "" { + s.Equal(s.connection.ClientID, s.csClientID, "client-state mismatch") + } + s.Greater(s.csHeight, int64(0), "client-state latest_height not positive") + s.T().Logf("Client status active; client-state height=%d type=%s", s.csHeight, s.csType) +} + +func (s *ibcSimdSuite) TestTransferToLumera() { + // Exercise a real packet flow from simd -> lumera and confirm balance change. + amount := getenv("SIMD_IBC_AMOUNT", "100"+s.simd.Denom) + ibcDenom := ibcutil.IBCDenom(s.portID, s.channel.ChannelID, s.simd.Denom) + + before, err := ibcutil.QueryBalanceREST(s.lumera.REST, s.lumeraRecipient, ibcDenom) + s.Require().NoError(err, "query lumera recipient balance before") + + err = ibcutil.SendIBCTransfer( + s.simdBin, s.simd.RPC, s.simdHome, + s.simd.KeyName, s.portID, s.channel.ChannelID, s.lumeraRecipient, amount, + s.simd.ChainID, "test", s.simdGasPrices, + ) + s.Require().NoError(err, "send ibc transfer to lumera") + + after, err := ibcutil.WaitForBalanceIncreaseREST(s.lumera.REST, s.lumeraRecipient, ibcDenom, before, 20, 3*time.Second) + s.Require().NoError(err, "wait for lumera recipient balance increase") + s.T().Logf("lumera recipient balance increased: %d -> %d", before, after) +} + +func TestIBCSimdSideSuite(t *testing.T) { + suite.Run(t, new(ibcSimdSuite)) +} + +func getenv(key, fallback string) string { + if val := os.Getenv(key); val != "" { + return val + } + return fallback +} + +func normalizeGRPCAddr(addr string) string { + out := strings.TrimSpace(addr) + out = strings.TrimPrefix(out, "http://") + out = strings.TrimPrefix(out, "https://") + return out +} + +func ensureLumeraBech32Prefixes() { + cfg := sdk.GetConfig() + if cfg.GetBech32AccountAddrPrefix() == "lumera" { + return + } + cfg.SetBech32PrefixForAccount("lumera", "lumerapub") + cfg.SetBech32PrefixForValidator("lumeravaloper", "lumeravaloperpub") + cfg.SetBech32PrefixForConsensusNode("lumeravalcons", "lumeravalconspub") + cfg.Seal() +} diff --git a/devnet/tests/hermes/test_main_test.go b/devnet/tests/hermes/test_main_test.go new file mode 100644 index 0000000..3ee9ff8 --- /dev/null +++ b/devnet/tests/hermes/test_main_test.go @@ -0,0 +1,12 @@ +package hermes + +import ( + "flag" + "os" + "testing" +) + +func TestMain(m *testing.M) { + _ = flag.Set("test.v", "true") + os.Exit(m.Run()) +} diff --git a/devnet/tests/ibcutil/ibcutil.go b/devnet/tests/ibcutil/ibcutil.go new file mode 100644 index 0000000..dac1b9f --- /dev/null +++ b/devnet/tests/ibcutil/ibcutil.go @@ -0,0 +1,420 @@ +package ibcutil + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "strconv" + "strings" + "time" + + transfertypes "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types" +) + +const ( + DefaultPortID = "transfer" + commandTimeout = 20 * time.Second + longTimeout = 120 * time.Second +) + +type ChannelInfo struct { + ChannelID string `json:"channel_id"` + PortID string `json:"port_id"` + CounterpartyChainID string `json:"counterparty_chain_id"` + CounterpartyChannel string `json:"counterparty_channel_id"` + AChainID string `json:"a_chain_id"` + BChainID string `json:"b_chain_id"` + CounterpartyClientID string `json:"counterparty_client_id"` +} + +type Channel struct { + State string `json:"state"` + PortID string `json:"port_id"` + ChannelID string `json:"channel_id"` + ConnectionHops []string `json:"connection_hops"` + Counterparty struct { + PortID string `json:"port_id"` + ChannelID string `json:"channel_id"` + } `json:"counterparty"` +} + +type ChannelsResponse struct { + Channels []Channel `json:"channels"` +} + +type Connection struct { + ID string `json:"id"` + ClientID string `json:"client_id"` + State string `json:"state"` + Counterparty struct { + ClientID string `json:"client_id"` + ConnectionID string `json:"connection_id"` + } `json:"counterparty"` +} + +type ConnectionsResponse struct { + Connections []Connection `json:"connections"` +} + +func LoadChannelInfo(path string) (ChannelInfo, error) { + var info ChannelInfo + data, err := os.ReadFile(path) + if err != nil { + return info, fmt.Errorf("read channel info: %w", err) + } + if err := json.Unmarshal(data, &info); err != nil { + return info, fmt.Errorf("parse channel info: %w", err) + } + return info, nil +} + +func RunJSON(bin string, args ...string) ([]byte, error) { + out, err := runWithTimeout(commandTimeout, bin, args...) + return out, err +} + +func RunWithOutput(bin string, args ...string) (string, error) { + out, err := runWithTimeout(commandTimeout, bin, args...) + return string(out), err +} + +func QueryChannels(bin, rpc string) ([]Channel, error) { + args := []string{"q", "ibc", "channel", "channels", "--output", "json"} + args = append(args, nodeArgs(rpc)...) + out, err := RunJSON(bin, args...) + if err != nil { + return nil, fmt.Errorf("query channels: %w", err) + } + var resp ChannelsResponse + if err := json.Unmarshal(out, &resp); err != nil { + return nil, fmt.Errorf("parse channels: %w", err) + } + return resp.Channels, nil +} + +func QueryConnections(bin, rpc string) ([]Connection, error) { + args := []string{"q", "ibc", "connection", "connections", "--output", "json"} + args = append(args, nodeArgs(rpc)...) + out, err := RunJSON(bin, args...) + if err != nil { + return nil, fmt.Errorf("query connections: %w", err) + } + var resp ConnectionsResponse + if err := json.Unmarshal(out, &resp); err != nil { + return nil, fmt.Errorf("parse connections: %w", err) + } + return resp.Connections, nil +} + +func QueryClientStatus(bin, rpc, clientID string) (string, error) { + args := []string{"q", "ibc", "client", "status", clientID, "--output", "json"} + args = append(args, nodeArgs(rpc)...) + out, err := RunJSON(bin, args...) + if err != nil { + return "", fmt.Errorf("query client status: %w", err) + } + var resp map[string]any + if err := json.Unmarshal(out, &resp); err != nil { + return "", fmt.Errorf("parse client status: %w", err) + } + status := getStringFromAny(resp["status"]) + if status == "" { + return "", fmt.Errorf("client status not found in response") + } + return status, nil +} + +func QueryChannelClientState(bin, rpc, portID, channelID string) (string, int64, string, error) { + args := []string{"q", "ibc", "channel", "client-state", portID, channelID, "--output", "json"} + args = append(args, nodeArgs(rpc)...) + out, err := RunJSON(bin, args...) + if err != nil { + return "", 0, "", fmt.Errorf("query channel client-state: %w", err) + } + var resp map[string]any + if err := json.Unmarshal(out, &resp); err != nil { + return "", 0, "", fmt.Errorf("parse client-state: %w", err) + } + + clientID := getStringPath(resp, "identified_client_state", "client_id") + if clientID == "" { + clientID = getStringPath(resp, "client_id") + } + + clientState := getMapPath(resp, "identified_client_state", "client_state") + if len(clientState) == 0 { + clientState = getMapPath(resp, "client_state") + } + + clientType := getStringPath(clientState, "@type") + heightStr := getStringPath(clientState, "latest_height", "revision_height") + height, _ := strconv.ParseInt(heightStr, 10, 64) + + return clientID, height, clientType, nil +} + +func QueryBalance(bin, rpc, address, denom string) (int64, error) { + args := []string{"q", "bank", "balances", address, "--output", "json"} + args = append(args, nodeArgs(rpc)...) + out, err := RunJSON(bin, args...) + if err != nil { + return 0, fmt.Errorf("query balance: %w", err) + } + var resp map[string]any + if err := json.Unmarshal(out, &resp); err != nil { + return 0, fmt.Errorf("parse balance: %w", err) + } + balances, ok := resp["balances"].([]any) + if !ok { + return 0, nil + } + for _, b := range balances { + m, ok := b.(map[string]any) + if !ok { + continue + } + if getStringFromAny(m["denom"]) == denom { + amtStr := getStringFromAny(m["amount"]) + amt, _ := strconv.ParseInt(amtStr, 10, 64) + return amt, nil + } + } + return 0, nil +} + +func SendIBCTransfer(bin, rpc, home, fromKey, portID, channelID, recipient, amount, chainID, keyring, gasPrices string) error { + args := []string{} + if home != "" { + args = append(args, "--home", home) + } + args = append(args, + "tx", "ibc-transfer", "transfer", + portID, channelID, recipient, amount, + "--from", fromKey, + "--chain-id", chainID, + "--keyring-backend", keyring, + "--gas", "auto", + "--gas-adjustment", "1.3", + "--broadcast-mode", "sync", + "--yes", + "--packet-timeout-height", "0-0", + "--packet-timeout-timestamp", "600000000000", // 10 minutes + ) + if gasPrices != "" { + args = append(args, "--gas-prices", gasPrices) + } + args = append(args, nodeArgs(rpc)...) + _, err := runWithTimeout(longTimeout, bin, args...) + if err != nil { + return fmt.Errorf("send ibc transfer: %w", err) + } + return nil +} + +func WaitForBalanceIncrease(bin, rpc, address, denom string, baseline int64, retries int, delay time.Duration) (int64, error) { + for i := 0; i < retries; i++ { + current, err := QueryBalance(bin, rpc, address, denom) + if err != nil { + return 0, err + } + if current > baseline { + return current, nil + } + time.Sleep(delay) + } + return 0, fmt.Errorf("balance for %s did not increase after %d retries", address, retries) +} + +func IBCDenom(portID, channelID, denom string) string { + trace := fmt.Sprintf("%s/%s/%s", portID, channelID, denom) + return transfertypes.ParseDenomTrace(trace).IBCDenom() +} + +func QueryBalanceREST(restAddr, address, denom string) (int64, error) { + if restAddr == "" { + return 0, fmt.Errorf("rest address is required") + } + url := strings.TrimSuffix(restAddr, "/") + "/cosmos/bank/v1beta1/balances/" + address + client := &http.Client{Timeout: 10 * time.Second} + resp, err := client.Get(url) + if err != nil { + return 0, fmt.Errorf("query balance (rest): %w", err) + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return 0, fmt.Errorf("read balance response: %w", err) + } + + var payload map[string]any + if err := json.Unmarshal(body, &payload); err != nil { + return 0, fmt.Errorf("parse balance response: %w", err) + } + if code, ok := payload["code"]; ok && getStringFromAny(code) != "" { + return 0, nil + } + + balances, ok := payload["balances"].([]any) + if !ok { + return 0, nil + } + for _, b := range balances { + m, ok := b.(map[string]any) + if !ok { + continue + } + if getStringFromAny(m["denom"]) == denom { + amtStr := getStringFromAny(m["amount"]) + amt, _ := strconv.ParseInt(amtStr, 10, 64) + return amt, nil + } + } + return 0, nil +} + +func WaitForBalanceIncreaseREST(restAddr, address, denom string, baseline int64, retries int, delay time.Duration) (int64, error) { + for i := 0; i < retries; i++ { + current, err := QueryBalanceREST(restAddr, address, denom) + if err != nil { + return 0, err + } + if current > baseline { + return current, nil + } + time.Sleep(delay) + } + return 0, fmt.Errorf("balance for %s did not increase after %d retries", address, retries) +} + +func ReadAddress(path string) (string, error) { + bz, err := os.ReadFile(path) + if err != nil { + return "", err + } + addr := strings.TrimSpace(string(bz)) + if addr == "" { + return "", fmt.Errorf("address file %s is empty", path) + } + return addr, nil +} + +func FindChannelByID(channels []Channel, portID, channelID string) *Channel { + for i := range channels { + if channels[i].PortID == portID && channels[i].ChannelID == channelID { + return &channels[i] + } + } + return nil +} + +func FindChannelByCounterparty(channels []Channel, portID, counterpartyChannelID string) *Channel { + for i := range channels { + if channels[i].PortID == portID && + channels[i].Counterparty.ChannelID == counterpartyChannelID { + return &channels[i] + } + } + return nil +} + +func FirstChannelByPort(channels []Channel, portID string) *Channel { + for i := range channels { + if channels[i].PortID == portID { + return &channels[i] + } + } + return nil +} + +func FindConnectionByID(conns []Connection, id string) *Connection { + for i := range conns { + if conns[i].ID == id { + return &conns[i] + } + } + return nil +} + +func FirstOpenConnection(conns []Connection) *Connection { + for i := range conns { + if IsOpenState(conns[i].State) { + return &conns[i] + } + } + return nil +} + +func IsOpenState(state string) bool { + s := strings.ToUpper(strings.TrimSpace(state)) + return s == "STATE_OPEN" || s == "OPEN" +} + +func IsActiveStatus(status string) bool { + return strings.EqualFold(strings.TrimSpace(status), "active") +} + +func nodeArgs(rpc string) []string { + if rpc == "" { + return nil + } + return []string{"--node", rpc} +} + +func getMapPath(m map[string]any, path ...string) map[string]any { + var cur any = m + for _, p := range path { + next, ok := cur.(map[string]any) + if !ok { + return nil + } + cur = next[p] + } + if out, ok := cur.(map[string]any); ok { + return out + } + return nil +} + +func getStringPath(m map[string]any, path ...string) string { + var cur any = m + for _, p := range path { + next, ok := cur.(map[string]any) + if !ok { + return "" + } + cur = next[p] + } + return getStringFromAny(cur) +} + +func getStringFromAny(v any) string { + switch val := v.(type) { + case string: + return val + case float64: + return strconv.FormatInt(int64(val), 10) + case json.Number: + return val.String() + default: + return "" + } +} + +func runWithTimeout(timeout time.Duration, bin string, args ...string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + cmd := exec.CommandContext(ctx, bin, args...) + out, err := cmd.CombinedOutput() + if ctx.Err() == context.DeadlineExceeded { + return out, fmt.Errorf("command timed out after %s: %s", timeout, strings.TrimSpace(string(out))) + } + if err != nil { + return out, fmt.Errorf("command failed: %w: %s", err, strings.TrimSpace(string(out))) + } + return out, nil +} diff --git a/devnet/tests/test-channel.sh b/devnet/tests/test-channel.sh deleted file mode 100755 index 2cf1789..0000000 --- a/devnet/tests/test-channel.sh +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -: "${CHANNEL_INFO_FILE:=/shared/status/hermes/channel_transfer.json}" -: "${PORT_ID:=transfer}" - -log() { - printf '[ibc-test] %s\n' "$1" -} - -require_cmd() { - if ! command -v "$1" >/dev/null 2>&1; then - log "Missing required command: $1" - exit 1 - fi -} - -require_cmd jq -require_cmd lumerad - -if [ ! -s "${CHANNEL_INFO_FILE}" ]; then - log "Channel info file ${CHANNEL_INFO_FILE} missing or empty" - exit 1 -fi - -CHANNEL_ID="$(jq -r '.channel_id // empty' "${CHANNEL_INFO_FILE}")" -COUNTERPARTY_CHAIN="$(jq -r '.b_chain_id // empty' "${CHANNEL_INFO_FILE}")" - -if [ -z "${CHANNEL_ID}" ]; then - log "channel_id not found in ${CHANNEL_INFO_FILE}" - exit 1 -fi - -log "Testing channel ${PORT_ID}/${CHANNEL_ID} (counterparty ${COUNTERPARTY_CHAIN:-unknown})" - -# Query all channels (array) and pick the exact match by port+channel -CHANNELS_JSON="$(lumerad q ibc channel channels --output json)" -log "${CHANNELS_JSON}" -MATCH_JSON="$(printf '%s\n' "${CHANNELS_JSON}" \ - | jq -c --arg p "${PORT_ID}" --arg ch "${CHANNEL_ID}" \ - '.channels[]? | select(.port_id==$p and .channel_id==$ch)')" - -if [ -z "${MATCH_JSON}" ]; then - log "Channel ${PORT_ID}/${CHANNEL_ID} not found in 'lumerad q ibc channel channels' output" - exit 1 -fi - -STATE="$(printf '%s\n' "${MATCH_JSON}" | jq -r '.state // empty')" - -if [ "${STATE}" != "STATE_OPEN" ]; then - log "Channel state is ${STATE}, expected STATE_OPEN" - exit 1 -fi -log "Channel state: ${STATE}" - -CONNECTION_ID="$(printf '%s\n' "${MATCH_JSON}" | jq -r '.connection_hops[0]')" -if [ -z "${CONNECTION_ID}" ] || [ "${CONNECTION_ID}" = "null" ]; then - log "Could not determine connection id from channel query" - exit 1 -fi -log "Connection ID: ${CONNECTION_ID}" - -# Fetch the connection (singular subcommand) -CONNECTION_JSON="$(lumerad q ibc connection connections --output json)" -log "${CONNECTION_JSON}" -MATCH_JSON="$(printf '%s\n' "${CONNECTION_JSON}" \ - | jq -c --arg id "${CONNECTION_ID}" ' - .connections // [] - | ( map(select(.id==$id)) - + (if length==0 then map(select(.state=="STATE_OPEN")) else [] end) - ) - | .[0] // empty - ' -)" - -if [ -z "${MATCH_JSON}" ]; then - log "Connection ${CONNECTION_ID} not found in 'lumerad q ibc connection connections' output" - exit 1 -fi - -CONNECTION_STATE="$(printf '%s\n' "${MATCH_JSON}" | jq -r '.state // empty')" -CLIENT_ID="$(printf '%s\n' "${MATCH_JSON}" | jq -r '.client_id // empty')" -COUNTERPARTY_CLIENT_ID="$(printf '%s\n' "${MATCH_JSON}" | jq -r '.counterparty.client_id // empty')" -COUNTERPARTY_CONN_ID="$(printf '%s\n' "${MATCH_JSON}" | jq -r '.counterparty.connection_id // empty')" - -log "Connection ${CONNECTION_ID} state: ${CONNECTION_STATE}" -log "Client ID: ${CLIENT_ID}, Counterparty: client=${COUNTERPARTY_CLIENT_ID}, connection=${COUNTERPARTY_CONN_ID}" - -if [ "${CONNECTION_STATE}" != "STATE_OPEN" ]; then - log "Connection state is ${CONNECTION_STATE}, expected STATE_OPEN" - exit 1 -fi - -CLIENT_STATUS_JSON="$(lumerad q ibc client status "${CLIENT_ID}" --output json)" -log "${CLIENT_STATUS_JSON}" -CLIENT_STATUS="$(printf '%s\n' "${CLIENT_STATUS_JSON}" | jq -r '.status // empty')" - -if [ "${CLIENT_STATUS}" != "Active" ]; then - log "Client ${CLIENT_ID} status is ${CLIENT_STATUS:-unknown}, expected Active" - exit 1 -fi -log "Client status: ${CLIENT_STATUS}" - -log "IBC channel ${PORT_ID}/${CHANNEL_ID} is open and healthy." - -# --- Client-state for this channel (sanity + consistency with connection) --- -CS_JSON="$(lumerad q ibc channel client-state "${PORT_ID}" "${CHANNEL_ID}" --output json)" -log "${CS_JSON}" - -# Extract fields robustly across SDK versions -CS_CLIENT_ID="$(printf '%s\n' "${CS_JSON}" \ - | jq -r '.identified_client_state.client_id // .client_id // empty')" - -CS_TYPE="$(printf '%s\n' "${CS_JSON}" \ - | jq -r '.identified_client_state.client_state."@type" // .client_state."@type" // empty')" - -CS_LATEST_HEIGHT="$(printf '%s\n' "${CS_JSON}" \ - | jq -r '( - .identified_client_state.client_state.latest_height.revision_height - // .client_state.latest_height.revision_height - // "0" - )')" - -if [ -z "${CS_CLIENT_ID}" ] || [ "${CS_CLIENT_ID}" = "null" ]; then - log "client-state query returned no client_id" - exit 1 -fi - -# Must match the client we read from the connection earlier -if [ "${CS_CLIENT_ID}" != "${CLIENT_ID}" ]; then - log "client-state client_id mismatch: got ${CS_CLIENT_ID}, expected ${CLIENT_ID}" - exit 1 -fi - -# Basic liveness: latest height should be a positive integer -if ! [ "${CS_LATEST_HEIGHT}" -gt 0 ] 2>/dev/null; then - log "client-state latest_height is not positive: ${CS_LATEST_HEIGHT}" - exit 1 -fi - -# Optional: type sanity (if present) -if [ -n "${CS_TYPE}" ] && [ "${CS_TYPE}" != "null" ]; then - log "client-state type: ${CS_TYPE}" -fi - -log "Channel client-state OK (client_id=${CS_CLIENT_ID}, latest_height=${CS_LATEST_HEIGHT})." \ No newline at end of file diff --git a/devnet/tests/validator/ibc_validator_test.go b/devnet/tests/validator/ibc_validator_test.go new file mode 100644 index 0000000..efbad52 --- /dev/null +++ b/devnet/tests/validator/ibc_validator_test.go @@ -0,0 +1,228 @@ +package validator + +import ( + "encoding/json" + "os" + "testing" + "time" + + "gen/tests/ibcutil" + "github.com/stretchr/testify/suite" +) + +const ( + defaultChannelInfoPath = "/shared/status/hermes/channel_transfer.json" + defaultLumeraBin = "lumerad" + defaultLumeraRPC = "http://supernova_validator_1:26657" + defaultLumeraChainID = "lumera-devnet-1" + defaultLumeraKeyName = "hermes-relayer" + defaultLumeraGasPrices = "0.025ulume" + defaultLumeraDenom = "ulume" + defaultSimdAddrFile = "/shared/hermes/simd-test.address" + defaultSimdREST = "http://hermes:1317" + defaultValidatorsFile = "/shared/config/validators.json" +) + +type ibcLumeraSuite struct { + suite.Suite + channelInfoPath string + lumeraBin string + lumeraRPC string + portID string + channelID string + lumeraChainID string + lumeraKeyName string + lumeraGasPrices string + lumeraDenom string + simdRecipient string + simdREST string + + info ibcutil.ChannelInfo + channels []ibcutil.Channel + channel *ibcutil.Channel + connections []ibcutil.Connection + connection *ibcutil.Connection + clientStatus string + csClientID string + csHeight int64 + csType string +} + +func (s *ibcLumeraSuite) SetupSuite() { + // Load environment-driven configuration and shared channel metadata. + s.channelInfoPath = getenv("CHANNEL_INFO_FILE", defaultChannelInfoPath) + s.lumeraBin = getenv("LUMERA_BIN", defaultLumeraBin) + s.lumeraRPC = getenv("LUMERA_RPC_ADDR", defaultLumeraRPC) + s.lumeraChainID = getenv("LUMERA_CHAIN_ID", defaultLumeraChainID) + if val := os.Getenv("LUMERA_KEY_NAME"); val != "" { + s.lumeraKeyName = val + } else { + s.lumeraKeyName = defaultLumeraKeyName + if key := loadPrimaryValidatorKey(getenv("LUMERA_VALIDATORS_FILE", defaultValidatorsFile)); key != "" { + s.lumeraKeyName = key + } + } + s.lumeraGasPrices = getenv("LUMERA_GAS_PRICES", defaultLumeraGasPrices) + s.lumeraDenom = getenv("LUMERA_DENOM", defaultLumeraDenom) + s.simdREST = getenv("SIMD_REST_ADDR", defaultSimdREST) + + info, err := ibcutil.LoadChannelInfo(s.channelInfoPath) + s.Require().NoError(err, "load channel info") + s.info = info + counterpartyChain := info.CounterpartyChainID + if counterpartyChain == "" && info.AChainID != "" && info.BChainID != "" { + if s.lumeraChainID == info.AChainID { + counterpartyChain = info.BChainID + } else if s.lumeraChainID == info.BChainID { + counterpartyChain = info.AChainID + } + } + info.CounterpartyChainID = counterpartyChain + s.T().Logf("Loaded channel info: port=%s channel=%s counterparty_chain=%s a_chain=%s b_chain=%s", + info.PortID, info.ChannelID, info.CounterpartyChainID, info.AChainID, info.BChainID) + s.T().Logf("Using lumera key name: %s", s.lumeraKeyName) + + // Resolve port/channel IDs from env or the generated channel info file. + portID := getenv("PORT_ID", "") + if portID == "" { + portID = info.PortID + } + if portID == "" { + portID = ibcutil.DefaultPortID + } + s.portID = portID + + s.channelID = getenv("CHANNEL_ID", info.ChannelID) + s.Require().NotEmpty(s.channelID, "channel_id missing in %s", s.channelInfoPath) + + // Default simd recipient from shared file for transfer tests. + simdAddrFile := getenv("SIMD_RECIPIENT_ADDR_FILE", defaultSimdAddrFile) + addr, err := ibcutil.ReadAddress(simdAddrFile) + s.Require().NoError(err, "read simd recipient address") + s.simdRecipient = addr + + s.T().Logf("Testing IBC on Lumera (port=%s channel=%s rpc=%s)", s.portID, s.channelID, s.lumeraRPC) + + // Discover channel/connection/client on lumera and cache it for the suite. + channels, err := ibcutil.QueryChannels(s.lumeraBin, s.lumeraRPC) + s.Require().NoError(err, "query channels") + s.channels = channels + s.T().Logf("Discovered %d lumera channels", len(channels)) + for _, ch := range channels { + s.T().Logf("lumera channel: port=%s channel=%s state=%s counterparty_port=%s counterparty_channel=%s conn_hops=%v", + ch.PortID, ch.ChannelID, ch.State, ch.Counterparty.PortID, ch.Counterparty.ChannelID, ch.ConnectionHops) + } + + channel := ibcutil.FindChannelByID(channels, s.portID, s.channelID) + s.Require().NotNil(channel, "channel %s/%s not found", s.portID, s.channelID) + s.channel = channel + + s.Require().NotEmpty(channel.ConnectionHops, "channel %s/%s missing connection hop", channel.PortID, channel.ChannelID) + connectionID := channel.ConnectionHops[0] + s.T().Logf("Channel open; connection=%s counterparty_channel=%s", connectionID, channel.Counterparty.ChannelID) + + connections, err := ibcutil.QueryConnections(s.lumeraBin, s.lumeraRPC) + s.Require().NoError(err, "query connections") + s.connections = connections + s.T().Logf("Discovered %d lumera connections", len(connections)) + for _, conn := range connections { + s.T().Logf("lumera connection: id=%s state=%s client_id=%s counterparty_client_id=%s counterparty_connection_id=%s", + conn.ID, conn.State, conn.ClientID, conn.Counterparty.ClientID, conn.Counterparty.ConnectionID) + } + + connection := ibcutil.FindConnectionByID(connections, connectionID) + if connection == nil { + connection = ibcutil.FirstOpenConnection(connections) + s.Require().NotNil(connection, "connection %s not found and no open connections", connectionID) + s.T().Logf("connection %s not found; using open connection %s", connectionID, connection.ID) + } + s.connection = connection + s.Require().NotEmpty(connection.ClientID, "connection %s missing client_id", connection.ID) + + // Capture client status and channel client-state for dedicated assertions. + status, err := ibcutil.QueryClientStatus(s.lumeraBin, s.lumeraRPC, connection.ClientID) + s.Require().NoError(err, "query client status") + s.clientStatus = status + + csClientID, csHeight, csType, err := ibcutil.QueryChannelClientState(s.lumeraBin, s.lumeraRPC, s.portID, s.channelID) + s.Require().NoError(err, "query channel client-state") + s.csClientID = csClientID + s.csHeight = csHeight + s.csType = csType +} + +func (s *ibcLumeraSuite) TestChannelOpen() { + s.Require().NotNil(s.channel, "channel is nil") + s.True(ibcutil.IsOpenState(s.channel.State), "channel %s/%s not open: %s", s.channel.PortID, s.channel.ChannelID, s.channel.State) +} + +func (s *ibcLumeraSuite) TestConnectionOpen() { + s.Require().NotNil(s.connection, "connection is nil") + s.True(ibcutil.IsOpenState(s.connection.State), "connection %s not open: %s", s.connection.ID, s.connection.State) +} + +func (s *ibcLumeraSuite) TestClientActive() { + s.True(ibcutil.IsActiveStatus(s.clientStatus), "client %s not active: %s", s.connection.ClientID, s.clientStatus) +} + +func (s *ibcLumeraSuite) TestChannelClientState() { + if s.csClientID != "" { + s.Equal(s.connection.ClientID, s.csClientID, "client-state mismatch") + } + s.Greater(s.csHeight, int64(0), "client-state latest_height not positive") + s.T().Logf("Client status active; client-state height=%d type=%s", s.csHeight, s.csType) +} + +func (s *ibcLumeraSuite) TestTransferToSimd() { + // Exercise a real packet flow from lumera -> simd and confirm balance change. + amount := getenv("LUMERA_IBC_AMOUNT", "100"+s.lumeraDenom) + ibcDenom := ibcutil.IBCDenom(s.portID, s.channelID, s.lumeraDenom) + + before, err := ibcutil.QueryBalanceREST(s.simdREST, s.simdRecipient, ibcDenom) + s.Require().NoError(err, "query simd recipient balance before") + + err = ibcutil.SendIBCTransfer( + s.lumeraBin, s.lumeraRPC, "", + s.lumeraKeyName, s.portID, s.channelID, s.simdRecipient, amount, + s.lumeraChainID, "test", s.lumeraGasPrices, + ) + s.Require().NoError(err, "send ibc transfer to simd") + + after, err := ibcutil.WaitForBalanceIncreaseREST(s.simdREST, s.simdRecipient, ibcDenom, before, 20, 3*time.Second) + s.Require().NoError(err, "wait for simd recipient balance increase") + s.T().Logf("simd recipient balance increased: %d -> %d", before, after) +} + +func TestIBCLumeraSideSuite(t *testing.T) { + suite.Run(t, new(ibcLumeraSuite)) +} + +func getenv(key, fallback string) string { + if val := os.Getenv(key); val != "" { + return val + } + return fallback +} + +func loadPrimaryValidatorKey(path string) string { + data, err := os.ReadFile(path) + if err != nil { + return "" + } + var vals []struct { + KeyName string `json:"key_name"` + Primary bool `json:"primary"` + } + if err := json.Unmarshal(data, &vals); err != nil { + return "" + } + for _, v := range vals { + if v.Primary && v.KeyName != "" { + return v.KeyName + } + } + if len(vals) > 0 { + return vals[0].KeyName + } + return "" +} diff --git a/devnet/tests/validator/test_main_test.go b/devnet/tests/validator/test_main_test.go new file mode 100644 index 0000000..40de16d --- /dev/null +++ b/devnet/tests/validator/test_main_test.go @@ -0,0 +1,12 @@ +package validator + +import ( + "flag" + "os" + "testing" +) + +func TestMain(m *testing.M) { + _ = flag.Set("test.v", "true") + os.Exit(m.Run()) +} diff --git a/go.mod b/go.mod index 77a6e31..c173924 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/LumeraProtocol/lumera -go 1.25.1 +go 1.25.5 replace ( github.com/cosmos/cosmos-sdk => github.com/cosmos/cosmos-sdk v0.50.14 @@ -8,7 +8,6 @@ replace ( github.com/lyft/protoc-gen-validate => github.com/envoyproxy/protoc-gen-validate v1.3.0 // replace broken goleveldb github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - // replace broken vanity url nhooyr.io/websocket => github.com/coder/websocket v1.8.7 ) @@ -195,6 +194,7 @@ require ( github.com/ghostiam/protogetter v0.3.9 // indirect github.com/go-chi/chi/v5 v5.2.3 // indirect github.com/go-critic/go-critic v0.12.0 // indirect + github.com/go-errors/errors v1.5.1 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-kit/kit v0.13.0 // indirect github.com/go-kit/log v0.2.1 // indirect @@ -247,7 +247,7 @@ require ( github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect github.com/gostaticanalysis/nilerr v0.1.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // 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.7.8 // indirect @@ -294,7 +294,7 @@ require ( github.com/ldez/usetesting v0.4.2 // indirect github.com/leonklingele/grouper v1.1.2 // indirect github.com/lib/pq v1.10.9 // indirect - github.com/linxGnu/grocksdb v1.9.2 // indirect + github.com/linxGnu/grocksdb v1.9.8 // indirect github.com/macabu/inamedparam v0.1.3 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/maratori/testableexamples v1.0.0 // indirect diff --git a/go.sum b/go.sum index 1548395..0f845f7 100644 --- a/go.sum +++ b/go.sum @@ -1097,8 +1097,8 @@ github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-critic/go-critic v0.12.0 h1:iLosHZuye812wnkEz1Xu3aBwn5ocCPfc9yqmFG9pa6w= github.com/go-critic/go-critic v0.12.0/go.mod h1:DpE0P6OVc6JzVYzmM5gq5jMU31zLr4am5mB/VfFK64w= -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-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= @@ -1385,8 +1385,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -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/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= 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/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= @@ -1566,8 +1566,8 @@ 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.9.2 h1:O3mzvO0wuzQ9mtlHbDrShixyVjVbmuqTjFrzlf43wZ8= -github.com/linxGnu/grocksdb v1.9.2/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/linxGnu/grocksdb v1.9.8 h1:vOIKv9/+HKiqJAElJIEYv3ZLcihRxyP7Suu/Mu8Dxjs= +github.com/linxGnu/grocksdb v1.9.8/go.mod h1:C3CNe9UYc9hlEM2pC82AqiGS3LRW537u9LFV4wIZuHk= github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= diff --git a/proto/lumera/action/v1/action.proto b/proto/lumera/action/v1/action.proto index 0b44c67..17d7ec4 100644 --- a/proto/lumera/action/v1/action.proto +++ b/proto/lumera/action/v1/action.proto @@ -20,4 +20,5 @@ message Action { int64 blockHeight = 8; repeated string superNodes = 9 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"]; int64 fileSizeKbs = 10; + bytes app_pubkey = 11; } diff --git a/readme.md b/readme.md index 3f4da71..4ae3e61 100644 --- a/readme.md +++ b/readme.md @@ -23,17 +23,17 @@ ignite chain build **Note2:** You might get error during build: ``` -Cosmos SDK's version is: v0.50.12 +Cosmos SDK's version is: v0.50.14 ✘ Cannot build app: -error while running command go mod tidy: go: cannot find "go1.24.1" in PATH +error while running command go mod tidy: go: cannot find "go1.25.5" in PATH : exit status 1 ``` Lumera project doesn't specify toolchain, but it seems `Ignite` sometime might still require it. Do this: ```cmd -go install golang.org/dl/go1.24.1@latest -go1.24.1 download +go install golang.org/dl/go1.25.5@latest +go1.25.5 download export GOTOOLCHAIN=auto ``` diff --git a/tests/systemtests/go.mod b/tests/systemtests/go.mod index a88ecbc..152725e 100644 --- a/tests/systemtests/go.mod +++ b/tests/systemtests/go.mod @@ -1,6 +1,6 @@ module github.com/LumeraProtocol/lumera/tests/systemtests -go 1.25.1 +go 1.25.5 replace ( github.com/LumeraProtocol/lumera => ../../ diff --git a/x/action/v1/keeper/crypto.go b/x/action/v1/keeper/crypto.go index 799ee6d..baf4271 100644 --- a/x/action/v1/keeper/crypto.go +++ b/x/action/v1/keeper/crypto.go @@ -35,12 +35,12 @@ var highCompressSem = semaphore.NewWeighted(maxParallelHighCompressCalls) // VerifySignature verifies that a signature is valid for given data and signer. // // Flow: -// 1) Try to get a pubkey from the context cache (creatorAccountCtxKey). For ICA creators +// - Try to get a pubkey from the context cache (creatorAccountCtxKey). For ICA creators // this uses the app-level pubkey provided on the message; for non-ICA creators it uses // the cached account pubkey when present. -// 2) If no cached key is found, resolve the account and pubkey from auth keeper + address codec. -// 3) Decode the base64 signature, coerce to r||s format, and verify. -// 4) If direct verification fails, retry using ADR-36 amino sign bytes (Keplr/browser flow). +// - If no cached key is found, resolve the account and pubkey from auth keeper + address codec. +// - Decode the base64 signature, coerce to r||s format, and verify. +// - If direct verification fails, retry using ADR-36 amino sign bytes (Keplr/browser flow). // // Parameters: // - data: The original data that was signed (string format) @@ -55,6 +55,7 @@ var highCompressSem = semaphore.NewWeighted(maxParallelHighCompressCalls) func (k *Keeper) VerifySignature(ctx sdk.Context, dataB64 string, signature string, signerAddress string) error { var pubKey cryptotypes.PubKey + // 1. Try to get pubkey from context cache (creatorAccountCtxKey) if info, ok := ctx.Value(creatorAccountCtxKey).(*creatorAccountInfo); ok { if info.isICA { if len(info.appPubkey) == 0 { @@ -68,7 +69,7 @@ func (k *Keeper) VerifySignature(ctx sdk.Context, dataB64 string, signature stri } } - // 1. Get account PubKey (fallback if not provided via context) + // 2. Get account PubKey (fallback if not provided via context) if pubKey == nil { accAddr, err := k.addressCodec.StringToBytes(signerAddress) if err != nil { @@ -87,23 +88,24 @@ func (k *Keeper) VerifySignature(ctx sdk.Context, dataB64 string, signature stri "account has no public key: %s", signerAddress) } - // 2. Decode the base64 signature + // 3. Decode the base64 signature sigRaw, err := base64.StdEncoding.DecodeString(signature) if err != nil { return errorsmod.Wrapf(actiontypes.ErrInvalidSignature, "failed to decode signature: %s", err) } + // 4. Coerce to r||s format sigRS, err := CoerceToRS64(sigRaw) if err != nil { return errorsmod.Wrapf(actiontypes.ErrInvalidSignature, "sig format: %s", err) } - // 3. Verify the signature + // 5. Verify the signature if pubKey.VerifySignature([]byte(dataB64), sigRS) { return nil } - // 4) ADR-36 (Keplr/browser) + // 6. ADR-36 (Keplr/browser) signBytes, err := MakeADR36AminoSignBytes(signerAddress, dataB64) if err == nil && pubKey.VerifySignature(signBytes, sigRS) { return nil diff --git a/x/action/v1/keeper/crypto_cache_test.go b/x/action/v1/keeper/crypto_cache_test.go index 0594feb..a212190 100644 --- a/x/action/v1/keeper/crypto_cache_test.go +++ b/x/action/v1/keeper/crypto_cache_test.go @@ -1,15 +1,15 @@ package keeper import ( - "encoding/base64" "context" + "encoding/base64" "testing" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/codec/address" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -63,3 +63,23 @@ func TestVerifySignatureUsesCachedAccountPubKey(t *testing.T) { require.NoError(t, k.VerifySignature(ctx, data, sigB64, signer)) } + +func TestVerifySignatureCachedICARequiresAppPubkey(t *testing.T) { + priv := secp256k1.GenPrivKey() + signerBz := sdk.AccAddress(priv.PubKey().Address()) + + addrCodec := address.NewBech32Codec("lumera") + signer, err := addrCodec.BytesToString(signerBz) + require.NoError(t, err) + + k := Keeper{addressCodec: addrCodec} + + ctx := sdk.Context{}.WithContext(context.Background()) + ctx = ctx.WithValue(creatorAccountCtxKey, &creatorAccountInfo{ + isICA: true, + }) + + err = k.VerifySignature(ctx, "payload", "invalid", signer) + require.Error(t, err) + require.ErrorContains(t, err, "app pubkey required") +} diff --git a/x/action/v1/keeper/crypto_test.go b/x/action/v1/keeper/crypto_test.go index f84e870..f626396 100644 --- a/x/action/v1/keeper/crypto_test.go +++ b/x/action/v1/keeper/crypto_test.go @@ -2,9 +2,11 @@ package keeper_test import ( "bytes" + "encoding/asn1" "encoding/base64" "encoding/json" "fmt" + "math/big" "math/rand" "strconv" "testing" @@ -12,12 +14,12 @@ import ( "github.com/cosmos/btcutil/base58" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - "lukechampine.com/blake3" - "github.com/stretchr/testify/require" "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "lukechampine.com/blake3" - keepertest "github.com/LumeraProtocol/lumera/testutil/keeper" "github.com/LumeraProtocol/lumera/testutil/cryptotestutils" + keepertest "github.com/LumeraProtocol/lumera/testutil/keeper" "github.com/LumeraProtocol/lumera/x/action/v1/keeper" ) @@ -83,6 +85,53 @@ func TestVerifySignature(t *testing.T) { } } +func TestVerifySignatureADR36Fallback(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + key, address := cryptotestutils.KeyAndAddress() + pubKey := key.PubKey() + pairs := []keepertest.AccountPair{{Address: address, PubKey: pubKey}} + k, ctx := keepertest.ActionKeeperWithAddress(t, ctrl, pairs) + + data := base64.StdEncoding.EncodeToString([]byte("payload")) + signBytes, err := keeper.MakeADR36AminoSignBytes(address.String(), data) + require.NoError(t, err) + + signature, err := key.Sign(signBytes) + require.NoError(t, err) + + sigB64 := base64.StdEncoding.EncodeToString(signature) + require.NoError(t, k.VerifySignature(ctx, data, sigB64, address.String())) +} + +func TestVerifySignatureAcceptsDERSignature(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + key, address := cryptotestutils.KeyAndAddress() + pubKey := key.PubKey() + pairs := []keepertest.AccountPair{{Address: address, PubKey: pubKey}} + k, ctx := keepertest.ActionKeeperWithAddress(t, ctrl, pairs) + + data := "test_data" + sigRS, err := key.Sign([]byte(data)) + require.NoError(t, err) + + es := struct { + R *big.Int + S *big.Int + }{ + R: new(big.Int).SetBytes(sigRS[:32]), + S: new(big.Int).SetBytes(sigRS[32:]), + } + der, err := asn1.Marshal(es) + require.NoError(t, err) + + sigB64 := base64.StdEncoding.EncodeToString(der) + require.NoError(t, k.VerifySignature(ctx, data, sigB64, address.String())) +} + func TestVerifyKademliaID(t *testing.T) { key, _ := cryptotestutils.KeyAndAddress() diff --git a/x/action/v1/keeper/msg_server_request_action.go b/x/action/v1/keeper/msg_server_request_action.go index b4b792c..2c7e1ca 100644 --- a/x/action/v1/keeper/msg_server_request_action.go +++ b/x/action/v1/keeper/msg_server_request_action.go @@ -92,6 +92,7 @@ func (k msgServer) RequestAction(goCtx context.Context, msg *types.MsgRequestAct ExpirationTime: expTime, State: types.ActionStatePending, FileSizeKbs: fileSizeKbs, + AppPubkey: msg.AppPubkey, } // Save the action (this generates the action ID) diff --git a/x/action/v1/keeper/msg_server_test.go b/x/action/v1/keeper/msg_server_test.go index ad7b85a..90153d9 100644 --- a/x/action/v1/keeper/msg_server_test.go +++ b/x/action/v1/keeper/msg_server_test.go @@ -10,7 +10,9 @@ import ( "github.com/LumeraProtocol/lumera/x/action/v1/types" actiontypes "github.com/LumeraProtocol/lumera/x/action/v1/types" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/gogoproto/jsonpb" + icatypes "github.com/cosmos/ibc-go/v10/modules/apps/27-interchain-accounts/types" ) type MsgServerTestSuite struct { @@ -108,6 +110,101 @@ func (suite *MsgServerTestSuite) TestMsgRequestAction() { suite.registerCascadeAction() } +func (suite *MsgServerTestSuite) TestMsgRequestActionStoresAppPubkey() { + base := authtypes.NewBaseAccountWithAddress(suite.creatorAddress) + ica := icatypes.NewInterchainAccount(base, "owner") + suite.keeper.GetAuthKeeper().SetAccount(suite.ctx, ica) + + cascadeMetadata := types.CascadeMetadata{ + DataHash: "test_hash", + FileName: "test_file", + RqIdsIc: 20, + Signatures: suite.signatureCascade, + } + + var cascadeMetadataBytes bytes.Buffer + marshaler := &jsonpb.Marshaler{} + err := marshaler.Marshal(&cascadeMetadataBytes, &cascadeMetadata) + suite.NoError(err) + + appPubkey := suite.accountPairs[3].PubKey.Bytes() + msg := types.MsgRequestAction{ + Creator: suite.creatorAddress.String(), + ActionType: "CASCADE", + Price: "100000ulume", + Metadata: cascadeMetadataBytes.String(), + FileSizeKbs: "123", + AppPubkey: appPubkey, + } + + res, err := suite.msgServer.RequestAction(suite.ctx, &msg) + suite.Require().NoError(err) + suite.Require().NotNil(res) + + action, found := suite.keeper.GetActionByID(suite.ctx, res.ActionId) + suite.True(found, "action not found") + suite.Equal(appPubkey, action.AppPubkey) +} + +func (suite *MsgServerTestSuite) TestMsgRequestActionRejectsAppPubkeyForNonICA() { + cascadeMetadata := types.CascadeMetadata{ + DataHash: "test_hash", + FileName: "test_file", + RqIdsIc: 20, + Signatures: suite.signatureCascade, + } + + var cascadeMetadataBytes bytes.Buffer + marshaler := &jsonpb.Marshaler{} + err := marshaler.Marshal(&cascadeMetadataBytes, &cascadeMetadata) + suite.NoError(err) + + msg := types.MsgRequestAction{ + Creator: suite.creatorAddress.String(), + ActionType: "CASCADE", + Price: "100000ulume", + Metadata: cascadeMetadataBytes.String(), + FileSizeKbs: "123", + AppPubkey: []byte{1, 2, 3}, + } + + res, err := suite.msgServer.RequestAction(suite.ctx, &msg) + suite.Error(err) + suite.Nil(res) + suite.ErrorIs(err, types.ErrInvalidAppPubKey) +} + +func (suite *MsgServerTestSuite) TestMsgRequestActionICARequiresAppPubkey() { + base := authtypes.NewBaseAccountWithAddress(suite.creatorAddress) + ica := icatypes.NewInterchainAccount(base, "owner") + suite.keeper.GetAuthKeeper().SetAccount(suite.ctx, ica) + + cascadeMetadata := types.CascadeMetadata{ + DataHash: "test_hash", + FileName: "test_file", + RqIdsIc: 20, + Signatures: suite.signatureCascade, + } + + var cascadeMetadataBytes bytes.Buffer + marshaler := &jsonpb.Marshaler{} + err := marshaler.Marshal(&cascadeMetadataBytes, &cascadeMetadata) + suite.NoError(err) + + msg := types.MsgRequestAction{ + Creator: suite.creatorAddress.String(), + ActionType: "CASCADE", + Price: "100000ulume", + Metadata: cascadeMetadataBytes.String(), + FileSizeKbs: "123", + } + + res, err := suite.msgServer.RequestAction(suite.ctx, &msg) + suite.Error(err) + suite.Nil(res) + suite.ErrorIs(err, types.ErrInvalidAppPubKey) +} + func (suite *MsgServerTestSuite) registerCascadeAction() string { cascadeMetadata := types.CascadeMetadata{ DataHash: "test_hash", @@ -124,10 +221,10 @@ func (suite *MsgServerTestSuite) registerCascadeAction() string { // Create a RequestAction message msg := types.MsgRequestAction{ - Creator: suite.creatorAddress.String(), - ActionType: "CASCADE", - Price: "100000ulume", - Metadata: cascadeMetadataBytes.String(), + Creator: suite.creatorAddress.String(), + ActionType: "CASCADE", + Price: "100000ulume", + Metadata: cascadeMetadataBytes.String(), FileSizeKbs: "123", } @@ -170,10 +267,10 @@ func (suite *MsgServerTestSuite) registerSenseAction() string { // Create a RequestAction message msg := types.MsgRequestAction{ - Creator: suite.creatorAddress.String(), - ActionType: "SENSE", - Price: "100000ulume", - Metadata: senseMetadataBytes.String(), + Creator: suite.creatorAddress.String(), + ActionType: "SENSE", + Price: "100000ulume", + Metadata: senseMetadataBytes.String(), FileSizeKbs: "456", } diff --git a/x/action/v1/keeper/query_action_by_metadata.go b/x/action/v1/keeper/query_action_by_metadata.go index c6fbd54..2b6285f 100644 --- a/x/action/v1/keeper/query_action_by_metadata.go +++ b/x/action/v1/keeper/query_action_by_metadata.go @@ -57,6 +57,7 @@ func (q queryServer) QueryActionByMetadata(goCtx context.Context, req *types.Que BlockHeight: act.BlockHeight, SuperNodes: act.SuperNodes, FileSizeKbs: act.FileSizeKbs, + AppPubkey: act.AppPubkey, }) } diff --git a/x/action/v1/keeper/query_action_by_metadata_test.go b/x/action/v1/keeper/query_action_by_metadata_test.go index 78b8e8e..ab11ea1 100644 --- a/x/action/v1/keeper/query_action_by_metadata_test.go +++ b/x/action/v1/keeper/query_action_by_metadata_test.go @@ -1,19 +1,19 @@ package keeper_test import ( - "testing" + "testing" - keepertest "github.com/LumeraProtocol/lumera/testutil/keeper" - "github.com/LumeraProtocol/lumera/x/action/v1/keeper" - "github.com/LumeraProtocol/lumera/x/action/v1/types" + keepertest "github.com/LumeraProtocol/lumera/testutil/keeper" + "github.com/LumeraProtocol/lumera/x/action/v1/keeper" + "github.com/LumeraProtocol/lumera/x/action/v1/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/query" - gogoproto "github.com/cosmos/gogoproto/proto" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + gogoproto "github.com/cosmos/gogoproto/proto" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) func TestQueryActionByMetadata(t *testing.T) { @@ -64,6 +64,7 @@ func TestQueryActionByMetadata(t *testing.T) { State: types.ActionStateProcessing, BlockHeight: 100, SuperNodes: []string{"supernode-1", "supernode-2"}, + AppPubkey: []byte{1, 2, 3}, } action2 := types.Action{ Creator: "creator2", @@ -141,6 +142,7 @@ func TestQueryActionByMetadata(t *testing.T) { require.NotNil(t, resp) require.Len(t, resp.Actions, 2) require.Equal(t, actionID1, resp.Actions[0].ActionID) + require.Equal(t, action1.AppPubkey, resp.Actions[0].AppPubkey) }, }, { diff --git a/x/action/v1/keeper/query_get_action.go b/x/action/v1/keeper/query_get_action.go index b8772ce..9af197c 100644 --- a/x/action/v1/keeper/query_get_action.go +++ b/x/action/v1/keeper/query_get_action.go @@ -34,5 +34,6 @@ func (q queryServer) GetAction(goCtx context.Context, req *types.QueryGetActionR BlockHeight: action.BlockHeight, SuperNodes: action.SuperNodes, FileSizeKbs: action.FileSizeKbs, + AppPubkey: action.AppPubkey, }}, nil } diff --git a/x/action/v1/keeper/query_get_action_test.go b/x/action/v1/keeper/query_get_action_test.go index ec63ba2..be6b330 100644 --- a/x/action/v1/keeper/query_get_action_test.go +++ b/x/action/v1/keeper/query_get_action_test.go @@ -31,6 +31,7 @@ func TestKeeper_GetAction(t *testing.T) { BlockHeight: 1, SuperNodes: []string{"node1", "node2"}, FileSizeKbs: 123, + AppPubkey: []byte{1, 2, 3}, } testCases := []struct { @@ -68,6 +69,7 @@ func TestKeeper_GetAction(t *testing.T) { require.Equal(t, action.Creator, resp.Action.Creator) require.Equal(t, action.Price, resp.Action.Price) require.Equal(t, action.FileSizeKbs, resp.Action.FileSizeKbs) + require.Equal(t, action.AppPubkey, resp.Action.AppPubkey) }, }, } diff --git a/x/action/v1/types/action.pb.go b/x/action/v1/types/action.pb.go index 803feef..646d2f0 100644 --- a/x/action/v1/types/action.pb.go +++ b/x/action/v1/types/action.pb.go @@ -36,6 +36,7 @@ type Action struct { BlockHeight int64 `protobuf:"varint,8,opt,name=blockHeight,proto3" json:"blockHeight,omitempty"` SuperNodes []string `protobuf:"bytes,9,rep,name=superNodes,proto3" json:"superNodes,omitempty"` FileSizeKbs int64 `protobuf:"varint,10,opt,name=fileSizeKbs,proto3" json:"fileSizeKbs,omitempty"` + AppPubkey []byte `protobuf:"bytes,11,opt,name=app_pubkey,json=appPubkey,proto3" json:"app_pubkey,omitempty"` } func (m *Action) Reset() { *m = Action{} } @@ -141,6 +142,13 @@ func (m *Action) GetFileSizeKbs() int64 { return 0 } +func (m *Action) GetAppPubkey() []byte { + if m != nil { + return m.AppPubkey + } + return nil +} + func init() { proto.RegisterType((*Action)(nil), "lumera.action.v1.Action") } @@ -148,32 +156,33 @@ func init() { func init() { proto.RegisterFile("lumera/action/v1/action.proto", fileDescriptor_0e3f0f18df833b98) } var fileDescriptor_0e3f0f18df833b98 = []byte{ - // 392 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xcf, 0x8e, 0xda, 0x30, - 0x10, 0xc6, 0x71, 0x53, 0xfe, 0xb9, 0x15, 0x6a, 0x2d, 0x0e, 0x2e, 0x2a, 0x51, 0x4a, 0xa5, 0x2a, - 0x52, 0x45, 0x10, 0x70, 0xed, 0x25, 0xa8, 0x87, 0x56, 0x95, 0x7a, 0x08, 0x55, 0x0f, 0xbd, 0x20, - 0x93, 0xb8, 0x59, 0x6b, 0x13, 0x1c, 0xd9, 0x06, 0xc1, 0x3e, 0xc5, 0x3e, 0xc8, 0x1e, 0x79, 0x88, - 0x3d, 0x22, 0x4e, 0x7b, 0x5c, 0xc1, 0x8b, 0xac, 0x62, 0x67, 0x51, 0x16, 0x89, 0xdb, 0xcc, 0x37, - 0x3f, 0xcf, 0x37, 0x9e, 0x81, 0xdd, 0x64, 0x99, 0x52, 0x41, 0x06, 0x24, 0x54, 0x8c, 0x2f, 0x06, - 0xab, 0x61, 0x11, 0x79, 0x99, 0xe0, 0x8a, 0xa3, 0x77, 0xa6, 0xec, 0x15, 0xe2, 0x6a, 0xd8, 0x69, - 0xc7, 0x3c, 0xe6, 0xba, 0x38, 0xc8, 0x23, 0xc3, 0x75, 0x3e, 0x84, 0x5c, 0xa6, 0x5c, 0xce, 0x4c, - 0xc1, 0x24, 0x45, 0xe9, 0xf3, 0x05, 0x87, 0x99, 0x54, 0x44, 0xd1, 0x02, 0xea, 0x5d, 0x82, 0xd4, - 0x26, 0x2b, 0x98, 0xde, 0x9d, 0x05, 0x6b, 0xbe, 0x56, 0xd1, 0x08, 0xd6, 0x43, 0x41, 0x89, 0xe2, - 0x02, 0x03, 0x07, 0xb8, 0xcd, 0x09, 0xde, 0x6f, 0xfb, 0xed, 0xc2, 0xd6, 0x8f, 0x22, 0x41, 0xa5, - 0x9c, 0x2a, 0xc1, 0x16, 0x71, 0xf0, 0x0c, 0xa2, 0x0e, 0x6c, 0x98, 0x9e, 0x3f, 0xbf, 0xe3, 0x57, - 0xf9, 0xa3, 0xe0, 0x94, 0xa3, 0x6f, 0x10, 0x9a, 0xf8, 0xcf, 0x26, 0xa3, 0xd8, 0x72, 0x80, 0xdb, - 0x1a, 0x7d, 0xf4, 0xce, 0xff, 0xee, 0xf9, 0x27, 0x26, 0x28, 0xf1, 0x79, 0xe7, 0x94, 0x2a, 0x12, - 0x11, 0x45, 0xf0, 0x6b, 0x07, 0xb8, 0x6f, 0x83, 0x53, 0x8e, 0xda, 0xb0, 0x9a, 0x09, 0x16, 0x52, - 0x5c, 0xd5, 0x96, 0x26, 0x41, 0x5f, 0x60, 0x8b, 0xae, 0x33, 0x26, 0x88, 0xee, 0xc1, 0x52, 0x8a, - 0x6b, 0x0e, 0x70, 0xad, 0xe0, 0x4c, 0x45, 0x63, 0x58, 0xd5, 0x5b, 0xc2, 0x75, 0x3d, 0x52, 0xf7, - 0xd2, 0x48, 0xd3, 0x1c, 0x0a, 0x0c, 0x8b, 0x1c, 0xf8, 0x66, 0x9e, 0xf0, 0xf0, 0xfa, 0x07, 0x65, - 0xf1, 0x95, 0xc2, 0x0d, 0xdd, 0xb9, 0x2c, 0x21, 0x1f, 0x42, 0xb9, 0xcc, 0xa8, 0xf8, 0xcd, 0x23, - 0x2a, 0x71, 0xd3, 0xb1, 0xdc, 0xe6, 0xe4, 0xd3, 0x7e, 0xdb, 0xef, 0x16, 0x1b, 0xfc, 0x4b, 0x12, - 0x16, 0xe5, 0x4b, 0x7b, 0xb9, 0xca, 0xd2, 0xa3, 0xdc, 0xe4, 0x3f, 0x4b, 0xe8, 0x94, 0xdd, 0xd0, - 0x5f, 0x73, 0x89, 0xa1, 0x31, 0x29, 0x49, 0x93, 0xaf, 0xf7, 0x07, 0x1b, 0xec, 0x0e, 0x36, 0x78, - 0x3c, 0xd8, 0xe0, 0xf6, 0x68, 0x57, 0x76, 0x47, 0xbb, 0xf2, 0x70, 0xb4, 0x2b, 0xff, 0xde, 0xaf, - 0x4b, 0x77, 0xce, 0x0f, 0x2c, 0xe7, 0x35, 0x7d, 0xe2, 0xf1, 0x53, 0x00, 0x00, 0x00, 0xff, 0xff, - 0x5d, 0xbb, 0xa0, 0xe2, 0x8f, 0x02, 0x00, 0x00, + // 411 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x8a, 0xd3, 0x40, + 0x18, 0xc7, 0x3b, 0xc6, 0x76, 0x37, 0xb3, 0xb2, 0xe8, 0xd0, 0xc3, 0x58, 0x4c, 0x88, 0x2b, 0x48, + 0x40, 0x36, 0x65, 0xb7, 0x57, 0x2f, 0x29, 0x1e, 0x14, 0x41, 0x24, 0x15, 0x0f, 0x5e, 0xca, 0x24, + 0x19, 0xe3, 0xd0, 0xa4, 0x33, 0xcc, 0x4c, 0x4b, 0xeb, 0x53, 0xf8, 0x30, 0x7d, 0x08, 0xc1, 0x4b, + 0xe9, 0xc9, 0xa3, 0xb4, 0x2f, 0x22, 0x99, 0x89, 0x25, 0x16, 0x7a, 0xfb, 0xbe, 0xff, 0xf7, 0x9b, + 0xef, 0x3f, 0xf9, 0x67, 0xa0, 0x57, 0x2e, 0x2a, 0x2a, 0xc9, 0x90, 0x64, 0x9a, 0xf1, 0xf9, 0x70, + 0x79, 0xd7, 0x54, 0x91, 0x90, 0x5c, 0x73, 0xf4, 0xd8, 0x8e, 0xa3, 0x46, 0x5c, 0xde, 0x0d, 0xfa, + 0x05, 0x2f, 0xb8, 0x19, 0x0e, 0xeb, 0xca, 0x72, 0x83, 0xa7, 0x19, 0x57, 0x15, 0x57, 0x53, 0x3b, + 0xb0, 0x4d, 0x33, 0x7a, 0x71, 0xc6, 0x61, 0xaa, 0x34, 0xd1, 0xb4, 0x81, 0x6e, 0xce, 0x41, 0x7a, + 0x2d, 0x1a, 0xe6, 0xe6, 0x97, 0x03, 0x7b, 0xb1, 0x51, 0xd1, 0x3d, 0xbc, 0xc8, 0x24, 0x25, 0x9a, + 0x4b, 0x0c, 0x02, 0x10, 0xba, 0x63, 0xbc, 0xdb, 0xdc, 0xf6, 0x1b, 0xdb, 0x38, 0xcf, 0x25, 0x55, + 0x6a, 0xa2, 0x25, 0x9b, 0x17, 0xc9, 0x3f, 0x10, 0x0d, 0xe0, 0xa5, 0xdd, 0xf9, 0xee, 0x0d, 0x7e, + 0x50, 0x1f, 0x4a, 0x8e, 0x3d, 0x7a, 0x0d, 0xa1, 0xad, 0x3f, 0xad, 0x05, 0xc5, 0x4e, 0x00, 0xc2, + 0xeb, 0xfb, 0x67, 0xd1, 0xe9, 0xb7, 0x47, 0xf1, 0x91, 0x49, 0x5a, 0x7c, 0xbd, 0xb9, 0xa2, 0x9a, + 0xe4, 0x44, 0x13, 0xfc, 0x30, 0x00, 0xe1, 0xa3, 0xe4, 0xd8, 0xa3, 0x3e, 0xec, 0x0a, 0xc9, 0x32, + 0x8a, 0xbb, 0xc6, 0xd2, 0x36, 0xe8, 0x25, 0xbc, 0xa6, 0x2b, 0xc1, 0x24, 0x31, 0x3b, 0x58, 0x45, + 0x71, 0x2f, 0x00, 0xa1, 0x93, 0x9c, 0xa8, 0x68, 0x04, 0xbb, 0x26, 0x25, 0x7c, 0x61, 0xae, 0xe4, + 0x9d, 0xbb, 0xd2, 0xa4, 0x86, 0x12, 0xcb, 0xa2, 0x00, 0x5e, 0xa5, 0x25, 0xcf, 0x66, 0x6f, 0x29, + 0x2b, 0xbe, 0x69, 0x7c, 0x69, 0x36, 0xb7, 0x25, 0x14, 0x43, 0xa8, 0x16, 0x82, 0xca, 0x0f, 0x3c, + 0xa7, 0x0a, 0xbb, 0x81, 0x13, 0xba, 0xe3, 0xe7, 0xbb, 0xcd, 0xad, 0xd7, 0x24, 0xf8, 0x99, 0x94, + 0x2c, 0xaf, 0x43, 0xfb, 0x3f, 0xca, 0xd6, 0xa1, 0xda, 0xe4, 0x2b, 0x2b, 0xe9, 0x84, 0x7d, 0xa7, + 0xef, 0x53, 0x85, 0xa1, 0x35, 0x69, 0x49, 0xc8, 0x83, 0x90, 0x08, 0x31, 0x15, 0x8b, 0x74, 0x46, + 0xd7, 0xf8, 0xca, 0xe4, 0xe2, 0x12, 0x21, 0x3e, 0x1a, 0x61, 0xfc, 0xea, 0xe7, 0xde, 0x07, 0xdb, + 0xbd, 0x0f, 0xfe, 0xec, 0x7d, 0xf0, 0xe3, 0xe0, 0x77, 0xb6, 0x07, 0xbf, 0xf3, 0xfb, 0xe0, 0x77, + 0xbe, 0x3c, 0x59, 0xb5, 0x9e, 0x41, 0xfd, 0xff, 0x55, 0xda, 0x33, 0x2f, 0x60, 0xf4, 0x37, 0x00, + 0x00, 0xff, 0xff, 0x40, 0x60, 0x07, 0xc4, 0xae, 0x02, 0x00, 0x00, } func (m *Action) Marshal() (dAtA []byte, err error) { @@ -196,6 +205,13 @@ func (m *Action) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.AppPubkey) > 0 { + i -= len(m.AppPubkey) + copy(dAtA[i:], m.AppPubkey) + i = encodeVarintAction(dAtA, i, uint64(len(m.AppPubkey))) + i-- + dAtA[i] = 0x5a + } if m.FileSizeKbs != 0 { i = encodeVarintAction(dAtA, i, uint64(m.FileSizeKbs)) i-- @@ -315,6 +331,10 @@ func (m *Action) Size() (n int) { if m.FileSizeKbs != 0 { n += 1 + sovAction(uint64(m.FileSizeKbs)) } + l = len(m.AppPubkey) + if l > 0 { + n += 1 + l + sovAction(uint64(l)) + } return n } @@ -610,6 +630,40 @@ func (m *Action) Unmarshal(dAtA []byte) error { break } } + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppPubkey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAction + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAction + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAction + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AppPubkey = append(m.AppPubkey[:0], dAtA[iNdEx:postIndex]...) + if m.AppPubkey == nil { + m.AppPubkey = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipAction(dAtA[iNdEx:]) diff --git a/x/supernode/v1/keeper/supernode.go b/x/supernode/v1/keeper/supernode.go index a79bf3a..ae922a5 100644 --- a/x/supernode/v1/keeper/supernode.go +++ b/x/supernode/v1/keeper/supernode.go @@ -36,6 +36,21 @@ func (k Keeper) SetSuperNode(ctx sdk.Context, supernode types.SuperNode) error { // Load any existing record so we can maintain secondary indices safely. existing, exists := k.QuerySuperNode(ctx, valOperAddr) + // Maintain secondary index by supernode account (if set). + accountIndexStore := prefix.NewStore(storeAdapter, types.SuperNodeByAccountKey) + + if supernode.SupernodeAccount != "" { + // Avoid partial writes if the account is already linked to another validator. + existingVal := accountIndexStore.Get([]byte(supernode.SupernodeAccount)) + if existingVal != nil && !bytes.Equal(existingVal, valOperAddr) { + return errorsmod.Wrapf( + sdkerrors.ErrInvalidRequest, + "supernode account %s already associated with another validator", + supernode.SupernodeAccount, + ) + } + } + // Create a prefix store so that all keys are under SuperNodeKey store := prefix.NewStore(storeAdapter, []byte(types.SuperNodeKey)) @@ -49,9 +64,6 @@ func (k Keeper) SetSuperNode(ctx sdk.Context, supernode types.SuperNode) error { // Note: prefix.NewStore automatically prepends the prefix we defined above. store.Set(valOperAddr, b) - // Maintain secondary index by supernode account (if set). - accountIndexStore := prefix.NewStore(storeAdapter, types.SuperNodeByAccountKey) - // Remove old index entry if the account has changed. if exists && existing.SupernodeAccount != "" && existing.SupernodeAccount != supernode.SupernodeAccount { accountIndexStore.Delete([]byte(existing.SupernodeAccount)) @@ -59,15 +71,6 @@ func (k Keeper) SetSuperNode(ctx sdk.Context, supernode types.SuperNode) error { // Set or update the index entry for the current supernode account. if supernode.SupernodeAccount != "" { - // Enforce 1:1 mapping: a SupernodeAccount can only be associated with one validator. - existingVal := accountIndexStore.Get([]byte(supernode.SupernodeAccount)) - if existingVal != nil && !bytes.Equal(existingVal, valOperAddr) { - return errorsmod.Wrapf( - sdkerrors.ErrInvalidRequest, - "supernode account %s already associated with another validator", - supernode.SupernodeAccount, - ) - } accountIndexStore.Set([]byte(supernode.SupernodeAccount), valOperAddr) }