diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 00000000..47c9985f --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,38 @@ +# Adapted from https://github.com/marketplace/actions/backporting +# +# Usage: +# - Let's say you want to backport a pull request on a branch named `production`. +# - Then label it with `backport production`. +# - That's it! When the pull request gets merged, it will be backported to +# the `production` branch. If the pull request cannot be backported, a comment +# explaining why will automatically be posted. +# +# Note: multiple backport labels can be added. For example, if a pull request +# has the labels `backport staging` and `backport production` it will be +# backported to both branches: `staging` and `production`. +name: Backport +on: + pull_request_target: + types: + - closed + - labeled + +jobs: + backport: + name: Backport + runs-on: ubuntu-latest + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged + && ( + github.event.action == 'closed' + || ( + github.event.action == 'labeled' + && contains(github.event.label.name, 'backport') + ) + ) + steps: + - uses: tibdex/backport@v2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/changelog-reminder.yml b/.github/workflows/changelog-reminder.yml new file mode 100644 index 00000000..ebe2a553 --- /dev/null +++ b/.github/workflows/changelog-reminder.yml @@ -0,0 +1,11 @@ +name: Changelog Reminder +on: + pull_request: + types: [assigned, opened, synchronize, reopened, labeled, unlabeled] + branches: + - main + +jobs: + changelog_reminder: + uses: babylonlabs-io/.github/.github/workflows/reusable_changelog_reminder.yml@v0.7.0 + secrets: inherit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b9c7eea..5c3dff14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,9 @@ jobs: go-lint-version: 'v1.60.2' run-unit-tests: true run-lint: true + run-build: true + run-gosec: true + gosec-args: "-exclude-generated -exclude-dir=itest -exclude-dir=testutil ./..." e2e_babylon: runs-on: ubuntu-22.04 @@ -26,17 +29,6 @@ jobs: - name: Run e2e Babylon tests run: make test-e2e-babylon - e2e_wasmd: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.23' - - name: Run e2e Wasmd tests - run: make test-e2e-wasmd - e2e_bcd: runs-on: ubuntu-22.04 steps: @@ -64,3 +56,5 @@ jobs: secrets: inherit with: publish: false + dockerfile: ./Dockerfile + repoName: finality-provider diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml new file mode 100644 index 00000000..7e304dcc --- /dev/null +++ b/.github/workflows/goreleaser.yml @@ -0,0 +1,11 @@ +name: goreleaser + +on: + push: + tags: + - '*' + +jobs: + release: + uses: babylonlabs-io/.github/.github/workflows/reusable_go_releaser.yml@v0.7.0 + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2a0e4854..7b577e4d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,6 +16,9 @@ jobs: go-lint-version: 'v1.60.2' run-unit-tests: true run-lint: true + run-build: true + run-gosec: true + gosec-args: "-exclude-generated -exclude-dir=itest -exclude-dir=testutil ./..." e2e_babylon: runs-on: ubuntu-22.04 @@ -28,17 +31,6 @@ jobs: - name: Run e2e Babylon tests run: make test-e2e-babylon - e2e_wasmd: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.23' - - name: Run e2e Wasmd tests - run: make test-e2e-wasmd - e2e_bcd: runs-on: ubuntu-22.04 steps: @@ -66,7 +58,15 @@ jobs: docker_pipeline: needs: ["lint_test"] - uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.7.0 + uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.10.2 secrets: inherit with: publish: true + dockerfile: ./Dockerfile + repoName: finality-provider + docker_scan: true + permissions: + # required for all workflows + security-events: write + # required to fetch internal or private CodeQL packs + packages: read diff --git a/.gitignore b/.gitignore index 1a9807e2..53b7f8eb 100644 --- a/.gitignore +++ b/.gitignore @@ -29,5 +29,6 @@ devnet-data/ main tmp/ build/ +dist/ *.swp diff --git a/.golangci.yml b/.golangci.yml index 7303ccee..cf43578f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,86 @@ run: - build-tags: + build-tags: - e2e_op - e2e_babylon - e2e_bcd - e2e_wasmd + timeout: 5m + +linters: + disable-all: true + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + - decorder + - dogsled + - durationcheck + - errcheck + - errchkjson + - errname + - errorlint + - exhaustive + - forbidigo + - forcetypeassert + - goconst + - gocritic + - gocyclo + - goheader + - gomodguard + - goprintffuncname + - gosimple + - govet + - grouper + - importas + - ineffassign + - loggercheck + - maintidx + - makezero + - misspell + - nakedret + - nilerr + - nlreturn + - noctx + - nonamedreturns + - nosprintfhostport + - paralleltest + - reassign + - revive + - rowserrcheck + - sqlclosecheck + - staticcheck + - stylecheck + - tenv + - testableexamples + - tparallel + - typecheck + - unconvert + - unparam + - usestdlibvars + - unused + - wastedassign + - whitespace +# - wrapcheck # we really should be using this, lax for now todo(lazar): unlax at somepoint, good practice + +issues: + max-same-issues: 0 + # Default: https://golangci-lint.run/usage/false-positives/#default-exclusions + exclude-dirs: + - e2etest + - itest + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + - nonamedreturns + - unparam + - path-except: _test\.go + linters: + - forbidigo diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 00000000..36cf54e7 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,73 @@ +project_name: finality-provider + +builds: + - id: fpd-linux-amd64 + main: ./finality-provider/cmd/fpd/main.go + binary: fpd + hooks: + pre: + - wget https://github.com/CosmWasm/wasmvm/releases/download/{{ .Env.COSMWASM_VERSION }}/libwasmvm_muslc.x86_64.a -O /usr/lib/libwasmvm_muslc.x86_64.a + goos: + - linux + goarch: + - amd64 + env: + - GO111MODULE=on + flags: + - -mod=readonly + - -trimpath + tags: + - netgo + - osusergo + + - id: eotsd-linux-amd64 + main: ./eotsmanager/cmd/eotsd/main.go + binary: eotsd + hooks: + pre: + - wget https://github.com/CosmWasm/wasmvm/releases/download/{{ .Env.COSMWASM_VERSION }}/libwasmvm_muslc.x86_64.a -O /usr/lib/libwasmvm_muslc.x86_64.a + goos: + - linux + goarch: + - amd64 + env: + - GO111MODULE=on + flags: + - -mod=readonly + - -trimpath + tags: + - netgo + - osusergo + +archives: + - id: zipped + builds: + - fpd-linux-amd64 + - eotsd-linux-amd64 + name_template: "{{.ProjectName}}-{{ .Version }}-{{ .Os }}-{{ .Arch }}" + format: tar.gz + files: + - none* + - id: binaries + builds: + - fpd-linux-amd64 + - eotsd-linux-amd64 + name_template: "{{.ProjectName}}-{{ .Version }}-{{ .Os }}-{{ .Arch }}" + format: binary + files: + - none* + +checksum: + name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt" + algorithm: sha256 + +release: + github: + owner: babylonlabs-io + name: finality-provider + +# Docs: https://goreleaser.com/customization/changelog/ +changelog: + disable: true + +dist: dist diff --git a/.trivyignore b/.trivyignore new file mode 100644 index 00000000..63355fc1 --- /dev/null +++ b/.trivyignore @@ -0,0 +1,5 @@ +# LND < 0.17.0 issue, not fixing +CVE-2024-27304 +GHSA-7jwh-3vrq-q3m8 +CVE-2024-27289 +CVE-2024-38359 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..cca1eaa3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,217 @@ + + +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) + +## Unreleased + +### Improvements + +* [#314](https://github.com/babylonlabs-io/finality-provider/pull/314) nit: Dockerfile AS casing + +## v0.15.0 + +### Bug Fixes + +* [#296](https://github.com/babylonlabs-io/finality-provider/pull/296) fix: edit finality provider commission-rate +* [#307](https://github.com/babylonlabs-io/finality-provider/pull/307) fix: increment fp_total_failed_votes + +### Improvements + +* [#251](https://github.com/babylonlabs-io/finality-provider/pull/251) Add nlreturn lint +* [#252](https://github.com/babylonlabs-io/finality-provider/pull/252) Remove interceptors and use context +* [#266](https://github.com/babylonlabs-io/finality-provider/pull/266) Change default config +* [#262](https://github.com/babylonlabs-io/finality-provider/pull/262) Add new command to export pop +* [#284](https://github.com/babylonlabs-io/finality-provider/pull/284) Add new command to delete pop +* [#277](https://github.com/babylonlabs-io/finality-provider/pull/277) Poll many blocks in poller +* [#291](https://github.com/babylonlabs-io/finality-provider/pull/291) chore: remove skip height +* [#294](https://github.com/babylonlabs-io/finality-provider/pull/294) chore: Improve fpd start +* [#297](https://github.com/babylonlabs-io/finality-provider/pull/297) Add new command to validate pop +* [#302](https://github.com/babylonlabs-io/finality-provider/pull/302) Update pop commands to write to a file +* [#301](https://github.com/babylonlabs-io/finality-provider/pull/301) chore: check tx index enabled +* [#308](https://github.com/babylonlabs-io/finality-provider/issues/308) chore: bump babylon to v1.0.0-rc.4 + +## v0.14.3 + +### Improvements + +* [#253](https://github.com/babylonlabs-io/finality-provider/issues/253) Refactor to start from the last finalized height +* [#260](https://github.com/babylonlabs-io/finality-provider/pull/260) Allow running of jailed fp + +## v0.14.2 + +### Bug Fixes + +* [#244](https://github.com/babylonlabs-io/finality-provider/pull/244) fix: save key name mapping +verifies if there is a eots client running +* [#246](https://github.com/babylonlabs-io/finality-provider/pull/246) fix: start fp after register + +## v0.14.1 + +### Bug Fixes + +* [#240](https://github.com/babylonlabs-io/finality-provider/pull/240) fix removed printf in cmd command + +## v0.14.0 + +### Improvements + +* [#207](https://github.com/babylonlabs-io/finality-provider/pull/207) create finality provider from JSON file +* [#208](https://github.com/babylonlabs-io/finality-provider/pull/208) Remove sync fp status loop +* [#211](https://github.com/babylonlabs-io/finality-provider/pull/211) Clean up unused cmd +* [#214](https://github.com/babylonlabs-io/finality-provider/pull/214) Gradual benchmark +* [#216](https://github.com/babylonlabs-io/finality-provider/pull/216) Add multiple fpd connecting to one eotsd in e2e tests +* [#218](https://github.com/babylonlabs-io/finality-provider/pull/218) Prune used merkle proof +* [#221](https://github.com/babylonlabs-io/finality-provider/pull/221) Cleanup TODOs +* [#228](https://github.com/babylonlabs-io/finality-provider/pull/228) Save key name mapping in eotsd import commands +* [#227](https://github.com/babylonlabs-io/finality-provider/pull/227) Fix FP submission loop +* [#226](https://github.com/babylonlabs-io/finality-provider/pull/226) Update local fp before register +* [#233](https://github.com/babylonlabs-io/finality-provider/pull/233) Refactor CommitPubRand +* [#234](https://github.com/babylonlabs-io/finality-provider/pull/234) eotsd ls command +* [#238](https://github.com/babylonlabs-io/finality-provider/pull/238) bump babylon v1.0.0-rc.1 + +## v0.13.1 + +### Bug Fixes + +* [#199](https://github.com/babylonlabs-io/finality-provider/pull/199) EOTS signing for multiple finality providers +* [#203](https://github.com/babylonlabs-io/finality-provider/pull/203) fpd cli: Withdraw rewards and set withdraw addr + +## v0.13.0 + +### Improvements + +* [#175](https://github.com/babylonlabs-io/finality-provider/pull/175) adds: `eotsd version` command +* [#179](https://github.com/babylonlabs-io/finality-provider/pull/179) Change `btc_pk` text to `eots_pk` in CLI +* [#182](https://github.com/babylonlabs-io/finality-provider/pull/182) Remove fp manager +* [#184](https://github.com/babylonlabs-io/finality-provider/pull/184) eots manager sign record store +* [#189](https://github.com/babylonlabs-io/finality-provider/pull/189) Remove `fpd register-finality-provider` cmd +* [#190](https://github.com/babylonlabs-io/finality-provider/pull/190) Benchmark pub rand +* [#193](https://github.com/babylonlabs-io/finality-provider/pull/193) adds unsafeSignEOTS for e2e tests +* [#195](https://github.com/babylonlabs-io/finality-provider/pull/195) Not block unjailing +* [#197](https://github.com/babylonlabs-io/finality-provider/pull/197) Bump Babylon to v0.18.0 + +### Bug Fixes + +* [#166](https://github.com/babylonlabs-io/finality-provider/pull/166) fix: `eotsd keys add` `--output` flag + +### Improvements + +* [#149](https://github.com/babylonlabs-io/finality-provider/pull/149) Remove update of config after `fpd keys add` +* [#148](https://github.com/babylonlabs-io/finality-provider/pull/148) Allow command `eotsd keys add` to use +empty HD path to derive new key and use master private key. +* [#153](https://github.com/babylonlabs-io/finality-provider/pull/153) Add `unsafe-commit-pubrand` command +* [#154](https://github.com/babylonlabs-io/finality-provider/pull/154) Use sign schnorr instead of getting private key from EOTS manager +* [#167](https://github.com/babylonlabs-io/finality-provider/pull/167) Remove last processed height +* [#168](https://github.com/babylonlabs-io/finality-provider/pull/168) Remove key creation in `create-finality-provider` +* [#176](https://github.com/babylonlabs-io/finality-provider/pull/176) Refactor +determining start height based on [ADR-35](https://github.com/babylonlabs-io/pm/blob/main/adr/adr-035-slashing-protection.md) + +### v0.12.1 + +### Bug Fixes + +* [#158](https://github.com/babylonlabs-io/finality-provider/pull/158) Remove start height validation + +## v0.12.0 + +### Bug Fixes + +* [#139](https://github.com/babylonlabs-io/finality-provider/pull/139) Ignore voting power not updated error + +### Improvements + +* [#127](https://github.com/babylonlabs-io/finality-provider/pull/127) Bump docker workflow version and fix some dockerfile issue +* [#132](https://github.com/babylonlabs-io/finality-provider/pull/132) Replace fast sync with batch processing +* [#146](https://github.com/babylonlabs-io/finality-provider/pull/146) Upgrade Babylon to v0.17.1 + +### Documentation + +[#120](https://github.com/babylonlabs-io/finality-provider/pull/120) Spec of +finality vote submission + +## v0.11.0 + +### Improvements + +* [#126](https://github.com/babylonlabs-io/finality-provider/pull/126) Adds linting config +* [#128](https://github.com/babylonlabs-io/finality-provider/pull/128) Upgrade Babylon to v0.16.0 + +### Documentation + +* [#117](https://github.com/babylonlabs-io/finality-provider/pull/117) Spec of +commit public randomness +* [#130](https://github.com/babylonlabs-io/finality-provider/pull/130) Finality +Provider operation documentation + +### Bug Fixes + +* [#124](https://github.com/babylonlabs-io/finality-provider/pull/124) Ignore +duplicated finality vote error + +## v0.10.0 + +### Improvements + +* [#114](https://github.com/babylonlabs-io/finality-provider/pull/114) Bump Babylon version to v0.15.0 +* [#102](https://github.com/babylonlabs-io/finality-provider/pull/102) Improve `eotsd keys add` command +* [#104](https://github.com/babylonlabs-io/finality-provider/pull/104) Print fpd binary version +* [#87](https://github.com/babylonlabs-io/finality-provider/pull/87) Rename ChainName to ChainType + +## v0.9.1 + +### Bug Fixes + +* [#107](https://github.com/babylonlabs-io/finality-provider/pull/107) Fix commit +start height when the finality activation height is higher than the current +block tip + +## v0.9.0 + +### Improvements + +* [#101](https://github.com/babylonlabs-io/finality-provider/pull/101) Add finality activation +height check in finality voting and commit pub rand start height and bump Babylon version to +v0.14.0 + +## v0.8.0 + +### Improvements + +* [#97](https://github.com/babylonlabs-io/finality-provider/pull/97) Bump Babylon version to v0.13.0 +* [#90](https://github.com/babylonlabs-io/finality-provider/pull/90) CLI edit finality provider +* [#91](https://github.com/babylonlabs-io/finality-provider/pull/91) Go releaser setup + and move changelog reminder out +* [#86](https://github.com/babylonlabs-io/finality-provider/pull/86) Remove running multiple fp instances support diff --git a/Dockerfile b/Dockerfile index a725680b..8a7f1b3e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22.7-alpine as builder +FROM golang:1.23-alpine AS builder # Version to build. Default is the Git HEAD. ARG VERSION="HEAD" @@ -6,10 +6,10 @@ ARG VERSION="HEAD" # Use muslc for static libs ARG BUILD_TAGS="muslc" - +# hadolint ignore=DL3018 RUN apk add --no-cache --update openssh git make build-base linux-headers libc-dev \ pkgconfig zeromq-dev musl-dev alpine-sdk libsodium-dev \ - libzmq-static libsodium-static gcc + libzmq-static libsodium-static gcc && rm -rf /var/cache/apk/* # Build @@ -21,12 +21,13 @@ RUN go mod download COPY ./ /go/src/github.com/babylonlabs-io/finality-provider/ # Cosmwasm - Download correct libwasmvm version +SHELL ["/bin/ash", "-eo", "pipefail", "-c"] RUN WASMVM_VERSION=$(grep github.com/CosmWasm/wasmvm go.mod | cut -d' ' -f2) && \ - wget https://github.com/CosmWasm/wasmvm/releases/download/$WASMVM_VERSION/libwasmvm_muslc.$(uname -m).a \ - -O /lib/libwasmvm_muslc.$(uname -m).a && \ + wget -q https://github.com/CosmWasm/wasmvm/releases/download/$WASMVM_VERSION/libwasmvm_muslc."$(uname -m)".a \ + -O /lib/libwasmvm_muslc."$(uname -m)".a && \ # verify checksum - wget https://github.com/CosmWasm/wasmvm/releases/download/$WASMVM_VERSION/checksums.txt -O /tmp/checksums.txt && \ - sha256sum /lib/libwasmvm_muslc.$(uname -m).a | grep $(cat /tmp/checksums.txt | grep libwasmvm_muslc.$(uname -m) | cut -d ' ' -f 1) + wget -q https://github.com/CosmWasm/wasmvm/releases/download/$WASMVM_VERSION/checksums.txt -O /tmp/checksums.txt && \ + sha256sum /lib/libwasmvm_muslc."$(uname -m)."a | grep $(cat /tmp/checksums.txt | grep libwasmvm_muslc."$(uname -m)" | cut -d ' ' -f 1) RUN CGO_LDFLAGS="$CGO_LDFLAGS -lstdc++ -lm -lsodium" \ CGO_ENABLED=1 \ @@ -35,11 +36,12 @@ RUN CGO_LDFLAGS="$CGO_LDFLAGS -lstdc++ -lm -lsodium" \ make build # FINAL IMAGE -FROM alpine:3.16 AS run +FROM alpine:3.20 AS run RUN addgroup --gid 1138 -S finality-provider && adduser --uid 1138 -S finality-provider -G finality-provider -RUN apk add bash curl jq +# hadolint ignore=DL3018 +RUN apk add --no-cache bash curl jq && rm -rf /var/cache/apk/* COPY --from=builder /go/src/github.com/babylonlabs-io/finality-provider/build/fpd /bin/fpd COPY --from=builder /go/src/github.com/babylonlabs-io/finality-provider/build/eotsd /bin/eotsd diff --git a/Makefile b/Makefile index b8f27b41..a280d8d1 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,6 @@ BUILDDIR ?= $(CURDIR)/build TOOLS_DIR := tools -BABYLON_PKG := github.com/babylonlabs-io/babylon/cmd/babylond -WASMD_PKG := github.com/CosmWasm/wasmd/cmd/wasmd BCD_PKG := github.com/babylonlabs-io/babylon-sdk/demo/cmd/bcd GO_BIN := ${GOPATH}/bin @@ -15,7 +13,9 @@ MOCKGEN_REPO=github.com/golang/mock/mockgen MOCKGEN_VERSION=v1.6.0 MOCKGEN_CMD=go run ${MOCKGEN_REPO}@${MOCKGEN_VERSION} -ldflags := $(LDFLAGS) +VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') + +ldflags := $(LDFLAGS) -X github.com/babylonlabs-io/finality-provider/version.version=$(VERSION) build_tags := $(BUILD_TAGS) build_args := $(BUILD_ARGS) @@ -67,59 +67,48 @@ lint: .PHONY: test test: - go test ./... - -install-babylond: - cd $(TOOLS_DIR); \ - go install -trimpath $(BABYLON_PKG) - -install-wasmd: - cd $(TOOLS_DIR); \ - go install -trimpath $(WASMD_PKG) + go test -v ./... install-bcd: cd $(TOOLS_DIR); \ go install -trimpath $(BCD_PKG) -.PHONY: clean-e2e test-e2e test-e2e-babylon test-e2e-babylon-ci test-e2e-wasmd test-e2e-bcd test-e2e-op test-e2e-op-ci +.PHONY: clean-e2e test-e2e test-e2e-babylon test-e2e-babylon-ci test-e2e-bcd test-e2e-op test-e2e-op-ci # Clean up environments by stopping processes and removing data directories clean-e2e: - @pids=$$(ps aux | grep -E 'babylond start|wasmd start|bcd start' | grep -v grep | awk '{print $$2}' | tr '\n' ' '); \ + @pids=$$(ps aux | grep -E 'babylond start|bcd start' | grep -v grep | awk '{print $$2}' | tr '\n' ' '); \ if [ -n "$$pids" ]; then \ echo $$pids | xargs kill; \ echo "Killed processes $$pids"; \ else \ echo "No processes to kill"; \ fi - rm -rf ~/.babylond ~/.wasmd ~/.bcd + rm -rf ~/.babylond ~/.bcd # Main test target that runs all e2e tests -test-e2e: test-e2e-babylon test-e2e-wasmd test-e2e-bcd test-e2e-op +test-e2e: test-e2e-babylon test-e2e-bcd test-e2e-op -test-e2e-babylon: clean-e2e install-babylond +test-e2e-babylon: clean-e2e @go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -count=1 --tags=e2e_babylon -test-e2e-babylon-ci: clean-e2e install-babylond +test-e2e-babylon-ci: clean-e2e go test -list . ./itest/babylon --tags=e2e_babylon | grep Test \ | circleci tests run --command \ "xargs go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -count=1 --tags=e2e_babylon --run" \ --split-by=name --timings-type=name -test-e2e-bcd: clean-e2e install-babylond install-bcd +test-e2e-bcd: clean-e2e install-bcd @go test -race -mod=readonly -timeout=25m -v $(PACKAGES_E2E_BCD) -count=1 --tags=e2e_bcd -test-e2e-wasmd: clean-e2e install-babylond install-wasmd - @go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -count=1 --tags=e2e_wasmd - -test-e2e-op: clean-e2e install-babylond +test-e2e-op: clean-e2e @go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E_OP) -count=1 --tags=e2e_op FILTER ?= . -test-e2e-op-filter: clean-e2e install-babylond +test-e2e-op-filter: clean-e2e @go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E_OP) -count=1 --tags=e2e_op --run ^$(FILTER)$ -test-e2e-op-ci: clean-e2e install-babylond +test-e2e-op-ci: clean-e2e go test -list . ./itest/opstackl2 --tags=e2e_op | grep Test \ | circleci tests run --command \ "xargs go test -race -mod=readonly -timeout=25m -v $(PACKAGES_E2E_OP) -count=1 --tags=e2e_op --run" \ @@ -148,3 +137,57 @@ update-changelog: ./scripts/update_changelog.sh $(sinceTag) $(upcomingTag) .PHONY: update-changelog + +############################################################################### +### Release ### +############################################################################### + +# The below is adapted from https://github.com/osmosis-labs/osmosis/blob/main/Makefile +GO_VERSION := $(shell grep -E '^go [0-9]+\.[0-9]+' go.mod | awk '{print $$2}') +GORELEASER_IMAGE := ghcr.io/goreleaser/goreleaser-cross:v$(GO_VERSION) +COSMWASM_VERSION := $(shell go list -m github.com/CosmWasm/wasmvm/v2 | sed 's/.* //') + +.PHONY: release-dry-run release-snapshot release +release-dry-run: + docker run \ + --rm \ + -e COSMWASM_VERSION=$(COSMWASM_VERSION) \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v `pwd`:/go/src/babylon \ + -w /go/src/babylon \ + $(GORELEASER_IMAGE) \ + release \ + --clean \ + --skip=publish + +release-snapshot: + docker run \ + --rm \ + -e COSMWASM_VERSION=$(COSMWASM_VERSION) \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v `pwd`:/go/src/babylon \ + -w /go/src/babylon \ + $(GORELEASER_IMAGE) \ + release \ + --clean \ + --snapshot \ + --skip=publish,validate \ + +# NOTE: By default, the CI will handle the release process. +# this is for manually releasing. +ifdef GITHUB_TOKEN +release: + docker run \ + --rm \ + -e GITHUB_TOKEN=$(GITHUB_TOKEN) \ + -e COSMWASM_VERSION=$(COSMWASM_VERSION) \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v `pwd`:/go/src/babylon \ + -w /go/src/babylon \ + $(GORELEASER_IMAGE) \ + release \ + --clean +else +release: + @echo "Error: GITHUB_TOKEN is not defined. Please define it before running 'make release'." +endif diff --git a/README.md b/README.md index ffeb8d1c..3f86fc75 100644 --- a/README.md +++ b/README.md @@ -1,117 +1,140 @@ # Finality Provider -A toolset crafted for the creation and -management of Finality Providers. +Finality providers are key participants in the Babylon BTC staking protocol. +They provide finality votes on top of +[CometBFT](https://github.com/cometbft/cometbft), Babylon's consensus mechanism, +and earn commissions from BTC staking delegations. + +The finality provider toolset operates on standard UNIX-based +systems and consists of three core components: + +1. **Babylon Node**: +A Babylon network node that provides chain data and transaction +submission capabilities. While not mandatory, running your own node is +strongly recommended for security rather than relying on third-party RPC nodes. +See the [Setup Node Guide](https://github.com/babylonlabs-io/networks/blob/main/bbn-test-5/babylon-node/README.md) +for details. +2. **Extractable One-Time Signature (EOTS) Manager**: +A secure key management daemon that handles EOTS key operations, +generates extractable one-time signatures, and produces public randomness. +For enhanced security, this component should run on a separate machine or +network segment. +3. **Finality Provider Daemon**: +The core daemon that polls Babylon blocks, commits public randomness, and +submits finality signatures. It manages the finality provider's status transitions +and handles rewards distribution. + +**Component Interactions**: +The Finality Provider daemon communicates with the Babylon Node to monitor blocks +and submit transactions. It interacts with the EOTS Manager for signature and +randomness generation. The EOTS Manager maintains secure key storage and handles +all EOTS key operations. + +![Finality Provider Architecture Diagram](./docs/static/finality-provider-arch.png) + +## Become a Finality Provider + +For instructions on creating and operating a finality provider, +see our [Finality Provider Guide](./docs/finality-provider-operation.md). + +## High Level Descriptions of EOTS and Finality Provider + + +### EOTS Manager + +The EOTS daemon is responsible for managing EOTS keys, producing EOTS randomness, and +using them to produce EOTS signatures. + +> ⚡ **Note:** EOTS stands for Extractable One Time Signature. You can read more about it +in +the [Babylon BTC Staking Litepaper](https://docs.babylonchain.io/assets/files/btc_staking_litepaper-32bfea0c243773f0bfac63e148387aef.pdf). +In short, the EOTS manager generates EOTS public/private randomness pairs. The +finality provider commits the public part of these pairs to Babylon for every future +block height that they intend to provide a finality signature for. If the finality +provider votes for two different blocks on the same height, they will have to reuse +the same private randomness which will lead to their EOTS private key being +exposed, leading to the slashing. + +Once a finality provider is double-signs, their voting power is immediately reduced +to zero, while their private key is exposed. A finality provider that double-signs +can never regain voting power (tombstoning). Additionally, the exposed private key +of the finality provider can be used to fully sign the slashing transactions of all +their stake delegations. + +The EOTS manager is responsible for the following operations: + +1. **EOTS Key Management:** + - Generates [Schnorr](https://en.wikipedia.org/wiki/Schnorr_signature) key pairs + for a given finality provider using the + [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) + standard as its EOTS key pair + - Persists generated key pairs in the internal Cosmos keyring. +2. **Randomness Generation:** + - Generates lists of EOTS randomness pairs based on the EOTS key, chain ID, and + block height. + - The randomness is deterministically generated and tied to specific parameters. +3. **Signature Generation:** + - Signs EOTS using the private key of the finality provider and the corresponding + secret randomness for a given chain at a specified height. + - Signs Schnorr signatures using the private key of the finality provider. + +### Finality Provider + +The Finality Provider Daemon is responsible for monitoring for new Babylon blocks, +committing public randomness for the blocks it intends to provide finality signatures +for, and submitting finality signatures. + +The daemon can manage multiple finality providers but only run a single +finality provider instance at a time performing the following operations: + +1. **Creation and Registration**: Creates and registers a finality provider to. + +2. **EOTS Randomness Commitment**: The daemon monitors the Babylon chain and commits + EOTS public randomness for every Babylon block the finality provider intends to + vote for. The commit intervals can be specified in the configuration. + +3. **Finality Votes Submission**: The daemon monitors the Babylon chain and produces + finality votes for each block the finality provider has committed to vote for. + +4. **Status Management**: The daemon continuously monitors voting power and overall + provider status. It manages state transitions between `ACTIVE`, `INACTIVE`, + `JAILED`, and `SLASHED` states, while handling the jailing process when violations + occur. + +5. **Security and Key Management**: The daemon manages Babylon keys for signing + transactions and rewards distribution. It maintains secure coordination with + the EOTS daemon for all key-related operations. + +The daemon is controlled by the `fpd` tool, which provides commands for +interacting with the running daemon. + +## Technical Documentation + +For detailed technical information about the finality provider's internal operations, see: +* [Core Heuristics](./docs/fp-core.md) +* [Public Randomness Commits](./docs/commit-pub-rand.md) +* [Finality Votes submission](./docs/send-finality-vote.md) + +## Overview of Keys for Finality Provider and EOTS Manager + +There are two distinct keys you'll be working with: + +- **EOTS Key**: + - Used for generating EOTS signatures, Schnorr signatures, and randomness pairs + - This serves as the unique identifier for the finality provider + - It's derived from a Bitcoin private key, using the secp256k1 + elliptic curve. + - Stored in the EOTS manager daemon's keyring + - This key is used in the Bitcoin-based security model of Babylon. + +- **Babylon Key**: + - Used for signing transactions on Babylon. + - Associated with a Babylon account that receives rewards + - Stored in the finality provider daemon's keyring -## 1. Overview - -Finality providers are responsible for voting -at a finality round on top of [CometBFT](https://github.com/cometbft/cometbft). -Similar to any native PoS validator, -a finality provider can receive voting power delegations from BTC stakers, and -can earn commission from the staking rewards denominated in Babylon tokens. - -The finality provider toolset does not have -any special hardware requirements -and can operate on standard mid-sized machines -running a UNIX-flavored operating system. -It consists of the following programs: - -- *Babylon full node*: An instance of a Babylon node connecting to - the Babylon network. Running one is not a strict requirement, - but it is recommended for security compared to trusting a third-party RPC node. -- *Extractable One-Time Signature (EOTS) manager*: - A daemon responsible for securely maintaining the finality provider’s - private key and producing extractable one time signatures from it. -- *Finality Provider*: A daemon managing the finality provider. - It connects to the EOTS manager to generate EOTS public randomness and - finality votes for Babylon blocks, which it submits to Babylon through - the node connection. - -The following graphic demonstrates the interconnections between the above programs: - -![Finality Provider Interconnections](./docs/finality-toolset.png) - -## 2. Installation - -### Prerequisites - -This project requires Go version 1.21 or later. - -Install Go by following the instructions on -the [official Go installation guide](https://golang.org/doc/install). - -### Downloading the code - -To get started, clone the repository to your local machine from Github: - -```bash -git clone https://github.com/babylonlabs-io/finality-provider.git -``` - -You can choose a specific version from -the [official releases page](https://github.com/babylonlabs-io/finality-provider/releases) - -```bash -cd finality-provider # cd into the project directory -git checkout -``` - -### Building and installing the binary - -At the top-level directory of the project - -```bash -make install -``` - -The above command will build and install the following binaries to -`$GOPATH/bin`: - -- `eotsd`: The daemon program for the EOTS manager. -- `fpd`: The daemon program for the finality-provider with overall commands. - -If your shell cannot find the installed binaries, make sure `$GOPATH/bin` is in -the `$PATH` of your shell. Usually these commands will do the job - -```bash -export PATH=$HOME/go/bin:$PATH -echo 'export PATH=$HOME/go/bin:$PATH' >> ~/.profile -``` - -## 3. Setting up a finality provider - -### 3.1. Setting up a Babylon Full Node - -Before setting up the finality provider toolset, -an operator must ensure a working connection with a Babylon full node. -It is highly recommended that operators run their own node to avoid -trusting third parties. Instructions on how to set up a full Babylon node -can be found in -[the Babylon documentation](https://docs.babylonchain.io/docs/user-guides/btc-staking-testnet/setup-node). - -### 3.2. Setting up the EOTS Manager - -After a node and a keyring have been set up, -the operator can set up and run the -Extractable One Time Signature (EOTS) manager daemon. -A complete overview of the EOTS manager, its operation, and -its configuration options can be found in the -[EOTS Manager page](docs/eots.md) - -### 3.3. Setting up a Finality Provider - -The last step is to set up and run -the finality daemon. -A complete overview of the finality daemon, its operation, and -its configuration options can be found in the -[Finality page](docs/finality-provider.md). - -## 4. Delegations & Rewards - -A finality provider receives BTC delegations through delegators -interacting with Babylon and choosing it as the recipient of their delegations. -To perform a self-delegation, -the operator can either visit the staking web app we provide, -or run the Babylon [BTC Staker program](https://github.com/babylonlabs-io/btc-staker) once. -The BTC staker connects to a Bitcoin wallet and Babylon to perform delegations. +This dual association allows the finality provider to interact with both the +Bitcoin network (for security) and the Babylon network (for rewards and +governance). + +Once a finality provider is created, neither key can be rotated or changed - +they are permanently associated with that specific finality provider instance. diff --git a/clientcontroller/api/interface.go b/clientcontroller/api/interface.go index 7af55c3a..1edfce0e 100644 --- a/clientcontroller/api/interface.go +++ b/clientcontroller/api/interface.go @@ -2,13 +2,18 @@ package api import ( "cosmossdk.io/math" + btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + "github.com/babylonlabs-io/finality-provider/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" - - "github.com/babylonlabs-io/finality-provider/types" ) +//nolint:revive,unused +const babylonConsumerChainType = "babylon" + type ClientController interface { + // Start - starts the client controller + Start() error // RegisterFinalityProvider registers a finality provider to the consumer chain // it returns tx hash and error. The address of the finality provider will be // the signer of the msg. @@ -20,12 +25,13 @@ type ClientController interface { description []byte, ) (*types.TxResponse, error) + // QueryFinalityProvider queries the finality provider by pk + QueryFinalityProvider(fpPk *btcec.PublicKey) (*btcstakingtypes.QueryFinalityProviderResponse, error) + // Note: the following queries are only for PoC - // QueryFinalityProviderSlashedOrJailed queries if the finality provider is slashed or slashed - // Note: if the FP wants to get the information from the consumer chain directly, they should add this interface - // function in ConsumerController. (https://github.com/babylonchain/finality-provider/pull/335#discussion_r1606175344) - QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (slashed bool, jailed bool, err error) + // EditFinalityProvider edits description and commission of a finality provider + EditFinalityProvider(fpPk *btcec.PublicKey, commission *math.LegacyDec, description []byte) (*btcstakingtypes.MsgEditFinalityProvider, error) Close() error } @@ -41,16 +47,29 @@ type ConsumerController interface { // SubmitBatchFinalitySigs submits a batch of finality signatures to the consumer chain SubmitBatchFinalitySigs(fpPk *btcec.PublicKey, blocks []*types.BlockInfo, pubRandList []*btcec.FieldVal, proofList [][]byte, sigs []*btcec.ModNScalar) (*types.TxResponse, error) - // Note: the following queries are only for PoC + // UnjailFinalityProvider sends an unjail transaction to the consumer chain + UnjailFinalityProvider(fpPk *btcec.PublicKey) (*types.TxResponse, error) + + /* + The following methods are queries to the consumer chain + */ // QueryFinalityProviderHasPower queries whether the finality provider has voting power at a given height QueryFinalityProviderHasPower(fpPk *btcec.PublicKey, blockHeight uint64) (bool, error) + // QueryFinalityProviderSlashedOrJailed queries if the finality provider is slashed or slashed + // Note: if the FP wants to get the information from the consumer chain directly, they should add this interface + // function in ConsumerController. (https://github.com/babylonchain/finality-provider/pull/335#discussion_r1606175344) + QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (slashed bool, jailed bool, err error) + // QueryLatestFinalizedBlock returns the latest finalized block // Note: nil will be returned if the finalized block does not exist QueryLatestFinalizedBlock() (*types.BlockInfo, error) - // QueryLastPublicRandCommit returns the last committed public randomness + // QueryFinalityProviderHighestVotedHeight queries the highest voted height of the given finality provider + QueryFinalityProviderHighestVotedHeight(fpPk *btcec.PublicKey) (uint64, error) + + // QueryLastPublicRandCommit returns the last public randomness commitment QueryLastPublicRandCommit(fpPk *btcec.PublicKey) (*types.PubRandCommit, error) // QueryBlock queries the block at the given height @@ -60,7 +79,7 @@ type ConsumerController interface { QueryIsBlockFinalized(height uint64) (bool, error) // QueryBlocks returns a list of blocks from startHeight to endHeight - QueryBlocks(startHeight, endHeight, limit uint64) ([]*types.BlockInfo, error) + QueryBlocks(startHeight, endHeight uint64, limit uint32) ([]*types.BlockInfo, error) // QueryLatestBlockHeight queries the tip block height of the consumer chain QueryLatestBlockHeight() (uint64, error) @@ -69,5 +88,12 @@ type ConsumerController interface { // error will be returned if the consumer chain has not been activated QueryActivatedHeight() (uint64, error) + // QueryFinalityActivationBlockHeight returns the block height of the consumer chain + // starts to accept finality voting and pub rand commit as start height + // error will be returned if the consumer chain failed to get this value + // if the consumer chain wants to accept finality voting at any block height + // the value zero should be returned. + QueryFinalityActivationBlockHeight() (uint64, error) + Close() error } diff --git a/clientcontroller/babylon/babylon.go b/clientcontroller/babylon/babylon.go index d7a491df..002d1bac 100644 --- a/clientcontroller/babylon/babylon.go +++ b/clientcontroller/babylon/babylon.go @@ -4,9 +4,14 @@ import ( "context" "fmt" "strings" + "time" + + "github.com/babylonlabs-io/babylon/client/babylonclient" + + "github.com/babylonlabs-io/finality-provider/finality-provider/proto" sdkErr "cosmossdk.io/errors" - "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" bbnclient "github.com/babylonlabs-io/babylon/client/client" bbntypes "github.com/babylonlabs-io/babylon/types" btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" @@ -22,8 +27,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkquery "github.com/cosmos/cosmos-sdk/types/query" sttypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/relayer/v2/relayer/provider" "go.uber.org/zap" + protobuf "google.golang.org/protobuf/proto" "github.com/babylonlabs-io/finality-provider/clientcontroller/api" fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" @@ -34,6 +39,7 @@ var _ api.ClientController = &BabylonController{} var emptyErrs = []*sdkErr.Error{} +//nolint:revive type BabylonController struct { bbnClient *bbnclient.Client cfg *fpcfg.BBNConfig @@ -42,11 +48,11 @@ type BabylonController struct { } func NewBabylonController( + bbnClient *bbnclient.Client, cfg *fpcfg.BBNConfig, btcParams *chaincfg.Params, logger *zap.Logger, ) (*BabylonController, error) { - bbnConfig := fpcfg.BBNConfigToBabylonConfig(cfg) bc, err := bbnclient.New( @@ -57,23 +63,43 @@ func NewBabylonController( return nil, fmt.Errorf("failed to create Babylon client: %w", err) } - // makes sure that the key in config really exists and it is a valid bech 32 addr - // to allow using mustGetTxSigner + // makes sure that the key in config really exists and is a valid bech32 addr + // to allow using MustGetTxSigner if _, err := bc.GetAddr(); err != nil { return nil, err } return &BabylonController{ - bc, + bbnClient, cfg, btcParams, logger, }, nil } +func (bc *BabylonController) Start() error { + // makes sure that the key in config really exists and is a valid bech32 addr + // to allow using mustGetTxSigner + if _, err := bc.bbnClient.GetAddr(); err != nil { + return fmt.Errorf("failed to get addr: %w", err) + } + + enabled, err := bc.NodeTxIndexEnabled() + if err != nil { + return err + } + + if !enabled { + return fmt.Errorf("tx indexing in the babylon node must be enabled") + } + + return nil +} + func (bc *BabylonController) MustGetTxSigner() string { signer := bc.GetKeyAddress() prefix := bc.cfg.AccountPrefix + return sdk.MustBech32ifyAddressBytes(prefix, signer) } @@ -96,11 +122,11 @@ func (bc *BabylonController) GetKeyAddress() sdk.AccAddress { return addr } -func (bc *BabylonController) reliablySendMsg(msg sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*provider.RelayerTxResponse, error) { +func (bc *BabylonController) reliablySendMsg(msg sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*babylonclient.RelayerTxResponse, error) { return bc.reliablySendMsgs([]sdk.Msg{msg}, expectedErrs, unrecoverableErrs) } -func (bc *BabylonController) reliablySendMsgs(msgs []sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*provider.RelayerTxResponse, error) { +func (bc *BabylonController) reliablySendMsgs(msgs []sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*babylonclient.RelayerTxResponse, error) { return bc.bbnClient.ReliablySendMsgs( context.Background(), msgs, @@ -115,7 +141,7 @@ func (bc *BabylonController) RegisterFinalityProvider( chainID string, fpPk *btcec.PublicKey, pop []byte, - commission *math.LegacyDec, + commission *sdkmath.LegacyDec, description []byte, ) (*types.TxResponse, error) { var bbnPop btcstakingtypes.ProofOfPossessionBTC @@ -184,7 +210,7 @@ func (bc *BabylonController) SubmitFinalitySig( fpPk *btcec.PublicKey, block *types.BlockInfo, pubRand *btcec.FieldVal, - proof []byte, // TODO: have a type for proof + proof []byte, sig *btcec.ModNScalar, ) (*types.TxResponse, error) { return bc.SubmitBatchFinalitySigs( @@ -230,7 +256,11 @@ func (bc *BabylonController) SubmitBatchFinalitySigs( btcstakingtypes.ErrFpAlreadySlashed, } - res, err := bc.reliablySendMsgs(msgs, emptyErrs, unrecoverableErrs) + expectedErrs := []*sdkErr.Error{ + finalitytypes.ErrDuplicatedFinalitySig, + } + + res, err := bc.reliablySendMsgs(msgs, expectedErrs, unrecoverableErrs) if err != nil { return nil, err } @@ -239,18 +269,32 @@ func (bc *BabylonController) SubmitBatchFinalitySigs( return &types.TxResponse{}, nil } - return &types.TxResponse{TxHash: res.TxHash}, nil + return &types.TxResponse{TxHash: res.TxHash, Events: res.Events}, nil } -// QueryFinalityProviderSlashedOrJailed - returns if the fp has been slashed, jailed, err -func (bc *BabylonController) QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (bool, bool, error) { - fpPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk) - res, err := bc.bbnClient.QueryClient.FinalityProvider(fpPubKey.MarshalHex()) +// UnjailFinalityProvider sends an unjail transaction to the consumer chain +func (bc *BabylonController) UnjailFinalityProvider(fpPk *btcec.PublicKey) (*types.TxResponse, error) { + msg := &finalitytypes.MsgUnjailFinalityProvider{ + Signer: bc.MustGetTxSigner(), + FpBtcPk: bbntypes.NewBIP340PubKeyFromBTCPK(fpPk), + } + + unrecoverableErrs := []*sdkErr.Error{ + btcstakingtypes.ErrFpNotFound, + btcstakingtypes.ErrFpNotJailed, + btcstakingtypes.ErrFpAlreadySlashed, + } + + res, err := bc.reliablySendMsg(msg, emptyErrs, unrecoverableErrs) if err != nil { - return false, false, fmt.Errorf("failed to query the finality provider %s: %v", fpPubKey.MarshalHex(), err) + return nil, err } - return res.FinalityProvider.SlashedBtcHeight > 0, res.FinalityProvider.Jailed, nil + if res == nil { + return &types.TxResponse{}, nil + } + + return &types.TxResponse{TxHash: res.TxHash}, nil } // QueryFinalityProviderHasPower queries whether the finality provider has voting power at a given height @@ -262,8 +306,9 @@ func (bc *BabylonController) QueryFinalityProviderHasPower(fpPk *btcec.PublicKey if err != nil { // voting power table not updated indicates that no fp has voting power // therefore, it should be treated as the fp having 0 voting power - if strings.Contains(err.Error(), btcstakingtypes.ErrVotingPowerTableNotUpdated.Error()) { + if strings.Contains(err.Error(), finalitytypes.ErrVotingPowerTableNotUpdated.Error()) { bc.logger.Info("the voting power table not updated yet") + return false, nil } @@ -273,18 +318,48 @@ func (bc *BabylonController) QueryFinalityProviderHasPower(fpPk *btcec.PublicKey return res.VotingPower > 0, nil } +// QueryFinalityProviderHighestVotedHeight queries the highest voted height of the given finality provider +func (bc *BabylonController) QueryFinalityProviderHighestVotedHeight(fpPk *btcec.PublicKey) (uint64, error) { + fpPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk) + res, err := bc.bbnClient.QueryClient.FinalityProvider(fpPubKey.MarshalHex()) + if err != nil { + return 0, fmt.Errorf("failed to query highest voted height for finality provider %s: %w", fpPubKey.MarshalHex(), err) + } + + return uint64(res.FinalityProvider.HighestVotedHeight), nil +} + func (bc *BabylonController) QueryLatestFinalizedBlocks(count uint64) ([]*types.BlockInfo, error) { return bc.queryLatestBlocks(nil, count, finalitytypes.QueriedBlockStatus_FINALIZED, true) } -func (bc *BabylonController) QueryBlocks(startHeight, endHeight, limit uint64) ([]*types.BlockInfo, error) { +// QueryLastCommittedPublicRand returns the last public randomness commitments +func (bc *BabylonController) QueryLastCommittedPublicRand(fpPk *btcec.PublicKey, count uint64) (map[uint64]*finalitytypes.PubRandCommitResponse, error) { + fpBtcPk := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk) + + pagination := &sdkquery.PageRequest{ + // NOTE: the count is limited by pagination queries + Limit: count, + Reverse: true, + } + + res, err := bc.bbnClient.QueryClient.ListPubRandCommit(fpBtcPk.MarshalHex(), pagination) + if err != nil { + return nil, fmt.Errorf("failed to query committed public randomness: %w", err) + } + + return res.PubRandCommitMap, nil +} + +func (bc *BabylonController) QueryBlocks(startHeight, endHeight uint64, limit uint32) ([]*types.BlockInfo, error) { if endHeight < startHeight { return nil, fmt.Errorf("the startHeight %v should not be higher than the endHeight %v", startHeight, endHeight) } count := endHeight - startHeight + 1 - if count > limit { - count = limit + if count > uint64(limit) { + count = uint64(limit) } + return bc.queryLatestBlocks(sdk.Uint64ToBigEndian(startHeight), count, finalitytypes.QueriedBlockStatus_ANY, false) } @@ -298,7 +373,7 @@ func (bc *BabylonController) queryLatestBlocks(startKey []byte, count uint64, st res, err := bc.bbnClient.QueryClient.ListBlocks(status, pagination) if err != nil { - return nil, fmt.Errorf("failed to query finalized blocks: %v", err) + return nil, fmt.Errorf("failed to query finalized blocks: %w", err) } for _, b := range res.Blocks { @@ -312,6 +387,84 @@ func (bc *BabylonController) queryLatestBlocks(startKey []byte, count uint64, st return blocks, nil } +func getContextWithCancel(timeout time.Duration) (context.Context, context.CancelFunc) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + + return ctx, cancel +} + +func (bc *BabylonController) QueryBlock(height uint64) (*types.BlockInfo, error) { + res, err := bc.bbnClient.QueryClient.Block(height) + if err != nil { + return nil, fmt.Errorf("failed to query indexed block at height %v: %w", height, err) + } + + return &types.BlockInfo{ + Height: height, + Hash: res.Block.AppHash, + Finalized: res.Block.Finalized, + }, nil +} + +func (bc *BabylonController) QueryActivatedHeight() (uint64, error) { + res, err := bc.bbnClient.QueryClient.ActivatedHeight() + if err != nil { + return 0, fmt.Errorf("failed to query activated height: %w", err) + } + + return res.Height, nil +} + +func (bc *BabylonController) QueryFinalityActivationBlockHeight() (uint64, error) { + res, err := bc.bbnClient.QueryClient.FinalityParams() + if err != nil { + return 0, fmt.Errorf("failed to query finality params to get finality activation block height: %w", err) + } + + return res.Params.FinalityActivationHeight, nil +} + +func (bc *BabylonController) QueryBestBlock() (*types.BlockInfo, error) { + blocks, err := bc.queryLatestBlocks(nil, 1, finalitytypes.QueriedBlockStatus_ANY, true) + if err != nil || len(blocks) != 1 { + // try query comet block if the index block query is not available + return bc.queryCometBestBlock() + } + + return blocks[0], nil +} + +func (bc *BabylonController) NodeTxIndexEnabled() (bool, error) { + res, err := bc.bbnClient.GetStatus() + if err != nil { + return false, fmt.Errorf("failed to query node status: %w", err) + } + + return res.TxIndexEnabled(), nil +} + +func (bc *BabylonController) queryCometBestBlock() (*types.BlockInfo, error) { + ctx, cancel := getContextWithCancel(bc.cfg.Timeout) + // this will return 20 items at max in the descending order (highest first) + chainInfo, err := bc.bbnClient.RPCClient.BlockchainInfo(ctx, 0, 0) + defer cancel() + + if err != nil { + return nil, err + } + + headerHeightInt64 := chainInfo.BlockMetas[0].Header.Height + if headerHeightInt64 < 0 { + return nil, fmt.Errorf("block height %v should be positive", headerHeightInt64) + } + // Returning response directly, if header with specified number did not exist + // at request will contain nil header + return &types.BlockInfo{ + Height: uint64(headerHeightInt64), + Hash: chainInfo.BlockMetas[0].Header.AppHash, + }, nil +} + func (bc *BabylonController) Close() error { if !bc.bbnClient.IsRunning() { return nil @@ -350,7 +503,8 @@ func (bc *BabylonController) CreateBTCDelegation( FpBtcPkList: fpBtcPks, StakingTime: stakingTime, StakingValue: stakingValue, - StakingTx: stakingTxInfo, + StakingTx: stakingTxInfo.Transaction, + StakingTxInclusionProof: btcstakingtypes.NewInclusionProof(stakingTxInfo.Key, stakingTxInfo.Proof), SlashingTx: slashingTx, DelegatorSlashingSig: delSlashingSig, UnbondingTx: unbondingTx, @@ -368,7 +522,7 @@ func (bc *BabylonController) CreateBTCDelegation( return &types.TxResponse{TxHash: res.TxHash}, nil } -func (bc *BabylonController) InsertBtcBlockHeaders(headers []bbntypes.BTCHeaderBytes) (*provider.RelayerTxResponse, error) { +func (bc *BabylonController) InsertBtcBlockHeaders(headers []bbntypes.BTCHeaderBytes) (*babylonclient.RelayerTxResponse, error) { msg := &btclctypes.MsgInsertHeaders{ Signer: bc.MustGetTxSigner(), Headers: headers, @@ -394,7 +548,7 @@ func (bc *BabylonController) QueryFinalityProviders() ([]*btcstakingtypes.Finali for { res, err := bc.bbnClient.QueryClient.FinalityProviders(pagination) if err != nil { - return nil, fmt.Errorf("failed to query finality providers: %v", err) + return nil, fmt.Errorf("failed to query finality providers: %w", err) } fps = append(fps, res.FinalityProviders...) if res.Pagination == nil || res.Pagination.NextKey == nil { @@ -407,16 +561,16 @@ func (bc *BabylonController) QueryFinalityProviders() ([]*btcstakingtypes.Finali return fps, nil } -func (bc *BabylonController) QueryConsumerFinalityProviders(consumerId string) ([]*bsctypes.FinalityProviderResponse, error) { +func (bc *BabylonController) QueryConsumerFinalityProviders(consumerID string) ([]*bsctypes.FinalityProviderResponse, error) { var fps []*bsctypes.FinalityProviderResponse pagination := &sdkquery.PageRequest{ Limit: 100, } for { - res, err := bc.bbnClient.QueryClient.QueryConsumerFinalityProviders(consumerId, pagination) + res, err := bc.bbnClient.QueryClient.QueryConsumerFinalityProviders(consumerID, pagination) if err != nil { - return nil, fmt.Errorf("failed to query finality providers: %v", err) + return nil, fmt.Errorf("failed to query finality providers: %w", err) } fps = append(fps, res.FinalityProviders...) if res.Pagination == nil || res.Pagination.NextKey == nil { @@ -429,10 +583,75 @@ func (bc *BabylonController) QueryConsumerFinalityProviders(consumerId string) ( return fps, nil } +func (bc *BabylonController) QueryFinalityProvider(fpPk *btcec.PublicKey) (*btcstakingtypes.QueryFinalityProviderResponse, error) { + fpPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk) + res, err := bc.bbnClient.QueryClient.FinalityProvider(fpPubKey.MarshalHex()) + if err != nil { + return nil, fmt.Errorf("failed to query the finality provider %s: %w", fpPubKey.MarshalHex(), err) + } + + return res, nil +} + +func (bc *BabylonController) EditFinalityProvider(fpPk *btcec.PublicKey, + rate *sdkmath.LegacyDec, description []byte) (*btcstakingtypes.MsgEditFinalityProvider, error) { + var reqDesc proto.Description + if err := protobuf.Unmarshal(description, &reqDesc); err != nil { + return nil, err + } + fpPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk) + + fpRes, err := bc.QueryFinalityProvider(fpPk) + if err != nil { + return nil, err + } + + if !strings.EqualFold(fpRes.FinalityProvider.Addr, bc.MustGetTxSigner()) { + return nil, fmt.Errorf("the signer does not correspond to the finality provider's "+ + "Babylon address, expected %s got %s", bc.MustGetTxSigner(), fpRes.FinalityProvider.Addr) + } + + getValueOrDefault := func(reqValue, defaultValue string) string { + if reqValue != "" { + return reqValue + } + + return defaultValue + } + + resDesc := fpRes.FinalityProvider.Description + + desc := &sttypes.Description{ + Moniker: getValueOrDefault(reqDesc.Moniker, resDesc.Moniker), + Identity: getValueOrDefault(reqDesc.Identity, resDesc.Identity), + Website: getValueOrDefault(reqDesc.Website, resDesc.Website), + SecurityContact: getValueOrDefault(reqDesc.SecurityContact, resDesc.SecurityContact), + Details: getValueOrDefault(reqDesc.Details, resDesc.Details), + } + + msg := &btcstakingtypes.MsgEditFinalityProvider{ + Addr: bc.MustGetTxSigner(), + BtcPk: fpPubKey.MustMarshal(), + Description: desc, + Commission: fpRes.FinalityProvider.Commission, + } + + if rate != nil { + msg.Commission = rate + } + + _, err = bc.reliablySendMsg(msg, emptyErrs, emptyErrs) + if err != nil { + return nil, fmt.Errorf("failed to query the finality provider %s: %w", fpPk.SerializeCompressed(), err) + } + + return msg, nil +} + func (bc *BabylonController) QueryBtcLightClientTip() (*btclctypes.BTCHeaderInfoResponse, error) { res, err := bc.bbnClient.QueryClient.BTCHeaderChainTip() if err != nil { - return nil, fmt.Errorf("failed to query BTC tip: %v", err) + return nil, fmt.Errorf("failed to query BTC tip: %w", err) } return res.Header, nil @@ -441,7 +660,7 @@ func (bc *BabylonController) QueryBtcLightClientTip() (*btclctypes.BTCHeaderInfo func (bc *BabylonController) QueryCurrentEpoch() (uint64, error) { res, err := bc.bbnClient.QueryClient.CurrentEpoch() if err != nil { - return 0, fmt.Errorf("failed to query BTC tip: %v", err) + return 0, fmt.Errorf("failed to query BTC tip: %w", err) } return res.CurrentEpoch, nil @@ -474,7 +693,7 @@ func (bc *BabylonController) queryDelegationsWithStatus(status btcstakingtypes.B res, err := bc.bbnClient.QueryClient.BTCDelegations(status, pagination) if err != nil { - return nil, fmt.Errorf("failed to query BTC delegations: %v", err) + return nil, fmt.Errorf("failed to query BTC delegations: %w", err) } return res.BtcDelegations, nil @@ -484,13 +703,13 @@ func (bc *BabylonController) QueryStakingParams() (*types.StakingParams, error) // query btc checkpoint params ckptParamRes, err := bc.bbnClient.QueryClient.BTCCheckpointParams() if err != nil { - return nil, fmt.Errorf("failed to query params of the btccheckpoint module: %v", err) + return nil, fmt.Errorf("failed to query params of the btccheckpoint module: %w", err) } // query btc staking params stakingParamRes, err := bc.bbnClient.QueryClient.BTCStakingParams() if err != nil { - return nil, fmt.Errorf("failed to query staking params: %v", err) + return nil, fmt.Errorf("failed to query staking params: %w", err) } covenantPks := make([]*btcec.PublicKey, 0, len(stakingParamRes.Params.CovenantPks)) @@ -510,7 +729,7 @@ func (bc *BabylonController) QueryStakingParams() (*types.StakingParams, error) SlashingPkScript: stakingParamRes.Params.SlashingPkScript, CovenantQuorum: stakingParamRes.Params.CovenantQuorum, SlashingRate: stakingParamRes.Params.SlashingRate, - MinUnbondingTime: stakingParamRes.Params.MinUnbondingTimeBlocks, + UnbondingTime: stakingParamRes.Params.UnbondingTimeBlocks, }, nil } @@ -540,7 +759,7 @@ func (bc *BabylonController) SubmitCovenantSigs( return &types.TxResponse{TxHash: res.TxHash}, nil } -func (bc *BabylonController) InsertSpvProofs(submitter string, proofs []*btcctypes.BTCSpvProof) (*provider.RelayerTxResponse, error) { +func (bc *BabylonController) InsertSpvProofs(submitter string, proofs []*btcctypes.BTCSpvProof) (*babylonclient.RelayerTxResponse, error) { msg := &btcctypes.MsgInsertBTCSpvProof{ Submitter: submitter, Proofs: proofs, @@ -555,12 +774,13 @@ func (bc *BabylonController) InsertSpvProofs(submitter string, proofs []*btcctyp } // RegisterConsumerChain registers a consumer chain via a MsgRegisterChain to Babylon -func (bc *BabylonController) RegisterConsumerChain(id, name, description string) (*types.TxResponse, error) { +func (bc *BabylonController) RegisterConsumerChain(id, name, description, ethL2FinalityContractAddress string) (*types.TxResponse, error) { msg := &bsctypes.MsgRegisterConsumer{ - Signer: bc.MustGetTxSigner(), - ConsumerId: id, - ConsumerName: name, - ConsumerDescription: description, + Signer: bc.MustGetTxSigner(), + ConsumerId: id, + ConsumerName: name, + ConsumerDescription: description, + EthL2FinalityContractAddress: ethL2FinalityContractAddress, } res, err := bc.reliablySendMsg(msg, emptyErrs, emptyErrs) diff --git a/clientcontroller/babylon/consumer.go b/clientcontroller/babylon/consumer.go index 1cfa31b3..4ecccfbd 100644 --- a/clientcontroller/babylon/consumer.go +++ b/clientcontroller/babylon/consumer.go @@ -2,11 +2,11 @@ package babylon import ( "context" - "encoding/json" "fmt" "strings" sdkErr "cosmossdk.io/errors" + "github.com/babylonlabs-io/babylon/client/babylonclient" bbnclient "github.com/babylonlabs-io/babylon/client/client" bbntypes "github.com/babylonlabs-io/babylon/types" btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" @@ -17,7 +17,6 @@ import ( cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" sdkquery "github.com/cosmos/cosmos-sdk/types/query" - "github.com/cosmos/relayer/v2/relayer/provider" "go.uber.org/zap" "github.com/babylonlabs-io/finality-provider/clientcontroller/api" @@ -27,6 +26,7 @@ import ( var _ api.ConsumerController = &BabylonConsumerController{} +//nolint:revive type BabylonConsumerController struct { bbnClient *bbnclient.Client cfg *fpcfg.BBNConfig @@ -39,7 +39,6 @@ func NewBabylonConsumerController( btcParams *chaincfg.Params, logger *zap.Logger, ) (*BabylonConsumerController, error) { - bbnConfig := fpcfg.BBNConfigToBabylonConfig(cfg) if err := bbnConfig.Validate(); err != nil { @@ -62,9 +61,10 @@ func NewBabylonConsumerController( }, nil } -func (bc *BabylonConsumerController) mustGetTxSigner() string { +func (bc *BabylonConsumerController) MustGetTxSigner() string { signer := bc.GetKeyAddress() prefix := bc.cfg.AccountPrefix + return sdk.MustBech32ifyAddressBytes(prefix, signer) } @@ -89,11 +89,11 @@ func (bc *BabylonConsumerController) GetKeyAddress() sdk.AccAddress { return addr } -func (bc *BabylonConsumerController) reliablySendMsg(msg sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*provider.RelayerTxResponse, error) { +func (bc *BabylonConsumerController) reliablySendMsg(msg sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*babylonclient.RelayerTxResponse, error) { return bc.reliablySendMsgs([]sdk.Msg{msg}, expectedErrs, unrecoverableErrs) } -func (bc *BabylonConsumerController) reliablySendMsgs(msgs []sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*provider.RelayerTxResponse, error) { +func (bc *BabylonConsumerController) reliablySendMsgs(msgs []sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*babylonclient.RelayerTxResponse, error) { return bc.bbnClient.ReliablySendMsgs( context.Background(), msgs, @@ -112,7 +112,7 @@ func (bc *BabylonConsumerController) CommitPubRandList( sig *schnorr.Signature, ) (*types.TxResponse, error) { msg := &finalitytypes.MsgCommitPubRandList{ - Signer: bc.mustGetTxSigner(), + Signer: bc.MustGetTxSigner(), FpBtcPk: bbntypes.NewBIP340PubKeyFromBTCPK(fpPk), StartHeight: startHeight, NumPubRand: numPubRand, @@ -149,7 +149,7 @@ func (bc *BabylonConsumerController) SubmitFinalitySig( } msg := &finalitytypes.MsgAddFinalitySig{ - Signer: bc.mustGetTxSigner(), + Signer: bc.MustGetTxSigner(), FpBtcPk: bbntypes.NewBIP340PubKeyFromBTCPK(fpPk), BlockHeight: block.Height, PubRand: bbntypes.NewSchnorrPubRandFromFieldVal(pubRand), @@ -164,12 +164,20 @@ func (bc *BabylonConsumerController) SubmitFinalitySig( btcstakingtypes.ErrFpAlreadySlashed, } - res, err := bc.reliablySendMsg(msg, emptyErrs, unrecoverableErrs) + expectedErrs := []*sdkErr.Error{ + finalitytypes.ErrDuplicatedFinalitySig, + } + + res, err := bc.reliablySendMsg(msg, expectedErrs, unrecoverableErrs) if err != nil { return nil, err } - return &types.TxResponse{TxHash: res.TxHash, Events: fromCosmosEventsToBytes(res.Events)}, nil + if res == nil { + return &types.TxResponse{}, nil + } + + return &types.TxResponse{TxHash: res.TxHash, Events: res.Events}, nil } // SubmitBatchFinalitySigs submits a batch of finality signatures to Babylon @@ -192,7 +200,7 @@ func (bc *BabylonConsumerController) SubmitBatchFinalitySigs( } msg := &finalitytypes.MsgAddFinalitySig{ - Signer: bc.mustGetTxSigner(), + Signer: bc.MustGetTxSigner(), FpBtcPk: bbntypes.NewBIP340PubKeyFromBTCPK(fpPk), BlockHeight: b.Height, PubRand: bbntypes.NewSchnorrPubRandFromFieldVal(pubRandList[i]), @@ -209,11 +217,19 @@ func (bc *BabylonConsumerController) SubmitBatchFinalitySigs( btcstakingtypes.ErrFpAlreadySlashed, } - res, err := bc.reliablySendMsgs(msgs, emptyErrs, unrecoverableErrs) + expectedErrs := []*sdkErr.Error{ + finalitytypes.ErrDuplicatedFinalitySig, + } + + res, err := bc.reliablySendMsgs(msgs, expectedErrs, unrecoverableErrs) if err != nil { return nil, err } + if res == nil { + return &types.TxResponse{}, nil + } + return &types.TxResponse{TxHash: res.TxHash}, nil } @@ -229,8 +245,9 @@ func (bc *BabylonConsumerController) QueryFinalityProviderHasPower( if err != nil { // voting power table not updated indicates that no fp has voting power // therefore, it should be treated as the fp having 0 voting power - if strings.Contains(err.Error(), btcstakingtypes.ErrVotingPowerTableNotUpdated.Error()) { + if strings.Contains(err.Error(), finalitytypes.ErrVotingPowerTableNotUpdated.Error()) { bc.logger.Info("the voting power table not updated yet") + return false, nil } @@ -245,17 +262,19 @@ func (bc *BabylonConsumerController) QueryLatestFinalizedBlock() (*types.BlockIn if blocks == nil { return nil, err } - return blocks[0], err + + return blocks[0], nil } -func (bc *BabylonConsumerController) QueryBlocks(startHeight, endHeight, limit uint64) ([]*types.BlockInfo, error) { +func (bc *BabylonConsumerController) QueryBlocks(startHeight, endHeight uint64, limit uint32) ([]*types.BlockInfo, error) { if endHeight < startHeight { return nil, fmt.Errorf("the startHeight %v should not be higher than the endHeight %v", startHeight, endHeight) } count := endHeight - startHeight + 1 - if count > limit { - count = limit + if count > uint64(limit) { + count = uint64(limit) } + return bc.queryLatestBlocks(sdk.Uint64ToBigEndian(startHeight), count, finalitytypes.QueriedBlockStatus_ANY, false) } @@ -269,7 +288,7 @@ func (bc *BabylonConsumerController) queryLatestBlocks(startKey []byte, count ui res, err := bc.bbnClient.QueryClient.ListBlocks(status, pagination) if err != nil { - return nil, fmt.Errorf("failed to query finalized blocks: %v", err) + return nil, fmt.Errorf("failed to query finalized blocks: %w", err) } for _, b := range res.Blocks { @@ -318,7 +337,7 @@ func (bc *BabylonConsumerController) QueryLastPublicRandCommit(fpPk *btcec.Publi return nil, fmt.Errorf("expected length to be 1, but get :%d", len(res.PubRandCommitMap)) } - var commit *types.PubRandCommit = nil + var commit *types.PubRandCommit for height, commitRes := range res.PubRandCommitMap { commit = &types.PubRandCommit{ StartHeight: height, @@ -343,6 +362,15 @@ func (bc *BabylonConsumerController) QueryIsBlockFinalized(height uint64) (bool, return res.Block.Finalized, nil } +func (bc *BabylonConsumerController) QueryFinalityActivationBlockHeight() (uint64, error) { + res, err := bc.bbnClient.QueryClient.FinalityParams() + if err != nil { + return 0, fmt.Errorf("failed to query finality params to get finality activation block height: %w", err) + } + + return res.Params.FinalityActivationHeight, nil +} + func (bc *BabylonConsumerController) QueryActivatedHeight() (uint64, error) { res, err := bc.bbnClient.QueryClient.ActivatedHeight() if err != nil { @@ -352,6 +380,17 @@ func (bc *BabylonConsumerController) QueryActivatedHeight() (uint64, error) { return res.Height, nil } +// QueryFinalityProviderHighestVotedHeight queries the highest voted height of the given finality provider +func (bc *BabylonConsumerController) QueryFinalityProviderHighestVotedHeight(fpPk *btcec.PublicKey) (uint64, error) { + fpPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk) + res, err := bc.bbnClient.QueryClient.FinalityProvider(fpPubKey.MarshalHex()) + if err != nil { + return 0, fmt.Errorf("failed to query highest voted height for finality provider %s: %w", fpPubKey.MarshalHex(), err) + } + + return uint64(res.FinalityProvider.HighestVotedHeight), nil +} + func (bc *BabylonConsumerController) QueryLatestBlockHeight() (uint64, error) { blocks, err := bc.queryLatestBlocks(nil, 1, finalitytypes.QueriedBlockStatus_ANY, true) if err != nil || len(blocks) != 1 { @@ -360,6 +399,7 @@ func (bc *BabylonConsumerController) QueryLatestBlockHeight() (uint64, error) { if err != nil { return 0, err } + return block.Height, nil } @@ -379,11 +419,22 @@ func (bc *BabylonConsumerController) queryCometBestBlock() (*types.BlockInfo, er // Returning response directly, if header with specified number did not exist // at request will contain nil header return &types.BlockInfo{ - Height: uint64(chainInfo.BlockMetas[0].Header.Height), + Height: uint64(chainInfo.BlockMetas[0].Header.Height), // #nosec G115 Hash: chainInfo.BlockMetas[0].Header.AppHash, }, nil } +// QueryFinalityProviderSlashedOrJailed - returns if the fp has been slashed, jailed, err +func (bc *BabylonConsumerController) QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (bool, bool, error) { + fpPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk) + res, err := bc.bbnClient.QueryClient.FinalityProvider(fpPubKey.MarshalHex()) + if err != nil { + return false, false, fmt.Errorf("failed to query the finality provider %s: %w", fpPubKey.MarshalHex(), err) + } + + return res.FinalityProvider.SlashedBtcHeight > 0, res.FinalityProvider.Jailed, nil +} + func (bc *BabylonConsumerController) Close() error { if !bc.bbnClient.IsRunning() { return nil @@ -392,10 +443,23 @@ func (bc *BabylonConsumerController) Close() error { return bc.bbnClient.Stop() } -func fromCosmosEventsToBytes(events []provider.RelayerEvent) []byte { - bytes, err := json.Marshal(events) +// UnjailFinalityProvider sends an unjail transaction to the consumer chain +func (bc *BabylonConsumerController) UnjailFinalityProvider(fpPk *btcec.PublicKey) (*types.TxResponse, error) { + msg := &finalitytypes.MsgUnjailFinalityProvider{ + Signer: bc.MustGetTxSigner(), + FpBtcPk: bbntypes.NewBIP340PubKeyFromBTCPK(fpPk), + } + + unrecoverableErrs := []*sdkErr.Error{ + btcstakingtypes.ErrFpNotFound, + btcstakingtypes.ErrFpNotJailed, + btcstakingtypes.ErrFpAlreadySlashed, + } + + res, err := bc.reliablySendMsg(msg, emptyErrs, unrecoverableErrs) if err != nil { - return nil + return nil, err } - return bytes + + return &types.TxResponse{TxHash: res.TxHash, Events: res.Events}, nil } diff --git a/clientcontroller/cosmwasm/consumer.go b/clientcontroller/cosmwasm/consumer.go index f3bdb92b..aae5b72c 100644 --- a/clientcontroller/cosmwasm/consumer.go +++ b/clientcontroller/cosmwasm/consumer.go @@ -10,6 +10,7 @@ import ( sdkErr "cosmossdk.io/errors" wasmdparams "github.com/CosmWasm/wasmd/app/params" wasmdtypes "github.com/CosmWasm/wasmd/x/wasm/types" + "github.com/babylonlabs-io/babylon/client/babylonclient" bbntypes "github.com/babylonlabs-io/babylon/types" "github.com/babylonlabs-io/finality-provider/clientcontroller/api" cwcclient "github.com/babylonlabs-io/finality-provider/cosmwasmclient/client" @@ -21,12 +22,12 @@ import ( coretypes "github.com/cometbft/cometbft/rpc/core/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" - "github.com/cosmos/relayer/v2/relayer/provider" "go.uber.org/zap" ) var _ api.ConsumerController = &CosmwasmConsumerController{} +//nolint:revive type CosmwasmConsumerController struct { cwClient *cwcclient.Client cfg *fpcfg.CosmwasmConfig @@ -61,17 +62,24 @@ func NewCosmwasmConsumerController( }, nil } -func (wc *CosmwasmConsumerController) reliablySendMsg(msg sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*provider.RelayerTxResponse, error) { +func (wc *CosmwasmConsumerController) reliablySendMsg(msg sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*babylonclient.RelayerTxResponse, error) { return wc.reliablySendMsgs([]sdk.Msg{msg}, expectedErrs, unrecoverableErrs) } -func (wc *CosmwasmConsumerController) reliablySendMsgs(msgs []sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*provider.RelayerTxResponse, error) { - return wc.cwClient.ReliablySendMsgs( +func (wc *CosmwasmConsumerController) reliablySendMsgs(msgs []sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*babylonclient.RelayerTxResponse, error) { + resp, err := wc.cwClient.ReliablySendMsgs( context.Background(), msgs, expectedErrs, unrecoverableErrs, ) + if err != nil { + return nil, err + } + + bbnResp := fptypes.NewBabylonTxResponse(resp) + + return bbnResp, nil } // CommitPubRandList commits a list of Schnorr public randomness via a MsgCommitPubRand to Babylon @@ -144,7 +152,7 @@ func (wc *CosmwasmConsumerController) SubmitFinalitySig( return nil, err } - return &fptypes.TxResponse{TxHash: res.TxHash, Events: fromCosmosEventsToBytes(res.Events)}, nil + return &fptypes.TxResponse{TxHash: res.TxHash, Events: res.Events}, nil } // SubmitBatchFinalitySigs submits a batch of finality signatures to Babylon @@ -209,7 +217,7 @@ func (wc *CosmwasmConsumerController) QueryFinalityProviderHasPower( } queryMsgBytes, err := json.Marshal(queryMsgStruct) if err != nil { - return false, fmt.Errorf("failed to marshal query message: %v", err) + return false, fmt.Errorf("failed to marshal query message: %w", err) } dataFromContract, err := wc.QuerySmartContractState(wc.cfg.BtcStakingContractAddress, string(queryMsgBytes)) if err != nil { @@ -232,7 +240,7 @@ func (wc *CosmwasmConsumerController) QueryFinalityProvidersByPower() (*Consumer queryMsgBytes, err := json.Marshal(queryMsgStruct) if err != nil { - return nil, fmt.Errorf("failed to marshal query message: %v", err) + return nil, fmt.Errorf("failed to marshal query message: %w", err) } dataFromContract, err := wc.QuerySmartContractState(wc.cfg.BtcStakingContractAddress, string(queryMsgBytes)) @@ -256,23 +264,25 @@ func (wc *CosmwasmConsumerController) QueryLatestFinalizedBlock() (*fptypes.Bloc if err != nil || len(blocks) == 0 { // do not return error here as FP handles this situation by // not running fast sync + //nolint:nilerr return nil, nil } return blocks[0], nil } -func (wc *CosmwasmConsumerController) QueryBlocks(startHeight, endHeight, limit uint64) ([]*fptypes.BlockInfo, error) { +func (wc *CosmwasmConsumerController) QueryBlocks(startHeight, endHeight uint64, _ uint32) ([]*fptypes.BlockInfo, error) { return wc.queryCometBlocksInRange(startHeight, endHeight) } func (wc *CosmwasmConsumerController) QueryBlock(height uint64) (*fptypes.BlockInfo, error) { - block, err := wc.cwClient.GetBlock(int64(height)) + block, err := wc.cwClient.GetBlock(int64(height)) // #nosec G115 if err != nil { return nil, err } + return &fptypes.BlockInfo{ - Height: uint64(block.Block.Header.Height), + Height: uint64(block.Block.Header.Height), // #nosec G115 Hash: block.Block.Header.AppHash, }, nil } @@ -290,7 +300,7 @@ func (wc *CosmwasmConsumerController) QueryLastPublicRandCommit(fpPk *btcec.Publ queryMsgBytes, err := json.Marshal(queryMsgStruct) if err != nil { - return nil, fmt.Errorf("failed to marshal query message: %v", err) + return nil, fmt.Errorf("failed to marshal query message: %w", err) } // Query the smart contract state @@ -320,7 +330,7 @@ func (wc *CosmwasmConsumerController) QueryLastPublicRandCommit(fpPk *btcec.Publ func (wc *CosmwasmConsumerController) QueryIsBlockFinalized(height uint64) (bool, error) { resp, err := wc.QueryIndexedBlock(height) if err != nil || resp == nil { - return false, nil + return false, err } return resp.Finalized, nil @@ -348,7 +358,7 @@ func (wc *CosmwasmConsumerController) QueryActivatedHeight() (uint64, error) { var resp struct { Height uint64 `json:"height"` } - err = json.Unmarshal(dataFromContract.Data, &resp) + err = json.Unmarshal(dataFromContract.Data, &resp) // #nosec G115 if err != nil { return 0, fmt.Errorf("failed to unmarshal response: %w", err) } @@ -365,7 +375,8 @@ func (wc *CosmwasmConsumerController) QueryLatestBlockHeight() (uint64, error) { if err != nil { return 0, err } - return block.Height, err + + return block.Height, nil } func (wc *CosmwasmConsumerController) QueryFinalitySignature(fpBtcPkHex string, height uint64) (*FinalitySignatureResponse, error) { @@ -377,7 +388,7 @@ func (wc *CosmwasmConsumerController) QueryFinalitySignature(fpBtcPkHex string, } queryMsgBytes, err := json.Marshal(queryMsgStruct) if err != nil { - return nil, fmt.Errorf("failed to marshal query message: %v", err) + return nil, fmt.Errorf("failed to marshal query message: %w", err) } dataFromContract, err := wc.QuerySmartContractState(wc.cfg.BtcStakingContractAddress, string(queryMsgBytes)) @@ -401,7 +412,7 @@ func (wc *CosmwasmConsumerController) QueryFinalityProviders() (*ConsumerFpsResp queryMsgBytes, err := json.Marshal(queryMsgStruct) if err != nil { - return nil, fmt.Errorf("failed to marshal query message: %v", err) + return nil, fmt.Errorf("failed to marshal query message: %w", err) } dataFromContract, err := wc.QuerySmartContractState(wc.cfg.BtcStakingContractAddress, string(queryMsgBytes)) @@ -425,7 +436,7 @@ func (wc *CosmwasmConsumerController) QueryDelegations() (*ConsumerDelegationsRe queryMsgBytes, err := json.Marshal(queryMsgStruct) if err != nil { - return nil, fmt.Errorf("failed to marshal query message: %v", err) + return nil, fmt.Errorf("failed to marshal query message: %w", err) } dataFromContract, err := wc.QuerySmartContractState(wc.cfg.BtcStakingContractAddress, string(queryMsgBytes)) @@ -498,7 +509,7 @@ func (wc *CosmwasmConsumerController) queryCometBestBlock() (*fptypes.BlockInfo, // Returning response directly, if header with specified number did not exist // at request will contain nil header return &fptypes.BlockInfo{ - Height: uint64(chainInfo.BlockMetas[0].Header.Height), + Height: uint64(chainInfo.BlockMetas[0].Header.Height), // #nosec G115 Hash: chainInfo.BlockMetas[0].Header.AppHash, }, nil } @@ -508,7 +519,7 @@ func (wc *CosmwasmConsumerController) queryCometBlocksInRange(startHeight, endHe defer cancel() // this will return 20 items at max in the descending order (highest first) - chainInfo, err := wc.cwClient.RPCClient.BlockchainInfo(ctx, int64(startHeight), int64(endHeight)) + chainInfo, err := wc.cwClient.RPCClient.BlockchainInfo(ctx, int64(startHeight), int64(endHeight)) // #nosec G115 if err != nil { return nil, err } @@ -522,7 +533,7 @@ func (wc *CosmwasmConsumerController) queryCometBlocksInRange(startHeight, endHe var blocks []*fptypes.BlockInfo for _, blockMeta := range chainInfo.BlockMetas { block := &fptypes.BlockInfo{ - Height: uint64(blockMeta.Header.Height), + Height: uint64(blockMeta.Header.Height), // #nosec G115 Hash: blockMeta.Header.AppHash, } blocks = append(blocks, block) @@ -544,7 +555,7 @@ func (wc *CosmwasmConsumerController) Close() error { return wc.cwClient.Stop() } -func (wc *CosmwasmConsumerController) ExecuteContract(msgBytes []byte) (*provider.RelayerTxResponse, error) { +func (wc *CosmwasmConsumerController) ExecuteContract(msgBytes []byte) (*babylonclient.RelayerTxResponse, error) { execMsg := &wasmdtypes.MsgExecuteContract{ Sender: wc.cwClient.MustGetAddr(), Contract: wc.cfg.BtcStakingContractAddress, @@ -577,10 +588,10 @@ func (wc *CosmwasmConsumerController) InstantiateContract(codeID uint64, initMsg return wc.cwClient.InstantiateContract(codeID, initMsg) } -// GetLatestCodeId returns the latest wasm code id. +// GetLatestCodeID returns the latest wasm code id. // NOTE: this function is only meant to be used in tests. -func (wc *CosmwasmConsumerController) GetLatestCodeId() (uint64, error) { - return wc.cwClient.GetLatestCodeId() +func (wc *CosmwasmConsumerController) GetLatestCodeID() (uint64, error) { + return wc.cwClient.GetLatestCodeID() } // ListContractsByCode lists all contracts by wasm code id @@ -618,7 +629,7 @@ func (wc *CosmwasmConsumerController) QueryIndexedBlock(height uint64) (*Indexed } queryMsgBytes, err := json.Marshal(queryMsgStruct) if err != nil { - return nil, fmt.Errorf("failed to marshal query message: %v", err) + return nil, fmt.Errorf("failed to marshal query message: %w", err) } // Query the smart contract state @@ -637,10 +648,25 @@ func (wc *CosmwasmConsumerController) QueryIndexedBlock(height uint64) (*Indexed return &resp, nil } -func fromCosmosEventsToBytes(events []provider.RelayerEvent) []byte { - bytes, err := json.Marshal(events) - if err != nil { - return nil - } - return bytes +func (wc *CosmwasmConsumerController) QueryFinalityActivationBlockHeight() (uint64, error) { + // TODO: implement finality activation feature in OP stack L2 + return 0, nil +} + +//nolint:revive +func (wc *CosmwasmConsumerController) QueryFinalityProviderHighestVotedHeight(fpPk *btcec.PublicKey) (uint64, error) { + // TODO: implement highest voted height feature in OP stack L2 + return 0, nil +} + +//nolint:revive +func (wc *CosmwasmConsumerController) QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (bool, bool, error) { + // TODO: implement slashed or jailed feature in OP stack L2 + return false, false, nil +} + +//nolint:revive +func (wc *CosmwasmConsumerController) UnjailFinalityProvider(fpPk *btcec.PublicKey) (*fptypes.TxResponse, error) { + // TODO: implement unjail feature in OP stack L2 + return nil, nil } diff --git a/clientcontroller/cosmwasm/msg.go b/clientcontroller/cosmwasm/msg.go index 48da2d87..892a63c3 100644 --- a/clientcontroller/cosmwasm/msg.go +++ b/clientcontroller/cosmwasm/msg.go @@ -14,8 +14,8 @@ type ConsumerFpsResponse struct { type SingleConsumerFpResponse struct { BtcPkHex string `json:"btc_pk_hex"` SlashedBabylonHeight uint64 `json:"slashed_babylon_height"` - SlashedBtcHeight uint64 `json:"slashed_btc_height"` - ConsumerId string `json:"consumer_id"` + SlashedBtcHeight uint32 `json:"slashed_btc_height"` + ConsumerID string `json:"consumer_id"` } type ConsumerDelegationsResponse struct { @@ -25,8 +25,8 @@ type ConsumerDelegationsResponse struct { type SingleConsumerDelegationResponse struct { BtcPkHex string `json:"btc_pk_hex"` FpBtcPkList []string `json:"fp_btc_pk_list"` - StartHeight uint64 `json:"start_height"` - EndHeight uint64 `json:"end_height"` + StartHeight uint32 `json:"start_height"` + EndHeight uint32 `json:"end_height"` TotalSat uint64 `json:"total_sat"` StakingTx []byte `json:"staking_tx"` SlashingTx []byte `json:"slashing_tx"` @@ -106,8 +106,8 @@ type ActiveBtcDelegation struct { StakerAddr string `json:"staker_addr"` BTCPkHex string `json:"btc_pk_hex"` FpBtcPkList []string `json:"fp_btc_pk_list"` - StartHeight uint64 `json:"start_height"` - EndHeight uint64 `json:"end_height"` + StartHeight uint32 `json:"start_height"` + EndHeight uint32 `json:"end_height"` TotalSat uint64 `json:"total_sat"` StakingTx []byte `json:"staking_tx"` SlashingTx []byte `json:"slashing_tx"` diff --git a/clientcontroller/factory.go b/clientcontroller/factory.go index d2265fab..665d7d7f 100644 --- a/clientcontroller/factory.go +++ b/clientcontroller/factory.go @@ -3,6 +3,7 @@ package clientcontroller import ( "fmt" + bbnclient "github.com/babylonlabs-io/babylon/client/client" "github.com/babylonlabs-io/finality-provider/clientcontroller/api" "github.com/babylonlabs-io/finality-provider/clientcontroller/babylon" "github.com/babylonlabs-io/finality-provider/clientcontroller/cosmwasm" @@ -18,9 +19,16 @@ const ( WasmConsumerChainType = "wasm" ) -// NewClientController TODO: this is always going to be babylon so rename accordingly -func NewClientController(config *fpcfg.Config, logger *zap.Logger) (api.ClientController, error) { - cc, err := babylon.NewBabylonController(config.BabylonConfig, &config.BTCNetParams, logger) +func NewBabylonController(config *fpcfg.Config, logger *zap.Logger) (api.ClientController, error) { + bbnConfig := fpcfg.BBNConfigToBabylonConfig(config.BabylonConfig) + bbnClient, err := bbnclient.New( + &bbnConfig, + logger, + ) + if err != nil { + return nil, fmt.Errorf("failed to create Babylon rpc client: %w", err) + } + cc, err := babylon.NewBabylonController(bbnClient, config.BabylonConfig, &config.BTCNetParams, logger) if err != nil { return nil, fmt.Errorf("failed to create Babylon rpc client: %w", err) } diff --git a/clientcontroller/opstackl2/consumer.go b/clientcontroller/opstackl2/consumer.go index 3cf28c66..e0cf11fe 100644 --- a/clientcontroller/opstackl2/consumer.go +++ b/clientcontroller/opstackl2/consumer.go @@ -8,6 +8,7 @@ import ( "math" "math/big" + "github.com/babylonlabs-io/babylon/client/babylonclient" bbnclient "github.com/babylonlabs-io/babylon/client/client" btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" @@ -27,7 +28,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2/schnorr" cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/relayer/v2/relayer/provider" "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" @@ -41,6 +41,7 @@ const ( var _ api.ConsumerController = &OPStackL2ConsumerController{} +// nolint:revive // Ignore stutter warning - full name provides clarity type OPStackL2ConsumerController struct { Cfg *fpcfg.OPStackL2Config CwClient *cwclient.Client @@ -57,7 +58,7 @@ func NewOPStackL2ConsumerController( return nil, fmt.Errorf("nil config for OP consumer controller") } if err := opl2Cfg.Validate(); err != nil { - return nil, err + return nil, fmt.Errorf("invalid config: %w", err) } cwConfig := opl2Cfg.ToCosmwasmConfig() @@ -114,21 +115,29 @@ func NewCwClient(cwConfig *cwconfig.CosmwasmConfig, logger *zap.Logger) (*cwclie cwEncodingCfg, logger, ) + if err != nil { + return nil, fmt.Errorf("failed to create CW client: %w", err) + } - return cwClient, err + return cwClient, nil } -func (cc *OPStackL2ConsumerController) ReliablySendMsg(msg sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*provider.RelayerTxResponse, error) { +func (cc *OPStackL2ConsumerController) ReliablySendMsg(msg sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*babylonclient.RelayerTxResponse, error) { return cc.reliablySendMsgs([]sdk.Msg{msg}, expectedErrs, unrecoverableErrs) } -func (cc *OPStackL2ConsumerController) reliablySendMsgs(msgs []sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*provider.RelayerTxResponse, error) { - return cc.CwClient.ReliablySendMsgs( +func (cc *OPStackL2ConsumerController) reliablySendMsgs(msgs []sdk.Msg, expectedErrs []*sdkErr.Error, unrecoverableErrs []*sdkErr.Error) (*babylonclient.RelayerTxResponse, error) { + resp, err := cc.CwClient.ReliablySendMsgs( context.Background(), msgs, expectedErrs, unrecoverableErrs, ) + if err != nil { + return nil, err + } + + return types.NewBabylonTxResponse(resp), nil } // CommitPubRandList commits a list of Schnorr public randomness to Babylon CosmWasm contract @@ -169,6 +178,7 @@ func (cc *OPStackL2ConsumerController) CommitPubRandList( zap.Uint64("start_height", startHeight), zap.Uint64("num_pub_rand", numPubRand), ) + return &types.TxResponse{TxHash: res.TxHash}, nil } @@ -241,6 +251,7 @@ func (cc *OPStackL2ConsumerController) SubmitBatchFinalitySigs( zap.Uint64("start_height", blocks[0].Height), zap.Uint64("end_height", blocks[len(blocks)-1].Height), ) + return &types.TxResponse{TxHash: res.TxHash}, nil } @@ -270,6 +281,7 @@ func (cc *OPStackL2ConsumerController) QueryFinalityProviderHasPower(fpPk *btcec "FP has 0 voting power because there is no public randomness at this height", zap.Uint64("height", blockHeight), ) + return false, nil } @@ -307,6 +319,7 @@ func (cc *OPStackL2ConsumerController) QueryFinalityProviderHasPower(fpPk *btcec zap.String("fp_btc_pk", fpBtcPkHex), zap.Uint64("height", blockHeight), ) + return false, nil } @@ -329,14 +342,14 @@ func (cc *OPStackL2ConsumerController) QueryLatestFinalizedBlock() (*types.Block }, nil } -func (cc *OPStackL2ConsumerController) QueryBlocks(startHeight, endHeight, limit uint64) ([]*types.BlockInfo, error) { +func (cc *OPStackL2ConsumerController) QueryBlocks(startHeight, endHeight uint64, limit uint32) ([]*types.BlockInfo, error) { if startHeight > endHeight { return nil, fmt.Errorf("the start height %v should not be higher than the end height %v", startHeight, endHeight) } // limit the number of blocks to query count := endHeight - startHeight + 1 - if limit > 0 && count >= limit { - count = limit + if limit > 0 && count >= uint64(limit) { + count = uint64(limit) } // create batch requests @@ -345,7 +358,7 @@ func (cc *OPStackL2ConsumerController) QueryBlocks(startHeight, endHeight, limit for i := range batchElemList { batchElemList[i] = ethrpc.BatchElem{ Method: "eth_getBlockByNumber", - Args: []interface{}{hexutil.EncodeUint64(startHeight + uint64(i)), false}, + Args: []interface{}{hexutil.EncodeUint64(startHeight + uint64(i)), false}, // #nosec G115 Result: &blockHeaders[i], } } @@ -359,7 +372,7 @@ func (cc *OPStackL2ConsumerController) QueryBlocks(startHeight, endHeight, limit return nil, batchElemList[i].Error } if blockHeaders[i] == nil { - return nil, fmt.Errorf("got null header for block %d", startHeight+uint64(i)) + return nil, fmt.Errorf("got null header for block %d", startHeight+uint64(i)) // #nosec G115 } } @@ -375,9 +388,10 @@ func (cc *OPStackL2ConsumerController) QueryBlocks(startHeight, endHeight, limit "Successfully batch query blocks", zap.Uint64("start_height", startHeight), zap.Uint64("end_height", endHeight), - zap.Uint64("limit", limit), + zap.Uint32("limit", limit), zap.String("last_block_hash", hex.EncodeToString(blocks[len(blocks)-1].Hash)), ) + return blocks, nil } @@ -394,6 +408,7 @@ func (cc *OPStackL2ConsumerController) QueryBlock(height uint64) (*types.BlockIn zap.Uint64("height", height), zap.String("block_hash", hex.EncodeToString(blockHashBytes)), ) + return &types.BlockInfo{ Height: height, Hash: blockHashBytes, @@ -419,6 +434,7 @@ func (cc *OPStackL2ConsumerController) QueryIsBlockFinalized(height uint64) (boo if height > l2Block.Height { return false, nil } + return true, nil } @@ -427,18 +443,21 @@ func (cc *OPStackL2ConsumerController) QueryActivatedHeight() (uint64, error) { finalityGadgetClient, err := fgclient.NewFinalityGadgetGrpcClient(cc.Cfg.BabylonFinalityGadgetRpc) if err != nil { cc.logger.Error("failed to initialize Babylon Finality Gadget Grpc client", zap.Error(err)) + return math.MaxUint64, err } activatedTimestamp, err := finalityGadgetClient.QueryBtcStakingActivatedTimestamp() if err != nil { cc.logger.Error("failed to query BTC staking activate timestamp", zap.Error(err)) + return math.MaxUint64, err } l2BlockNumber, err := cc.GetBlockNumberByTimestamp(context.Background(), activatedTimestamp) if err != nil { cc.logger.Error("failed to convert L2 block number from the given BTC staking activation timestamp", zap.Error(err)) + return math.MaxUint64, err } @@ -495,8 +514,8 @@ func (cc *OPStackL2ConsumerController) QueryLastPublicRandCommit(fpPk *btcec.Pub func ConvertProof(cmtProof cmtcrypto.Proof) Proof { return Proof{ - Total: uint64(cmtProof.Total), - Index: uint64(cmtProof.Index), + Total: uint64(cmtProof.Total), // #nosec G115 + Index: uint64(cmtProof.Index), // #nosec G115 LeafHash: cmtProof.LeafHash, Aunts: cmtProof.Aunts, } @@ -533,16 +552,17 @@ func (cc *OPStackL2ConsumerController) GetBlockNumberByTimestamp(ctx context.Con for lowerBound <= upperBound { midBlockNumber := (lowerBound + upperBound) / 2 - block, err := cc.opl2Client.HeaderByNumber(ctx, big.NewInt(int64(midBlockNumber))) + block, err := cc.opl2Client.HeaderByNumber(ctx, big.NewInt(int64(midBlockNumber))) // #nosec G115 if err != nil { return math.MaxUint64, err } - if block.Time < targetTimestamp { + switch { + case block.Time < targetTimestamp: lowerBound = midBlockNumber + 1 - } else if block.Time > targetTimestamp { + case block.Time > targetTimestamp: upperBound = midBlockNumber - 1 - } else { + default: return midBlockNumber, nil } } @@ -550,30 +570,55 @@ func (cc *OPStackL2ConsumerController) GetBlockNumberByTimestamp(ctx context.Con return lowerBound, nil } +// QueryFinalityProviderSlashedOrJailed - returns if the fp has been slashed, jailed, err +// nolint:revive // Ignore stutter warning - full name provides clarity +func (cc *OPStackL2ConsumerController) QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (bool, bool, error) { + // TODO: implement slashed or jailed feature in OP stack L2 + return false, false, nil +} + +func (cc *OPStackL2ConsumerController) QueryFinalityActivationBlockHeight() (uint64, error) { + // TODO: implement finality activation feature in OP stack L2 + return 0, nil +} + +// nolint:revive // Ignore stutter warning - full name provides clarity +func (cc *OPStackL2ConsumerController) QueryFinalityProviderHighestVotedHeight(fpPk *btcec.PublicKey) (uint64, error) { + // TODO: implement highest voted height feature in OP stack L2 + return 0, nil +} + +// nolint:revive // Ignore stutter warning - full name provides clarity +func (cc *OPStackL2ConsumerController) UnjailFinalityProvider(fpPk *btcec.PublicKey) (*types.TxResponse, error) { + // TODO: implement unjail feature in OP stack L2 + return nil, nil +} + func (cc *OPStackL2ConsumerController) Close() error { cc.opl2Client.Close() + return cc.CwClient.Stop() } +// nolint:unparam func (cc *OPStackL2ConsumerController) isDelegationActive( btcStakingParams *btcstakingtypes.QueryParamsResponse, btcDel *btcstakingtypes.BTCDelegationResponse, ) (bool, error) { - covQuorum := btcStakingParams.GetParams().CovenantQuorum ud := btcDel.UndelegationResponse - if len(ud.GetDelegatorUnbondingSigHex()) > 0 { + if ud.DelegatorUnbondingInfoResponse != nil { return false, nil } - if uint32(len(btcDel.CovenantSigs)) < covQuorum { + if len(btcDel.CovenantSigs) < int(covQuorum) { // #nosec G115 return false, nil } - if len(ud.CovenantUnbondingSigList) < int(covQuorum) { + if len(ud.CovenantUnbondingSigList) < int(covQuorum) { // #nosec G115 return false, nil } - if len(ud.CovenantSlashingSigs) < int(covQuorum) { + if len(ud.CovenantSlashingSigs) < int(covQuorum) { // #nosec G115 return false, nil } diff --git a/clientcontroller/retry_utils.go b/clientcontroller/retry_utils.go index 10813622..71e405a4 100644 --- a/clientcontroller/retry_utils.go +++ b/clientcontroller/retry_utils.go @@ -42,6 +42,7 @@ func (e ExpectedError) Error() string { if e.error == nil { return "expected error" } + return e.error.Error() } @@ -52,6 +53,7 @@ func (e ExpectedError) Unwrap() error { // Is adds support for errors.Is usage on isExpected func (ExpectedError) Is(err error) bool { _, isExpected := err.(ExpectedError) + return isExpected } diff --git a/clientcontroller/retry_utils_test.go b/clientcontroller/retry_utils_test.go index d1f178a6..c2d887e6 100644 --- a/clientcontroller/retry_utils_test.go +++ b/clientcontroller/retry_utils_test.go @@ -8,6 +8,7 @@ import ( ) func TestExpectedErr(t *testing.T) { + t.Parallel() expectedErr := Expected(fmt.Errorf("some error")) require.True(t, IsExpected(expectedErr)) wrappedErr := fmt.Errorf("expected: %w", expectedErr) diff --git a/cosmwasmclient/client/client.go b/cosmwasmclient/client/client.go index d86d5f36..a5b4c848 100644 --- a/cosmwasmclient/client/client.go +++ b/cosmwasmclient/client/client.go @@ -2,6 +2,7 @@ package client import ( "context" + "fmt" "sync" "time" @@ -31,7 +32,7 @@ func New(cfg *config.CosmwasmConfig, chainName string, encodingCfg wasmdparams.E // ensure cfg is valid if err := cfg.Validate(); err != nil { - return nil, err + return nil, fmt.Errorf("invalid config: %w", err) } // use the existing logger or create a new one if not given @@ -39,7 +40,7 @@ func New(cfg *config.CosmwasmConfig, chainName string, encodingCfg wasmdparams.E if zapLogger == nil { zapLogger, err = newRootLogger("console", true) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create root logger: %w", err) } } @@ -50,10 +51,13 @@ func New(cfg *config.CosmwasmConfig, chainName string, encodingCfg wasmdparams.E chainName, ) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create cosmos provider: %w", err) } - cp := provider.(*cosmos.CosmosProvider) + cp, ok := provider.(*cosmos.CosmosProvider) + if !ok { + return nil, fmt.Errorf("failed to cast provider to CosmosProvider") + } cp.PCfg.KeyDirectory = cfg.KeyDirectory cp.Cdc = cosmos.Codec{ InterfaceRegistry: encodingCfg.InterfaceRegistry, @@ -66,9 +70,8 @@ func New(cfg *config.CosmwasmConfig, chainName string, encodingCfg wasmdparams.E // NOTE: this will create a RPC client. The RPC client will be used for // submitting txs and making ad hoc queries. It won't create WebSocket // connection with wasmd node - err = cp.Init(context.Background()) - if err != nil { - return nil, err + if err = cp.Init(context.Background()); err != nil { + return nil, fmt.Errorf("failed to initialize cosmos provider: %w", err) } // create a queryClient so that the Client inherits all query functions @@ -77,11 +80,11 @@ func New(cfg *config.CosmwasmConfig, chainName string, encodingCfg wasmdparams.E // see https://github.com/strangelove-ventures/cometbft-client c, err := rpchttp.NewWithTimeout(cp.PCfg.RPCAddr, "/websocket", uint(cfg.Timeout.Seconds())) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create RPC client: %w", err) } queryClient, err := query.NewWithClient(c, cfg.Timeout) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create query client: %w", err) } return &Client{ diff --git a/cosmwasmclient/client/contract.go b/cosmwasmclient/client/contract.go index 6be3efcd..3726affc 100644 --- a/cosmwasmclient/client/contract.go +++ b/cosmwasmclient/client/contract.go @@ -13,8 +13,8 @@ import ( sdkquery "github.com/cosmos/cosmos-sdk/types/query" ) -func (cwClient *Client) StoreWasmCode(wasmFile string) error { - wasmCode, err := os.ReadFile(wasmFile) +func (c *Client) StoreWasmCode(wasmFile string) error { + wasmCode, err := os.ReadFile(wasmFile) // #nosec G304 if err != nil { return err } @@ -33,10 +33,10 @@ func (cwClient *Client) StoreWasmCode(wasmFile string) error { } storeMsg := &wasmdtypes.MsgStoreCode{ - Sender: cwClient.MustGetAddr(), + Sender: c.MustGetAddr(), WASMByteCode: wasmCode, } - _, err = cwClient.ReliablySendMsg(context.Background(), storeMsg, nil, nil) + _, err = c.ReliablySendMsg(context.Background(), storeMsg, nil, nil) if err != nil { return err } @@ -44,17 +44,17 @@ func (cwClient *Client) StoreWasmCode(wasmFile string) error { return nil } -func (cwClient *Client) InstantiateContract(codeID uint64, initMsg []byte) error { +func (c *Client) InstantiateContract(codeID uint64, initMsg []byte) error { instantiateMsg := &wasmdtypes.MsgInstantiateContract{ - Sender: cwClient.MustGetAddr(), - Admin: cwClient.MustGetAddr(), + Sender: c.MustGetAddr(), + Admin: c.MustGetAddr(), CodeID: codeID, Label: "cw", Msg: initMsg, Funds: nil, } - _, err := cwClient.ReliablySendMsg(context.Background(), instantiateMsg, nil, nil) + _, err := c.ReliablySendMsg(context.Background(), instantiateMsg, nil, nil) if err != nil { return err } @@ -62,15 +62,15 @@ func (cwClient *Client) InstantiateContract(codeID uint64, initMsg []byte) error return nil } -func (cwClient *Client) ExecuteContract(contractAddr string, execMsg []byte, funds []sdk.Coin) error { +func (c *Client) ExecuteContract(contractAddr string, execMsg []byte, funds []sdk.Coin) error { executeMsg := &wasmdtypes.MsgExecuteContract{ - Sender: cwClient.MustGetAddr(), + Sender: c.MustGetAddr(), Contract: contractAddr, Msg: execMsg, Funds: funds, } - _, err := cwClient.ReliablySendMsg(context.Background(), executeMsg, nil, nil) + _, err := c.ReliablySendMsg(context.Background(), executeMsg, nil, nil) if err != nil { return err } @@ -79,12 +79,12 @@ func (cwClient *Client) ExecuteContract(contractAddr string, execMsg []byte, fun } // returns the latest wasm code id. -func (cwClient *Client) GetLatestCodeId() (uint64, error) { +func (c *Client) GetLatestCodeID() (uint64, error) { pagination := &sdkquery.PageRequest{ Limit: 1, Reverse: true, } - resp, err := cwClient.ListCodes(pagination) + resp, err := c.ListCodes(pagination) if err != nil { return 0, err } diff --git a/cosmwasmclient/client/keys.go b/cosmwasmclient/client/keys.go index baf6b7ba..178fe0c7 100644 --- a/cosmwasmclient/client/keys.go +++ b/cosmwasmclient/client/keys.go @@ -15,8 +15,9 @@ func (c *Client) GetAddr() (string, error) { func (c *Client) MustGetAddr() string { addr, err := c.provider.Address() if err != nil { - panic(fmt.Errorf("failed to get signer: %v", err)) + panic(fmt.Errorf("failed to get signer: %w", err)) } + return addr } diff --git a/cosmwasmclient/client/log.go b/cosmwasmclient/client/log.go index 6e1cb0db..8b546f4e 100644 --- a/cosmwasmclient/client/log.go +++ b/cosmwasmclient/client/log.go @@ -33,6 +33,7 @@ func newRootLogger(format string, debug bool) (*zap.Logger, error) { if debug { level = zap.DebugLevel } + return zap.New(zapcore.NewCore( enc, os.Stderr, diff --git a/cosmwasmclient/client/tx.go b/cosmwasmclient/client/tx.go index c3486ced..9861d3d0 100644 --- a/cosmwasmclient/client/tx.go +++ b/cosmwasmclient/client/tx.go @@ -17,8 +17,9 @@ import ( func ToProviderMsgs(msgs []sdk.Msg) []pv.RelayerMessage { relayerMsgs := []pv.RelayerMessage{} for _, m := range msgs { - relayerMsgs = append(relayerMsgs, cosmos.NewCosmosMessage(m, func(signer string) {})) + relayerMsgs = append(relayerMsgs, cosmos.NewCosmosMessage(m, func(_ string) {})) } + return relayerMsgs } @@ -42,11 +43,13 @@ func (c *Client) SendMsgsToMempool(ctx context.Context, msgs []sdk.Msg) error { }) if krErr != nil { c.logger.Error("unrecoverable err when submitting the tx, skip retrying", zap.Error(krErr)) + return retry.Unrecoverable(krErr) } + return sendMsgErr }, retry.Context(ctx), rtyAtt, rtyDel, rtyErr, retry.OnRetry(func(n uint, err error) { - c.logger.Debug("retrying", zap.Uint("attemp", n+1), zap.Uint("max_attempts", rtyAttNum), zap.Error(err)) + c.logger.Debug("retrying", zap.Uint("attempt", n+1), zap.Uint("max_attempts", rtyAttNum), zap.Error(err)) })); err != nil { return err } @@ -93,11 +96,13 @@ func (c *Client) ReliablySendMsgs(ctx context.Context, msgs []sdk.Msg, expectedE }) if krErr != nil { c.logger.Error("unrecoverable err when submitting the tx, skip retrying", zap.Error(krErr)) + return retry.Unrecoverable(krErr) } if sendMsgErr != nil { if errorContained(sendMsgErr, unrecoverableErrors) { c.logger.Error("unrecoverable err when submitting the tx, skip retrying", zap.Error(sendMsgErr)) + return retry.Unrecoverable(sendMsgErr) } if errorContained(sendMsgErr, expectedErrors) { @@ -106,13 +111,16 @@ func (c *Client) ReliablySendMsgs(ctx context.Context, msgs []sdk.Msg, expectedE // that the inside wg.Done will not be executed wg.Done() c.logger.Error("expected err when submitting the tx, skip retrying", zap.Error(sendMsgErr)) + return nil } + return sendMsgErr } + return nil }, retry.Context(ctx), rtyAtt, rtyDel, rtyErr, retry.OnRetry(func(n uint, err error) { - c.logger.Debug("retrying", zap.Uint("attemp", n+1), zap.Uint("max_attempts", rtyAttNum), zap.Error(err)) + c.logger.Debug("retrying", zap.Uint("attempt", n+1), zap.Uint("max_attempts", rtyAttNum), zap.Error(err)) })); err != nil { return nil, err } @@ -123,6 +131,7 @@ func (c *Client) ReliablySendMsgs(ctx context.Context, msgs []sdk.Msg, expectedE if errorContained(callbackErr, expectedErrors) { return nil, nil } + return nil, callbackErr } diff --git a/cosmwasmclient/config/encoding.go b/cosmwasmclient/config/encoding.go index 1d48948b..5525e7bb 100644 --- a/cosmwasmclient/config/encoding.go +++ b/cosmwasmclient/config/encoding.go @@ -19,6 +19,7 @@ func GetWasmdEncodingConfig() wasmdparams.EncodingConfig { if err != nil { panic("failed to create temp dir: " + err.Error()) } + return dir } diff --git a/cosmwasmclient/config/query_config.go b/cosmwasmclient/config/query_config.go index 13e170ea..0ab12ebc 100644 --- a/cosmwasmclient/config/query_config.go +++ b/cosmwasmclient/config/query_config.go @@ -19,6 +19,7 @@ func (cfg *WasmQueryConfig) Validate() error { if cfg.Timeout <= 0 { return fmt.Errorf("cfg.Timeout must be positive") } + return nil } diff --git a/cosmwasmclient/config/query_config_test.go b/cosmwasmclient/config/query_config_test.go index f48bb320..ddfad100 100644 --- a/cosmwasmclient/config/query_config_test.go +++ b/cosmwasmclient/config/query_config_test.go @@ -9,6 +9,7 @@ import ( // TestWasmQueryConfig ensures that the default Babylon query config is valid func TestWasmQueryConfig(t *testing.T) { + t.Parallel() defaultConfig := config.DefaultWasmQueryConfig() err := defaultConfig.Validate() require.NoError(t, err) diff --git a/cosmwasmclient/query/client.go b/cosmwasmclient/query/client.go index d132a027..43722b2c 100644 --- a/cosmwasmclient/query/client.go +++ b/cosmwasmclient/query/client.go @@ -13,6 +13,8 @@ import ( // QueryClient is a client that can only perform queries to a Babylon node // It only requires `Cfg` to have `Timeout` and `RPCAddr`, but not other fields // such as keyring, chain ID, etc.. +// +//nolint:revive type QueryClient struct { RPCClient rpcclient.Client timeout time.Duration @@ -66,5 +68,6 @@ func (c *QueryClient) IsRunning() bool { // (adapted from https://github.com/strangelove-ventures/lens/blob/v0.5.4/client/query/query_options.go#L29-L36) func (c *QueryClient) getQueryContext() (context.Context, context.CancelFunc) { ctx, cancel := context.WithTimeout(context.Background(), c.timeout) + return ctx, cancel } diff --git a/cosmwasmclient/query/tendermint.go b/cosmwasmclient/query/tendermint.go index cf00304d..e2642e55 100644 --- a/cosmwasmclient/query/tendermint.go +++ b/cosmwasmclient/query/tendermint.go @@ -47,7 +47,7 @@ func (c *QueryClient) GetTx(hash []byte) (*coretypes.ResultTx, error) { return c.RPCClient.Tx(ctx, hash, false) } -func (c *QueryClient) Subscribe(subscriber, query string, outCapacity ...int) (out <-chan coretypes.ResultEvent, err error) { +func (c *QueryClient) Subscribe(subscriber, query string, outCapacity ...int) (<-chan coretypes.ResultEvent, error) { return c.RPCClient.Subscribe(context.Background(), subscriber, query, outCapacity...) } diff --git a/cosmwasmclient/query/wasm.go b/cosmwasmclient/query/wasm.go index 59d45afb..decea1f2 100644 --- a/cosmwasmclient/query/wasm.go +++ b/cosmwasmclient/query/wasm.go @@ -26,6 +26,7 @@ func (c *QueryClient) ListCodes(pagination *sdkquerytypes.PageRequest) (*wasmtyp Pagination: pagination, } resp, err = queryClient.Codes(ctx, req) + return err }) @@ -41,6 +42,7 @@ func (c *QueryClient) ListContractsByCode(codeID uint64, pagination *sdkquerytyp Pagination: pagination, } resp, err = queryClient.ContractsByCode(ctx, req) + return err }) @@ -56,6 +58,7 @@ func (c *QueryClient) QuerySmartContractState(contractAddress string, queryData QueryData: wasmtypes.RawContractMessage(queryData), } resp, err = queryClient.SmartContractState(ctx, req) + return err }) diff --git a/docs/commit-pub-rand.md b/docs/commit-pub-rand.md new file mode 100644 index 00000000..fd3d1215 --- /dev/null +++ b/docs/commit-pub-rand.md @@ -0,0 +1,139 @@ +# Public Randomness Commit Specification + +## Overview + +The finality provider periodically commits public randomness to the consumer +chain to be used for future block finalization. This document specifies the +process of committing public randomness. + +## Commit Process + +### Generating a Commit + +A randomness pair is essentially a pair of `32-byte` points over `secp256k1`. +A public randomness commit is a list of public +randomness, each committed to a specific height. In particular, a commit +consists of: + +- a merkle root containing a list of public randomness values, +- a start height, indicating from which height the randomness starts, and +- the number of randomness contained in the merkle tree. + +To generate a new commit, following steps are needed: + +1. Generate a list of randomness. This requires an RPC call to the EOTS manager + (eotsd) to generate a list of public randomness, each corresponding to a + specific height according to the start height and the number of randomness in + the request. Randomness generation is required to be deterministic. +2. Construct the merkle tree based on the list of randomness using the CometBFT's [merkle](https://github.com/cometbft/cometbft/tree/main/crypto/merkle) + library. The merkle root will be used in the commit, while each randomness + number and their merkle proofs will be used for finality vote submission + in the future. +3. Send a [Schnorr](https://github.com/btcsuite/btcd/blob/684d64ad74fed203fb846c032f2b55b3e3c36734/btcec/schnorr/signature.go#L391) + signature request to the EOTS manager over the hash of the commit + (concatenated by the start height, number of randomness, and the merkle root). +4. Build the commit message ([MsgCommitPubRandList](https://github.com/babylonlabs-io/babylon/blob/aa99e2eb093e06cb9a28a58f373e8fa5f2494383/proto/babylon/finality/v1/tx.proto#L29)) + and send a transaction to Babylon. + +### Timing to Commit + +Public randomness is an essential component of finality. It should be +committed before finality votes can be sent. Otherwise, the finality provider +looses voting power for this height. + +To this end, when a finality provider is started, it runs a loop to periodically +check whether it needs to make a new commit and calculate the start height of +the next commit. In particular: + +```go + tipHeightWithDelay := tipHeight + uint64(fp.cfg.TimestampingDelayBlocks) + var startHeight uint64 + switch { + case lastCommittedHeight < tipHeightWithDelay: + // the start height should consider the timestamping delay + // as it is only available to use after tip height + estimated timestamping delay + startHeight = tipHeightWithDelay + case lastCommittedHeight < tipHeightWithDelay+uint64(fp.cfg.NumPubRand): + startHeight = lastCommittedHeight + 1 + default: + // randomness is sufficient, do not need to make a commit +``` + +where: + +- `lastCommittedHeight` is the end height (`startHeight + numRand - 1`) +from the latest public randomness commit recorded on the consumer chain +- `tipHeight` is the current height of the consumer chain +- `TimestampingDelayBlocks` is a configuration value, which measures when to make a + new commit +- `NumPubRand` is the number of randomness in a commit defined in the config. + +### Determining TimestampingDelayBlocks + +The value of `TimestampingDelayBlocks` must account for BTC-timestamping +delays, which is needed to activate the randomness for a specific height +after the committed epoch is BTC-timestamped. Here's an example: + +- The consumer chain receives a commit with: + - Start height: 100 + - Number of randomness values: 1000 + - Current epoch: 10 +- This means randomness for heights [100, 1099] becomes available after epoch 10 + is finalized + +The BTC-timestamping protocol requires: + +- 100 BTC blocks for epoch finalization +- ≈ 1000 minutes (17 hours) at 10-minute average block time +- With consumer chain blocks every 10 seconds, this equals approximately 6,000 + blocks + +Therefore, + +- `TimestampingDelayBlocks` should be around 6,000 +- Recommended production value: > 10,000 to provide additional safety margin + +### Determining Start Height + +To determine the start height of a commit: + +1. For first-time commit: + - `startHeight = baseHeight + 1`, + - where `baseHeight` is a future height which is estimated based on the + BTC-timestamping delays. +2. For subsequent commit: + - `startHeight = lastCommittedHeight + 1`, + - where `lastCommittedHeight` is obtained from the consumer chain. + +The `baseHeight` can be specified via configuration or CLI options. + +**Important Notes:** + +- After long downtime, treat as first-time commit by specifying `baseHeight`. +- Consecutiveness across commits is not enforced by the system but + different commits must not overlap. +- `startHeight` should not be higher than `finalityActivationHeight`, +a parameter defined in Babylon. Therefore, +`startHeight = max(startHeight, finalityActivationHeight)`. + +### Determining the Number of Randomness + +The number of randomness contained in a commit is specified in the config +`NumPubRand`. A general strategy is that the value should be as large +as possible. This is because each commit to the consumer chain costs gas. + +However, in real life, this stategy might not always gain due to the following +reasons: + +- A finality provider might not have voting power for every block. Randomness + for those heights is a waste. +- Generating more randomness leads to a larger merkle proof size which will be + used for sending finality votes. +- Generating randomness and saving the merkle proofs require time. + +Additionally, given that the end height of a commit equals to +`startHeight + NumPubRand - 1`, we should ensure that the condition +`lastCommittedHeight > tipHeight + uint64(TimestampingDelayBlocks)` can hold for +a long period of time to avoid frequent commit of randomness. +In real life, the value of `NumPubRand` should be much larger than +`TimestampingDelayBlocks`, e.g., `NumPubRand = 2 * TimestampingDelayBlocks`. diff --git a/docs/eots.md b/docs/eots.md deleted file mode 100644 index 8b20cb03..00000000 --- a/docs/eots.md +++ /dev/null @@ -1,188 +0,0 @@ -# EOTS Manager - -## 1. Overview - -The EOTS daemon is responsible for managing EOTS keys, producing EOTS randomness, and -using them to produce EOTS signatures. - -**Note:** EOTS stands for Extractable One Time Signature. You can read more about it -in -the [Babylon BTC Staking Litepaper](https://docs.babylonchain.io/assets/files/btc_staking_litepaper-32bfea0c243773f0bfac63e148387aef.pdf). -In short, the EOTS manager produces EOTS public/private randomness pairs. The -finality provider commits the public part of these pairs to Babylon for every future -block height that they intend to provide a finality signature for. If the finality -provider votes for two different blocks on the same height, they will have to reuse -the same private randomness which will lead to their underlying private key being -exposed, leading to the slashing of them and all their delegators. - -The EOTS manager is responsible for the following operations: - -1. **EOTS Key Management:** - - Generates [Schnorr](https://en.wikipedia.org/wiki/Schnorr_signature) key pairs - for a given finality provider using the - [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) - standard. - - Persists generated key pairs in the internal Cosmos keyring. -2. **Randomness Generation:** - - Generates lists of EOTS randomness pairs based on the EOTS key, chainID, and - block height. - - The randomness is deterministically generated and tied to specific parameters. -3. **Signature Generation:** - - Signs EOTS using the private key of the finality provider and the corresponding - secret randomness for a given chain at a specified height. - - Signs Schnorr signatures using the private key of the finality provider. - -The EOTS manager functions as a daemon controlled by the `eotsd` tool. - -## 2. Configuration - -The `eotsd init` command initializes a home directory for the EOTS manager. This -directory is created in the default home location or in a location specified by -the `--home` flag. - -```bash -eotsd init --home /path/to/eotsd/home/ -``` - -After initialization, the home directory will have the following structure - -```bash -ls /path/to/eotsd/home/ - ├── eotsd.conf # Eotsd-specific configuration file. - ├── logs # Eotsd logs -``` - -If the `--home` flag is not specified, then the default home location will be used. -For different operating systems, those are: - -- **MacOS** `~/Users//Library/Application Support/Eotsd` -- **Linux** `~/.Eotsd` -- **Windows** `C:\Users\\AppData\Local\Eotsd` - -## 3. Keys Management - -Handles the keys for EOTS. - -### 3.1. Create EOTS Keys - -The binary `eotsd` has the option to add a new key to the keyring for -later usage with signing EOTS and Schnorr signatures. Keep in mind -that new keys can be created on demand by the GRPC call from `fpd`. -But, if you would like to add a new EOTS key manually, run `eotsd keys add`. - -This command has several flag options: - -- `--home` specifies the home directory of the `eotsd` in which -the new key will be stored. -- `--key-name` mandatory flag and identifies the name of the key to be generated. -- `--passphrase` specifies the password used to encrypt the key, if such a -passphrase is required. -- `--hd-path` the hd derivation path of the private key. -- `--keyring-backend` specifies the keyring backend, any of `[file, os, kwallet, test, pass, memory]` -are available, by default `test` is used. -- `--recover` indicates whether the user wants to provide a seed phrase to recover -the existing key instead of randomly creating. - -```shell -eotsd keys add --home /path/to/eotsd/home/ --key-name my-key-name --keyring-backend file - -Enter keyring passphrase (attempt 1/3): -... - -2024-04-25T17:11:09.369163Z info successfully created an EOTS key {"key name": "my-key-name", "pk": "50b106208c921b5e8a1c45494306fe1fc2cf68f33b8996420867dc7667fde383"} -New key for the BTC chain is created (mnemonic should be kept in a safe place for recovery): -{ - "name": "my-key-name", - "pub_key_hex": "50b106208c921b5e8a1c45494306fe1fc2cf68f33b8996420867dc7667fde383", - "mnemonic": "bad mnemonic private tilt wish bulb miss plate achieve manage feel word safe dash vanish little miss hockey connect tail certain spread urban series" -} -``` - -> Store the mnemonic in a safe place. With the mnemonic only it is possible to -recover the generated keys by using the `--recover` flag. - -### 3.2. Recover Keys - -To recover the keys from a mnemonic, run: - -```shell -eotsd keys add --home /path/to/eotsd/home/ --key-name my-key-name --keyring-backend file --recover - -> Enter your mnemonic -bad mnemonic private tilt wish bulb miss plate achieve manage feel word safe dash vanish little miss hockey connect tail certain spread urban series -2024-04-25T17:13:29.681324Z info successfully created an EOTS key {"key name": "my-key-name", "pk": "50b106208c921b5e8a1c45494306fe1fc2cf68f33b8996420867dc7667fde383"} -New key for the BTC chain is created (mnemonic should be kept in a safe place for recovery): -{ - "name": "my-key-name", - "pub_key_hex": "50b106208c921b5e8a1c45494306fe1fc2cf68f33b8996420867dc7667fde383", - "mnemonic": "noise measure tuition inform battle swallow slender bundle horn pigeon wage mule average bicycle claim solve home swamp banner idle chapter surround edit gossip" -} -``` - -You will be prompted to provide the mnemonic on key creation. - -### 3.3. Sign Schnorr Signatures - -You can use your key to create a Schnorr signature over arbitrary data -through the `eotsd sign-schnorr` command. -The command takes as an argument the file path, hashes the file content using -sha256, and signs the hash with the EOTS private key in Schnorr format by the -given `key-name` or `eots-pk`. If both flags `--key-name` and `--eots-pk` are -provided, `eots-pk` takes priority. - -```shell -eotsd sign-schnorr /path/to/data/file --home /path/to/eotsd/home/ --key-name my-key-name --keyring-backend file -{ - "key_name": "my-key-name", - "pub_key_hex": "50b106208c921b5e8a1c45494306fe1fc2cf68f33b8996420867dc7667fde383", - "signed_data_hash_hex": "b123ef5f69545cd07ad505c6d3b4931aa87b6adb361fb492275bb81374d98953", - "schnorr_signature_hex": "b91fc06b30b78c0ca66a7e033184d89b61cd6ab572329b20f6052411ab83502effb5c9a1173ed69f20f6502a741eeb5105519bb3f67d37612bc2bcce411f8d72" -} -``` - -### 3.4. Verify Schnorr Signatures - -You can verify the Schnorr signature signed in the previous step through -the `eptsd veify-schnorr-sig` command. -The command takes as an argument the file path, hashes the file content using -sha256 to generate the signed data, and verifies the signature from the `--signature` -flag using the given public key from the `--eots-pk` flag. -If the signature is valid, you will see `Verification is successful!` in the output. -Otherwise, an error message will be printed out. - -```shell -eotsd verify-schnorr-sig /path/to/data/file --eots-pk 50b106208c921b5e8a1c45494306fe1fc2cf68f33b8996420867dc7667fde383 \ ---signature b91fc06b30b78c0ca66a7e033184d89b61cd6ab572329b20f6052411ab83502effb5c9a1173ed69f20f6502a741eeb5105519bb3f67d37612bc2bcce411f8d72 \ ---keyring-backend file -``` - -## 4. Starting the EOTS Daemon - -You can start the EOTS daemon using the following command: - -```bash -eotsd start --home /path/to/eotsd/home -``` - -If the `--home` flag is not specified, then the default home location will be used. - -This will start the EOTS rpc server at the address specified in `eotsd.conf` under -the `RpcListener` field, which is by default set to `127.0.0.1:12582`. You can change -this value in the configuration file or override this value and specify a custom -address using the `--rpc-listener` flag. - -```bash -eotsd start - -2024-02-08T17:59:11.467212Z info RPC server listening {"address": "127.0.0.1:12582"} -2024-02-08T17:59:11.467660Z info EOTS Manager Daemon is fully active! -``` - -All the available cli options can be viewed using the `--help` flag. These options -can also be set in the configuration file. - -**Note**: It is recommended to run the `eotsd` daemon on a separate machine or -network segment to enhance security. This helps isolate the key management -functionality and reduces the potential attack surface. You can edit the -`EOTSManagerAddress` in the configuration file of the finality provider to reference -the address of the machine where `eotsd` is running. diff --git a/docs/finality-provider-operation.md b/docs/finality-provider-operation.md new file mode 100644 index 00000000..c98d878d --- /dev/null +++ b/docs/finality-provider-operation.md @@ -0,0 +1,787 @@ +# Finality Provider Operation + +This document guides operators through the complete +lifecycle of running a finality provider, including: + +* Installing and configuring the finality provider toolset + (EOTS Manager and Finality Provider daemon) +* Managing keys (EOTS key for signatures and Babylon key for rewards) +* Registering your finality provider on the Babylon network +* Operating and maintaining your finality provider +* Collecting rewards + +This is an operational guide intended for technical finality provider administrators. +For conceptual understanding, see our [Technical Documentation](./fp-core.md). +Please review the [high-level explainer](../README.md) before proceeding to +gain an overall understanding of the finality provider. + +## Table of Contents + +1. [A note about Phase-1 Finality Providers](#1-a-note-about-phase-1-finality-providers) +2. [Install Finality Provider Toolset](#2-install-finality-provider-toolset) +3. [Setting up the EOTS Daemon](#3-setting-up-the-eots-daemon) + 1. [Initialize the EOTS Daemon](#31-initialize-the-eots-daemon) + 2. [Add an EOTS Key](#32-add-an-eots-key) + 1. [Create an EOTS key](#321-create-an-eots-key) + 2. [Import an existing EOTS key](#322-import-an-existing-eots-key) + 3. [Starting the EOTS Daemon](#33-starting-the-eots-daemon) +4. [Setting up the Finality Provider](#4-setting-up-the-finality-provider) + 1. [Initialize the Finality Provider Daemon](#41-initialize-the-finality-provider-daemon) + 2. [Add key for the Babylon account](#42-add-key-for-the-babylon-account) + 3. [Configure Your Finality Provider](#43-configure-your-finality-provider) + 4. [Starting the Finality Provider Daemon](#44-starting-the-finality-provider-daemon) +5. [Finality Provider Operation](#5-finality-provider-operations) + 1. [Create Finality Provider](#51-create-finality-provider) + 2. [Editing your finality provider](#52-editing-your-finality-provider) + 3. [Withdrawing Rewards](#53-withdrawing-rewards) + 4. [Jailing and Unjailing](#54-jailing-and-unjailing) + 5. [Slashing](#55-slashing) + 6. [Prometheus Metrics](#56-prometheus-metrics) + 7. [Withdrawing Rewards](#57-withdrawing-rewards) + +## 1. A note about Phase-1 Finality Providers + +Thank you for participating in the first phase of the Babylon launch. This guide +provides instructions for setting up the full finality provider toolset required +for your participation in the second phase of the Babylon launch. + +Finality providers that received delegations on the first phase of the launch +are required to transition their finality providers to the second phase +using the same EOTS key that they used and registered with during Phase-1. +The usage of a different key corresponds to setting up an entirely +different finality provider which will not inherit the Phase-1 delegations. +Not transitioning your Phase-1 finality provider prevents your Phase-1 delegations +from transitioning to the second phase. + +If you already have set up a key during Phase-1, please proceed to +[Adding Keys](#32-add-an-eots-key) to import your Phase-1 key. + +## 2. Install Finality Provider Toolset + + +The finality provider toolset requires [Golang 1.23](https://go.dev/dl) +to be installed. +Please follow the installation instructions [here](https://go.dev/dl). +You can verify the installation with the following command: + +```shell +go version +``` + +### 2.1. Clone the Finality Provider Repository + +Subsequently, clone the finality provider +[repository](https://github.com/babylonlabs-io/finality-provider) and checkout +to `v0.14.4`. + +```shell +git clone https://github.com/babylonlabs-io/finality-provider.git +cd finality-provider +git checkout v0.14.4 +``` + +### 2.2. Install Finality Provider Toolset Binaries + +Run the following command to build the binaries and +install them to your `$GOPATH/bin` directory: + +```shell +make install +``` + +This command will: +- Build and compile all Go packages +- Install binaries to `$GOPATH/bin`: + - `eotsd`: EOTS manager daemon + - `fpd`: Finality provider daemon +- Make commands globally accessible from your terminal + +### 2.3. Verify Installation + +Run the following command to verify the installation: + +```shell +fpd version +Version: 0.14.4 +Git Commit: c8c41f0 +Git Timestamp: 2025-01-27T14:17:02Z +``` + +If your shell cannot find the installed binaries, make sure `$GOPATH/bin` is in +the `$PATH` of your shell. Use the following command to add it to your profile +depending on your shell. + +```shell +echo 'export PATH=$HOME/go/bin:$PATH' >> ~/.profile +``` + +## 3. Setting up the EOTS Daemon + +The EOTS manager daemon is a core component of the finality provider +stack responsible for managing your EOTS keys and producing EOTS signatures +to be used for votes. In this section, we are going to go through +its setup and key generation process. + +### 3.1. Initialize the EOTS Daemon + +If you haven't already, initialize a home directory for the EOTS Manager +with the following command: + +```shell +eotsd init --home +``` + +If `eotsd.conf` already exists `init` will not succeed, if the operator wishes to +overwrite the config file they need to use `--force`. + +Parameters: +- `--home`: Directory for EOTS Manager configuration and data + - Default: `DefaultEOTSDir` the default EOTS home directory: + - `C:\Users\\AppData\Local\ on Windows` + - `~/.eotsd` on Linux + - `~/Library/Application Support/Eotsd` on MacOS + - Example: `--home ./eotsHome` + +### 3.2. Add an EOTS Key + +This section explains the process of setting up the private keys for the +EOTS manager. Operators *must* create an EOTS key before starting the +EOTS daemon. + +We will be using the [Cosmos SDK](https://docs.cosmos.network/v0.50/user/run-node/keyring) +backends for key storage. + +Since this key is accessed by an automated daemon process, it must be stored +unencrypted on disk and associated with the `test` keyring backend. +This ensures that we can access the eots keys when requested to promptly submit +transactions, such as block votes and public randomness submissions that are +essential for its liveness and earning of rewards. + +If you already have an existing key from Phase-1, proceed to +[Import an existing EOTS key](#321-import-an-existing-eots-key) + +#### 3.2.1. Create an EOTS key + +If you have not created an EOTS key, +use the following command to create a new one: + +``` shell +eotsd keys add --home --keyring-backend test +``` + +Parameters: +- ``: Name for your EOTS key (e.g., "eots-key-1"). We do not allow +the same `keyname` for an existing keyname. +- `--home`: Path to your EOTS daemon home directory (e.g., "~/.eotsHome") +- `--keyring-backend`: Type of keyring storage (`test`) + +The command will return a JSON response containing your EOTS key details: + +``` +{ + "name": "eots", + "pub_key_hex": + "e1e72d270b90b24f395e76b417218430a75683bd07cf98b91cf9219d1c777c19", + "mnemonic": "parade hybrid century project toss gun undo ocean exercise + figure decorate basket peace raw spot gap dose daring patch ski purchase + prefer can pair" +} +``` + +> **🔒 Security Tip**: The mnemonic phrase must be stored securely and kept private. +> It is the only way to recover your EOTS key if you lose access to it and +> if lost it can be used by third parties to get access to your key. + +#### 3.2.2. Import an existing EOTS key + +> ⚡ This section is for Finality Providers who already possess an EOTS key. +> If you don't have keys or want to create new ones, you can skip this section. + +There are 3 supported methods of loading your existing EOTS keys: +1. using a mnemonic phrase +2. importing the `.asc` file +3. importing a backed up home directory + +We have outlined each of these three paths for you below. + +#### Option 1: Using your mnemonic phrase + +If you are using your mnemonic seed phrase, use the following command to import +your key: + +```shell +eotsd keys add --home --recover --keyring-backend test +``` + +You'll be prompted to enter: +1. Your BIP39 mnemonic phrase (24 words) +2. HD path (optional - press Enter to use the default) + +> ⚡ The HD path is optional. If you used the default path when creating your key, +you can skip this by pressing `Enter` , which by default uses your original private +key. + +#### Option 2: Using your `.asc` file + +If you exported your key to a `.asc` file. The `.asc` file should be in the +following format: + +``` +-----BEGIN TENDERMINT PRIVATE KEY----- +salt: 35ED0BBC00376EC7FC696838F34A7C36 +type: secp256k1 +kdf: argon2 + +8VOGhpuaZhTPZrKHysx24OhaxuBhVnKqb3WcTwJY+jvfNv/EJRoqmrHZfCnNgd13 +VP88GFE= +=D87O +-----END TENDERMINT PRIVATE KEY----- +``` + +To load the key, use the following command: + +```shell +eotsd keys import --home --keyring-backend test +``` + +#### Option 3: Using a File System Backup + +If you backed up your entire EOTS home directory, +you can load it manually to the machine you intend to operate +the EOTS daemon on and specify its location as the `--home` argument. + +#### Verify the Key Import + +After importing, you can verify that your EOTS key was successfully loaded: + +```shell +eotsd keys list --home --keyring-backend test +``` + +Parameters: +* ``: Name of the EOTS key to verify +* `--keyring-backend`: Type of keyring backend to use (`test`) +* `--home`: Directory containing EOTS Manager configuration and data + +You should see your EOTS key listed with the correct details, confirming that +it has been imported correctly. + +> ⚠️ **Important**: +> If you are a finality provider transitioning your stack from Phase-1, +> make sure that you are using the same EOTS key that you +> registered in Phase-1. + +### 3.3. Starting the EOTS Daemon + +To start the EOTS daemon, use the following command: + +```shell +eotsd start --home +``` + +This command starts the EOTS RPC server at the address specified in `eotsd.conf` +under the `RPCListener` field (default: `127.0.0.1:12582`). You can override +this value by specifying a custom address with the `--rpc-listener` flag. + +```shell +2024-10-30T12:42:29.393259Z info Metrics server is starting +{"addr": "127.0.0.1:2113"} +2024-10-30T12:42:29.393278Z info RPC server listening{"address": "127.0.0.1:12582"} +2024-10-30T12:42:29.393363Z info EOTS Manager Daemon is fully active! +EOTS Manager Daemon is fully active! +``` + +>**🔒 Security Tip**: +> * `eotsd` holds your private keys which are used for signing +> * operate the daemon in a separate machine or network segment +> with enhanced security +> * only allow access to the RPC server specified by the `RPCListener` +> port to trusted sources. You can edit the `EOTSManagerAddress` in +> the configuration file of the finality provider to +> reference the address of the machine where `eotsd` is running + +## 4. Setting up the Finality Provider + +### 4.1. Initialize the Finality Provider Daemon + +To initialize the finality provider daemon home directory, +use the following command: + +```shell +fpd init --home +``` + +If `fpd.conf` already exists `init` will not succeed, if the operator wishes to +overwrite the config file they need to use `--force`. + + +> ⚡ Running this command may return the message +> `service injective.evm.v1beta1.Msg does not have cosmos.msg.v1.service proto annotation`, +> which is expected and can be ignored. + +### 4.2. Add key for the Babylon account + +Each finality provider maintains a Babylon keyring containing +an account used to receive BTC Staking reward commissions and pay fees for +transactions necessary for the finality provider's operation. + +Since this key is accessed by an automated daemon process, it must be stored +unencrypted on disk and associated with the `test` keyring backend. +This ensures that the finality provider daemon can promptly submit +transactions, such as block votes and public randomness submissions that are +essential for its liveness and earning of rewards. + +For the `fpd` keyring, the `test` backend will be exclusively used, and it is +mandatory that you follow this practice until automated key management becomes +available. Additionally, we are also exploring options to support different +withdrawal addresses, so that rewards can go to a separate address. + +It is also important to note that the finality provider daemon will refund +fees for the submission of valid votes as those are essential for the protocol. +All other transactions, will require gas, but will be happening infrequently +or only once. As this keyring is used for both earning and +operational purposes, we strongly recommend maintaining only the necessary +funds for operations in the keyring, and extracting the rest into +more secure locations. + +> ⚠️ **Important**: +> To operate your Finality Provider, ensure your Babylon account is funded. +> Block vote transactions have their gas fees refunded, but public randomness +> submissions require gas payments. For testnet, you can obtain funds from our +> testnet faucet. + +Use the following command to add the Babylon key for your finality provider: + +```shell +fpd keys add --keyring-backend test --home +``` + +The above `keys add` command will create a new key pair and store it in your keyring. +The output should look similar to the one below: + +``` json +{ + "address": "bbn19gulf0a4yz87twpjl8cxnerc2wr2xqm9fsygn9", + "name": "finality-provider", + "pubkey": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "AhZAL00gKplLQKpLMiXPBqaKCoiessoewOaEATKd4Rcy" + }, + "type": "local" +} +``` + +### 4.3. Configure Your Finality Provider + +Edit the `fpd.conf` file in your finality provider home directory with the +following parameters: + +```shell +[Application Options] +EOTSManagerAddress = 127.0.0.1:12582 +RPCListener = 127.0.0.1:12581 + +[babylon] +Key = # the key you used above +ChainID = bbn-test-5 # chain ID of the Babylon chain +RPCAddr = http://127.0.0.1:26657 +GRPCAddr = https://127.0.0.1:9090 +KeyDirectory = # The `--home` path to the directory where the keyring is stored +``` + +> ⚠️ **Important**: Operating a finality provider requires a connection to a +> Babylon blockchain node. It is **highly recommended** to operate your own +> Babylon full node instead of relying on third parties. You can find +> instructions on setting up a Babylon node +> [here](https://github.com/babylonlabs-io/networks/tree/main/bbn-test-5/babylon-node/README.md). + +> ⚠️ **Critical RPC Configuration**: +> When configuring your finality provider to a Babylon RPC node, you should +> connect to a **single** node directly. Additionally you **must** +> ensure that this node has transaction indexing enabled (`indexer = "kv"`). +> Using multiple RPC nodes or load balancers can lead to sync issues. + +Configuration parameters explained: +* `EOTSManagerAddress`: Address where your EOTS daemon is running +* `RPCListener`: Address for the finality provider RPC server +* `Key`: Your Babylon key name from Step 2 +* `ChainID`: The Babylon network chain ID +* `RPCAddr`: Your Babylon node's RPC endpoint +* `GRPCAddr`: Your Babylon node's GRPC endpoint +* `KeyDirectory`: Path to your keyring directory (same as `--home` path) + +Please verify the `chain-id` and other network parameters from the official +[Babylon Networks +repository](https://github.com/babylonlabs-io/networks/tree/main/bbn-test-5/). + +### 4.4. Starting the Finality Provider Daemon + +The finality provider daemon (FPD) needs to be running before proceeding with +registration or voting participation. + +Start the daemon with: + +``` shell +fpd start --home +``` +An example of the `--home` flag is `--home ./fpHome`. + +The command flags: +- `start`: Runs the FPD daemon +- `--home`: Specifies the directory for daemon data and configuration +- `--eots-pk`: The finality provider instance that will be started identified + by the EOTS public key. + +It will start the finality provider daemon listening for registration and other +operations. If there is already a finality provider created (described in a +later [section](#51-create-finality-provider)), `fpd start` will also start +the finality provider. If there are multiple finality providers created, +`--eots-pk` is required. + +The daemon will establish a connection with the Babylon node and +boot up its RPC server for executing CLI requests. + +You should see logs indicating successful startup: + +``` +[INFO] Starting finality provider daemon... +[INFO] RPC server listening on... +``` + +> ⚠️ **Important**: The daemon needs to run continuously. It's recommended to set +> up a system service (like `systemd` on Linux or `launchd` on macOS) to manage +> the daemon process, handle automatic restarts, and collect logs. + +The above will start the Finality provider RPC server at the address specified +in `fpd.conf` under the `RPCListener` field, which has a default value +of `127.0.0.1:12581`. You can change this value in the configuration file or +override this value and specify a custom address using +the `--rpc-listener` flag. + +All the available CLI options can be viewed using the `--help` flag. These +options can also be set in the configuration file. + +### 4.5. Interaction with the EOTS Manager + +There are two pieces to a finality provider entity: the EOTS manager and the +finality provider instance. These components work together and are managed by +separate daemons (`eotsd` and `fpd`). + +The EOTS manager is responsible for managing the keys for finality providers and +handles operations such as key management, signature generation, and randomness +commitments. Whereas the finality provider is responsible for creating and +registering finality providers and handling the monitoring of the Babylon chain. +The finality provider daemon is also responsible for coordinating various +operations. + +The interactions between the EOTS Manager and the finality provider happen +through RPC calls. These calls handle key operations, signature generation, +and randomness commitments. An easy way to think about it is the EOTS Manager +maintains the keys while the FP daemon coordinates any interactions with the +Babylon chain. + +The EOTS Manager is designed to handle multiple finality provider keys, operating +as a centralized key management system. When starting a finality provider instance, +you specify which EOTS key to use through the `--eots-pk` flag. This allows you +to run different finality provider instances using different keys from the same +EOTS Manager. Note that someone having access to your EOTS Manager +RPC will have access to all the EOTS keys held within it. + +For example, after registering a finality provider, you can start its daemon by +providing the EOTS public key `fpd start --eots-pk `. +Note that a single finality provider daemon can only run with a single +finality provider instance at a time. + +## 5. Finality Provider Operations + +### 5.1 Create Finality Provider + +The `create-finality-provider` command initializes a new finality provider, +submits `MsgCreateFinalityProvider` to register it on the Babylon chain, and +saves the finality provider information in the database. + +``` shell +fpd create-finality-provider \ + --chain-id bbn-test-5 \ + --eots-pk \ + --commission-rate 0.05 \ + --key-name finality-provider \ + --moniker "MyFinalityProvider" \ + --website "https://myfinalityprovider.com" \ + --security-contact "security@myfinalityprovider.com" \ + --details "finality provider for the Babylon network" \ + --home ./fpHome +``` + +Required parameters: +- `--chain-id`: The Babylon chain ID (e.g., for the testnet, `bbn-test-5`) +- `--eots-pk`: The EOTS public key maintained by the connected EOTS manager + instance that the finality provider should use. If one is not provided the + finality provider will request the creation of a new one from the connected + EOTS manager instance. +- `--commission`: The commission rate (between 0 and 1) that you'll receive from + delegators +- `--key-name`: The key name in your Babylon keyring that your finality + provider will be associated with +- `--moniker`: A human-readable name for your finality provider +- `--home`: Path to your finality provider daemon home directory + +> ⚠️ **Important**: The same EOTS key should not be used by different +> finality providers. This could lead to slashing. + +Optional parameters: +- `--website`: Your finality provider's website +- `--security-contact`: Contact email for security issues +- `--details`: Additional description of your finality provider +- `--daemon-address`: RPC address of the finality provider daemon + (default: `127.0.0.1:12581`) + + +Alternatively, you can create a finality provider by providing a JSON file +with the finality provider details, similar to the following: + +```json +{ + "keyName": "The unique key name of the finality provider's Babylon account", + "chainID": "The identifier of the consumer chain", + "passphrase": "The pass phrase used to encrypt the keys", + "commissionRate": "The commission rate for the finality provider, e.g., 0.05", + "moniker": "A human-readable name for the finality provider", + "identity": "A optional identity signature", + "website": "Validator's (optional) website", + "securityContract": "Validator's (optional) security contact email", + "details": "Validator's (optional) details", + "eotsPK": "The hex string of the finality provider's EOTS public key" +} +``` + +To create a finality provider using the JSON file, you can use the following command: + +```shell +fpd create-finality-provider --json-file +``` + +Upon successful creation, the command will return a JSON response containing +your finality provider's details: + +``` json +{ + "finality_provider": + { + "fp_addr": "bbn1ht2nxa6hlyl89m8xpdde9xsj40n0sxd2f9shsq", + "eots_pk_hex": + "cf0f03b9ee2d4a0f27240e2d8b8c8ef609e24358b2eb3cfd89ae4e4f472e1a41", + "description": + { + "moniker": "MyFinalityProvider", + "website": "https://myfinalityprovider.com", + "security_contact": "security@myfinalityprovider.com", + "details": "finality provider for the Babylon network" + }, + "commission": "0.050000000000000000", + "status": "REGISTERED" + } + "tx_hash": "C08377CF289DF0DC5FA462E6409ADCB65A3492C22A112C58EA449F4DC544A3B1" +} +Your finality provider is successfully created. Please restart your fpd. +``` + +The response includes: +- `fp_addr`: Your Babylon account address of the finality provider +- `eots_pk_hex`: Your unique EOTS public key identifier +- `description`: Your finality provider's metadata +- `commission`: Your set commission rate +- `status`: Current status of the finality provider. +- `tx_hash`: Babylon transaction hash of the finality provider creation + transaction, which you can use to verify the success of the transaction + on the Babylon chain. + +Below you can see a list of the statuses that a finality provider can transition +to: +- `REGISTERED`: defines a finality provider that has been created and registered + to the consumer chain but has no delegated stake +- `ACTIVE`: defines a finality provider that is delegated to vote +- `INACTIVE`: defines a finality provider whose delegations are reduced to + zero but not slashed +- `JAILED`: defines a finality provider that has been jailed +- `SLASHED`: Defines a finality provider that has been permanently removed from + the network for double signing (signing conflicting blocks at the same height). + This state is irreversible. + +To check the status of a finality provider, you can use the following command: + +```shell +fpd finality-provider-info +``` +This will return the same response as the `create-finality-provider` +command but you will be able to check in real time the status of the +finality provider. + +For more information on statuses please refer to diagram in the core documentation +[fp-core](fp-core.md). + +After successful registration, you may start the finality provider instance +by running: +```shell +fpd start --eots-pk +``` + +If `--eots-pk` is not specified, the command will start the finality provider +if it is the only one stored in the database. If multiple finality providers +are in the database, specifying `--eots-pk` is required. + +### 5.2. Editing your finality provider + +If you need to edit your finality provider's information, you can use the +following command: + +```shell +fpd edit-finality-provider \ + --commission-rate \ + --home + # Add any other parameters you would like to modify +``` + +Parameters: +- ``: The EOTS public key of the finality provider +- `--commission-rate`: A required flag for the commission rate for the finality + provider +- `--home`: An optional flag for the path to your finality provider daemon home + directory + +Parameters you can edit: +- `--moniker`: A human-readable name for your finality provider +- `--website`: Your finality provider's website +- `--security-contact`: Contact email for security issues +- `--details`: Additional description of your finality provider + +You can then use the following command to check if the finality provider has been +edited successfully: + +```shell +fpd finality-provider-info +``` + +### 5.3. Withdrawing Rewards + +As a participant in the Finality Provider Program, you will earn rewards that +can be withdrawn. The functionality for withdrawing rewards is currently under +development and will be available soon. Further updates will be provided once +this feature is implemented. + +### 5.4. Jailing and Unjailing + +When jailed, the following happens to a finality provider: +- Their voting power becomes `0` +- Status is set to `JAILED` +- Delegator rewards stop + +To unjail a finality provider, you must complete the following steps: +1. Fix the underlying issue that caused jailing (e.g., ensure your node is + properly synced and voting) +2. Wait for the jailing period to pass (defined by finality module parameters) +3. Send the unjail transaction to the Babylon chain using the following command: + +```shell +fpd unjail-finality-provider --daemon-address --home +``` + +Parameters: +- ``: Your finality provider's EOTS public key in hex format +- `--daemon-address`: RPC server address of fpd (default: `127.0.0.1:12581`) +- `--home`: Path to your finality provider daemon home directory + +> ⚠️ Before unjailing, ensure you've fixed the underlying issue that caused jailing + +If unjailing is successful, you may start running the finality provider by +`fpd start --eots-pk `. + +### 5.5. Slashing + +**Slashing occurs** when a finality provider **double signs**, meaning that the +finality provider signs conflicting blocks at the same height. This results in +the extraction of the finality provider's private key and their immediate +removal from the active set. + +> ⚠️ **Critical**: Slashing is irreversible and results in +> permanent removal from the network. + +### 5.6. Prometheus Metrics + +The finality provider exposes Prometheus metrics for monitoring your +finality provider. The metrics endpoint is configurable in `fpd.conf`: + +#### Core Metrics + +1. **Status for Finality Providers** + - `fp_status`: Current status of a finality provider + - `babylon_tip_height`: The current tip height of the Babylon network + - `last_polled_height`: The most recent block height checked by the poller + +2. **Key Operations** + - `fp_seconds_since_last_vote`: Seconds since the last finality sig vote + - `fp_seconds_since_last_randomness`: Seconds since the last public + randomness commitment + - `fp_total_failed_votes`: The total number of failed votes + - `fp_total_failed_randomness`: The total number of failed + randomness commitments + +Each metric with `fp_` prefix includes the finality provider's BTC public key +hex as a label. + +> 💡 **Tip**: Monitor these metrics to detect issues before they lead to jailing: +> - Large gaps in `fp_seconds_since_last_vote` +> - Increasing `fp_total_failed_votes` + +For a complete list of available metrics, see: +- Finality Provider metrics: [fp_collectors.go](../metrics/fp_collectors.go) +- EOTS metrics: [eots_collectors.go](../metrics/eots_collectors.go) + +### 5.7. Withdrawing Rewards + +When you are ready to withdraw your rewards, you have the option first to set +the address to withdraw your rewards to. + +```shell +fpd set-withdraw-addr --from +--keyring-backend test --home --fees +``` + +Parameters: +- ``: The new address to withdraw rewards to. +- `--from`: The finality provider's registered Babylon address. +- `--keyring-backend`: The keyring backend to use. +- `--home`: The home directory for the finality provider. +- `--fees`: The fees to pay for the transaction, should be over `400ubbn`. + These fees are paid from the account specified in `--from`. + +This command should ask you to +`confirm transaction before signing and broadcasting [y/N]:` and output the +transaction hash. + +Once you have set the address, you can withdraw your rewards by running the +following command: + +```shell +fpd withdraw-reward --from +--keyring-backend test --home --fees +``` + +Parameters: +- ``: The type of reward to withdraw (one of `finality_provider`, + `btc_delegation`) +- `--from`: The finality provider's registered Babylon address. +- `--keyring-backend`: The keyring backend to use. +- `--home`: The home directory for the finality provider. +- `--fees`: The fees to pay for the transaction, should be over `400ubbn`. + These fees are paid from the account specified in `--from`. + +Again, this command should ask you to +`confirm transaction before signing and broadcasting [y/N]:` and output the +transaction hash. + +This will withdraw **ALL** accumulated rewards to the address you set in the +`set-withdraw-addr` command if you set one. If no withdrawal address was set, +the rewards will be withdrawn to your finality provider address. + +Congratulations! You have successfully set up and operated a finality provider. diff --git a/docs/finality-provider.md b/docs/finality-provider.md deleted file mode 100644 index 527eafc6..00000000 --- a/docs/finality-provider.md +++ /dev/null @@ -1,262 +0,0 @@ -# Finality Provider - -## 1. Overview - -The Finality Provider Daemon is responsible for monitoring for new Babylon blocks, -committing public randomness for the blocks it intends to provide finality signatures -for, and submitting finality signatures. - -The daemon can manage and perform the following operations for multiple finality -providers: - -1. **Creation and Registration**: Creates and registers finality providers to - Babylon. -2. **EOTS Randomness Commitment**: The daemon monitors the Babylon chain and commits - EOTS public randomness for every Babylon block each finality provider intends to - vote for. The commit intervals can be specified in the configuration. The EOTS - public randomness is retrieved through the finality provider daemon's connection - with the [EOTS daemon](eots.md). -3. **Finality Votes Submission**: The daemon monitors the Babylon chain and produces - finality votes for each block each maintained finality provider has committed to - vote for. - -The daemon is controlled by the `fpd` tool, which has overall commands for -interacting with the running daemon. - -## 2. Configuration - -The `fpd init` command initializes a home directory for the finality provider daemon. -This directory is created in the default home location or in a location specified by -the `--home` flag. - -```bash -fpd init --home /path/to/fpd/home/ -``` - -After initialization, the home directory will have the following structure - -```bash -ls /path/to/fpd/home/ - ├── fpd.conf # Fpd-specific configuration file. - ├── logs # Fpd logs -``` - -If the `--home` flag is not specified, then the default home directory will be used. -For different operating systems, those are: - -- **MacOS** `~/Users//Library/Application Support/Fpd` -- **Linux** `~/.Fpd` -- **Windows** `C:\Users\\AppData\Local\Fpd` - -Below are some important parameters of the `fpd.conf` file. - -**Note**: -The configuration below requires pointing to the path where this keyring is -stored `KeyDirectory`. This `Key` field stores the key name of the finality -provider wallet that is going to be used for interacting with the consumer -chain and to sign the messages of your finality provider. It will be specified -along with the `KeyringBackend` field in the next -[step](#3-add-key-for-the-consumer-chain). So we can ignore the setting of the -two fields in this step. - -```bash -[Application Options] -# RPC Address of the EOTS Daemon -EOTSManagerAddress = 127.0.0.1:12582 - -# RPC Address of the Finality Provider Daemon -RpcListener = 127.0.0.1:12581 - -[babylon] -# Name of the key of the finality provider to sign transactions with -Key = - -# Chain id of the chain to connect to -# Please verify the `ChainID` from the Babylon RPC node https://rpc.testnet3.babylonchain.io/status -ChainID = bbn-test-3 - -# RPC Address of Babylon node -RPCAddr = http://127.0.0.1:26657 - -# GRPC Address of Babylon node -GRPCAddr = https://127.0.0.1:9090 - -# Directory to store keys in -KeyDirectory = /path/to/fpd/home -``` - -To see the complete list of configuration options, check the `fpd.conf` file. - -**Additional Notes:** - -If you encounter any gas-related errors while performing staking operations, consider -adjusting the `GasAdjustment` and `GasPrices` parameters. For example, you can set: - -```bash -GasAdjustment = 1.5 -GasPrices = 0.002ubbn -``` - -## 3. Add key for the consumer chain - -The finality provider daemon requires the existence of a keyring that contains an -account for the finality provider with Babylon token funds to pay and sign -transactions. This key identifies the finality provider and will be also used -to pay for fees of transactions to the consumer chain. - -Use the following command to add the key: - -```bash -fpd keys add my-finality-provider -``` - -After executing the above command, the key name will be saved in the config file -created in [step](#2-configuration). - -## 4. Starting the Finality Provider Daemon - -You can start the finality provider daemon using the following command: - -```bash -fpd start --home /path/to/fpd/home -``` - -If the `--home` flag is not specified, then the default home location will be used. - -This will start the Finality provider RPC server at the address specified -in `fpd.conf` under the `RpcListener` field, which has a default value -of `127.0.0.1:12581`. You can change this value in the configuration file or override -this value and specify a custom address using the `--rpc-listener` flag. - -This will also start all the registered finality provider instances except for -slashed ones added in [step](#5-create-and-register-a-finality-provider). To start -the daemon with a specific finality provider instance, use the -`--eots-pk` flag followed by the hex string of the BTC public key of the finality -provider (`btc_pk_hex`) obtained -in [step](#5-create-and-register-a-finality-provider). - -```bash -fpd start - -2024-02-08T18:43:00.705008Z info successfully connected to a remote EOTS manager {"address": "127.0.0.1:12582"} -2024-02-08T18:43:00.712995Z info Starting FinalityProviderApp -2024-02-08T18:43:00.716682Z info RPC server listening {"address": "127.0.0.1:12581"} -2024-02-08T18:43:00.716979Z info Finality Provider Daemon is fully active! -``` - -All the available CLI options can be viewed using the `--help` flag. These options -can also be set in the configuration file. - -## 5. Create and Register a Finality Provider - -We create a finality provider instance through the -`fpd create-finality-provider` or `fpd cfp` command. The created instance is -associated with a BTC public key which serves as its unique identifier and a Babylon -account to which staking rewards will be directed. Note that if the `--key-name` flag -is not specified, the `Key` field of config specified -in [step](#3-add-key-for-the-consumer-chain) will be used. - -```bash -fpd create-finality-provider --key-name my-finality-provider \ - --chain-id bbn-test-3 --moniker my-name -{ - "fp_addr": "bbn19khdh5vf8zv9x49f84cfuxx5t45m7klwq827mp", - "btc_pk_hex": "d0fc4db48643fbb4339dc4bbf15f272411716b0d60f18bdfeb3861544bf5ef63", - "description": { - "moniker": "my-name" - }, - "status": "CREATED" -} -``` - -We register a created finality provider in Babylon through -the `fpd register-finality-provider` or `fpd rfp` command. The output contains -the hash of the Babylon finality provider registration transaction. - -```bash -fpd register-finality-provider \ - --eots-pk d0fc4db48643fbb4339dc4bbf15f272411716b0d60f18bdfeb3861544bf5ef63 -{ - "tx_hash": "800AE5BBDADE974C5FA5BD44336C7F1A952FAB9F5F9B43F7D4850BA449319BAA" -} - -``` - -A finality provider instance will be initiated and start running right after the -finality provider is successfully registered in Babylon. - -We can view the status of all the running finality providers through -the `fpd list-finality-providers` or `fpd ls` command. The `status` field can -receive the following values: - -- `CREATED`: The finality provider is created but not registered yet -- `REGISTERED`: The finality provider is registered but has not received any active - delegations yet -- `ACTIVE`: The finality provider has active delegations and is empowered to send - finality signatures -- `INACTIVE`: The finality provider used to be ACTIVE but the voting power is reduced - to zero -- `SLASHED`: The finality provider is slashed due to malicious behavior - -```bash -fpd list-finality-providers -{ - "finality-providers": [ - ... - { - "fp_addr": "bbn19khdh5vf8zv9x49f84cfuxx5t45m7klwq827mp", - "btc_pk_hex": "d0fc4db48643fbb4339dc4bbf15f272411716b0d60f18bdfeb3861544bf5ef63", - "description": { - "moniker": "my-name" - }, - "last_vote_height": 1 - "status": "REGISTERED" - } - ] -} -``` - -After the creation of the finality provider in the local db, it is possible -to export the finality provider information through the `fpd export-finality-provider` command. -This command connects with the `fpd` daemon to retrieve the finality -provider previously created using the flag `--eots-pk` as key. - -This command also has several flag options: - -- `--eots-pk` the hex string of the BTC public key. -- `--daemon-address` the RPC server address of `fpd` daemon. -- `--signed` signs the finality provider with the chain key of the PoS -chain secured as a proof of untempered exported data. -- `--key-name` identifies the name of the key to use to sign the finality provider. -- `--home` specifies the home directory of the finality provider daemon in which -the finality provider db is stored. -- `--passphrase` specifies the password used to encrypt the key, if such a -passphrase is required. -- `--hd-path` the hd derivation path of the private key. - -```shell -$ fpd export-finality-provider --eots-pk 02face5996b2792114677604ec9dfad4fe66eeace3df92dab834754add5bdd7077 \ ---home ./export-fp/fpd --key-name finality-provider --signed -``` - -The expected result is a JSON object corresponding to the finality provider information. - -```json -{ - "description": { - "moniker": "my-fp-nickname", - "identity": "anyIdentity", - "website": "www.my-public-available-website.com", - "security_contact": "your.email@gmail.com", - "details": "other overall info" - }, - "commission": "0.050000000000000000", - "fp_addr": "bbn19khdh5vf8zv9x49f84cfuxx5t45m7klwq827mp", - "btc_pk": "02face5996b2792114677604ec9dfad4fe66eeace3df92dab834754add5bdd7077", - "pop": { - "btc_sig": "sHLpEHVTyTp9K55oeHxnPlkV4unc/r1obqzKn5S1gq95oXA3AgL1jyCzd/mGb23RfKbEyABjYUdcIBtZ02l5jg==" - }, - "master_pub_rand": "xpub661MyMwAqRbcFLhUq9uPM7GncSytVZvoNg4w7LLx1Y74GeeAZerkpV1amvGBTcw4ECmrwFsTNMNf1LFBKkA2pmd8aJ5Jmp8uKD5xgVSezBq", - "fp_sig_hex": "8ded8158bf65d492c5c6d1ff61c04a2176da9c55ea92dcce5638d11a177b999732a094db186964ab1b73c6a69aaa664672a36620dedb9da41c05e88ad981edda" -} -``` diff --git a/docs/finality-toolset.png b/docs/finality-toolset.png deleted file mode 100644 index c165ba21..00000000 Binary files a/docs/finality-toolset.png and /dev/null differ diff --git a/docs/fp-core.md b/docs/fp-core.md new file mode 100644 index 00000000..99173686 --- /dev/null +++ b/docs/fp-core.md @@ -0,0 +1,92 @@ +# Finality Provider Core + +This doc is to provide basic invariants and heuristics one should follow to +build the finality provider. This part should be agnostic to any consumer +chains. + +## Status Transitions + +A finality provider has the following 5 possible states: `Registered`, +`Inactive`, `Active`, `Jailed`, `Slashed`. The state transition is depicted +in the following diagram. + +![Finality Provider Status Transition](./static/fp-status-transition.png). + +## Invariants + +We define invariants that the finality provider should satisfy: + +- The finality provider should submit a finality signature for a block if: + - the block is not finalized, + - the finality provider has positive voting power for this block, + - the finality provider has not cast a finality signature for this block, and + - the finality provider has cast finality votes for all non-finalized blocks + prior to this block for which it has voting power. + +- The finality provider should commit public randomness for a block if: + - the block is not finalized, and + - the finality provider has not committed any randomness for this block + +- A finality provider instance should not be running if it’s status is `slashed` + or `jailed` on Babylon. + +### Internal variables + +The finality provider maintains the following internal variables. +* `last_voted_height`: the last block height on which the fp has cast a vote, +* `last_processed_height`: the last block height on which the fp either has + cast a vote or it does not have voting power, and +* `next_height`: the next block to poll. + +Internal variables need to satisfy the following rules: +* `last_processed_height >= last_voted_height` should always hold, and +* `next_height = max{latest_finalized_height, last_processed_height} + 1` should + always hold. + +## Heuristics + +### Sending finality votes + +* The poller polls blocks from `start_height` one by one monotonically. +* `start_height` should be the least height that satisfy + `start_height = max{latest_finalized_height, last_processed_height} + 1`. +* For the next block from the poller, the finality provider retries to send + a finality signature until the invariant is not satisfied. + +### Committing public randomness + +* Finality providers should start to send public randomness commit right + after it is registered on Babylon. This is because a public randomness commit + only takes into effect after the committed epoch is finalized. So the finality + provider should start sending the commit as soon as possible. +* If the remaining randomness is not sufficient (defined by config) by the + current babylon height, the finality provider should make a new commit. +* The next commit should be made with `start_height` right after the last + committed height. This is to ensure no gap between each commit. + +The above approach of committing public randomness may cause waste of some +public randomness in cases where the finality provider does not have voting +power for some heights, but given that the randomness are constructed in a +merkle-tree and the commit only contains the root hash of the tree. Therefore, +the waste is negligible. + +### Fast sync + +Fast sync is needed for bootstrap and in case the finality provider falls +behind due to network latency. + +* The finality provider enters fast sync if: + * the finality provider is bootstrapping, or + * the `current_block_height - last_processed_height > gap` (configured value). + This condition might not be needed if we can do batching in normal processing + loop. + +During fast sync: +1. block the poller, +2. find a range of blocks starting from `last_processed_height + 1` to + `current_block_height`, +3. check invariant against each of the blocks, batch signature messages, and + send it to Babylon, +4. clear the buffer of the poller and reset the next height of the poller to + `last_processed_height + 1`, and +5. unblock the poller. diff --git a/docs/pop_format_spec.md b/docs/pop_format_spec.md new file mode 100644 index 00000000..0a390be7 --- /dev/null +++ b/docs/pop_format_spec.md @@ -0,0 +1,80 @@ +# Proof of Possession (PoP) Specification + +## Overview + +The Proof of Possession (PoP) structured specification outlined in this +document allows for the verification of the mutual ownership of a Babylon +key pair and an EOTS key pair. In the following, we outline the five essential +attributes exposed by the `PoPExport` structure and provide examples and +validation procedures. + +## Attributes + +The `PoPExport` structure is defined bellow: + +```go +// PoPExport the data needed to prove ownership of the eots and babylon key pairs. +type PoPExport struct { + // Btc public key is the EOTS PK *bbntypes.BIP340PubKey marshal hex + EotsPublicKey string `json:"eotsPublicKey"` + // Babylon public key is the *secp256k1.PubKey marshal hex + BabyPublicKey string `json:"babyPublicKey"` + + // Babylon key pair signs EOTS public key as hex + BabySignEotsPk string `json:"babySignEotsPk"` + // Schnorr signature of EOTS private key over the SHA256(Baby address) + EotsSignBaby string `json:"eotsSignBaby"` + + // Babylon address ex.: bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm + BabyAddress string `json:"babyAddress"` +} +``` + +Detailed specification of each field: + +- `EotsPublicKey`: The EOTS public key of the finality provider in hexadecimal format. +- `BabyPublicKey` – The Babylon secp256k1 public key in base64 format. +- `BabyAddress` – The Babylon account address (`bbn` prefix). The address is +derived from the `BabyPublicKey` and used as the primary identifier on the +Babylon network. +- `EotsSignBaby` – A Schnorr signature in base64 format, created by signing the +`sha256(BabyAddress)` with the EOTS private key. +- `BabySignEotsPk` – A signature of the `EotsPublicKey`, created by the Babylon +private key. This signature follows the Cosmos +[ADR-036](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-036-arbitrary-signature.md) +specification and is encoded in base64. + +## Example + +Below is an example JSON representation of the PoPExport structure: + +```json +{ + "eotsPublicKey": "3d0bebcbe800236ce8603c5bb1ab6c2af0932e947db4956a338f119797c37f1e", + "babyPublicKey": "A0V6yw74EdvoAWVauFqkH/GVM9YIpZitZf6bVEzG69tT", + "babySignEotsPk": "AOoIG2cwC2IMiJL3OL0zLEIUY201X1qKumDr/1qDJ4oQvAp78W1nb5EnVasRPQ/XrKXqudUDnZFprLd0jaRJtQ==", + "eotsSignBaby": "pR6vxgU0gXq+VqO+y7dHpZgHTz3zr5hdqXXh0WcWNkqUnRjHrizhYAHDMV8gh4vks4PqzKAIgZ779Wqwf5UrXQ==", + "babyAddress": "bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm" +} +``` + +## Validation + +The function responsible for validating the `PoPExport` is `VerifyPopExport`, +which can be found [here](https://github.com/babylonlabs-io/finality-provider/blob/cc07bcd4dc434f7095668724aad6865bffe425e0/eotsmanager/cmd/eotsd/daemon/pop.go#L211). + +`VerifyPopExport` ensures the authenticity and integrity of the `PoPExport` +by cross-verifying the provided signatures and public keys. This process +consists of two core validation steps: + +- `ValidEotsSignBaby` – This function checks the validity of the Schnorr +signature `(EotsSignBaby)` by verifying that the EOTS private key has correctly +signed the SHA256 hash of the BABY address. +- `ValidBabySignEots` – This function confirms that the BABY private key has +signed the EOTS public key `(EotsPublicKey)`, ensuring mutual validation +between the key pairs. + +If both signatures pass verification, the export is deemed valid, confirming +that the finality provider holds both key pairs. This function plays a critical +role in maintaining trust and security in the finality provider's key +management process. diff --git a/docs/send-finality-vote.md b/docs/send-finality-vote.md new file mode 100644 index 00000000..8bd8b411 --- /dev/null +++ b/docs/send-finality-vote.md @@ -0,0 +1,99 @@ +# Finality Votes Submission Specification + +## Overview + +Finality providers submit votes to finalize blocks on the consumer chain. +This document specifies the process of submitting finality votes. + +## Submission Process + +### Internal State + +The finality provider maintains a persistent state variable `lastVotedHeight` +to track the most recent height where a finality vote was successfully submitted +This is to prevent voting for a previously voted height. + +### Bootstrapping + +To determine the initial processing height `startHeight`: + +1. Query consumer chain for: + - `lastFinalizedHeight` (defaults to `0` if no blocks are finalized) + - `finalityActivationHeight` + - `lastVotedHeightRemote` (defaults to `0` if no votes exist) + +2. Synchronize local state: + - Verify local `lastVotedHeight` >= `lastVotedHeightRemote` + - If verification fails, update local state to match remote + +3. Calculate starting height based on reward distribution policy: + - If rewards are available for already finalized blocks: + + ```go + startHeight = max(lastVotedHeight + 1, finalityActivationHeight) + ``` + + - If rewards are only for unfinalized blocks: + + ```go + startHeight = max(lastFinalizedHeight + 1, finalityActivationHeight) + ``` + +The choice between using `lastVotedHeight` or `lastFinalizedHeight` depends on +the consumer chain's reward distribution mechanism. +Use `lastVotedHeight` if the chain allows collecting rewards for already +finalized blocks. Otherwise, use `lastFinalizedHeight` to only process +unfinalized blocks. + +### Normal Submission Loop + +After the finality provider is bootstrap, it continuously monitors for +new blocks from a trusted full node of the consumer chain. +For each new block, it performs these validation checks: + +1. Block hasn't been previously processed +2. Block height exceeds finality activation height +3. Finality provider has sufficient voting power + +Upon passing all checks, the finality provider: + +1. Requests a finality signature from the EOTS manager +2. Submits the vote transaction to the consumer chain +3. Implements retry logic until either: + - Maximum retry attempts are reached + - Block becomes finalized + +#### Batch submission + +A batch submission mechanism is needed to deal with cases where: + +- recovery from downtime, and +- the consumer chain has rapid block production. + +Batch sumission puts multiple new blocks into a batch and +process them in the same loop, after which all the finality votes will be sent +in the same transaction to the consumer chain. + +### Generating Finality Votes + +To submit a finality vote, the finality provider needs to fill the +[MsgAddFinalitySig](https://github.com/babylonlabs-io/babylon/blob/e7ac8fdf888406b16727b9ffca1f2e48364e9f53/x/finality/types/tx.pb.go#L154): + +1. Finality provider public key: the BTC PK of the finality provider that casts + the vote in [BIP340 format](https://github.com/babylonlabs-io/babylon/blob/79615c6b057de041a9f4c1c4466ef212a0c678d6/types/btc_schnorr_pk.go#L14). +2. Block height: the height of the block that the vote is signed for. +3. Public randomness: the public randomness that is retrieved from the local, + which is a [32-byte point](https://github.com/babylonlabs-io/babylon/blob/5f8af8ced17d24f3f0c6172293cd37fb3d055807/types/btc_schnorr_pub_rand.go#L12) over `secp256k1`. +4. Merkle proof: the merkle proof of the public randomness, which is generated + when constructing the public randomness commit using the CometBFT's [merkle](https://github.com/cometbft/cometbft/tree/main/crypto/merkle) + library. +5. Block hash: the hash bytes of the block that the vote is signed. +6. Finality signature: the [EOTS signature](https://github.com/babylonlabs-io/babylon/blob/067082b9d3dd8dbe775d5ada70cd60151fe0f577/types/btc_schnorr_eots.go#L11) + that is [signed](https://github.com/babylonlabs-io/babylon/blob/f19de7d0fcc4ea786a070a700a03d2cde3f57b7f/crypto/eots/eots.go#L54) + by the finality provider's private key and the corresponding private randomness. + +The consumer chain verifies: + +- The finality provider has voting power for the given height +- Randomness was pre-committed and BTC-timestamped +- EOTS signature validity diff --git a/docs/static/finality-provider-arch.png b/docs/static/finality-provider-arch.png new file mode 100644 index 00000000..2f202e9a Binary files /dev/null and b/docs/static/finality-provider-arch.png differ diff --git a/docs/static/fp-status-transition.png b/docs/static/fp-status-transition.png new file mode 100644 index 00000000..74222b43 Binary files /dev/null and b/docs/static/fp-status-transition.png differ diff --git a/eotsmanager/client/rpcclient.go b/eotsmanager/client/rpcclient.go index 821ecb18..3d0a17f7 100644 --- a/eotsmanager/client/rpcclient.go +++ b/eotsmanager/client/rpcclient.go @@ -83,6 +83,16 @@ func (c *EOTSManagerGRpcClient) CreateRandomnessPairList(uid, chainID []byte, st return pubRandFieldValList, nil } +func (c *EOTSManagerGRpcClient) SaveEOTSKeyName(pk *btcec.PublicKey, keyName string) error { + req := &proto.SaveEOTSKeyNameRequest{ + KeyName: keyName, + EotsPk: pk.SerializeUncompressed(), + } + _, err := c.client.SaveEOTSKeyName(context.Background(), req) + + return err +} + func (c *EOTSManagerGRpcClient) KeyRecord(uid []byte, passphrase string) (*types.KeyRecord, error) { req := &proto.KeyRecordRequest{Uid: uid, Passphrase: passphrase} @@ -118,6 +128,25 @@ func (c *EOTSManagerGRpcClient) SignEOTS(uid, chaiID, msg []byte, height uint64, return &s, nil } +func (c *EOTSManagerGRpcClient) UnsafeSignEOTS(uid, chaiID, msg []byte, height uint64, passphrase string) (*btcec.ModNScalar, error) { + req := &proto.SignEOTSRequest{ + Uid: uid, + ChainId: chaiID, + Msg: msg, + Height: height, + Passphrase: passphrase, + } + res, err := c.client.UnsafeSignEOTS(context.Background(), req) + if err != nil { + return nil, err + } + + var s btcec.ModNScalar + s.SetByteSlice(res.Sig) + + return &s, nil +} + func (c *EOTSManagerGRpcClient) SignSchnorrSig(uid, msg []byte, passphrase string) (*schnorr.Signature, error) { req := &proto.SignSchnorrSigRequest{Uid: uid, Msg: msg, Passphrase: passphrase} res, err := c.client.SignSchnorrSig(context.Background(), req) diff --git a/eotsmanager/cmd/eotsd/daemon/flags.go b/eotsmanager/cmd/eotsd/daemon/flags.go index 42ec8901..0a21ac87 100644 --- a/eotsmanager/cmd/eotsd/daemon/flags.go +++ b/eotsmanager/cmd/eotsd/daemon/flags.go @@ -1,22 +1,22 @@ package daemon -import "github.com/cosmos/cosmos-sdk/crypto/keyring" - const ( - homeFlag = "home" - forceFlag = "force" - rpcListenerFlag = "rpc-listener" - eotsPkFlag = "eots-pk" - signatureFlag = "signature" - - // flags for keys - keyNameFlag = "key-name" - passphraseFlag = "passphrase" - hdPathFlag = "hd-path" - keyringBackendFlag = "keyring-backend" - recoverFlag = "recover" - - defaultKeyringBackend = keyring.BackendTest - defaultHdPath = "" - defaultPassphrase = "" + keyNameFlag = "key-name" + eotsPkFlag = "eots-pk" + passphraseFlag = "passphrase" + forceFlag = "force" + rpcListenerFlag = "rpc-listener" + rpcClientFlag = "rpc-client" + flagInteractive = "interactive" + flagNoBackup = "no-backup" + flagMultisig = "multisig" + flagMultiSigThreshold = "multisig-threshold" + flagPubKeyBase64 = "pubkey-base64" + flagNoSort = "nosort" + flagCoinType = "coin-type" + flagAccount = "account" + flagHDPath = "hd-path" + flagIndex = "index" + flagRecover = "recover" + flagMnemonicSrc = "source" ) diff --git a/eotsmanager/cmd/eotsd/daemon/init.go b/eotsmanager/cmd/eotsd/daemon/init.go index b2f33dcb..cdfd6b7a 100644 --- a/eotsmanager/cmd/eotsd/daemon/init.go +++ b/eotsmanager/cmd/eotsd/daemon/init.go @@ -2,41 +2,35 @@ package daemon import ( "fmt" - "path/filepath" "github.com/jessevdk/go-flags" - "github.com/urfave/cli" + "github.com/spf13/cobra" eotscfg "github.com/babylonlabs-io/finality-provider/eotsmanager/config" "github.com/babylonlabs-io/finality-provider/util" ) -var InitCommand = cli.Command{ - Name: "init", - Usage: "Initialize the eotsd home directory.", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: homeFlag, - Usage: "Path to where the home directory will be initialized", - Value: eotscfg.DefaultEOTSDir, - }, - cli.BoolFlag{ - Name: forceFlag, - Usage: "Override existing configuration", - Required: false, - }, - }, - Action: initHome, +func NewInitCmd() *cobra.Command { + initCmd := &cobra.Command{ + Use: "init ", + Short: "Initialize the eotsd home directory.", + RunE: initHome, + } + + initCmd.Flags().Bool(forceFlag, false, "Override existing configuration") + + return initCmd } -func initHome(c *cli.Context) error { - homePath, err := filepath.Abs(c.String(homeFlag)) +func initHome(cmd *cobra.Command, _ []string) error { + homePath, err := getHomePath(cmd) + if err != nil { + return err + } + force, err := cmd.Flags().GetBool(forceFlag) if err != nil { return err } - // Create home directory - homePath = util.CleanAndExpandPath(homePath) - force := c.Bool(forceFlag) if util.FileExists(homePath) && !force { return fmt.Errorf("home path %s already exists", homePath) @@ -60,5 +54,5 @@ func initHome(c *cli.Context) error { defaultConfig.DatabaseConfig.DBPath = dataDir fileParser := flags.NewParser(defaultConfig, flags.Default) - return flags.NewIniParser(fileParser).WriteFile(eotscfg.ConfigFile(homePath), flags.IniIncludeComments|flags.IniIncludeDefaults) + return flags.NewIniParser(fileParser).WriteFile(eotscfg.CfgFile(homePath), flags.IniIncludeComments|flags.IniIncludeDefaults) } diff --git a/eotsmanager/cmd/eotsd/daemon/keys.go b/eotsmanager/cmd/eotsd/daemon/keys.go index 32db17ec..1ebb9776 100644 --- a/eotsmanager/cmd/eotsd/daemon/keys.go +++ b/eotsmanager/cmd/eotsd/daemon/keys.go @@ -1,167 +1,332 @@ package daemon import ( - "bufio" + "bytes" "encoding/json" - "errors" "fmt" - "os" + "io" + "strings" - "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/go-bip39" - "github.com/urfave/cli" + "github.com/btcsuite/btcd/btcec/v2/schnorr" - bbntypes "github.com/babylonlabs-io/babylon/types" + "github.com/babylonlabs-io/babylon/types" "github.com/babylonlabs-io/finality-provider/eotsmanager" + eotsclient "github.com/babylonlabs-io/finality-provider/eotsmanager/client" "github.com/babylonlabs-io/finality-provider/eotsmanager/config" "github.com/babylonlabs-io/finality-provider/log" + "github.com/babylonlabs-io/finality-provider/util" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/keys" + cryptokeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/spf13/cobra" + "sigs.k8s.io/yaml" ) -type KeyOutput struct { - Name string `json:"name" yaml:"name"` - PubKeyHex string `json:"pub_key_hex" yaml:"pub_key_hex"` - Mnemonic string `json:"mnemonic,omitempty" yaml:"mnemonic"` +type KeyOutputWithPubKeyHex struct { + keys.KeyOutput + PubKeyHex string `json:"pubkey_hex" yaml:"pubkey_hex"` } -var KeysCommands = []cli.Command{ - { - Name: "keys", - Usage: "Command sets of managing keys for interacting with BTC eots keys.", - Category: "Key management", - Subcommands: []cli.Command{ - AddKeyCmd, - }, - }, +func NewKeysCmd() *cobra.Command { + keysCmd := keys.Commands() + + // Find the "add" subcommand + addCmd := util.GetSubCommand(keysCmd, "add") + if addCmd == nil { + panic("failed to find keys add command") + } + + addCmd.Flags().String(rpcClientFlag, "", "The RPC address of a running eotsd to connect and save new key") + + // Override the original RunE function to run almost the same as + // the sdk, but it allows empty hd path and allow to save the key + // in the name mapping + addCmd.RunE = func(cmd *cobra.Command, args []string) error { + oldOut := cmd.OutOrStdout() + + // Create a buffer to intercept the key items + var buf bytes.Buffer + cmd.SetOut(&buf) + + // Run the original command + err := runAddCmdPrepare(cmd, args) + if err != nil { + return err + } + + cmd.SetOut(oldOut) + keyName := args[0] + eotsPk, err := saveKeyNameMapping(cmd, keyName) + if err != nil { + return err + } + + return printFromKey(cmd, keyName, eotsPk) + } + + saveKeyOnPostRun(keysCmd, "import") + saveKeyOnPostRun(keysCmd, "import-hex") + + return keysCmd } -var AddKeyCmd = cli.Command{ - Name: "add", - Usage: "Add a key to the EOTS manager keyring.", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: homeFlag, - Usage: "Path to the keyring directory", - Value: config.DefaultEOTSDir, - }, - cli.StringFlag{ - Name: keyNameFlag, - Usage: "The name of the key to be created", - Required: true, - }, - cli.StringFlag{ - Name: passphraseFlag, - Usage: "The pass phrase used to encrypt the keys", - Value: defaultPassphrase, - }, - cli.StringFlag{ - Name: hdPathFlag, - Usage: "The hd path used to derive the private key", - Value: defaultHdPath, - }, - cli.StringFlag{ - Name: keyringBackendFlag, - Usage: "The backend of the keyring", - Value: defaultKeyringBackend, - }, - cli.BoolFlag{ - Name: recoverFlag, - Usage: `Will need to provide a seed phrase to recover - the existing key instead of creating`, - }, - }, - Action: addKey, +func saveKeyOnPostRun(cmd *cobra.Command, commandName string) { + subCmd := util.GetSubCommand(cmd, commandName) + if subCmd == nil { + panic(fmt.Sprintf("failed to find keys %s command", commandName)) + } + + subCmd.Flags().String(rpcClientFlag, "", "The RPC address of a running eotsd to connect and save new key") + + subCmd.PostRunE = func(cmd *cobra.Command, args []string) error { + keyName := args[0] + _, err := saveKeyNameMapping(cmd, keyName) + + return err + } } -func addKey(ctx *cli.Context) error { - keyName := ctx.String(keyNameFlag) - keyringBackend := ctx.String(keyringBackendFlag) +func saveKeyNameMapping(cmd *cobra.Command, keyName string) (*types.BIP340PubKey, error) { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return nil, err + } - homePath, err := getHomeFlag(ctx) + // Load configuration + cfg, err := config.LoadConfig(clientCtx.HomeDir) if err != nil { - return fmt.Errorf("failed to load home flag: %w", err) + return nil, fmt.Errorf("failed to load config: %w", err) } - cfg, err := config.LoadConfig(homePath) + rpcListener, err := cmd.Flags().GetString(rpcClientFlag) if err != nil { - return fmt.Errorf("failed to load config at %s: %w", homePath, err) + return nil, err } - logger, err := log.NewRootLoggerWithFile(config.LogFile(homePath), cfg.LogLevel) + if len(rpcListener) > 0 { + client, err := eotsclient.NewEOTSManagerGRpcClient(rpcListener) + if err != nil { + return nil, err + } + + kr, err := eotsmanager.InitKeyring(clientCtx.HomeDir, clientCtx.Keyring.Backend(), strings.NewReader("")) + if err != nil { + return nil, fmt.Errorf("failed to init keyring: %w", err) + } + + eotsPk, err := eotsmanager.LoadBIP340PubKeyFromKeyName(kr, keyName) + if err != nil { + return nil, fmt.Errorf("failed to get public key for key %s: %w", keyName, err) + } + + if err := client.SaveEOTSKeyName(eotsPk.MustToBTCPK(), keyName); err != nil { + return nil, fmt.Errorf("failed to save key name mapping: %w", err) + } + + return eotsPk, nil + } + + // Setup logger + logger, err := log.NewRootLoggerWithFile(config.LogFile(clientCtx.HomeDir), cfg.LogLevel) if err != nil { - return fmt.Errorf("failed to load the logger") + return nil, fmt.Errorf("failed to load the logger: %w", err) } - dbBackend, err := cfg.DatabaseConfig.GetDbBackend() + // Get database backend + dbBackend, err := cfg.DatabaseConfig.GetDBBackend() if err != nil { - return fmt.Errorf("failed to create db backend: %w", err) + return nil, fmt.Errorf("failed to create db backend: %w", err) } defer dbBackend.Close() - eotsManager, err := eotsmanager.NewLocalEOTSManager(homePath, keyringBackend, dbBackend, logger) + // Create EOTS manager + eotsManager, err := eotsmanager.NewLocalEOTSManager(clientCtx.HomeDir, clientCtx.Keyring.Backend(), dbBackend, logger) + if err != nil { + return nil, fmt.Errorf("failed to create EOTS manager: %w", err) + } + + // Get the public key for the newly added key + eotsPk, err := eotsManager.LoadBIP340PubKeyFromKeyName(keyName) if err != nil { - return fmt.Errorf("failed to create EOTS manager: %w", err) + return nil, fmt.Errorf("failed to get public key for key %s: %w", keyName, err) + } + + // Save the public key to key name mapping + if err := eotsManager.SaveEOTSKeyName(eotsPk.MustToBTCPK(), keyName); err != nil { + return nil, fmt.Errorf("failed to save key name mapping: %w", err) + } + + return eotsPk, nil +} + +// CommandPrintAllKeys prints all EOTS keys +func CommandPrintAllKeys() *cobra.Command { + var cmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "Print all EOTS key names and public keys mapping from database.", + Example: `eotsd list --home=/path/to/cfg`, + Args: cobra.NoArgs, + RunE: runCommandPrintAllKeys, } - eotsPk, mnemonic, err := createKey(ctx, eotsManager, keyName) + cmd.Flags().String(flags.FlagHome, config.DefaultEOTSDir, "The path to the eotsd home directory") + + return cmd +} + +func runCommandPrintAllKeys(cmd *cobra.Command, _ []string) error { + eotsKeys, err := getAllEOTSKeys(cmd) if err != nil { - return fmt.Errorf("failed to create key: %w", err) + return err + } + + for keyName, key := range eotsKeys { + pk, err := schnorr.ParsePubKey(key) + if err != nil { + return err + } + eotsPk := types.NewBIP340PubKeyFromBTCPK(pk) + cmd.Printf("Key Name: %s, EOTS PK: %s\n", keyName, eotsPk.MarshalHex()) } - printRespJSONKeys( - KeyOutput{ - Name: keyName, - PubKeyHex: eotsPk.MarshalHex(), - Mnemonic: mnemonic, - }, - ) return nil } -// createKey checks if recover flag is set to create a key from mnemonic or if not set, randomly creates it. -func createKey( - ctx *cli.Context, - eotsManager *eotsmanager.LocalEOTSManager, - keyName string, -) (eotsPk *bbntypes.BIP340PubKey, mnemonic string, err error) { - passphrase := ctx.String(passphraseFlag) - hdPath := ctx.String(hdPathFlag) +func getAllEOTSKeys(cmd *cobra.Command) (map[string][]byte, error) { + homePath, err := getHomePath(cmd) + if err != nil { + return nil, err + } + + // Load configuration + cfg, err := config.LoadConfig(homePath) + if err != nil { + return nil, fmt.Errorf("failed to load config: %w", err) + } + + // Setup logger + logger, err := log.NewRootLoggerWithFile(config.LogFile(homePath), cfg.LogLevel) + if err != nil { + return nil, fmt.Errorf("failed to load the logger: %w", err) + } + + // Get database backend + dbBackend, err := cfg.DatabaseConfig.GetDBBackend() + if err != nil { + return nil, fmt.Errorf("failed to create db backend: %w", err) + } + defer dbBackend.Close() + + // Create EOTS manager + eotsManager, err := eotsmanager.NewLocalEOTSManager(homePath, cfg.KeyringBackend, dbBackend, logger) + if err != nil { + return nil, fmt.Errorf("failed to create EOTS manager: %w", err) + } - mnemonic, err = getMnemonic(ctx) + res, err := eotsManager.ListEOTSKeys() if err != nil { - return nil, "", err + return nil, fmt.Errorf("failed to get keys from db: %w", err) } - eotsPk, err = eotsManager.CreateKeyWithMnemonic(keyName, passphrase, hdPath, mnemonic) + return res, nil +} + +func printFromKey(cmd *cobra.Command, keyName string, eotsPk *types.BIP340PubKey) error { + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { - return nil, "", err + return err } - return eotsPk, mnemonic, nil + + k, err := clientCtx.Keyring.Key(keyName) + if err != nil { + return fmt.Errorf("failed to get public get key %s: %w", keyName, err) + } + + ctx := cmd.Context() + mnemonic := ctx.Value(mnemonicCtxKey).(string) //nolint: forcetypeassert + showMnemonic := ctx.Value(mnemonicShowCtxKey).(bool) + + return printCreatePubKeyHex(cmd, k, eotsPk, showMnemonic, mnemonic, clientCtx.OutputFormat) } -func getMnemonic(ctx *cli.Context) (string, error) { - if ctx.Bool(recoverFlag) { - reader := bufio.NewReader(os.Stdin) - mnemonic, err := input.GetString("Enter your mnemonic", reader) +func printCreatePubKeyHex(cmd *cobra.Command, k *cryptokeyring.Record, eotsPk *types.BIP340PubKey, showMnemonic bool, mnemonic, outputFormat string) error { + out, err := keys.MkAccKeyOutput(k) + if err != nil { + return err + } + keyOutput := newKeyOutputWithPubKeyHex(out, eotsPk) + + switch outputFormat { + case flags.OutputFormatText: + cmd.PrintErrln() + if err := printKeyringRecord(cmd.OutOrStdout(), keyOutput, outputFormat); err != nil { + return err + } + + // print mnemonic unless requested not to. + if showMnemonic { + if _, err := fmt.Fprintf(cmd.ErrOrStderr(), "\n**Important** write this mnemonic phrase in a safe place.\nIt is the only way to recover your account if you ever forget your password.\n\n%s\n", mnemonic); err != nil { + return fmt.Errorf("failed to print mnemonic: %s", err.Error()) + } + } + case flags.OutputFormatJSON: + if showMnemonic { + keyOutput.Mnemonic = mnemonic + } + + jsonString, err := json.MarshalIndent(keyOutput, "", " ") if err != nil { - return "", fmt.Errorf("failed to read mnemonic from stdin: %w", err) + return err } - if !bip39.IsMnemonicValid(mnemonic) { - return "", errors.New("invalid mnemonic") + + cmd.Println(string(jsonString)) + + default: + return fmt.Errorf("invalid output format %s", outputFormat) + } + + return nil +} + +func newKeyOutputWithPubKeyHex(k keys.KeyOutput, eotsPk *types.BIP340PubKey) KeyOutputWithPubKeyHex { + return KeyOutputWithPubKeyHex{ + KeyOutput: k, + PubKeyHex: eotsPk.MarshalHex(), + } +} + +func printKeyringRecord(w io.Writer, ko KeyOutputWithPubKeyHex, output string) error { + switch output { + case flags.OutputFormatText: + if err := printTextRecords(w, []KeyOutputWithPubKeyHex{ko}); err != nil { + return err + } + + case flags.OutputFormatJSON: + out, err := json.Marshal(ko) + if err != nil { + return err } - return mnemonic, nil + if _, err := fmt.Fprintln(w, string(out)); err != nil { + return err + } } - return eotsmanager.NewMnemonic() + return nil } -func printRespJSONKeys(resp interface{}) { - jsonBytes, err := json.MarshalIndent(resp, "", " ") +func printTextRecords(w io.Writer, kos []KeyOutputWithPubKeyHex) error { + out, err := yaml.Marshal(&kos) if err != nil { - fmt.Println("unable to decode response: ", err) - return + return err } - fmt.Printf("New key for the BTC chain is created "+ - "(mnemonic should be kept in a safe place for recovery):\n%s\n", jsonBytes) + if _, err := fmt.Fprintln(w, string(out)); err != nil { + return err + } + + return nil } diff --git a/eotsmanager/cmd/eotsd/daemon/keys_test.go b/eotsmanager/cmd/eotsd/daemon/keys_test.go new file mode 100644 index 00000000..04b82dfe --- /dev/null +++ b/eotsmanager/cmd/eotsd/daemon/keys_test.go @@ -0,0 +1,92 @@ +package daemon + +import ( + "bytes" + "fmt" + "math/rand" + "os" + "path/filepath" + "testing" + + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/babylonlabs-io/finality-provider/eotsmanager" + eotscfg "github.com/babylonlabs-io/finality-provider/eotsmanager/config" + "github.com/babylonlabs-io/finality-provider/testutil" +) + +func FuzzNewKeysCmd(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + root := NewRootCmd() + + tDir := t.TempDir() + tempHome := filepath.Join(tDir, "homeeots") + homeFlagFilled := fmt.Sprintf("--%s=%s", sdkflags.FlagHome, tempHome) + rootCmdBuff := new(bytes.Buffer) + defer func() { + err := os.RemoveAll(tempHome) + require.NoError(t, err) + }() + + // Initialize the EOTS manager + _, _ = exec(t, root, rootCmdBuff, "init", homeFlagFilled) + + // Generate a random key name + keyName := testutil.GenRandomHexStr(r, 5) + + // Execute the keys add command + keyringBackendFlagFilled := fmt.Sprintf("--%s=%s", sdkflags.FlagKeyringBackend, keyring.BackendTest) + _, _ = exec(t, root, rootCmdBuff, "keys", "add", keyName, homeFlagFilled, keyringBackendFlagFilled) + + // Execute the keys list command + _, listOutput := exec(t, root, rootCmdBuff, "keys", "list", homeFlagFilled) + + // Check if the added key is in the list + require.Contains(t, listOutput, keyName) + + // Load the EOTS manager and verify the key existence + eotsCfg := eotscfg.DefaultConfigWithHomePath(tempHome) + dbBackend, err := eotsCfg.DatabaseConfig.GetDBBackend() + require.NoError(t, err) + defer func() { + err := dbBackend.Close() + require.NoError(t, err) + }() + + eotsManager, err := eotsmanager.NewLocalEOTSManager(tempHome, "test", dbBackend, zap.NewNop()) + require.NoError(t, err, "Should be able to create EOTS manager") + + pubKey, err := eotsManager.LoadBIP340PubKeyFromKeyName(keyName) + require.NoError(t, err, "Should be able to load public key") + require.NotNil(t, pubKey, "Public key should not be nil") + }) +} + +// exec executes a command based on the cmd passed, the args should only search for subcommands, not parent commands +func exec(t *testing.T, root *cobra.Command, rootCmdBuf *bytes.Buffer, args ...string) (c *cobra.Command, output string) { + buf := new(bytes.Buffer) + root.SetOut(buf) + root.SetErr(buf) + root.SetArgs(args) + + c, err := root.ExecuteC() + require.NoError(t, err) + + outStr := buf.String() + if len(outStr) > 0 { + return c, outStr + } + + _, err = buf.Write(rootCmdBuf.Bytes()) + require.NoError(t, err) + + return c, buf.String() +} diff --git a/eotsmanager/cmd/eotsd/daemon/keysadd.go b/eotsmanager/cmd/eotsd/daemon/keysadd.go new file mode 100644 index 00000000..d17d76ed --- /dev/null +++ b/eotsmanager/cmd/eotsd/daemon/keysadd.go @@ -0,0 +1,330 @@ +package daemon + +import ( + "bufio" + "bytes" + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "sort" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/input" + "github.com/cosmos/cosmos-sdk/client/keys" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/hd" + cryptokeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/go-bip39" + "github.com/spf13/cobra" +) + +const ( + mnemonicEntropySize = 256 + mnemonicCtxKey = "mnemonic_ctx" + mnemonicShowCtxKey = "mnemonic_show_ctx" +) + +func runAddCmdPrepare(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + buf := bufio.NewReader(clientCtx.Input) + + return runAddCmd(clientCtx, cmd, args, buf) +} + +/* +Code adapted from cosmos-sdk https://github.com/cosmos/cosmos-sdk/blob/c64d1010800d60677cc25e2fca5b3d8c37b683cc/client/keys/add.go#L127-L128 +where the only diff is to allow empty HD path +input + - bip39 mnemonic + - bip39 passphrase + - bip44 path + - local encryption password + +output + - armor encrypted private key (saved to file) + mostly coppied from the cosmos-sdk@v0.50.9 and + replaced to allow use of empty hd path +*/ +//nolint:gocyclo,maintidx +func runAddCmd(ctx client.Context, cmd *cobra.Command, args []string, inBuf *bufio.Reader) error { + var err error + + name := args[0] + interactive, _ := cmd.Flags().GetBool(flagInteractive) + noBackup, _ := cmd.Flags().GetBool(flagNoBackup) + showMnemonic := !noBackup + kb := ctx.Keyring + + keyringAlgos, _ := kb.SupportedAlgorithms() + algoStr, _ := cmd.Flags().GetString(flags.FlagKeyType) + algo, err := cryptokeyring.NewSigningAlgoFromString(algoStr, keyringAlgos) + if err != nil { + return err + } + + if dryRun, _ := cmd.Flags().GetBool(flags.FlagDryRun); dryRun { + // use in memory keybase + kb = cryptokeyring.NewInMemory(ctx.Codec) + } else { + _, err = kb.Key(name) + if err == nil { + // account exists, ask for user confirmation + response, err2 := input.GetConfirmation(fmt.Sprintf("override the existing name %s", name), inBuf, cmd.ErrOrStderr()) + if err2 != nil { + return err2 + } + + if !response { + return errors.New("aborted") + } + + err2 = kb.Delete(name) + if err2 != nil { + return err2 + } + } + + multisigKeys, _ := cmd.Flags().GetStringSlice(flagMultisig) + if len(multisigKeys) != 0 { + pks := make([]cryptotypes.PubKey, len(multisigKeys)) + multisigThreshold, _ := cmd.Flags().GetInt(flagMultiSigThreshold) + if err := validateMultisigThreshold(multisigThreshold, len(multisigKeys)); err != nil { + return err + } + + for i, keyname := range multisigKeys { + k, err := kb.Key(keyname) + if err != nil { + return err + } + + key, err := k.GetPubKey() + if err != nil { + return err + } + pks[i] = key + } + + if noSort, _ := cmd.Flags().GetBool(flagNoSort); !noSort { + sort.Slice(pks, func(i, j int) bool { + return bytes.Compare(pks[i].Address(), pks[j].Address()) < 0 + }) + } + + pk := multisig.NewLegacyAminoPubKey(multisigThreshold, pks) + _, err := kb.SaveMultisig(name, pk) + if err != nil { + return err + } + + return nil + } + } + + pubKey, _ := cmd.Flags().GetString(keys.FlagPublicKey) + pubKeyBase64, _ := cmd.Flags().GetString(flagPubKeyBase64) + if pubKey != "" && pubKeyBase64 != "" { + return fmt.Errorf(`flags %s and %s cannot be used simultaneously`, keys.FlagPublicKey, flagPubKeyBase64) + } + if pubKey != "" { + var pk cryptotypes.PubKey + if err = ctx.Codec.UnmarshalInterfaceJSON([]byte(pubKey), &pk); err != nil { + return err + } + + _, err := kb.SaveOfflineKey(name, pk) + if err != nil { + return err + } + + return nil + } + if pubKeyBase64 != "" { + b64, err := base64.StdEncoding.DecodeString(pubKeyBase64) + if err != nil { + return err + } + + var pk cryptotypes.PubKey + // create an empty pubkey in order to get the algo TypeUrl. + tempAny, err := codectypes.NewAnyWithValue(algo.Generate()([]byte{}).PubKey()) + if err != nil { + return err + } + + jsonPub, err := json.Marshal(struct { + Type string `json:"@type,omitempty"` + Key string `json:"key,omitempty"` + }{tempAny.TypeUrl, string(b64)}) + if err != nil { + return fmt.Errorf("failed to JSON marshal typeURL and base64 key: %w", err) + } + + if err = ctx.Codec.UnmarshalInterfaceJSON(jsonPub, &pk); err != nil { + return err + } + + _, err = kb.SaveOfflineKey(name, pk) + if err != nil { + return fmt.Errorf("failed to save offline key: %w", err) + } + + return nil + } + + coinType, _ := cmd.Flags().GetUint32(flagCoinType) + account, _ := cmd.Flags().GetUint32(flagAccount) + index, _ := cmd.Flags().GetUint32(flagIndex) + hdPath, _ := cmd.Flags().GetString(flagHDPath) + useLedger, _ := cmd.Flags().GetBool(flags.FlagUseLedger) + + // This is the diff point, the sdk verifies if the hd path in flag was empty, + // and if it was it would assign something, we do allow empty hd path to be used. + // https://github.com/cosmos/cosmos-sdk/blob/c64d1010800d60677cc25e2fca5b3d8c37b683cc/client/keys/add.go#L261 + if useLedger { + return errors.New("cannot set custom bip32 path with ledger") + } + + // If we're using ledger, only thing we need is the path and the bech32 prefix. + if useLedger { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + _, err := kb.SaveLedgerKey(name, hd.Secp256k1, bech32PrefixAccAddr, coinType, account, index) + if err != nil { + return err + } + + return nil + } + + // Get bip39 mnemonic + var mnemonic, bip39Passphrase string + + recoverFlag, _ := cmd.Flags().GetBool(flagRecover) + mnemonicSrc, _ := cmd.Flags().GetString(flagMnemonicSrc) + if recoverFlag { + if mnemonicSrc != "" { + mnemonic, err = readMnemonicFromFile(mnemonicSrc) + if err != nil { + return err + } + } else { + mnemonic, err = input.GetString("Enter your bip39 mnemonic", inBuf) + if err != nil { + return err + } + } + + if !bip39.IsMnemonicValid(mnemonic) { + return errors.New("invalid mnemonic") + } + } else if interactive { + if mnemonicSrc != "" { + mnemonic, err = readMnemonicFromFile(mnemonicSrc) + if err != nil { + return err + } + } else { + mnemonic, err = input.GetString("Enter your bip39 mnemonic, or hit enter to generate one.", inBuf) + if err != nil { + return err + } + } + + if !bip39.IsMnemonicValid(mnemonic) && mnemonic != "" { + return errors.New("invalid mnemonic") + } + } + + if len(mnemonic) == 0 { + // read entropy seed straight from cmtcrypto.Rand and convert to mnemonic + entropySeed, err := bip39.NewEntropy(mnemonicEntropySize) + if err != nil { + return err + } + + mnemonic, err = bip39.NewMnemonic(entropySeed) + if err != nil { + return err + } + } + + // override bip39 passphrase + if interactive { + bip39Passphrase, err = input.GetString( + "Enter your bip39 passphrase. This is combined with the mnemonic to derive the seed. "+ + "Most users should just hit enter to use the default, \"\"", inBuf) + if err != nil { + return err + } + + // if they use one, make them re-enter it + if len(bip39Passphrase) != 0 { + p2, err := input.GetString("Repeat the passphrase:", inBuf) + if err != nil { + return err + } + + if bip39Passphrase != p2 { + return errors.New("passphrases don't match") + } + } + } + + _, err = kb.NewAccount(name, mnemonic, bip39Passphrase, hdPath, algo) + if err != nil { + return err + } + + // Recover key from seed passphrase + if recoverFlag { + // Hide mnemonic from output + showMnemonic = false + mnemonic = "" + } + + // used later for printing the values if needed + ctxWithValues := context.WithValue(cmd.Context(), mnemonicCtxKey, mnemonic) //nolint: revive,staticcheck + cmd.SetContext(context.WithValue(ctxWithValues, mnemonicShowCtxKey, showMnemonic)) //nolint: revive,staticcheck + + return nil +} + +func validateMultisigThreshold(k, nKeys int) error { + if k <= 0 { + return fmt.Errorf("threshold must be a positive integer") + } + if nKeys < k { + return fmt.Errorf( + "threshold k of n multisignature: %d < %d", nKeys, k) + } + + return nil +} + +func readMnemonicFromFile(filePath string) (string, error) { + file, err := os.Open(filepath.Clean(filePath)) + if err != nil { + return "", err + } + defer file.Close() + + bz, err := io.ReadAll(file) + if err != nil { + return "", err + } + + return string(bz), nil +} diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index a0006739..eb59aec1 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -1,139 +1,651 @@ package daemon import ( + "encoding/base64" + "encoding/json" "fmt" + "io" + "os" + "path/filepath" + "strconv" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/cometbft/cometbft/crypto/tmhash" + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/spf13/cobra" bbnparams "github.com/babylonlabs-io/babylon/app/params" - bbn "github.com/babylonlabs-io/babylon/types" - btcstktypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + bbntypes "github.com/babylonlabs-io/babylon/types" + + "github.com/babylonlabs-io/finality-provider/codec" "github.com/babylonlabs-io/finality-provider/eotsmanager" "github.com/babylonlabs-io/finality-provider/eotsmanager/config" "github.com/babylonlabs-io/finality-provider/log" - "github.com/urfave/cli" +) + +const ( + flagHomeBaby = "baby-home" + flagKeyNameBaby = "baby-key-name" + flagKeyringBackendBaby = "baby-keyring-backend" + flagMessage = "message" + flagOutputFile = "output-file" ) func init() { bbnparams.SetAddressPrefixes() } -// PoPExport the data for exporting the PoP. -// The PubKeyHex is the public key of the finality provider EOTS key to load -// the private key and sign the AddressSiged. +// PoPExport the data needed to prove ownership of the eots and baby key pairs. type PoPExport struct { - PubKeyHex string `json:"pub_key_hex"` - PoPHex string `json:"pop_hex"` - BabylonAddress string `json:"babylon_address"` -} - -var ExportPoPCommand = cli.Command{ - Name: "pop-export", - Usage: "Exports the Proof of Possession by signing over the finality provider's Babylon address with the EOTS private key.", - UsageText: "pop-export [bbn-address]", - Description: `Parse the address received as argument, hash it with - sha256 and sign based on the EOTS key associated with the key-name or eots-pk flag. - If the both flags are supplied, eots-pk takes priority. Use the generated signature - to build a Proof of Possession and export it.`, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: homeFlag, - Usage: "Path to the keyring directory", - Value: config.DefaultEOTSDir, - }, - cli.StringFlag{ - Name: keyNameFlag, - Usage: "The name of the key to load private key for signing", - }, - cli.StringFlag{ - Name: eotsPkFlag, - Usage: "The public key of the finality-provider to load private key for signing", - }, - cli.StringFlag{ - Name: passphraseFlag, - Usage: "The passphrase used to decrypt the keyring", - Value: defaultPassphrase, - }, - cli.StringFlag{ - Name: keyringBackendFlag, - Usage: "The backend of the keyring", - Value: defaultKeyringBackend, - }, - }, - Action: ExportPoP, + // Btc public key is the EOTS PK *bbntypes.BIP340PubKey marshal hex + EotsPublicKey string `json:"eotsPublicKey"` + // Baby public key is the *secp256k1.PubKey marshal hex + BabyPublicKey string `json:"babyPublicKey"` + + // Babylon key pair signs EOTS public key as hex + BabySignEotsPk string `json:"babySignEotsPk"` + // Schnorr signature of EOTS private key over the SHA256(Baby address) + EotsSignBaby string `json:"eotsSignBaby"` + + // Babylon address ex.: bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm + BabyAddress string `json:"babyAddress"` } -func ExportPoP(ctx *cli.Context) error { - keyName := ctx.String(keyNameFlag) - fpPkStr := ctx.String(eotsPkFlag) - passphrase := ctx.String(passphraseFlag) - keyringBackend := ctx.String(keyringBackendFlag) +// PoPExportDelete the data needed to delete an ownership previously created. +type PoPExportDelete struct { + // Btc public key is the EOTS PK *bbntypes.BIP340PubKey marshal hex + EotsPublicKey string `json:"eotsPublicKey"` + // Baby public key is the *secp256k1.PubKey marshal hex + BabyPublicKey string `json:"babyPublicKey"` + + // Babylon key pair signs message + BabySignature string `json:"babySignature"` + + // Babylon address ex.: bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm + BabyAddress string `json:"babyAddress"` +} + +func NewPopCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "pop", + Short: "Proof of Possession commands", + } - args := ctx.Args() - bbnAddressStr := args.First() - bbnAddr, err := sdk.AccAddressFromBech32(bbnAddressStr) + cmd.AddCommand( + NewPopExportCmd(), + NewPopDeleteCmd(), + NewPopValidateExportCmd(), + ) + + return cmd +} + +func NewPopExportCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "export", + Short: "Exports the Proof of Possession by (1) signing over the BABY address with the EOTS private key and (2) signing over the EOTS public key with the BABY private key.", + Long: `Parse the address from the BABY keyring, load the address, hash it with + sha256 and sign based on the EOTS key associated with the key-name or eots-pk flag. + If the both flags are supplied, eots-pk takes priority. Use the generated signature + to build a Proof of Possession. For the creation of the BABY signature over the eots pk, + it loads the BABY key pair and signs the eots-pk hex and exports it.`, + RunE: exportPop, + } + + f := cmd.Flags() + + f.String(sdkflags.FlagHome, config.DefaultEOTSDir, "EOTS home directory") + f.String(keyNameFlag, "", "EOTS key name") + f.String(eotsPkFlag, "", "EOTS public key of the finality-provider") + f.String(passphraseFlag, "", "EOTS passphrase used to decrypt the keyring") + f.String(sdkflags.FlagKeyringBackend, keyring.BackendTest, "EOTS backend of the keyring") + + f.String(flagHomeBaby, "", "BABY home directory") + f.String(flagKeyNameBaby, "", "BABY key name") + f.String(flagKeyringBackendBaby, keyring.BackendTest, "BABY backend of the keyring") + + f.String(flagOutputFile, "", "Path to output JSON file") + + return cmd +} + +func NewPopValidateExportCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "validate", + Short: "Validates the PoP of the pop export command.", + Long: `Receives as an argument the file path of the JSON output of the command eotsd pop export`, + Example: `eotsd pop validate /path/to/pop.json`, + RunE: validatePop, + Args: cobra.ExactArgs(1), + } + + return cmd +} + +func NewPopDeleteCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete", + Short: "Generate the delete data for removing a proof of possession previously created.", + Long: `Parse the message from the flag --message and sign with the BABY keyring, it also loads + the EOTS public key based on the EOTS key associated with the key-name or eots-pk flag. + If the both flags are supplied, eots-pk takes priority.`, + RunE: deletePop, + } + + f := cmd.Flags() + + f.String(sdkflags.FlagHome, config.DefaultEOTSDir, "EOTS home directory") + f.String(keyNameFlag, "", "EOTS key name") + f.String(eotsPkFlag, "", "EOTS public key of the finality-provider") + f.String(sdkflags.FlagKeyringBackend, keyring.BackendTest, "EOTS backend of the keyring") + + f.String(flagHomeBaby, "", "BABY home directory") + f.String(flagKeyNameBaby, "", "BABY key name") + f.String(flagKeyringBackendBaby, keyring.BackendTest, "BABY backend of the keyring") + + f.String(flagMessage, "", "Message to be signed") + + f.String(flagOutputFile, "", "Path to output JSON file") + + return cmd +} + +func validatePop(cmd *cobra.Command, args []string) error { + path := args[0] + + // Add path validation + // #nosec G304 - The file path is provided by the user and not externally + bzExportJSON, err := os.ReadFile(filepath.Clean(path)) + if err != nil { + return fmt.Errorf("failed to read pop file: %w", err) + } + + var pop PoPExport + if err := json.Unmarshal(bzExportJSON, &pop); err != nil { + return fmt.Errorf("failed to marshal %s into PoPExport structure", string(bzExportJSON)) + } + + valid, err := ValidPopExport(pop) + if err != nil { + return fmt.Errorf("failed to validate pop %+v, reason: %w", pop, err) + } + if !valid { + return fmt.Errorf("invalid pop %+v", pop) + } + + cmd.Println("Proof of Possession is valid!") + + return nil +} + +func exportPop(cmd *cobra.Command, _ []string) error { + eotsPassphrase, err := cmd.Flags().GetString(passphraseFlag) if err != nil { - return fmt.Errorf("invalid argument %s, please provide a valid bbn address as argument, err: %w", bbnAddressStr, err) + return err } - if len(fpPkStr) == 0 && len(keyName) == 0 { - return fmt.Errorf("at least one of the flags: %s, %s needs to be informed", keyNameFlag, eotsPkFlag) + eotsHomePath, eotsKeyName, eotsFpPubKeyStr, eotsKeyringBackend, err := eotsFlags(cmd) + if err != nil { + return err } - homePath, err := getHomeFlag(ctx) + babyHomePath, babyKeyName, babyKeyringBackend, err := babyFlags(cmd) if err != nil { - return fmt.Errorf("failed to load home flag: %w", err) + return err } - cfg, err := config.LoadConfig(homePath) + babyKeyring, babyPubKey, bbnAddr, err := babyKeyring(babyHomePath, babyKeyName, babyKeyringBackend, cmd.InOrStdin()) if err != nil { - return fmt.Errorf("failed to load config at %s: %w", homePath, err) + return err } - logger, err := log.NewRootLoggerWithFile(config.LogFile(homePath), cfg.LogLevel) + eotsManager, err := loadEotsManager(eotsHomePath, eotsFpPubKeyStr, eotsKeyName, eotsKeyringBackend) if err != nil { - return fmt.Errorf("failed to load the logger") + return err } + defer cmdCloseEots(cmd, eotsManager) - dbBackend, err := cfg.DatabaseConfig.GetDbBackend() + bbnAddrStr := bbnAddr.String() + hashOfMsgToSign := tmhash.Sum([]byte(bbnAddrStr)) + schnorrSigOverBabyAddr, eotsPk, err := eotsSignMsg(eotsManager, eotsKeyName, eotsFpPubKeyStr, eotsPassphrase, hashOfMsgToSign) if err != nil { - return fmt.Errorf("failed to create db backend: %w", err) + return fmt.Errorf("failed to sign address %s: %w", bbnAddrStr, err) } - defer dbBackend.Close() - eotsManager, err := eotsmanager.NewLocalEOTSManager(homePath, keyringBackend, dbBackend, logger) + eotsPkHex := eotsPk.MarshalHex() + babySignature, err := SignCosmosAdr36(babyKeyring, babyKeyName, bbnAddrStr, []byte(eotsPkHex)) if err != nil { - return fmt.Errorf("failed to create EOTS manager: %w", err) + return err } - hashOfMsgToSign := tmhash.Sum(bbnAddr.Bytes()) - btcSig, pubKey, err := singMsg(eotsManager, keyName, fpPkStr, passphrase, hashOfMsgToSign) + out := PoPExport{ + EotsPublicKey: eotsPkHex, + BabyPublicKey: base64.StdEncoding.EncodeToString(babyPubKey.Bytes()), + + BabyAddress: bbnAddrStr, + + EotsSignBaby: base64.StdEncoding.EncodeToString(schnorrSigOverBabyAddr.Serialize()), + BabySignEotsPk: base64.StdEncoding.EncodeToString(babySignature), + } + + return handleOutputJSON(cmd, out) +} + +func deletePop(cmd *cobra.Command, _ []string) error { + eotsHomePath, eotsKeyName, eotsFpPubKeyStr, eotsKeyringBackend, err := eotsFlags(cmd) + if err != nil { + return err + } + + babyHomePath, babyKeyName, babyKeyringBackend, err := babyFlags(cmd) + if err != nil { + return err + } + + babyKeyring, babyPubKey, bbnAddr, err := babyKeyring(babyHomePath, babyKeyName, babyKeyringBackend, cmd.InOrStdin()) + if err != nil { + return err + } + + eotsManager, err := loadEotsManager(eotsHomePath, eotsFpPubKeyStr, eotsKeyName, eotsKeyringBackend) + if err != nil { + return err + } + defer cmdCloseEots(cmd, eotsManager) + + btcPubKey, err := eotsPubKey(eotsManager, eotsKeyName, eotsFpPubKeyStr) + if err != nil { + return fmt.Errorf("failed to get eots pk %w", err) + } + + interpretedMsg, err := getInterpretedMessage(cmd) + if err != nil { + return err + } + + bbnAddrStr := bbnAddr.String() + babySignature, err := SignCosmosAdr36(babyKeyring, babyKeyName, bbnAddrStr, []byte(interpretedMsg)) + if err != nil { + return err + } + + out := PoPExportDelete{ + EotsPublicKey: btcPubKey.MarshalHex(), + BabyPublicKey: base64.StdEncoding.EncodeToString(babyPubKey.Bytes()), + + BabyAddress: bbnAddrStr, + + BabySignature: base64.StdEncoding.EncodeToString(babySignature), + } + + return handleOutputJSON(cmd, out) +} + +// babyFlags returns the values of flagHomeBaby, flagKeyNameBaby and +// flagKeyringBackendBaby respectively or error if something fails +func babyFlags(cmd *cobra.Command) (string, string, string, error) { + f := cmd.Flags() + + babyHomePath, err := getCleanPath(cmd, flagHomeBaby) + if err != nil { + return "", "", "", fmt.Errorf("failed to load baby home flag: %w", err) + } + + babyKeyName, err := f.GetString(flagKeyNameBaby) + if err != nil { + return "", "", "", err + } + + babyKeyringBackend, err := f.GetString(flagKeyringBackendBaby) + if err != nil { + return "", "", "", err + } + + return babyHomePath, babyKeyName, babyKeyringBackend, nil +} + +// eotsFlags returns the values of FlagHome, keyNameFlag, +// eotsPkFlag, FlagKeyringBackend respectively or error +// if something fails +func eotsFlags(cmd *cobra.Command) (string, string, string, string, error) { + f := cmd.Flags() + + eotsKeyName, err := f.GetString(keyNameFlag) + if err != nil { + return "", "", "", "", err + } + + eotsFpPubKeyStr, err := f.GetString(eotsPkFlag) + if err != nil { + return "", "", "", "", err + } + + eotsKeyringBackend, err := f.GetString(sdkflags.FlagKeyringBackend) + if err != nil { + return "", "", "", "", err + } + + eotsHomePath, err := getHomePath(cmd) + if err != nil { + return "", "", "", "", fmt.Errorf("failed to load home flag: %w", err) + } + + return eotsHomePath, eotsKeyName, eotsFpPubKeyStr, eotsKeyringBackend, nil +} + +func getInterpretedMessage(cmd *cobra.Command) (string, error) { + msg, err := cmd.Flags().GetString(flagMessage) if err != nil { - return fmt.Errorf("failed to sign address %s: %w", bbnAddr.String(), err) + return "", err + } + if len(msg) == 0 { + return "", fmt.Errorf("flage --%s is empty", flagMessage) } - bip340Sig := bbn.NewBIP340SignatureFromBTCSig(btcSig) - btcSigBz, err := bip340Sig.Marshal() + // We are assuming we are receiving string literal with escape characters + interpretedMsg, err := strconv.Unquote(`"` + msg + `"`) if err != nil { - return fmt.Errorf("failed to marshal BTC Sig: %w", err) + return "", err } - pop := btcstktypes.ProofOfPossessionBTC{ - BtcSigType: btcstktypes.BTCSigType_BIP340, - BtcSig: btcSigBz, + return interpretedMsg, nil +} + +func handleOutputJSON(cmd *cobra.Command, out any) error { + jsonBz, err := json.MarshalIndent(out, "", " ") + if err != nil { + return err } - popHex, err := pop.ToHexStr() + outputFilePath, err := cmd.Flags().GetString(flagOutputFile) if err != nil { - return fmt.Errorf("failed to marshal pop to hex: %w", err) + return err } - printRespJSON(PoPExport{ - PubKeyHex: pubKey.MarshalHex(), - PoPHex: popHex, - BabylonAddress: bbnAddr.String(), - }) + if len(outputFilePath) > 0 { + // Add path validation + cleanPath, err := filepath.Abs(filepath.Clean(outputFilePath)) + if err != nil { + return fmt.Errorf("invalid output file path: %w", err) + } + + // Create directory if it doesn't exist + dir := filepath.Dir(cleanPath) + if err := os.MkdirAll(dir, 0750); err != nil { + return fmt.Errorf("failed to create output directory: %w", err) + } + + if err := os.WriteFile(cleanPath, jsonBz, 0600); err != nil { + return fmt.Errorf("failed to write output file: %w", err) + } + } + + cmd.Println(string(jsonBz)) return nil } + +func babyKeyring( + babyHomePath, babyKeyName, babyKeyringBackend string, + userInput io.Reader, +) (keyring.Keyring, *secp256k1.PubKey, sdk.AccAddress, error) { + cdc := codec.MakeCodec() + babyKeyring, err := keyring.New("baby", babyKeyringBackend, babyHomePath, userInput, cdc) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to create keyring: %w", err) + } + + babyKeyRecord, err := babyKeyring.Key(babyKeyName) + if err != nil { + return nil, nil, nil, err + } + + babyPubKey, err := babyPk(babyKeyRecord) + if err != nil { + return nil, nil, nil, err + } + + return babyKeyring, babyPubKey, sdk.AccAddress(babyPubKey.Address().Bytes()), nil +} + +func loadEotsManager(eotsHomePath, eotsFpPubKeyStr, eotsKeyName, eotsKeyringBackend string) (*eotsmanager.LocalEOTSManager, error) { + if len(eotsFpPubKeyStr) == 0 && len(eotsKeyName) == 0 { + return nil, fmt.Errorf("at least one of the flags: %s, %s needs to be informed", keyNameFlag, eotsPkFlag) + } + + cfg, err := config.LoadConfig(eotsHomePath) + if err != nil { + return nil, fmt.Errorf("failed to load config at %s: %w", eotsHomePath, err) + } + + logger, err := log.NewRootLoggerWithFile(config.LogFile(eotsHomePath), cfg.LogLevel) + if err != nil { + return nil, fmt.Errorf("failed to load the logger") + } + + dbBackend, err := cfg.DatabaseConfig.GetDBBackend() + if err != nil { + return nil, fmt.Errorf("failed to create db backend: %w", err) + } + + eotsManager, err := eotsmanager.NewLocalEOTSManager(eotsHomePath, eotsKeyringBackend, dbBackend, logger) + if err != nil { + return nil, fmt.Errorf("failed to create EOTS manager: %w", err) + } + + return eotsManager, nil +} + +func SignCosmosAdr36( + kr keyring.Keyring, + keyName string, + cosmosBech32Address string, + bytesToSign []byte, +) ([]byte, error) { + base64Bytes := base64.StdEncoding.EncodeToString(bytesToSign) + + signDoc := NewCosmosSignDoc( + cosmosBech32Address, + base64Bytes, + ) + + marshaled, err := json.Marshal(signDoc) + if err != nil { + return nil, fmt.Errorf("failed to marshal sign doc: %w", err) + } + + bz := sdk.MustSortJSON(marshaled) + + babySignBytes, _, err := kr.Sign( + keyName, + bz, + signing.SignMode_SIGN_MODE_DIRECT, + ) + + if err != nil { + return nil, fmt.Errorf("failed to sign btc address bytes: %w", err) + } + + return babySignBytes, nil +} + +func ValidPopExport(pop PoPExport) (bool, error) { + valid, err := ValidEotsSignBaby(pop.EotsPublicKey, pop.BabyAddress, pop.EotsSignBaby) + if err != nil || !valid { + return false, err + } + + return ValidBabySignEots( + pop.BabyPublicKey, + pop.BabyAddress, + pop.EotsPublicKey, + pop.BabySignEotsPk, + ) +} + +func ValidEotsSignBaby(eotsPk, babyAddr, eotsSigOverBabyAddr string) (bool, error) { + eotsPubKey, err := bbntypes.NewBIP340PubKeyFromHex(eotsPk) + if err != nil { + return false, err + } + + schnorrSigBase64, err := base64.StdEncoding.DecodeString(eotsSigOverBabyAddr) + if err != nil { + return false, err + } + + schnorrSig, err := schnorr.ParseSignature(schnorrSigBase64) + if err != nil { + return false, err + } + sha256Addr := tmhash.Sum([]byte(babyAddr)) + + return schnorrSig.Verify(sha256Addr, eotsPubKey.MustToBTCPK()), nil +} + +func ValidBabySignEots(babyPk, babyAddr, eotsPkHex, babySigOverEotsPk string) (bool, error) { + babyPubKeyBz, err := base64.StdEncoding.DecodeString(babyPk) + if err != nil { + return false, err + } + + babyPubKey := &secp256k1.PubKey{ + Key: babyPubKeyBz, + } + + eotsPk, err := bbntypes.NewBIP340PubKeyFromHex(eotsPkHex) + if err != nil { + return false, err + } + + babySignEots := []byte(eotsPk.MarshalHex()) + base64Bytes := base64.StdEncoding.EncodeToString(babySignEots) + babySignBtcDoc := NewCosmosSignDoc(babyAddr, base64Bytes) + babySignBtcMarshaled, err := json.Marshal(babySignBtcDoc) + if err != nil { + return false, err + } + + babySignEotsBz := sdk.MustSortJSON(babySignBtcMarshaled) + + secp256SigBase64, err := base64.StdEncoding.DecodeString(babySigOverEotsPk) + if err != nil { + return false, err + } + + return babyPubKey.VerifySignature(babySignEotsBz, secp256SigBase64), nil +} + +func babyPk(babyRecord *keyring.Record) (*secp256k1.PubKey, error) { + pubKey, err := babyRecord.GetPubKey() + if err != nil { + return nil, err + } + + switch v := pubKey.(type) { + case *secp256k1.PubKey: + return v, nil + default: + return nil, fmt.Errorf("unsupported key type in keyring") + } +} + +func eotsPubKey( + eotsManager *eotsmanager.LocalEOTSManager, + keyName, fpPkStr string, +) (*bbntypes.BIP340PubKey, error) { + if len(fpPkStr) > 0 { + fpPk, err := bbntypes.NewBIP340PubKeyFromHex(fpPkStr) + if err != nil { + return nil, fmt.Errorf("invalid finality-provider public key %s: %w", fpPkStr, err) + } + + return fpPk, nil + } + + return eotsManager.LoadBIP340PubKeyFromKeyName(keyName) +} + +func eotsSignMsg( + eotsManager *eotsmanager.LocalEOTSManager, + keyName, fpPkStr, passphrase string, + hashOfMsgToSign []byte, +) (*schnorr.Signature, *bbntypes.BIP340PubKey, error) { + if len(fpPkStr) > 0 { + fpPk, err := bbntypes.NewBIP340PubKeyFromHex(fpPkStr) + if err != nil { + return nil, nil, fmt.Errorf("invalid finality-provider public key %s: %w", fpPkStr, err) + } + signature, err := eotsManager.SignSchnorrSig(*fpPk, hashOfMsgToSign, passphrase) + if err != nil { + return nil, nil, fmt.Errorf("unable to sign msg with pk %s: %w", fpPkStr, err) + } + + return signature, fpPk, nil + } + + return eotsManager.SignSchnorrSigFromKeyname(keyName, passphrase, hashOfMsgToSign) +} + +func cmdCloseEots( + cmd *cobra.Command, + eotsManager *eotsmanager.LocalEOTSManager, +) { + err := eotsManager.Close() + if err != nil { + cmd.Printf("error closing eots manager: %s", err.Error()) + } +} + +type Msg struct { + Type string `json:"type"` + Value MsgValue `json:"value"` +} + +type SignDoc struct { + ChainID string `json:"chain_id"` + AccountNumber string `json:"account_number"` + Sequence string `json:"sequence"` + Fee Fee `json:"fee"` + Msgs []Msg `json:"msgs"` + Memo string `json:"memo"` +} + +type Fee struct { + Gas string `json:"gas"` + Amount []string `json:"amount"` +} + +type MsgValue struct { + Signer string `json:"signer"` + Data string `json:"data"` +} + +func NewCosmosSignDoc( + signer string, + data string, +) *SignDoc { + return &SignDoc{ + ChainID: "", + AccountNumber: "0", + Sequence: "0", + Fee: Fee{ + Gas: "0", + Amount: []string{}, + }, + Msgs: []Msg{ + { + Type: "sign/MsgSignData", + Value: MsgValue{ + Signer: signer, + Data: data, + }, + }, + }, + Memo: "", + } +} diff --git a/eotsmanager/cmd/eotsd/daemon/pop_test.go b/eotsmanager/cmd/eotsd/daemon/pop_test.go index 57bf90bc..d07d465a 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop_test.go +++ b/eotsmanager/cmd/eotsd/daemon/pop_test.go @@ -1,71 +1,98 @@ package daemon_test import ( + "bytes" + "context" "encoding/json" "fmt" - "math/rand" + "os" "path/filepath" "testing" - "github.com/babylonlabs-io/babylon/testutil/datagen" - bbn "github.com/babylonlabs-io/babylon/types" - btcstktypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - dcli "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" - "github.com/babylonlabs-io/finality-provider/testutil" - "github.com/btcsuite/btcd/chaincfg" + "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" "github.com/stretchr/testify/require" - "github.com/urfave/cli" ) -func FuzzPoPExport(f *testing.F) { - testutil.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) +var popsToVerify = []daemon.PoPExport{ + daemon.PoPExport{ + EotsPublicKey: "3d0bebcbe800236ce8603c5bb1ab6c2af0932e947db4956a338f119797c37f1e", + BabyPublicKey: "A0V6yw74EdvoAWVauFqkH/GVM9YIpZitZf6bVEzG69tT", - tempDir := t.TempDir() - homeDir := filepath.Join(tempDir, "eots-home") - app := testApp() + BabySignEotsPk: "GO7xlC+BIypdcQdnIDsM+Ts75X9JKTOkDpXt5t4TSOIt/P1puAHVNhaYbweStVs25J9uRK+4XfrjD0M+t0Qy4g==", + EotsSignBaby: "pR6vxgU0gXq+VqO+y7dHpZgHTz3zr5hdqXXh0WcWNkqUnRjHrizhYAHDMV8gh4vks4PqzKAIgZ779Wqwf5UrXQ==", - // init config in home folder - hFlag := fmt.Sprintf("--home=%s", homeDir) - err := app.Run([]string{"eotsd", "init", hFlag}) - require.NoError(t, err) + BabyAddress: "bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm", + }, + daemon.PoPExport{ + EotsPublicKey: "b1bc317bacf02fba17abea4f695c89997d55fe513a56ad8126237226212dd487", + BabyPublicKey: "AyAU94yfGIa+MPq60oR3QnNn1DJ+9cZZrHDCu4Nx1Uo3", + + BabySignEotsPk: "kNcxCeqmQFQO//LvqhbzUQwbD3+/FfSbrvfyEa4Xf1MP5YDZp0XKYlOh+wA6dqFsb5IA7Wciz0WRbkGxRwxHVg==", + EotsSignBaby: "L9wYf+rg2fQRzy2rau2MTO9V5sEBCSUWtWcxJxweSrvkDpR6tJTwKz3Ba0c/q7yNCT91Ag7H4rKMVhKmyN4tkQ==", - keyName := testutil.GenRandomHexStr(r, 10) - keyNameFlag := fmt.Sprintf("--key-name=%s", keyName) + BabyAddress: "bbn1ayrme3m73xv294t50k7v5pfj6pauyps03atepn", + }, +} - outputKeysAdd := appRunWithOutput(r, t, app, []string{"eotsd", "keys", "add", hFlag, keyNameFlag}) - keyOutJson := searchInTxt(outputKeysAdd, "for recovery):") +func TestPoPValidEotsSignBaby(t *testing.T) { + t.Parallel() - var keyOut dcli.KeyOutput - err = json.Unmarshal([]byte(keyOutJson), &keyOut) + for _, pop := range popsToVerify { + valid, err := daemon.ValidEotsSignBaby( + pop.EotsPublicKey, + pop.BabyAddress, + pop.EotsSignBaby, + ) require.NoError(t, err) + require.True(t, valid) + } +} - bbnAddr := datagen.GenRandomAccount().GetAddress() +func TestPoPValidBabySignEotsPk(t *testing.T) { + t.Parallel() + for _, pop := range popsToVerify { + valid, err := daemon.ValidBabySignEots( + pop.BabyPublicKey, + pop.BabyAddress, + pop.EotsPublicKey, + pop.BabySignEotsPk, + ) + require.NoError(t, err) + require.True(t, valid) + } +} - eotsBtcPkFlag := fmt.Sprintf("--eots-pk=%s", keyOut.PubKeyHex) - exportedPoP := appRunPoPExport(r, t, app, []string{bbnAddr.String(), hFlag, eotsBtcPkFlag}) - pop, err := btcstktypes.NewPoPBTCFromHex(exportedPoP.PoPHex) +func TestPoPVerify(t *testing.T) { + t.Parallel() + for _, pop := range popsToVerify { + valid, err := daemon.ValidPopExport(pop) require.NoError(t, err) + require.True(t, valid) + } +} - require.NotNil(t, exportedPoP) - require.NoError(t, pop.ValidateBasic()) +func TestPoPValidate(t *testing.T) { + t.Parallel() + validateCmd := daemon.NewPopValidateExportCmd() - btcPubKey, err := bbn.NewBIP340PubKeyFromHex(exportedPoP.PubKeyHex) + tmp := t.TempDir() + + for i, pop := range popsToVerify { + jsonString, err := json.MarshalIndent(pop, "", " ") require.NoError(t, err) - require.NoError(t, pop.Verify(bbnAddr, btcPubKey, &chaincfg.SigNetParams)) - }) -} -func appRunPoPExport(r *rand.Rand, t *testing.T, app *cli.App, arguments []string) dcli.PoPExport { - args := []string{"eotsd", "pop-export"} - args = append(args, arguments...) - outputSign := appRunWithOutput(r, t, app, args) - signatureStr := searchInTxt(outputSign, "") + writer := bytes.NewBuffer([]byte{}) + validateCmd.SetOutput(writer) + + fileName := filepath.Join(tmp, fmt.Sprintf("%d-pop-out.json", i)) + err = os.WriteFile(fileName, jsonString, 0644) + require.NoError(t, err) + + validateCmd.SetArgs([]string{fileName}) - var dataSigned dcli.PoPExport - err := json.Unmarshal([]byte(signatureStr), &dataSigned) - require.NoError(t, err) + err = validateCmd.ExecuteContext(context.Background()) + require.NoError(t, err) - return dataSigned + require.Equal(t, writer.String(), "Proof of Possession is valid!\n") + } } diff --git a/eotsmanager/cmd/eotsd/daemon/root.go b/eotsmanager/cmd/eotsd/daemon/root.go new file mode 100644 index 00000000..b9f0a9ec --- /dev/null +++ b/eotsmanager/cmd/eotsd/daemon/root.go @@ -0,0 +1,33 @@ +package daemon + +import ( + "github.com/babylonlabs-io/finality-provider/version" + "github.com/cosmos/cosmos-sdk/client" + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + + "github.com/babylonlabs-io/finality-provider/eotsmanager/config" +) + +// NewRootCmd creates a new root command for fpd. It is called once in the main function. +func NewRootCmd() *cobra.Command { + rootCmd := &cobra.Command{ + Use: "eotsd", + Short: "A daemon program from managing Extractable One Time Signatures (eotsd).", + SilenceErrors: false, + PersistentPreRunE: PersistClientCtx(client.Context{}), + } + + rootCmd.PersistentFlags().String(sdkflags.FlagHome, config.DefaultEOTSDir, "The application home directory") + + rootCmd.AddCommand( + NewInitCmd(), + NewKeysCmd(), + NewStartCmd(), + version.CommandVersion("eotsd"), + CommandPrintAllKeys(), + NewPopCmd(), + ) + + return rootCmd +} diff --git a/eotsmanager/cmd/eotsd/daemon/sign.go b/eotsmanager/cmd/eotsd/daemon/sign.go deleted file mode 100644 index 04aca0b5..00000000 --- a/eotsmanager/cmd/eotsd/daemon/sign.go +++ /dev/null @@ -1,230 +0,0 @@ -package daemon - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "os" - - bbntypes "github.com/babylonlabs-io/babylon/types" - "github.com/babylonlabs-io/finality-provider/eotsmanager" - "github.com/babylonlabs-io/finality-provider/eotsmanager/config" - "github.com/babylonlabs-io/finality-provider/log" - "github.com/btcsuite/btcd/btcec/v2/schnorr" - "github.com/urfave/cli" -) - -type DataSigned struct { - KeyName string `json:"key_name"` - PubKeyHex string `json:"pub_key_hex"` - SignedDataHashHex string `json:"signed_data_hash_hex"` - SchnorrSignatureHex string `json:"schnorr_signature_hex"` -} - -var SignSchnorrSig = cli.Command{ - Name: "sign-schnorr", - Usage: "Signs a Schnorr signature over arbitrary data with the EOTS private key.", - UsageText: "sign-schnorr [file-path]", - Description: fmt.Sprintf(`Read the file received as argument, hash it with - sha256 and sign based on the Schnorr key associated with the %s or %s flag. - If the both flags are supplied, %s takes priority`, keyNameFlag, eotsPkFlag, eotsPkFlag), - Flags: []cli.Flag{ - cli.StringFlag{ - Name: homeFlag, - Usage: "Path to the keyring directory", - Value: config.DefaultEOTSDir, - }, - cli.StringFlag{ - Name: keyNameFlag, - Usage: "The name of the key to load private key for signing", - }, - cli.StringFlag{ - Name: eotsPkFlag, - Usage: "The public key of the finality-provider to load private key for signing", - }, - cli.StringFlag{ - Name: passphraseFlag, - Usage: "The passphrase used to decrypt the keyring", - Value: defaultPassphrase, - }, - cli.StringFlag{ - Name: keyringBackendFlag, - Usage: "The backend of the keyring", - Value: defaultKeyringBackend, - }, - }, - Action: SignSchnorr, -} - -var VerifySchnorrSig = cli.Command{ - Name: "verify-schnorr-sig", - Usage: "Verify a Schnorr signature over arbitrary data with the given public key.", - UsageText: "verify-schnorr-sig [file-path]", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: eotsPkFlag, - Usage: "The EOTS public key that will be used to verify the signature", - Required: true, - }, - cli.StringFlag{ - Name: signatureFlag, - Usage: "The hex signature to verify", - Required: true, - }, - }, - Action: SignSchnorrVerify, -} - -func SignSchnorrVerify(ctx *cli.Context) error { - fpPkStr := ctx.String(eotsPkFlag) - signatureHex := ctx.String(signatureFlag) - - args := ctx.Args() - inputFilePath := args.First() - if len(inputFilePath) == 0 { - return errors.New("invalid argument, please provide a valid file path as input argument") - } - - hashOfMsgToSign, err := hashFromFile(inputFilePath) - if err != nil { - return fmt.Errorf("failed to generate hash from file %s: %w", inputFilePath, err) - } - - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(fpPkStr) - if err != nil { - return fmt.Errorf("invalid finality-provider public key %s: %w", fpPkStr, err) - } - - pubKey, err := schnorr.ParsePubKey(*fpPk) - if err != nil { - return fmt.Errorf("unable to parse public key %s: %w", fpPkStr, err) - } - - signatureBz, err := hex.DecodeString(signatureHex) - if err != nil { - return fmt.Errorf("unable to decode signature %s: %w", signatureHex, err) - } - - signature, err := schnorr.ParseSignature(signatureBz) - if err != nil { - return fmt.Errorf("unable to parse schnorr signature %s: %w", signatureBz, err) - } - - if !signature.Verify(hashOfMsgToSign, pubKey) { - return errors.New("invalid signature") - } - - fmt.Print("Verification is successful!") - return nil -} - -func SignSchnorr(ctx *cli.Context) error { - keyName := ctx.String(keyNameFlag) - fpPkStr := ctx.String(eotsPkFlag) - passphrase := ctx.String(passphraseFlag) - keyringBackend := ctx.String(keyringBackendFlag) - - args := ctx.Args() - inputFilePath := args.First() - if len(inputFilePath) == 0 { - return errors.New("invalid argument, please provide a valid file path as input argument") - } - - if len(fpPkStr) == 0 && len(keyName) == 0 { - return fmt.Errorf("at least one of the flags: %s, %s needs to be informed", keyNameFlag, eotsPkFlag) - } - - homePath, err := getHomeFlag(ctx) - if err != nil { - return fmt.Errorf("failed to load home flag: %w", err) - } - - cfg, err := config.LoadConfig(homePath) - if err != nil { - return fmt.Errorf("failed to load config at %s: %w", homePath, err) - } - - logger, err := log.NewRootLoggerWithFile(config.LogFile(homePath), cfg.LogLevel) - if err != nil { - return fmt.Errorf("failed to load the logger") - } - - dbBackend, err := cfg.DatabaseConfig.GetDbBackend() - if err != nil { - return fmt.Errorf("failed to create db backend: %w", err) - } - defer dbBackend.Close() - - eotsManager, err := eotsmanager.NewLocalEOTSManager(homePath, keyringBackend, dbBackend, logger) - if err != nil { - return fmt.Errorf("failed to create EOTS manager: %w", err) - } - - hashOfMsgToSign, err := hashFromFile(inputFilePath) - if err != nil { - return fmt.Errorf("failed to generate hash from file %s: %w", inputFilePath, err) - } - - signature, pubKey, err := singMsg(eotsManager, keyName, fpPkStr, passphrase, hashOfMsgToSign) - if err != nil { - return fmt.Errorf("failed to sign msg: %w", err) - } - - printRespJSON(DataSigned{ - KeyName: keyName, - PubKeyHex: pubKey.MarshalHex(), - SignedDataHashHex: hex.EncodeToString(hashOfMsgToSign), - SchnorrSignatureHex: hex.EncodeToString(signature.Serialize()), - }) - - return nil -} - -func hashFromFile(inputFilePath string) ([]byte, error) { - h := sha256.New() - - f, err := os.Open(inputFilePath) - if err != nil { - return nil, fmt.Errorf("failed to open the file %s: %w", inputFilePath, err) - } - defer f.Close() - - if _, err := io.Copy(h, f); err != nil { - return nil, err - } - - return h.Sum(nil), nil -} - -func singMsg( - eotsManager *eotsmanager.LocalEOTSManager, - keyName, fpPkStr, passphrase string, - hashOfMsgToSign []byte, -) (*schnorr.Signature, *bbntypes.BIP340PubKey, error) { - if len(fpPkStr) > 0 { - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(fpPkStr) - if err != nil { - return nil, nil, fmt.Errorf("invalid finality-provider public key %s: %w", fpPkStr, err) - } - signature, err := eotsManager.SignSchnorrSig(*fpPk, hashOfMsgToSign, passphrase) - if err != nil { - return nil, nil, fmt.Errorf("unable to sign msg with pk %s: %w", fpPkStr, err) - } - return signature, fpPk, nil - } - - return eotsManager.SignSchnorrSigFromKeyname(keyName, passphrase, hashOfMsgToSign) -} - -func printRespJSON(resp interface{}) { - jsonBytes, err := json.MarshalIndent(resp, "", " ") - if err != nil { - fmt.Println("unable to decode response: ", err) - return - } - - fmt.Printf("%s\n", jsonBytes) -} diff --git a/eotsmanager/cmd/eotsd/daemon/sign_test.go b/eotsmanager/cmd/eotsd/daemon/sign_test.go deleted file mode 100644 index 48a85f98..00000000 --- a/eotsmanager/cmd/eotsd/daemon/sign_test.go +++ /dev/null @@ -1,155 +0,0 @@ -package daemon_test - -import ( - "bytes" - "encoding/json" - "fmt" - "math/rand" - "os" - "path/filepath" - "strings" - "testing" - - sdkmath "cosmossdk.io/math" - dcli "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" - "github.com/babylonlabs-io/finality-provider/testutil" - "github.com/stretchr/testify/require" - "github.com/urfave/cli" - - stktypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -type FpInfo struct { - Description stktypes.Description `json:"description"` - BtcPk string `json:"btc_pk"` - Commision sdkmath.LegacyDec `json:"commission"` -} - -func FuzzSignAndVerifySchnorrSig(f *testing.F) { - testutil.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - tempDir := t.TempDir() - homeDir := filepath.Join(tempDir, "eots-home") - app := testApp() - - // init config in home folder - hFlag := fmt.Sprintf("--home=%s", homeDir) - err := app.Run([]string{"eotsd", "init", hFlag}) - require.NoError(t, err) - - keyName := testutil.GenRandomHexStr(r, 10) - keyNameFlag := fmt.Sprintf("--key-name=%s", keyName) - - outputKeysAdd := appRunWithOutput(r, t, app, []string{"eotsd", "keys", "add", hFlag, keyNameFlag}) - keyOutJson := searchInTxt(outputKeysAdd, "for recovery):") - - var keyOut dcli.KeyOutput - err = json.Unmarshal([]byte(keyOutJson), &keyOut) - require.NoError(t, err) - - fpInfoPath := filepath.Join(tempDir, "fpInfo.json") - writeFpInfoToFile(r, t, fpInfoPath, keyOut.PubKeyHex) - - eotsBtcPkFlag := fmt.Sprintf("--eots-pk=%s", keyOut.PubKeyHex) - dataSignedBtcPk := appRunSignSchnorr(r, t, app, []string{fpInfoPath, hFlag, eotsBtcPkFlag}) - err = app.Run([]string{"eotsd", "verify-schnorr-sig", fpInfoPath, eotsBtcPkFlag, fmt.Sprintf("--signature=%s", dataSignedBtcPk.SchnorrSignatureHex)}) - require.NoError(t, err) - - dataSignedKeyName := appRunSignSchnorr(r, t, app, []string{fpInfoPath, hFlag, keyNameFlag}) - err = app.Run([]string{"eotsd", "verify-schnorr-sig", fpInfoPath, eotsBtcPkFlag, fmt.Sprintf("--signature=%s", dataSignedKeyName.SchnorrSignatureHex)}) - require.NoError(t, err) - - // check if both generated signatures match - require.Equal(t, dataSignedBtcPk.PubKeyHex, dataSignedKeyName.PubKeyHex) - require.Equal(t, dataSignedBtcPk.SchnorrSignatureHex, dataSignedKeyName.SchnorrSignatureHex) - require.Equal(t, dataSignedBtcPk.SignedDataHashHex, dataSignedKeyName.SignedDataHashHex) - - // sign with both keys and eots-pk, should give eots-pk preference - dataSignedBoth := appRunSignSchnorr(r, t, app, []string{fpInfoPath, hFlag, eotsBtcPkFlag, keyNameFlag}) - require.Equal(t, dataSignedBoth, dataSignedKeyName) - - // the keyname can even be from a invalid keyname, since it gives eots-pk preference - badKeyname := "badKeyName" - dataSignedBothBadKeyName := appRunSignSchnorr(r, t, app, []string{fpInfoPath, hFlag, eotsBtcPkFlag, fmt.Sprintf("--key-name=%s", badKeyname)}) - require.Equal(t, badKeyname, dataSignedBothBadKeyName.KeyName) - require.Equal(t, dataSignedBtcPk.PubKeyHex, dataSignedBothBadKeyName.PubKeyHex) - require.Equal(t, dataSignedBtcPk.SchnorrSignatureHex, dataSignedBothBadKeyName.SchnorrSignatureHex) - require.Equal(t, dataSignedBtcPk.SignedDataHashHex, dataSignedBothBadKeyName.SignedDataHashHex) - }) -} - -func appRunSignSchnorr(r *rand.Rand, t *testing.T, app *cli.App, arguments []string) dcli.DataSigned { - args := []string{"eotsd", "sign-schnorr"} - args = append(args, arguments...) - outputSign := appRunWithOutput(r, t, app, args) - signatureStr := searchInTxt(outputSign, "") - - var dataSigned dcli.DataSigned - err := json.Unmarshal([]byte(signatureStr), &dataSigned) - require.NoError(t, err) - - return dataSigned -} - -func appRunWithOutput(r *rand.Rand, t *testing.T, app *cli.App, arguments []string) (output string) { - outPut := filepath.Join(t.TempDir(), fmt.Sprintf("%s-out.txt", testutil.GenRandomHexStr(r, 10))) - outPutFile, err := os.Create(outPut) - require.NoError(t, err) - defer outPutFile.Close() - - // set file to stdout to read. - oldStd := os.Stdout - os.Stdout = outPutFile - - err = app.Run(arguments) - require.NoError(t, err) - - // set to old stdout - os.Stdout = oldStd - return readFromFile(t, outPutFile) -} - -func searchInTxt(text, search string) string { - idxOfRecovery := strings.Index(text, search) - jsonKeyOutputOut := text[idxOfRecovery+len(search):] - return strings.ReplaceAll(jsonKeyOutputOut, "\n", "") -} - -func readFromFile(t *testing.T, f *os.File) string { - _, err := f.Seek(0, 0) - require.NoError(t, err) - - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(f) - require.NoError(t, err) - return buf.String() -} - -func writeFpInfoToFile(r *rand.Rand, t *testing.T, fpInfoPath, btcPk string) { - desc := testutil.RandomDescription(r) - fpInfo := FpInfo{ - BtcPk: btcPk, - Commision: sdkmath.LegacyMustNewDecFromStr("0.5"), - Description: *desc, - } - - bzFpInfo, err := json.Marshal(fpInfo) - require.NoError(t, err) - - fpInfoFile, err := os.Create(fpInfoPath) - require.NoError(t, err) - - _, err = fpInfoFile.Write(bzFpInfo) - require.NoError(t, err) - fpInfoFile.Close() -} - -func testApp() *cli.App { - app := cli.NewApp() - app.Name = "eotsd" - app.Commands = append(app.Commands, dcli.StartCommand, dcli.InitCommand, dcli.SignSchnorrSig, dcli.VerifySchnorrSig, dcli.ExportPoPCommand) - app.Commands = append(app.Commands, dcli.KeysCommands...) - return app -} diff --git a/eotsmanager/cmd/eotsd/daemon/start.go b/eotsmanager/cmd/eotsd/daemon/start.go index e32513eb..ba88cdd5 100644 --- a/eotsmanager/cmd/eotsd/daemon/start.go +++ b/eotsmanager/cmd/eotsd/daemon/start.go @@ -3,38 +3,32 @@ package daemon import ( "fmt" "net" - "path/filepath" - "github.com/lightningnetwork/lnd/signal" - "github.com/urfave/cli" + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" "github.com/babylonlabs-io/finality-provider/eotsmanager" "github.com/babylonlabs-io/finality-provider/eotsmanager/config" eotsservice "github.com/babylonlabs-io/finality-provider/eotsmanager/service" "github.com/babylonlabs-io/finality-provider/log" - "github.com/babylonlabs-io/finality-provider/util" ) -var StartCommand = cli.Command{ - Name: "start", - Usage: "Start the Extractable One Time Signature Daemon.", - Description: "Start the Extractable One Time Signature Daemon.", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: homeFlag, - Usage: "The path to the eotsd home directory", - Value: config.DefaultEOTSDir, - }, - cli.StringFlag{ - Name: rpcListenerFlag, - Usage: "The address that the RPC server listens to", - }, - }, - Action: startFn, +func NewStartCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "start", + Short: "Start the Extractable One Time Signature Daemon", + Long: "Start the Extractable One Time Signature Daemon and run it until shutdown.", + RunE: startFn, + } + + cmd.Flags().String(sdkflags.FlagHome, config.DefaultEOTSDir, "The path to the eotsd home directory") + cmd.Flags().String(rpcListenerFlag, "", "The address that the RPC server listens to") + + return cmd } -func startFn(ctx *cli.Context) error { - homePath, err := getHomeFlag(ctx) +func startFn(cmd *cobra.Command, _ []string) error { + homePath, err := getHomePath(cmd) if err != nil { return fmt.Errorf("failed to load home flag: %w", err) } @@ -44,21 +38,24 @@ func startFn(ctx *cli.Context) error { return fmt.Errorf("failed to load config at %s: %w", homePath, err) } - rpcListener := ctx.String(rpcListenerFlag) + rpcListener, err := cmd.Flags().GetString(rpcListenerFlag) + if err != nil { + return fmt.Errorf("failed to get RPC listener flag: %w", err) + } if rpcListener != "" { _, err := net.ResolveTCPAddr("tcp", rpcListener) if err != nil { - return fmt.Errorf("invalid RPC listener address %s, %w", rpcListener, err) + return fmt.Errorf("invalid RPC listener address %s: %w", rpcListener, err) } - cfg.RpcListener = rpcListener + cfg.RPCListener = rpcListener } logger, err := log.NewRootLoggerWithFile(config.LogFile(homePath), cfg.LogLevel) if err != nil { - return fmt.Errorf("failed to load the logger") + return fmt.Errorf("failed to load the logger: %w", err) } - dbBackend, err := cfg.DatabaseConfig.GetDbBackend() + dbBackend, err := cfg.DatabaseConfig.GetDBBackend() if err != nil { return fmt.Errorf("failed to create db backend: %w", err) } @@ -68,21 +65,7 @@ func startFn(ctx *cli.Context) error { return fmt.Errorf("failed to create EOTS manager: %w", err) } - // Hook interceptor for os signals. - shutdownInterceptor, err := signal.Intercept() - if err != nil { - return err - } - - eotsServer := eotsservice.NewEOTSManagerServer(cfg, logger, eotsManager, dbBackend, shutdownInterceptor) - - return eotsServer.RunUntilShutdown() -} + eotsServer := eotsservice.NewEOTSManagerServer(cfg, logger, eotsManager, dbBackend) -func getHomeFlag(ctx *cli.Context) (string, error) { - homePath, err := filepath.Abs(ctx.String(homeFlag)) - if err != nil { - return "", err - } - return util.CleanAndExpandPath(homePath), nil + return eotsServer.RunUntilShutdown(cmd.Context()) } diff --git a/eotsmanager/cmd/eotsd/daemon/utils.go b/eotsmanager/cmd/eotsd/daemon/utils.go new file mode 100644 index 00000000..46920818 --- /dev/null +++ b/eotsmanager/cmd/eotsd/daemon/utils.go @@ -0,0 +1,58 @@ +package daemon + +import ( + "os" + "path/filepath" + + "github.com/babylonlabs-io/babylon/app/params" + "github.com/cosmos/cosmos-sdk/client" + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/std" + "github.com/spf13/cobra" + + "github.com/babylonlabs-io/finality-provider/util" +) + +func getHomePath(cmd *cobra.Command) (string, error) { + return getCleanPath(cmd, sdkflags.FlagHome) +} + +func getCleanPath(cmd *cobra.Command, flag string) (string, error) { + rawPath, err := cmd.Flags().GetString(flag) + if err != nil { + return "", err + } + + cleanPath, err := filepath.Abs(rawPath) + if err != nil { + return "", err + } + + return util.CleanAndExpandPath(cleanPath), nil +} + +// PersistClientCtx persist some vars from the cmd or config to the client context. +// It gives preferences to flags over the values in the config. If the flag is not set +// and exists a value in the config that could be used, it will be set in the ctx. +func PersistClientCtx(ctx client.Context) func(cmd *cobra.Command, _ []string) error { + return func(cmd *cobra.Command, _ []string) error { + encCfg := params.DefaultEncodingConfig() + std.RegisterInterfaces(encCfg.InterfaceRegistry) + + ctx = ctx. + WithCodec(encCfg.Codec). + WithInterfaceRegistry(encCfg.InterfaceRegistry). + WithTxConfig(encCfg.TxConfig). + WithLegacyAmino(encCfg.Amino). + WithInput(os.Stdin) + + // set the default command outputs + cmd.SetOut(cmd.OutOrStdout()) + cmd.SetErr(cmd.ErrOrStderr()) + + ctx = ctx.WithCmdContext(cmd.Context()) + + // updates the ctx in the cmd in case something was modified bt the config + return client.SetCmdClientContextHandler(ctx, cmd) + } +} diff --git a/eotsmanager/cmd/eotsd/main.go b/eotsmanager/cmd/eotsd/main.go index 285296b5..7fddadfc 100644 --- a/eotsmanager/cmd/eotsd/main.go +++ b/eotsmanager/cmd/eotsd/main.go @@ -1,30 +1,21 @@ package main import ( + "context" "fmt" "os" + "os/signal" + "syscall" - "github.com/urfave/cli" - - dcli "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" + "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" ) -func fatal(err error) { - fmt.Fprintf(os.Stderr, "[eotsd] %v\n", err) - os.Exit(1) -} - func main() { - app := cli.NewApp() - app.Name = "eotsd" - app.Usage = "Extractable One Time Signature Daemon (eotsd)." - app.Commands = append( - app.Commands, dcli.StartCommand, dcli.InitCommand, dcli.SignSchnorrSig, dcli.VerifySchnorrSig, - dcli.ExportPoPCommand, - ) - app.Commands = append(app.Commands, dcli.KeysCommands...) + ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer cancel() - if err := app.Run(os.Args); err != nil { - fatal(err) + if err := daemon.NewRootCmd().ExecuteContext(ctx); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Error while executing eotsd CLI: %s", err.Error()) + os.Exit(1) //nolint:gocritic } } diff --git a/eotsmanager/config/config.go b/eotsmanager/config/config.go index 5cc49d35..8baa4a1c 100644 --- a/eotsmanager/config/config.go +++ b/eotsmanager/config/config.go @@ -31,13 +31,14 @@ var ( // ~/Library/Application Support/Eotsd on MacOS DefaultEOTSDir = btcutil.AppDataDir("eotsd", false) + //nolint:revive,stylecheck defaultRpcListener = fmt.Sprintf("%s:%d", DefaultRPCHost, DefaultRPCPort) ) type Config struct { LogLevel string `long:"loglevel" description:"Logging level for all subsystems" choice:"trace" choice:"debug" choice:"info" choice:"warn" choice:"error" choice:"fatal"` KeyringBackend string `long:"keyring-type" description:"Type of keyring to use"` - RpcListener string `long:"rpclistener" description:"the listener for RPC connections, e.g., 127.0.0.1:1234"` + RPCListener string `long:"rpclistener" description:"the listener for RPC connections, e.g., 127.0.0.1:1234"` Metrics *metrics.Config `group:"metrics" namespace:"metrics"` DatabaseConfig *DBConfig `group:"dbconfig" namespace:"dbconfig"` @@ -54,7 +55,7 @@ type Config struct { func LoadConfig(homePath string) (*Config, error) { // The home directory is required to have a configuration file with a specific name // under it. - cfgFile := ConfigFile(homePath) + cfgFile := CfgFile(homePath) if !util.FileExists(cfgFile) { return nil, fmt.Errorf("specified config file does "+ "not exist in %s", cfgFile) @@ -80,9 +81,9 @@ func LoadConfig(homePath string) (*Config, error) { // illegal values or combination of values are set. All file system paths are // normalized. The cleaned up config is returned on success. func (cfg *Config) Validate() error { - _, err := net.ResolveTCPAddr("tcp", cfg.RpcListener) + _, err := net.ResolveTCPAddr("tcp", cfg.RPCListener) if err != nil { - return fmt.Errorf("invalid RPC listener address %s, %w", cfg.RpcListener, err) + return fmt.Errorf("invalid RPC listener address %s, %w", cfg.RPCListener, err) } if cfg.KeyringBackend == "" { @@ -100,7 +101,7 @@ func (cfg *Config) Validate() error { return nil } -func ConfigFile(homePath string) string { +func CfgFile(homePath string) string { return filepath.Join(homePath, defaultConfigFileName) } @@ -129,13 +130,14 @@ func DefaultConfigWithHomePathAndPorts(homePath string, rpcPort, metricsPort int LogLevel: defaultLogLevel, KeyringBackend: defaultKeyringBackend, DatabaseConfig: DefaultDBConfigWithHomePath(homePath), - RpcListener: defaultRpcListener, + RPCListener: defaultRpcListener, Metrics: metrics.DefaultEotsConfig(), } - cfg.RpcListener = fmt.Sprintf("%s:%d", DefaultRPCHost, rpcPort) + cfg.RPCListener = fmt.Sprintf("%s:%d", DefaultRPCHost, rpcPort) cfg.Metrics.Port = metricsPort if err := cfg.Validate(); err != nil { panic(err) } + return cfg } diff --git a/eotsmanager/config/db.go b/eotsmanager/config/db.go index 08e7f965..693113a4 100644 --- a/eotsmanager/config/db.go +++ b/eotsmanager/config/db.go @@ -7,7 +7,7 @@ import ( ) const ( - defaultDbName = "eots.db" + defaultDBName = "eots.db" ) type DBConfig struct { @@ -47,7 +47,7 @@ func DefaultDBConfig() *DBConfig { func DefaultDBConfigWithHomePath(homePath string) *DBConfig { return &DBConfig{ DBPath: DataDir(homePath), - DBFileName: defaultDbName, + DBFileName: defaultDBName, NoFreelistSync: true, AutoCompact: false, AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge, @@ -66,6 +66,6 @@ func (db *DBConfig) DBConfigToBoltBackendConfig() *kvdb.BoltBackendConfig { } } -func (db *DBConfig) GetDbBackend() (kvdb.Backend, error) { +func (db *DBConfig) GetDBBackend() (kvdb.Backend, error) { return kvdb.GetBoltBackend(db.DBConfigToBoltBackendConfig()) } diff --git a/eotsmanager/eotsmanager.go b/eotsmanager/eotsmanager.go index 04d02316..aa4537bc 100644 --- a/eotsmanager/eotsmanager.go +++ b/eotsmanager/eotsmanager.go @@ -28,13 +28,22 @@ type EOTSManager interface { // SignEOTS signs an EOTS using the private key of the finality provider and the corresponding // secret randomness of the given chain at the given height // It fails if the finality provider does not exist or there's no randomness committed to the given height - // or passPhrase is incorrect + // or passPhrase is incorrect. Has built-in anti-slashing mechanism to ensure signature + // for the same height will not be signed twice. SignEOTS(uid []byte, chainID []byte, msg []byte, height uint64, passphrase string) (*btcec.ModNScalar, error) + // UnsafeSignEOTS should only be used in e2e tests for demonstration purposes. + // Does not offer double sign protection. + // Use SignEOTS for real operations. + UnsafeSignEOTS(uid []byte, chainID []byte, msg []byte, height uint64, passphrase string) (*btcec.ModNScalar, error) + // SignSchnorrSig signs a Schnorr signature using the private key of the finality provider // It fails if the finality provider does not exist or the message size is not 32 bytes // or passPhrase is incorrect SignSchnorrSig(uid []byte, msg []byte, passphrase string) (*schnorr.Signature, error) + // SaveEOTSKeyName saves a new key under the EOTS key name mapping + SaveEOTSKeyName(pk *btcec.PublicKey, keyName string) error + Close() error } diff --git a/eotsmanager/localmanager.go b/eotsmanager/localmanager.go index eb819287..4edc5a46 100644 --- a/eotsmanager/localmanager.go +++ b/eotsmanager/localmanager.go @@ -1,6 +1,7 @@ package eotsmanager import ( + "bytes" "encoding/hex" "fmt" "strings" @@ -49,7 +50,7 @@ func NewLocalEOTSManager(homeDir, keyringBackend string, dbbackend kvdb.Backend, return nil, fmt.Errorf("failed to initialize store: %w", err) } - kr, err := initKeyring(homeDir, keyringBackend, inputReader) + kr, err := InitKeyring(homeDir, keyringBackend, inputReader) if err != nil { return nil, fmt.Errorf("failed to initialize keyring: %w", err) } @@ -65,7 +66,7 @@ func NewLocalEOTSManager(homeDir, keyringBackend string, dbbackend kvdb.Backend, }, nil } -func initKeyring(homeDir, keyringBackend string, inputReader *strings.Reader) (keyring.Keyring, error) { +func InitKeyring(homeDir, keyringBackend string, inputReader *strings.Reader) (keyring.Keyring, error) { return keyring.New( "eots-manager", keyringBackend, @@ -119,17 +120,17 @@ func (lm *LocalEOTSManager) CreateKeyWithMnemonic(name, passphrase, hdPath, mnem // as when creating an account, passphrase will be asked twice // by the keyring lm.input.Reset(passphrase + "\n" + passphrase) - record, err := lm.kr.NewAccount(name, mnemonic, passphrase, hdPath, algo) + _, err = lm.kr.NewAccount(name, mnemonic, passphrase, hdPath, algo) if err != nil { return nil, err } - eotsPk, err := loadBIP340PubKeyFromKeyringRecord(record) + eotsPk, err := lm.LoadBIP340PubKeyFromKeyName(name) if err != nil { return nil, err } - if err := lm.es.AddEOTSKeyName(eotsPk.MustToBTCPK(), name); err != nil { + if err := lm.SaveEOTSKeyName(eotsPk.MustToBTCPK(), name); err != nil { return nil, err } @@ -143,8 +144,20 @@ func (lm *LocalEOTSManager) CreateKeyWithMnemonic(name, passphrase, hdPath, mnem return eotsPk, nil } -func loadBIP340PubKeyFromKeyringRecord(record *keyring.Record) (*bbntypes.BIP340PubKey, error) { - pubKey, err := record.GetPubKey() +func (lm *LocalEOTSManager) SaveEOTSKeyName(pk *btcec.PublicKey, keyName string) error { + return lm.es.AddEOTSKeyName(pk, keyName) +} + +func (lm *LocalEOTSManager) LoadBIP340PubKeyFromKeyName(keyName string) (*bbntypes.BIP340PubKey, error) { + return LoadBIP340PubKeyFromKeyName(lm.kr, keyName) +} + +func LoadBIP340PubKeyFromKeyName(kr keyring.Keyring, keyName string) (*bbntypes.BIP340PubKey, error) { + info, err := kr.Key(keyName) + if err != nil { + return nil, fmt.Errorf("failed to load keyring record for key %s: %w", keyName, err) + } + pubKey, err := info.GetPubKey() if err != nil { return nil, err } @@ -157,16 +170,13 @@ func loadBIP340PubKeyFromKeyringRecord(record *keyring.Record) (*bbntypes.BIP340 return nil, err } eotsPk = bbntypes.NewBIP340PubKeyFromBTCPK(pk) + return eotsPk, nil default: return nil, fmt.Errorf("unsupported key type in keyring") } } -// TODO the current implementation is a PoC, which does not contain any anti-slasher mechanism -// -// a simple anti-slasher mechanism could be that the manager remembers the tuple (fpPk, chainID, height) or -// the hash of each generated randomness and return error if the same randomness is requested twice func (lm *LocalEOTSManager) CreateRandomnessPairList(fpPk []byte, chainID []byte, startHeight uint64, num uint32, passphrase string) ([]*btcec.FieldVal, error) { prList := make([]*btcec.FieldVal, 0, num) @@ -185,7 +195,68 @@ func (lm *LocalEOTSManager) CreateRandomnessPairList(fpPk []byte, chainID []byte return prList, nil } -func (lm *LocalEOTSManager) SignEOTS(fpPk []byte, chainID []byte, msg []byte, height uint64, passphrase string) (*btcec.ModNScalar, error) { +func (lm *LocalEOTSManager) SignEOTS(eotsPk []byte, chainID []byte, msg []byte, height uint64, passphrase string) (*btcec.ModNScalar, error) { + record, found, err := lm.es.GetSignRecord(eotsPk, chainID, height) + if err != nil { + return nil, fmt.Errorf("error getting sign record: %w", err) + } + + if found { + if bytes.Equal(msg, record.Msg) { + var s btcec.ModNScalar + s.SetByteSlice(record.Signature) + + lm.logger.Warn( + "duplicate sign requested", + zap.String("eots_pk", hex.EncodeToString(eotsPk)), + zap.String("hash", hex.EncodeToString(msg)), + zap.Uint64("height", height), + zap.String("chainID", string(chainID)), + ) + + return &s, nil + } + + lm.logger.Error( + "double sign requested", + zap.String("eots_pk", hex.EncodeToString(eotsPk)), + zap.String("hash", hex.EncodeToString(msg)), + zap.Uint64("height", height), + zap.String("chainID", string(chainID)), + ) + + return nil, eotstypes.ErrDoubleSign + } + + privRand, _, err := lm.getRandomnessPair(eotsPk, chainID, height, passphrase) + if err != nil { + return nil, fmt.Errorf("failed to get private randomness: %w", err) + } + + privKey, err := lm.getEOTSPrivKey(eotsPk, passphrase) + if err != nil { + return nil, fmt.Errorf("failed to get EOTS private key: %w", err) + } + + // Update metrics + lm.metrics.IncrementEotsFpTotalEotsSignCounter(hex.EncodeToString(eotsPk)) + lm.metrics.SetEotsFpLastEotsSignHeight(hex.EncodeToString(eotsPk), float64(height)) + + signedBytes, err := eots.Sign(privKey, privRand, msg) + if err != nil { + return nil, fmt.Errorf("failed to sign eots") + } + + b := signedBytes.Bytes() + if err := lm.es.SaveSignRecord(height, chainID, msg, eotsPk, b[:]); err != nil { + return nil, fmt.Errorf("failed to save signing record: %w", err) + } + + return signedBytes, nil +} + +// UnsafeSignEOTS should only be used in e2e test to demonstrate double sign +func (lm *LocalEOTSManager) UnsafeSignEOTS(fpPk []byte, chainID []byte, msg []byte, height uint64, passphrase string) (*btcec.ModNScalar, error) { privRand, _, err := lm.getRandomnessPair(fpPk, chainID, height, passphrase) if err != nil { return nil, fmt.Errorf("failed to get private randomness: %w", err) @@ -216,22 +287,19 @@ func (lm *LocalEOTSManager) SignSchnorrSig(fpPk []byte, msg []byte, passphrase s func (lm *LocalEOTSManager) signSchnorrSigFromPrivKey(privKey *btcec.PrivateKey, fpPk []byte, msg []byte) (*schnorr.Signature, error) { // Update metrics lm.metrics.IncrementEotsFpTotalSchnorrSignCounter(hex.EncodeToString(fpPk)) + return schnorr.Sign(privKey, msg) } func (lm *LocalEOTSManager) SignSchnorrSigFromKeyname(keyName, passphrase string, msg []byte) (*schnorr.Signature, *bbntypes.BIP340PubKey, error) { lm.input.Reset(passphrase) - k, err := lm.kr.Key(keyName) - if err != nil { - return nil, nil, fmt.Errorf("failed to load keyring record for key %s: %w", keyName, err) - } - eotsPk, err := loadBIP340PubKeyFromKeyringRecord(k) + eotsPk, err := lm.LoadBIP340PubKeyFromKeyName(keyName) if err != nil { return nil, nil, err } - privKey, err := eotsPrivKeyFromRecord(k) + privKey, err := lm.eotsPrivKeyFromKeyName(keyName) if err != nil { return nil, nil, err } @@ -240,11 +308,12 @@ func (lm *LocalEOTSManager) SignSchnorrSigFromKeyname(keyName, passphrase string if err != nil { return nil, nil, fmt.Errorf("failed to schnorr sign: %w", err) } + return signature, eotsPk, nil } func (lm *LocalEOTSManager) Close() error { - return nil + return lm.es.Close() } // getRandomnessPair returns a randomness pair generated based on the given finality provider key, chainID and height @@ -254,10 +323,10 @@ func (lm *LocalEOTSManager) getRandomnessPair(fpPk []byte, chainID []byte, heigh return nil, nil, err } privRand, pubRand := randgenerator.GenerateRandomness(record.PrivKey.Serialize(), chainID, height) + return privRand, pubRand, nil } -// TODO: we ignore passPhrase in local implementation for now func (lm *LocalEOTSManager) KeyRecord(fpPk []byte, passphrase string) (*eotstypes.KeyRecord, error) { name, err := lm.es.GetEOTSKeyName(fpPk) if err != nil { @@ -283,21 +352,22 @@ func (lm *LocalEOTSManager) getEOTSPrivKey(fpPk []byte, passphrase string) (*btc } lm.input.Reset(passphrase) + + return lm.eotsPrivKeyFromKeyName(keyName) +} + +func (lm *LocalEOTSManager) eotsPrivKeyFromKeyName(keyName string) (*btcec.PrivateKey, error) { k, err := lm.kr.Key(keyName) if err != nil { return nil, err } - - return eotsPrivKeyFromRecord(k) -} - -func eotsPrivKeyFromRecord(k *keyring.Record) (*btcec.PrivateKey, error) { privKeyCached := k.GetLocal().PrivKey.GetCachedValue() var privKey *btcec.PrivateKey switch v := privKeyCached.(type) { case *secp256k1.PrivKey: privKey, _ = btcec.PrivKeyFromBytes(v.Key) + return privKey, nil default: return nil, fmt.Errorf("unsupported key type in keyring") @@ -306,5 +376,10 @@ func eotsPrivKeyFromRecord(k *keyring.Record) (*btcec.PrivateKey, error) { func (lm *LocalEOTSManager) keyExists(name string) bool { _, err := lm.kr.Key(name) + return err == nil } + +func (lm *LocalEOTSManager) ListEOTSKeys() (map[string][]byte, error) { + return lm.es.GetAllEOTSKeyNames() +} diff --git a/eotsmanager/localmanager_test.go b/eotsmanager/localmanager_test.go index 4db6fd1c..0f3672d2 100644 --- a/eotsmanager/localmanager_test.go +++ b/eotsmanager/localmanager_test.go @@ -6,7 +6,9 @@ import ( "path/filepath" "testing" + "github.com/babylonlabs-io/babylon/crypto/eots" "github.com/babylonlabs-io/babylon/testutil/datagen" + bbntypes "github.com/babylonlabs-io/babylon/types" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -30,7 +32,7 @@ func FuzzCreateKey(f *testing.F) { fpName := testutil.GenRandomHexStr(r, 4) homeDir := filepath.Join(t.TempDir(), "eots-home") eotsCfg := eotscfg.DefaultConfigWithHomePath(homeDir) - dbBackend, err := eotsCfg.DatabaseConfig.GetDbBackend() + dbBackend, err := eotsCfg.DatabaseConfig.GetDBBackend() require.NoError(t, err) defer func() { dbBackend.Close() @@ -65,7 +67,7 @@ func FuzzCreateRandomnessPairList(f *testing.F) { fpName := testutil.GenRandomHexStr(r, 4) homeDir := filepath.Join(t.TempDir(), "eots-home") eotsCfg := eotscfg.DefaultConfigWithHomePath(homeDir) - dbBackend, err := eotsCfg.DatabaseConfig.GetDbBackend() + dbBackend, err := eotsCfg.DatabaseConfig.GetDBBackend() defer func() { dbBackend.Close() err := os.RemoveAll(homeDir) @@ -92,3 +94,59 @@ func FuzzCreateRandomnessPairList(f *testing.F) { } }) } + +func FuzzSignRecord(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + homeDir := filepath.Join(t.TempDir(), "eots-home") + eotsCfg := eotscfg.DefaultConfigWithHomePath(homeDir) + dbBackend, err := eotsCfg.DatabaseConfig.GetDBBackend() + defer func() { + dbBackend.Close() + err := os.RemoveAll(homeDir) + require.NoError(t, err) + }() + require.NoError(t, err) + lm, err := eotsmanager.NewLocalEOTSManager(homeDir, eotsCfg.KeyringBackend, dbBackend, zap.NewNop()) + require.NoError(t, err) + + startHeight := datagen.RandomInt(r, 100) + numRand := r.Intn(10) + 1 + + msg := datagen.GenRandomByteArray(r, 32) + numFps := 3 + for i := 0; i < numFps; i++ { + chainID := datagen.GenRandomByteArray(r, 10) + fpName := testutil.GenRandomHexStr(r, 4) + fpPk, err := lm.CreateKey(fpName, passphrase, hdPath) + require.NoError(t, err) + pubRandList, err := lm.CreateRandomnessPairList(fpPk, chainID, startHeight, uint32(numRand), passphrase) + require.NoError(t, err) + require.Len(t, pubRandList, numRand) + + sig, err := lm.SignEOTS(fpPk, chainID, msg, startHeight, passphrase) + require.NoError(t, err) + require.NotNil(t, sig) + + eotsPk, err := bbntypes.NewBIP340PubKey(fpPk) + require.NoError(t, err) + + err = eots.Verify(eotsPk.MustToBTCPK(), pubRandList[0], msg, sig) + require.NoError(t, err) + + // we expect return from db + sig2, err := lm.SignEOTS(fpPk, chainID, msg, startHeight, passphrase) + require.NoError(t, err) + require.Equal(t, sig, sig2) + + err = eots.Verify(eotsPk.MustToBTCPK(), pubRandList[0], msg, sig2) + require.NoError(t, err) + + // same height diff msg + _, err = lm.SignEOTS(fpPk, chainID, datagen.GenRandomByteArray(r, 32), startHeight, passphrase) + require.ErrorIs(t, err, types.ErrDoubleSign) + } + }) +} diff --git a/eotsmanager/proto/eotsmanager.pb.go b/eotsmanager/proto/eotsmanager.pb.go index eaf5ac07..1207629d 100644 --- a/eotsmanager/proto/eotsmanager.pb.go +++ b/eotsmanager/proto/eotsmanager.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 +// protoc-gen-go v1.33.0 // protoc (unknown) // source: eotsmanager.proto @@ -702,6 +702,102 @@ func (x *SignSchnorrSigResponse) GetSig() []byte { return nil } +type SaveEOTSKeyNameRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // KeyName is the name of the key which corresponds to the + // BIP340 public key + KeyName string `protobuf:"bytes,1,opt,name=key_name,json=keyName,proto3" json:"key_name,omitempty"` + // EotsPK is the public key of the EOTS key BIP340PubKey + EotsPk []byte `protobuf:"bytes,2,opt,name=eots_pk,json=eotsPk,proto3" json:"eots_pk,omitempty"` +} + +func (x *SaveEOTSKeyNameRequest) Reset() { + *x = SaveEOTSKeyNameRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_eotsmanager_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SaveEOTSKeyNameRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SaveEOTSKeyNameRequest) ProtoMessage() {} + +func (x *SaveEOTSKeyNameRequest) ProtoReflect() protoreflect.Message { + mi := &file_eotsmanager_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SaveEOTSKeyNameRequest.ProtoReflect.Descriptor instead. +func (*SaveEOTSKeyNameRequest) Descriptor() ([]byte, []int) { + return file_eotsmanager_proto_rawDescGZIP(), []int{12} +} + +func (x *SaveEOTSKeyNameRequest) GetKeyName() string { + if x != nil { + return x.KeyName + } + return "" +} + +func (x *SaveEOTSKeyNameRequest) GetEotsPk() []byte { + if x != nil { + return x.EotsPk + } + return nil +} + +type SaveEOTSKeyNameResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SaveEOTSKeyNameResponse) Reset() { + *x = SaveEOTSKeyNameResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_eotsmanager_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SaveEOTSKeyNameResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SaveEOTSKeyNameResponse) ProtoMessage() {} + +func (x *SaveEOTSKeyNameResponse) ProtoReflect() protoreflect.Message { + mi := &file_eotsmanager_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SaveEOTSKeyNameResponse.ProtoReflect.Descriptor instead. +func (*SaveEOTSKeyNameResponse) Descriptor() ([]byte, []int) { + return file_eotsmanager_proto_rawDescGZIP(), []int{13} +} + var File_eotsmanager_proto protoreflect.FileDescriptor var file_eotsmanager_proto_rawDesc = []byte{ @@ -760,39 +856,55 @@ var file_eotsmanager_proto_rawDesc = []byte{ 0x0a, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x22, 0x2a, 0x0a, 0x16, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x32, 0xb7, 0x03, 0x0a, 0x0b, 0x45, 0x4f, 0x54, 0x53, - 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, - 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x4c, 0x0a, 0x16, 0x53, 0x61, 0x76, 0x65, 0x45, + 0x4f, 0x54, 0x53, 0x4b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, + 0x65, 0x6f, 0x74, 0x73, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x65, + 0x6f, 0x74, 0x73, 0x50, 0x6b, 0x22, 0x19, 0x0a, 0x17, 0x53, 0x61, 0x76, 0x65, 0x45, 0x4f, 0x54, + 0x53, 0x4b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x32, 0xcc, 0x04, 0x0a, 0x0b, 0x45, 0x4f, 0x54, 0x53, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, + 0x12, 0x2f, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x61, 0x69, 0x72, - 0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x61, 0x69, - 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x64, 0x6f, - 0x6d, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x61, 0x69, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x4f, 0x54, - 0x53, 0x12, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x4f, - 0x54, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x4f, 0x54, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, - 0x72, 0x53, 0x69, 0x67, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, - 0x6e, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x53, - 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x62, 0x61, 0x62, 0x79, 0x6c, 0x6f, 0x6e, 0x6c, 0x61, 0x62, 0x73, 0x2d, 0x69, 0x6f, 0x2f, 0x66, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x2f, 0x65, 0x6f, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x6b, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x64, 0x6f, + 0x6d, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x61, 0x69, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x64, + 0x6f, 0x6d, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x61, 0x69, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x61, + 0x69, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, + 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x17, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, + 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x4f, 0x54, 0x53, 0x12, 0x16, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x4f, 0x54, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x45, + 0x4f, 0x54, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x55, + 0x6e, 0x73, 0x61, 0x66, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x4f, 0x54, 0x53, 0x12, 0x16, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x4f, 0x54, 0x53, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, + 0x67, 0x6e, 0x45, 0x4f, 0x54, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, + 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, + 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x63, 0x68, + 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x63, 0x68, 0x6e, 0x6f, + 0x72, 0x72, 0x53, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, + 0x0f, 0x53, 0x61, 0x76, 0x65, 0x45, 0x4f, 0x54, 0x53, 0x4b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x45, 0x4f, 0x54, + 0x53, 0x4b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x45, 0x4f, 0x54, 0x53, + 0x4b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, + 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, + 0x62, 0x79, 0x6c, 0x6f, 0x6e, 0x6c, 0x61, 0x62, 0x73, 0x2d, 0x69, 0x6f, 0x2f, 0x66, 0x69, 0x6e, + 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x65, + 0x6f, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -807,7 +919,7 @@ func file_eotsmanager_proto_rawDescGZIP() []byte { return file_eotsmanager_proto_rawDescData } -var file_eotsmanager_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_eotsmanager_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_eotsmanager_proto_goTypes = []interface{}{ (*PingRequest)(nil), // 0: proto.PingRequest (*PingResponse)(nil), // 1: proto.PingResponse @@ -821,6 +933,8 @@ var file_eotsmanager_proto_goTypes = []interface{}{ (*SignEOTSResponse)(nil), // 9: proto.SignEOTSResponse (*SignSchnorrSigRequest)(nil), // 10: proto.SignSchnorrSigRequest (*SignSchnorrSigResponse)(nil), // 11: proto.SignSchnorrSigResponse + (*SaveEOTSKeyNameRequest)(nil), // 12: proto.SaveEOTSKeyNameRequest + (*SaveEOTSKeyNameResponse)(nil), // 13: proto.SaveEOTSKeyNameResponse } var file_eotsmanager_proto_depIdxs = []int32{ 0, // 0: proto.EOTSManager.Ping:input_type -> proto.PingRequest @@ -828,15 +942,19 @@ var file_eotsmanager_proto_depIdxs = []int32{ 4, // 2: proto.EOTSManager.CreateRandomnessPairList:input_type -> proto.CreateRandomnessPairListRequest 6, // 3: proto.EOTSManager.KeyRecord:input_type -> proto.KeyRecordRequest 8, // 4: proto.EOTSManager.SignEOTS:input_type -> proto.SignEOTSRequest - 10, // 5: proto.EOTSManager.SignSchnorrSig:input_type -> proto.SignSchnorrSigRequest - 1, // 6: proto.EOTSManager.Ping:output_type -> proto.PingResponse - 3, // 7: proto.EOTSManager.CreateKey:output_type -> proto.CreateKeyResponse - 5, // 8: proto.EOTSManager.CreateRandomnessPairList:output_type -> proto.CreateRandomnessPairListResponse - 7, // 9: proto.EOTSManager.KeyRecord:output_type -> proto.KeyRecordResponse - 9, // 10: proto.EOTSManager.SignEOTS:output_type -> proto.SignEOTSResponse - 11, // 11: proto.EOTSManager.SignSchnorrSig:output_type -> proto.SignSchnorrSigResponse - 6, // [6:12] is the sub-list for method output_type - 0, // [0:6] is the sub-list for method input_type + 8, // 5: proto.EOTSManager.UnsafeSignEOTS:input_type -> proto.SignEOTSRequest + 10, // 6: proto.EOTSManager.SignSchnorrSig:input_type -> proto.SignSchnorrSigRequest + 12, // 7: proto.EOTSManager.SaveEOTSKeyName:input_type -> proto.SaveEOTSKeyNameRequest + 1, // 8: proto.EOTSManager.Ping:output_type -> proto.PingResponse + 3, // 9: proto.EOTSManager.CreateKey:output_type -> proto.CreateKeyResponse + 5, // 10: proto.EOTSManager.CreateRandomnessPairList:output_type -> proto.CreateRandomnessPairListResponse + 7, // 11: proto.EOTSManager.KeyRecord:output_type -> proto.KeyRecordResponse + 9, // 12: proto.EOTSManager.SignEOTS:output_type -> proto.SignEOTSResponse + 9, // 13: proto.EOTSManager.UnsafeSignEOTS:output_type -> proto.SignEOTSResponse + 11, // 14: proto.EOTSManager.SignSchnorrSig:output_type -> proto.SignSchnorrSigResponse + 13, // 15: proto.EOTSManager.SaveEOTSKeyName:output_type -> proto.SaveEOTSKeyNameResponse + 8, // [8:16] is the sub-list for method output_type + 0, // [0:8] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -992,6 +1110,30 @@ func file_eotsmanager_proto_init() { return nil } } + file_eotsmanager_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SaveEOTSKeyNameRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_eotsmanager_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SaveEOTSKeyNameResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -999,7 +1141,7 @@ func file_eotsmanager_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_eotsmanager_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, diff --git a/eotsmanager/proto/eotsmanager.proto b/eotsmanager/proto/eotsmanager.proto index cc7efec0..c0cbc91e 100644 --- a/eotsmanager/proto/eotsmanager.proto +++ b/eotsmanager/proto/eotsmanager.proto @@ -23,9 +23,17 @@ service EOTSManager { rpc SignEOTS (SignEOTSRequest) returns (SignEOTSResponse); + // UnsafeSignEOTS used only for testing purpose. Use SignEOTS for real operations + rpc UnsafeSignEOTS (SignEOTSRequest) + returns (SignEOTSResponse); + // SignSchnorrSig signs a Schnorr sig with the EOTS private key rpc SignSchnorrSig (SignSchnorrSigRequest) returns (SignSchnorrSigResponse); + + // SaveEOTSKeyName saves a new key name mapping for the EOTS public key + rpc SaveEOTSKeyName (SaveEOTSKeyNameRequest) + returns (SaveEOTSKeyNameResponse); } message PingRequest {} @@ -109,3 +117,13 @@ message SignSchnorrSigResponse { // sig is the Schnorr signature bytes sig = 1; } + +message SaveEOTSKeyNameRequest { + // KeyName is the name of the key which corresponds to the + // BIP340 public key + string key_name = 1; + // EotsPK is the public key of the EOTS key BIP340PubKey + bytes eots_pk = 2; +} + +message SaveEOTSKeyNameResponse {} diff --git a/eotsmanager/proto/eotsmanager_grpc.pb.go b/eotsmanager/proto/eotsmanager_grpc.pb.go index 0a8814ad..4b695e92 100644 --- a/eotsmanager/proto/eotsmanager_grpc.pb.go +++ b/eotsmanager/proto/eotsmanager_grpc.pb.go @@ -24,7 +24,9 @@ const ( EOTSManager_CreateRandomnessPairList_FullMethodName = "/proto.EOTSManager/CreateRandomnessPairList" EOTSManager_KeyRecord_FullMethodName = "/proto.EOTSManager/KeyRecord" EOTSManager_SignEOTS_FullMethodName = "/proto.EOTSManager/SignEOTS" + EOTSManager_UnsafeSignEOTS_FullMethodName = "/proto.EOTSManager/UnsafeSignEOTS" EOTSManager_SignSchnorrSig_FullMethodName = "/proto.EOTSManager/SignSchnorrSig" + EOTSManager_SaveEOTSKeyName_FullMethodName = "/proto.EOTSManager/SaveEOTSKeyName" ) // EOTSManagerClient is the client API for EOTSManager service. @@ -40,8 +42,12 @@ type EOTSManagerClient interface { KeyRecord(ctx context.Context, in *KeyRecordRequest, opts ...grpc.CallOption) (*KeyRecordResponse, error) // SignEOTS signs an EOTS with the EOTS private key and the relevant randomness SignEOTS(ctx context.Context, in *SignEOTSRequest, opts ...grpc.CallOption) (*SignEOTSResponse, error) + // UnsafeSignEOTS used only for testing purpose. Use SignEOTS for real operations + UnsafeSignEOTS(ctx context.Context, in *SignEOTSRequest, opts ...grpc.CallOption) (*SignEOTSResponse, error) // SignSchnorrSig signs a Schnorr sig with the EOTS private key SignSchnorrSig(ctx context.Context, in *SignSchnorrSigRequest, opts ...grpc.CallOption) (*SignSchnorrSigResponse, error) + // SaveEOTSKeyName saves a new key name mapping for the EOTS public key + SaveEOTSKeyName(ctx context.Context, in *SaveEOTSKeyNameRequest, opts ...grpc.CallOption) (*SaveEOTSKeyNameResponse, error) } type eOTSManagerClient struct { @@ -97,6 +103,15 @@ func (c *eOTSManagerClient) SignEOTS(ctx context.Context, in *SignEOTSRequest, o return out, nil } +func (c *eOTSManagerClient) UnsafeSignEOTS(ctx context.Context, in *SignEOTSRequest, opts ...grpc.CallOption) (*SignEOTSResponse, error) { + out := new(SignEOTSResponse) + err := c.cc.Invoke(ctx, EOTSManager_UnsafeSignEOTS_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *eOTSManagerClient) SignSchnorrSig(ctx context.Context, in *SignSchnorrSigRequest, opts ...grpc.CallOption) (*SignSchnorrSigResponse, error) { out := new(SignSchnorrSigResponse) err := c.cc.Invoke(ctx, EOTSManager_SignSchnorrSig_FullMethodName, in, out, opts...) @@ -106,6 +121,15 @@ func (c *eOTSManagerClient) SignSchnorrSig(ctx context.Context, in *SignSchnorrS return out, nil } +func (c *eOTSManagerClient) SaveEOTSKeyName(ctx context.Context, in *SaveEOTSKeyNameRequest, opts ...grpc.CallOption) (*SaveEOTSKeyNameResponse, error) { + out := new(SaveEOTSKeyNameResponse) + err := c.cc.Invoke(ctx, EOTSManager_SaveEOTSKeyName_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // EOTSManagerServer is the server API for EOTSManager service. // All implementations must embed UnimplementedEOTSManagerServer // for forward compatibility @@ -119,8 +143,12 @@ type EOTSManagerServer interface { KeyRecord(context.Context, *KeyRecordRequest) (*KeyRecordResponse, error) // SignEOTS signs an EOTS with the EOTS private key and the relevant randomness SignEOTS(context.Context, *SignEOTSRequest) (*SignEOTSResponse, error) + // UnsafeSignEOTS used only for testing purpose. Use SignEOTS for real operations + UnsafeSignEOTS(context.Context, *SignEOTSRequest) (*SignEOTSResponse, error) // SignSchnorrSig signs a Schnorr sig with the EOTS private key SignSchnorrSig(context.Context, *SignSchnorrSigRequest) (*SignSchnorrSigResponse, error) + // SaveEOTSKeyName saves a new key name mapping for the EOTS public key + SaveEOTSKeyName(context.Context, *SaveEOTSKeyNameRequest) (*SaveEOTSKeyNameResponse, error) mustEmbedUnimplementedEOTSManagerServer() } @@ -143,9 +171,15 @@ func (UnimplementedEOTSManagerServer) KeyRecord(context.Context, *KeyRecordReque func (UnimplementedEOTSManagerServer) SignEOTS(context.Context, *SignEOTSRequest) (*SignEOTSResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SignEOTS not implemented") } +func (UnimplementedEOTSManagerServer) UnsafeSignEOTS(context.Context, *SignEOTSRequest) (*SignEOTSResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnsafeSignEOTS not implemented") +} func (UnimplementedEOTSManagerServer) SignSchnorrSig(context.Context, *SignSchnorrSigRequest) (*SignSchnorrSigResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SignSchnorrSig not implemented") } +func (UnimplementedEOTSManagerServer) SaveEOTSKeyName(context.Context, *SaveEOTSKeyNameRequest) (*SaveEOTSKeyNameResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SaveEOTSKeyName not implemented") +} func (UnimplementedEOTSManagerServer) mustEmbedUnimplementedEOTSManagerServer() {} // UnsafeEOTSManagerServer may be embedded to opt out of forward compatibility for this service. @@ -249,6 +283,24 @@ func _EOTSManager_SignEOTS_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _EOTSManager_UnsafeSignEOTS_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignEOTSRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EOTSManagerServer).UnsafeSignEOTS(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: EOTSManager_UnsafeSignEOTS_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EOTSManagerServer).UnsafeSignEOTS(ctx, req.(*SignEOTSRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _EOTSManager_SignSchnorrSig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SignSchnorrSigRequest) if err := dec(in); err != nil { @@ -267,6 +319,24 @@ func _EOTSManager_SignSchnorrSig_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } +func _EOTSManager_SaveEOTSKeyName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SaveEOTSKeyNameRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EOTSManagerServer).SaveEOTSKeyName(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: EOTSManager_SaveEOTSKeyName_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EOTSManagerServer).SaveEOTSKeyName(ctx, req.(*SaveEOTSKeyNameRequest)) + } + return interceptor(ctx, in, info, handler) +} + // EOTSManager_ServiceDesc is the grpc.ServiceDesc for EOTSManager service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -294,10 +364,18 @@ var EOTSManager_ServiceDesc = grpc.ServiceDesc{ MethodName: "SignEOTS", Handler: _EOTSManager_SignEOTS_Handler, }, + { + MethodName: "UnsafeSignEOTS", + Handler: _EOTSManager_UnsafeSignEOTS_Handler, + }, { MethodName: "SignSchnorrSig", Handler: _EOTSManager_SignSchnorrSig_Handler, }, + { + MethodName: "SaveEOTSKeyName", + Handler: _EOTSManager_SaveEOTSKeyName_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "eotsmanager.proto", diff --git a/eotsmanager/proto/signstore.pb.go b/eotsmanager/proto/signstore.pb.go new file mode 100644 index 00000000..1e9058a3 --- /dev/null +++ b/eotsmanager/proto/signstore.pb.go @@ -0,0 +1,169 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc (unknown) +// source: signstore.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// SigningRecord represents a record of a signing operation. +// it is keyed by (chain_id || public_key || height) +type SigningRecord struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // msg is the message that the signature is signed over + Msg []byte `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` + // eots_sig is the eots signature + EotsSig []byte `protobuf:"bytes,2,opt,name=eots_sig,json=eotsSig,proto3" json:"eots_sig,omitempty"` + // timestamp is the timestamp of the signing operation, in Unix seconds. + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *SigningRecord) Reset() { + *x = SigningRecord{} + if protoimpl.UnsafeEnabled { + mi := &file_signstore_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SigningRecord) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SigningRecord) ProtoMessage() {} + +func (x *SigningRecord) ProtoReflect() protoreflect.Message { + mi := &file_signstore_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SigningRecord.ProtoReflect.Descriptor instead. +func (*SigningRecord) Descriptor() ([]byte, []int) { + return file_signstore_proto_rawDescGZIP(), []int{0} +} + +func (x *SigningRecord) GetMsg() []byte { + if x != nil { + return x.Msg + } + return nil +} + +func (x *SigningRecord) GetEotsSig() []byte { + if x != nil { + return x.EotsSig + } + return nil +} + +func (x *SigningRecord) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +var File_signstore_proto protoreflect.FileDescriptor + +var file_signstore_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5a, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x65, + 0x6f, 0x74, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, + 0x6f, 0x74, 0x73, 0x53, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x62, 0x79, 0x6c, 0x6f, 0x6e, 0x6c, 0x61, 0x62, 0x73, 0x2d, 0x69, + 0x6f, 0x2f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x2f, 0x65, 0x6f, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_signstore_proto_rawDescOnce sync.Once + file_signstore_proto_rawDescData = file_signstore_proto_rawDesc +) + +func file_signstore_proto_rawDescGZIP() []byte { + file_signstore_proto_rawDescOnce.Do(func() { + file_signstore_proto_rawDescData = protoimpl.X.CompressGZIP(file_signstore_proto_rawDescData) + }) + return file_signstore_proto_rawDescData +} + +var file_signstore_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_signstore_proto_goTypes = []interface{}{ + (*SigningRecord)(nil), // 0: proto.SigningRecord +} +var file_signstore_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_signstore_proto_init() } +func file_signstore_proto_init() { + if File_signstore_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_signstore_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SigningRecord); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_signstore_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_signstore_proto_goTypes, + DependencyIndexes: file_signstore_proto_depIdxs, + MessageInfos: file_signstore_proto_msgTypes, + }.Build() + File_signstore_proto = out.File + file_signstore_proto_rawDesc = nil + file_signstore_proto_goTypes = nil + file_signstore_proto_depIdxs = nil +} diff --git a/eotsmanager/proto/signstore.proto b/eotsmanager/proto/signstore.proto new file mode 100644 index 00000000..a20f1a04 --- /dev/null +++ b/eotsmanager/proto/signstore.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package proto; + +option go_package = "github.com/babylonlabs-io/finality-provider/eotsmanager/proto"; + +// SigningRecord represents a record of a signing operation. +// it is keyed by (chain_id || public_key || height) +message SigningRecord { + // msg is the message that the signature is signed over + bytes msg = 1; + // eots_sig is the eots signature + bytes eots_sig = 2; + // timestamp is the timestamp of the signing operation, in Unix seconds. + int64 timestamp = 3; +} diff --git a/eotsmanager/service/rpcserver.go b/eotsmanager/service/rpcserver.go index bbdd9cf4..cd91aa01 100644 --- a/eotsmanager/service/rpcserver.go +++ b/eotsmanager/service/rpcserver.go @@ -3,6 +3,7 @@ package service import ( "context" + "github.com/btcsuite/btcd/btcec/v2" "google.golang.org/grpc" "github.com/babylonlabs-io/finality-provider/eotsmanager" @@ -21,7 +22,6 @@ type rpcServer struct { func newRPCServer( em eotsmanager.EOTSManager, ) *rpcServer { - return &rpcServer{ em: em, } @@ -32,17 +32,17 @@ func newRPCServer( func (r *rpcServer) RegisterWithGrpcServer(grpcServer *grpc.Server) error { // Register the main RPC server. proto.RegisterEOTSManagerServer(grpcServer, r) + return nil } -func (r *rpcServer) Ping(ctx context.Context, req *proto.PingRequest) (*proto.PingResponse, error) { +func (r *rpcServer) Ping(_ context.Context, _ *proto.PingRequest) (*proto.PingResponse, error) { return &proto.PingResponse{}, nil } // CreateKey generates and saves an EOTS key -func (r *rpcServer) CreateKey(ctx context.Context, req *proto.CreateKeyRequest) ( +func (r *rpcServer) CreateKey(_ context.Context, req *proto.CreateKeyRequest) ( *proto.CreateKeyResponse, error) { - pk, err := r.em.CreateKey(req.Name, req.Passphrase, req.HdPath) if err != nil { @@ -53,9 +53,8 @@ func (r *rpcServer) CreateKey(ctx context.Context, req *proto.CreateKeyRequest) } // CreateRandomnessPairList returns a list of Schnorr randomness pairs -func (r *rpcServer) CreateRandomnessPairList(ctx context.Context, req *proto.CreateRandomnessPairListRequest) ( +func (r *rpcServer) CreateRandomnessPairList(_ context.Context, req *proto.CreateRandomnessPairListRequest) ( *proto.CreateRandomnessPairListResponse, error) { - pubRandList, err := r.em.CreateRandomnessPairList(req.Uid, req.ChainId, req.StartHeight, req.Num, req.Passphrase) if err != nil { @@ -73,9 +72,8 @@ func (r *rpcServer) CreateRandomnessPairList(ctx context.Context, req *proto.Cre } // KeyRecord returns the key record -func (r *rpcServer) KeyRecord(ctx context.Context, req *proto.KeyRecordRequest) ( +func (r *rpcServer) KeyRecord(_ context.Context, req *proto.KeyRecordRequest) ( *proto.KeyRecordResponse, error) { - record, err := r.em.KeyRecord(req.Uid, req.Passphrase) if err != nil { return nil, err @@ -90,9 +88,8 @@ func (r *rpcServer) KeyRecord(ctx context.Context, req *proto.KeyRecordRequest) } // SignEOTS signs an EOTS with the EOTS private key and the relevant randomness -func (r *rpcServer) SignEOTS(ctx context.Context, req *proto.SignEOTSRequest) ( +func (r *rpcServer) SignEOTS(_ context.Context, req *proto.SignEOTSRequest) ( *proto.SignEOTSResponse, error) { - sig, err := r.em.SignEOTS(req.Uid, req.ChainId, req.Msg, req.Height, req.Passphrase) if err != nil { return nil, err @@ -103,10 +100,22 @@ func (r *rpcServer) SignEOTS(ctx context.Context, req *proto.SignEOTSRequest) ( return &proto.SignEOTSResponse{Sig: sigBytes[:]}, nil } +// UnsafeSignEOTS only used for testing purposes. Doesn't offer slashing protection! +func (r *rpcServer) UnsafeSignEOTS(_ context.Context, req *proto.SignEOTSRequest) ( + *proto.SignEOTSResponse, error) { + sig, err := r.em.UnsafeSignEOTS(req.Uid, req.ChainId, req.Msg, req.Height, req.Passphrase) + if err != nil { + return nil, err + } + + sigBytes := sig.Bytes() + + return &proto.SignEOTSResponse{Sig: sigBytes[:]}, nil +} + // SignSchnorrSig signs a Schnorr sig with the EOTS private key -func (r *rpcServer) SignSchnorrSig(ctx context.Context, req *proto.SignSchnorrSigRequest) ( +func (r *rpcServer) SignSchnorrSig(_ context.Context, req *proto.SignSchnorrSigRequest) ( *proto.SignSchnorrSigResponse, error) { - sig, err := r.em.SignSchnorrSig(req.Uid, req.Msg, req.Passphrase) if err != nil { return nil, err @@ -114,3 +123,16 @@ func (r *rpcServer) SignSchnorrSig(ctx context.Context, req *proto.SignSchnorrSi return &proto.SignSchnorrSigResponse{Sig: sig.Serialize()}, nil } + +// SaveEOTSKeyName signs a Schnorr sig with the EOTS private key +func (r *rpcServer) SaveEOTSKeyName( + _ context.Context, + req *proto.SaveEOTSKeyNameRequest, +) (*proto.SaveEOTSKeyNameResponse, error) { + eotsPk, err := btcec.ParsePubKey(req.EotsPk) + if err != nil { + return nil, err + } + + return &proto.SaveEOTSKeyNameResponse{}, r.em.SaveEOTSKeyName(eotsPk, req.KeyName) +} diff --git a/eotsmanager/service/server.go b/eotsmanager/service/server.go index f566bf65..9b80c7ec 100644 --- a/eotsmanager/service/server.go +++ b/eotsmanager/service/server.go @@ -10,7 +10,6 @@ import ( "github.com/babylonlabs-io/finality-provider/metrics" "github.com/lightningnetwork/lnd/kvdb" - "github.com/lightningnetwork/lnd/signal" "go.uber.org/zap" "google.golang.org/grpc" @@ -27,28 +26,26 @@ type Server struct { cfg *config.Config logger *zap.Logger - rpcServer *rpcServer - db kvdb.Backend - interceptor signal.Interceptor + rpcServer *rpcServer + db kvdb.Backend quit chan struct{} } // NewEOTSManagerServer creates a new server with the given config. -func NewEOTSManagerServer(cfg *config.Config, l *zap.Logger, em eotsmanager.EOTSManager, db kvdb.Backend, sig signal.Interceptor) *Server { +func NewEOTSManagerServer(cfg *config.Config, l *zap.Logger, em eotsmanager.EOTSManager, db kvdb.Backend) *Server { return &Server{ - cfg: cfg, - logger: l, - rpcServer: newRPCServer(em), - db: db, - interceptor: sig, - quit: make(chan struct{}, 1), + cfg: cfg, + logger: l, + rpcServer: newRPCServer(em), + db: db, + quit: make(chan struct{}, 1), } } // RunUntilShutdown runs the main EOTS manager server loop until a signal is // received to shut down the process. -func (s *Server) RunUntilShutdown() error { +func (s *Server) RunUntilShutdown(ctx context.Context) error { if atomic.AddInt32(&s.started, 1) != 1 { return nil } @@ -66,20 +63,25 @@ func (s *Server) RunUntilShutdown() error { defer func() { s.logger.Info("Closing database...") - s.db.Close() - s.logger.Info("Database closed") - metricsServer.Stop(context.Background()) + if err := s.db.Close(); err != nil { + s.logger.Error("Failed to close database", zap.Error(err)) + } else { + s.logger.Info("Database closed") + } + metricsServer.Stop(ctx) s.logger.Info("Metrics server stopped") }() - listenAddr := s.cfg.RpcListener + listenAddr := s.cfg.RPCListener // we create listeners from the RPCListeners defined // in the config. lis, err := net.Listen("tcp", listenAddr) if err != nil { return fmt.Errorf("failed to listen on %s: %w", listenAddr, err) } - defer lis.Close() + defer func() { + _ = lis.Close() + }() grpcServer := grpc.NewServer() defer grpcServer.Stop() @@ -90,22 +92,19 @@ func (s *Server) RunUntilShutdown() error { // All the necessary components have been registered, so we can // actually start listening for requests. - if err := s.startGrpcListen(grpcServer, []net.Listener{lis}); err != nil { - return fmt.Errorf("failed to start gRPC listener: %v", err) - } + s.startGrpcListen(grpcServer, []net.Listener{lis}) s.logger.Info("EOTS Manager Daemon is fully active!") // Wait for shutdown signal from either a graceful server stop or from // the interrupt handler. - <-s.interceptor.ShutdownChannel() + <-ctx.Done() return nil } // startGrpcListen starts the GRPC server on the passed listeners. -func (s *Server) startGrpcListen(grpcServer *grpc.Server, listeners []net.Listener) error { - +func (s *Server) startGrpcListen(grpcServer *grpc.Server, listeners []net.Listener) { // Use a WaitGroup so we can be sure the instructions on how to input the // password is the last thing to be printed to the console. var wg sync.WaitGroup @@ -125,6 +124,4 @@ func (s *Server) startGrpcListen(grpcServer *grpc.Server, listeners []net.Listen // Wait for gRPC servers to be up running. wg.Wait() - - return nil } diff --git a/eotsmanager/store/eotsstore.go b/eotsmanager/store/eotsstore.go index cc68691b..f7ee03b1 100644 --- a/eotsmanager/store/eotsstore.go +++ b/eotsmanager/store/eotsstore.go @@ -1,7 +1,13 @@ package store import ( + "errors" "fmt" + "time" + + pm "google.golang.org/protobuf/proto" + + "github.com/babylonlabs-io/finality-provider/eotsmanager/proto" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" @@ -10,7 +16,8 @@ import ( ) var ( - eotsBucketName = []byte("fpKeyNames") + eotsBucketName = []byte("fpKeyNames") + signRecordBucketName = []byte("signRecord") ) type EOTSStore struct { @@ -33,6 +40,11 @@ func (s *EOTSStore) initBuckets() error { return err } + _, err = tx.CreateTopLevelBucket(signRecordBucketName) + if err != nil { + return err + } + return nil }) } @@ -84,6 +96,7 @@ func (s *EOTSStore) GetEOTSKeyName(pk []byte) (string, error) { } keyName = string(keyNameBytes) + return nil }, func() {}) @@ -93,3 +106,101 @@ func (s *EOTSStore) GetEOTSKeyName(pk []byte) (string, error) { return keyName, nil } + +// GetAllEOTSKeyNames retrieves all keys and values. +// Returns keyName -> btcPK +func (s *EOTSStore) GetAllEOTSKeyNames() (map[string][]byte, error) { + result := make(map[string][]byte) + + err := s.db.View(func(tx kvdb.RTx) error { + eotsBucket := tx.ReadBucket(eotsBucketName) + if eotsBucket == nil { + return ErrCorruptedEOTSDb + } + + return eotsBucket.ForEach(func(k, v []byte) error { + if k == nil || v == nil { + return fmt.Errorf("encountered invalid key or value in bucket") + } + result[string(v)] = k + + return nil + }) + }, func() {}) + + if err != nil { + return nil, err + } + + return result, nil +} + +func (s *EOTSStore) SaveSignRecord( + height uint64, + chainID []byte, + msg []byte, + publicKey []byte, + signature []byte, +) error { + key := getSignRecordKey(chainID, publicKey, height) + + return kvdb.Batch(s.db, func(tx kvdb.RwTx) error { + bucket := tx.ReadWriteBucket(signRecordBucketName) + if bucket == nil { + return ErrCorruptedEOTSDb + } + + if bucket.Get(key) != nil { + return ErrDuplicateSignRecord + } + + signRecord := &proto.SigningRecord{ + Msg: msg, + EotsSig: signature, + Timestamp: time.Now().UnixMilli(), + } + + marshalled, err := pm.Marshal(signRecord) + if err != nil { + return err + } + + return bucket.Put(key, marshalled) + }) +} + +func (s *EOTSStore) GetSignRecord(eotsPk, chainID []byte, height uint64) (*SigningRecord, bool, error) { + key := getSignRecordKey(chainID, eotsPk, height) + protoRes := &proto.SigningRecord{} + + err := s.db.View(func(tx kvdb.RTx) error { + bucket := tx.ReadBucket(signRecordBucketName) + if bucket == nil { + return ErrCorruptedEOTSDb + } + + signRecordBytes := bucket.Get(key) + if signRecordBytes == nil { + return ErrSignRecordNotFound + } + + return pm.Unmarshal(signRecordBytes, protoRes) + }, func() {}) + + if err != nil { + if errors.Is(err, ErrSignRecordNotFound) { + return nil, false, nil + } + + return nil, false, err + } + + res := &SigningRecord{} + res.FromProto(protoRes) + + return res, true, nil +} + +func (s *EOTSStore) Close() error { + return s.db.Close() +} diff --git a/eotsmanager/store/eotsstore_test.go b/eotsmanager/store/eotsstore_test.go index 4161da0c..244533d5 100644 --- a/eotsmanager/store/eotsstore_test.go +++ b/eotsmanager/store/eotsstore_test.go @@ -18,12 +18,13 @@ import ( func FuzzEOTSStore(f *testing.F) { testutil.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { + t.Parallel() r := rand.New(rand.NewSource(seed)) homePath := t.TempDir() cfg := config.DefaultDBConfigWithHomePath(homePath) - dbBackend, err := cfg.GetDbBackend() + dbBackend, err := cfg.GetDBBackend() require.NoError(t, err) vs, err := store.NewEOTSStore(dbBackend) @@ -53,9 +54,9 @@ func FuzzEOTSStore(f *testing.F) { ) require.ErrorIs(t, err, store.ErrDuplicateEOTSKeyName) - keyNameFromDb, err := vs.GetEOTSKeyName(schnorr.SerializePubKey(btcPk)) + keyNameFromDB, err := vs.GetEOTSKeyName(schnorr.SerializePubKey(btcPk)) require.NoError(t, err) - require.Equal(t, expectedKeyName, keyNameFromDb) + require.Equal(t, expectedKeyName, keyNameFromDB) _, randomBtcPk, err := datagen.GenRandomBTCKeyPair(r) require.NoError(t, err) @@ -63,3 +64,115 @@ func FuzzEOTSStore(f *testing.F) { require.ErrorIs(t, err, store.ErrEOTSKeyNameNotFound) }) } + +// FuzzSignStore tests save sign records +func FuzzSignStore(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + t.Parallel() + r := rand.New(rand.NewSource(seed)) + + homePath := t.TempDir() + cfg := config.DefaultDBConfigWithHomePath(homePath) + + dbBackend, err := cfg.GetDBBackend() + require.NoError(t, err) + + vs, err := store.NewEOTSStore(dbBackend) + require.NoError(t, err) + + defer func() { + dbBackend.Close() + err := os.RemoveAll(homePath) + require.NoError(t, err) + }() + + expectedHeight := r.Uint64() + pk := testutil.GenRandomByteArray(r, 32) + msg := testutil.GenRandomByteArray(r, 32) + eotsSig := testutil.GenRandomByteArray(r, 32) + + chainID := []byte("test-chain") + // save for the first time + err = vs.SaveSignRecord( + expectedHeight, + chainID, + msg, + pk, + eotsSig, + ) + require.NoError(t, err) + + // try to save the record at the same height + err = vs.SaveSignRecord( + expectedHeight, + chainID, + msg, + pk, + eotsSig, + ) + require.ErrorIs(t, err, store.ErrDuplicateSignRecord) + + signRecordFromDB, found, err := vs.GetSignRecord(pk, chainID, expectedHeight) + require.True(t, found) + require.NoError(t, err) + require.Equal(t, msg, signRecordFromDB.Msg) + require.Equal(t, eotsSig, signRecordFromDB.Signature) + + rndHeight := r.Uint64() + _, found, err = vs.GetSignRecord(pk, chainID, rndHeight) + require.NoError(t, err) + require.False(t, found) + }) +} + +// FuzzListKeysEOTSStore tests getting all keys from store +func FuzzListKeysEOTSStore(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + t.Parallel() + r := rand.New(rand.NewSource(seed)) + + homePath := t.TempDir() + cfg := config.DefaultDBConfigWithHomePath(homePath) + + dbBackend, err := cfg.GetDBBackend() + require.NoError(t, err) + + vs, err := store.NewEOTSStore(dbBackend) + require.NoError(t, err) + + defer func() { + dbBackend.Close() + }() + + expected := make(map[string][]byte) + for i := 0; i < r.Intn(10); i++ { + expectedKeyName := testutil.GenRandomHexStr(r, 10) + _, btcPk, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + expected[expectedKeyName] = schnorr.SerializePubKey(btcPk) + + err = vs.AddEOTSKeyName( + btcPk, + expectedKeyName, + ) + require.NoError(t, err) + } + + keys, err := vs.GetAllEOTSKeyNames() + require.NoError(t, err) + + for keyName, btcPk := range expected { + gotBtcPk, ok := keys[keyName] + require.True(t, ok) + + parsedGot, err := schnorr.ParsePubKey(gotBtcPk) + require.NoError(t, err) + parsedExpected, err := schnorr.ParsePubKey(btcPk) + require.NoError(t, err) + + require.Equal(t, parsedExpected, parsedGot) + } + }) +} diff --git a/eotsmanager/store/errors.go b/eotsmanager/store/errors.go index 21b89d71..b7140119 100644 --- a/eotsmanager/store/errors.go +++ b/eotsmanager/store/errors.go @@ -11,4 +11,10 @@ var ( // ErrEOTSKeyNameNotFound The EOTS key name we try to fetch is not found in db ErrEOTSKeyNameNotFound = errors.New("EOTS key name not found") + + // ErrSignRecordNotFound sign record not found at given height + ErrSignRecordNotFound = errors.New("sign record not found") + + // ErrDuplicateSignRecord indicates err if sign record is already saved at given height + ErrDuplicateSignRecord = errors.New("sign record for given height already exists") ) diff --git a/eotsmanager/store/storedsigningrecord.go b/eotsmanager/store/storedsigningrecord.go new file mode 100644 index 00000000..07c2729d --- /dev/null +++ b/eotsmanager/store/storedsigningrecord.go @@ -0,0 +1,33 @@ +package store + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/babylonlabs-io/finality-provider/eotsmanager/proto" +) + +type SigningRecord struct { + Msg []byte // The message that the signature is signed over. + Signature []byte + Timestamp int64 // The timestamp of the signing operation, in Unix seconds. +} + +func (s *SigningRecord) FromProto(sr *proto.SigningRecord) { + s.Msg = sr.Msg + s.Timestamp = sr.Timestamp + s.Signature = sr.EotsSig +} + +// the record key is (chainID || pk || height) +func getSignRecordKey(chainID, pk []byte, height uint64) []byte { + // Convert height to bytes + heightBytes := sdk.Uint64ToBigEndian(height) + + // Concatenate all components to create the key + key := make([]byte, 0, len(pk)+len(chainID)+len(heightBytes)) + key = append(key, chainID...) + key = append(key, pk...) + key = append(key, heightBytes...) + + return key +} diff --git a/eotsmanager/types/errors.go b/eotsmanager/types/errors.go index 2d2879fd..0979f140 100644 --- a/eotsmanager/types/errors.go +++ b/eotsmanager/types/errors.go @@ -4,4 +4,5 @@ import "errors" var ( ErrFinalityProviderAlreadyExisted = errors.New("the finality provider has already existed") + ErrDoubleSign = errors.New("double sign") ) diff --git a/finality-provider/cmd/cmd.go b/finality-provider/cmd/cmd.go index 8da3b6bd..4a20110e 100644 --- a/finality-provider/cmd/cmd.go +++ b/finality-provider/cmd/cmd.go @@ -4,12 +4,14 @@ import ( "context" "os" + "github.com/babylonlabs-io/babylon/app/params" + bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/std" "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/babylonlabs-io/babylon/app" fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" ) @@ -18,17 +20,15 @@ import ( // and exists a value in the config that could be used, it will be set in the ctx. func PersistClientCtx(ctx client.Context) func(cmd *cobra.Command, _ []string) error { return func(cmd *cobra.Command, _ []string) error { - // TODO(verify): if it uses the default encoding config it fails to list keys! output: - // "xx" is not a valid name or address: unable to unmarshal item.Data: - // Bytes left over in UnmarshalBinaryLengthPrefixed, should read 10 more bytes but have 154 - // [cosmos/cosmos-sdk@v0.50.6/crypto/keyring/keyring.go:973 - tempApp := app.NewTmpBabylonApp() + encCfg := params.DefaultEncodingConfig() + std.RegisterInterfaces(encCfg.InterfaceRegistry) + bstypes.RegisterInterfaces(encCfg.InterfaceRegistry) ctx = ctx. - WithCodec(tempApp.AppCodec()). - WithInterfaceRegistry(tempApp.InterfaceRegistry()). - WithTxConfig(tempApp.TxConfig()). - WithLegacyAmino(tempApp.LegacyAmino()). + WithCodec(encCfg.Codec). + WithInterfaceRegistry(encCfg.InterfaceRegistry). + WithTxConfig(encCfg.TxConfig). + WithLegacyAmino(encCfg.Amino). WithInput(os.Stdin) // ensure cmd context is not nil. Later it will be replaced @@ -39,7 +39,6 @@ func PersistClientCtx(ctx client.Context) func(cmd *cobra.Command, _ []string) e cmd.SetOut(cmd.OutOrStdout()) cmd.SetErr(cmd.ErrOrStderr()) - ctx = ctx.WithCmdContext(cmd.Context()) if err := client.SetCmdClientContextHandler(ctx, cmd); err != nil { return err } @@ -48,6 +47,7 @@ func PersistClientCtx(ctx client.Context) func(cmd *cobra.Command, _ []string) e // check the config file exists cfg, err := fpcfg.LoadConfig(ctx.HomeDir) if err != nil { + //nolint:nilerr return nil // if no conifg is found just stop. } diff --git a/finality-provider/cmd/cmd_test.go b/finality-provider/cmd/cmd_test.go index 811902e5..e804c3b1 100644 --- a/finality-provider/cmd/cmd_test.go +++ b/finality-provider/cmd/cmd_test.go @@ -1,25 +1,28 @@ package cmd_test import ( + "context" "math/rand" "path/filepath" "testing" + "github.com/babylonlabs-io/babylon/testutil/datagen" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + goflags "github.com/jessevdk/go-flags" "github.com/spf13/cobra" "github.com/stretchr/testify/require" - "github.com/babylonlabs-io/babylon/testutil/datagen" fpcmd "github.com/babylonlabs-io/finality-provider/finality-provider/cmd" fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" "github.com/babylonlabs-io/finality-provider/util" - goflags "github.com/jessevdk/go-flags" ) func TestPersistClientCtx(t *testing.T) { + t.Parallel() ctx := client.Context{} - cmd := cobra.Command{} + cmd := &cobra.Command{} + cmd.SetContext(context.Background()) tempDir := t.TempDir() defaultHome := filepath.Join(tempDir, "defaultHome") @@ -27,11 +30,11 @@ func TestPersistClientCtx(t *testing.T) { cmd.Flags().String(flags.FlagHome, defaultHome, "The application home directory") cmd.Flags().String(flags.FlagChainID, "", "chain id") - err := fpcmd.PersistClientCtx(ctx)(&cmd, []string{}) + err := fpcmd.PersistClientCtx(ctx)(cmd, []string{}) require.NoError(t, err) // verify that has the defaults to ctx - ctx = client.GetClientContextFromCmd(&cmd) + ctx = client.GetClientContextFromCmd(cmd) require.Equal(t, defaultHome, ctx.HomeDir) require.Equal(t, "", ctx.ChainID) @@ -39,10 +42,10 @@ func TestPersistClientCtx(t *testing.T) { err = cmd.Flags().Set(flags.FlagHome, flagHomeValue) require.NoError(t, err) - err = fpcmd.PersistClientCtx(ctx)(&cmd, []string{}) + err = fpcmd.PersistClientCtx(ctx)(cmd, []string{}) require.NoError(t, err) - ctx = client.GetClientContextFromCmd(&cmd) + ctx = client.GetClientContextFromCmd(cmd) require.Equal(t, flagHomeValue, ctx.HomeDir) r := rand.New(rand.NewSource(10)) @@ -56,14 +59,14 @@ func TestPersistClientCtx(t *testing.T) { config.BabylonConfig.ChainID = randChainID fileParser := goflags.NewParser(&config, goflags.Default) - err = goflags.NewIniParser(fileParser).WriteFile(fpcfg.ConfigFile(flagHomeValue), goflags.IniIncludeComments|goflags.IniIncludeDefaults) + err = goflags.NewIniParser(fileParser).WriteFile(fpcfg.CfgFile(flagHomeValue), goflags.IniIncludeComments|goflags.IniIncludeDefaults) require.NoError(t, err) // parses the ctx from cmd with config, should modify the chain ID - err = fpcmd.PersistClientCtx(ctx)(&cmd, []string{}) + err = fpcmd.PersistClientCtx(ctx)(cmd, []string{}) require.NoError(t, err) - ctx = client.GetClientContextFromCmd(&cmd) + ctx = client.GetClientContextFromCmd(cmd) require.Equal(t, flagHomeValue, ctx.HomeDir) require.Equal(t, randChainID, ctx.ChainID) @@ -73,10 +76,10 @@ func TestPersistClientCtx(t *testing.T) { // parses the ctx from cmd with config, but it has set in flags which should give // preference over the config set, so it should use from the flag value set. - err = fpcmd.PersistClientCtx(ctx)(&cmd, []string{}) + err = fpcmd.PersistClientCtx(ctx)(cmd, []string{}) require.NoError(t, err) - ctx = client.GetClientContextFromCmd(&cmd) + ctx = client.GetClientContextFromCmd(cmd) require.Equal(t, flagHomeValue, ctx.HomeDir) require.Equal(t, flagChainID, ctx.ChainID) } diff --git a/finality-provider/cmd/fpd/daemon/commit_pr.go b/finality-provider/cmd/fpd/daemon/commit_pr.go index 3ae39089..09b5f6e1 100644 --- a/finality-provider/cmd/fpd/daemon/commit_pr.go +++ b/finality-provider/cmd/fpd/daemon/commit_pr.go @@ -33,6 +33,7 @@ WARNING: this can drain the finality provider's balance if the target height is RunE: fpcmd.RunEWithClientCtx(runCommandCommitPubRand), } cmd.Flags().Uint64("start-height", math.MaxUint64, "The block height to start committing pubrand from (optional)") + return cmd } @@ -51,8 +52,7 @@ func runCommandCommitPubRand(ctx client.Context, cmd *cobra.Command, args []stri } // Get homePath from context like in start.go - clientCtx := client.GetClientContextFromCmd(cmd) - homePath, err := filepath.Abs(clientCtx.HomeDir) + homePath, err := filepath.Abs(ctx.HomeDir) if err != nil { return err } @@ -68,7 +68,7 @@ func runCommandCommitPubRand(ctx client.Context, cmd *cobra.Command, args []stri return fmt.Errorf("failed to initialize the logger: %w", err) } - db, err := cfg.DatabaseConfig.GetDbBackend() + db, err := cfg.DatabaseConfig.GetDBBackend() if err != nil { return fmt.Errorf("failed to create db backend: %w", err) } @@ -81,10 +81,13 @@ func runCommandCommitPubRand(ctx client.Context, cmd *cobra.Command, args []stri if err != nil { return fmt.Errorf("failed to initiate public randomness store: %w", err) } - cc, err := fpcc.NewClientController(cfg, logger) + cc, err := fpcc.NewBabylonController(cfg, logger) if err != nil { return fmt.Errorf("failed to create rpc client for the Babylon chain: %w", err) } + if err := cc.Start(); err != nil { + return fmt.Errorf("failed to start client controller: %w", err) + } consumerCon, err := fpcc.NewConsumerController(cfg, logger) if err != nil { return fmt.Errorf("failed to create rpc client for the consumer chain %s: %w", cfg.ChainType, err) @@ -94,7 +97,7 @@ func runCommandCommitPubRand(ctx client.Context, cmd *cobra.Command, args []stri return fmt.Errorf("failed to create EOTS manager client: %w", err) } - fp, err := service.TestNewUnregisteredFinalityProviderInstance( + fp, err := service.NewFinalityProviderInstance( fpPk, cfg, fpStore, pubRandStore, cc, consumerCon, em, metrics.NewFpMetrics(), "", make(chan<- *service.CriticalError), logger) if err != nil { @@ -103,7 +106,7 @@ func runCommandCommitPubRand(ctx client.Context, cmd *cobra.Command, args []stri if startHeight == math.MaxUint64 { return fp.TestCommitPubRand(targetHeight) - } else { - return fp.TestCommitPubRandWithStartHeight(startHeight, targetHeight) } + + return fp.TestCommitPubRandWithStartHeight(startHeight, targetHeight) } diff --git a/finality-provider/cmd/fpd/daemon/daemon_commands.go b/finality-provider/cmd/fpd/daemon/daemon_commands.go index b8937702..e8308195 100644 --- a/finality-provider/cmd/fpd/daemon/daemon_commands.go +++ b/finality-provider/cmd/fpd/daemon/daemon_commands.go @@ -5,17 +5,20 @@ import ( "encoding/hex" "encoding/json" "fmt" + "os" "strconv" + "strings" "cosmossdk.io/math" "github.com/babylonlabs-io/babylon/types" - bbntypes "github.com/babylonlabs-io/babylon/types" "github.com/cosmos/cosmos-sdk/client" sdkflags "github.com/cosmos/cosmos-sdk/client/flags" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/spf13/cobra" "github.com/spf13/pflag" + "github.com/babylonlabs-io/finality-provider/finality-provider/proto" + fpcmd "github.com/babylonlabs-io/finality-provider/finality-provider/cmd" fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" dc "github.com/babylonlabs-io/finality-provider/finality-provider/service/client" @@ -37,10 +40,11 @@ func CommandGetDaemonInfo() *cobra.Command { RunE: runCommandGetDaemonInfo, } cmd.Flags().String(fpdDaemonAddressFlag, defaultFpdDaemonAddress, "The RPC server address of fpd") + return cmd } -func runCommandGetDaemonInfo(cmd *cobra.Command, args []string) error { +func runCommandGetDaemonInfo(cmd *cobra.Command, _ []string) error { daemonAddress, err := cmd.Flags().GetString(fpdDaemonAddressFlag) if err != nil { return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) @@ -50,7 +54,11 @@ func runCommandGetDaemonInfo(cmd *cobra.Command, args []string) error { if err != nil { return err } - defer cleanUp() + defer func() { + if err := cleanUp(); err != nil { + fmt.Printf("Failed to clean up grpc client: %v\n", err) + } + }() info, err := client.GetInfo(context.Background()) if err != nil { @@ -58,6 +66,7 @@ func runCommandGetDaemonInfo(cmd *cobra.Command, args []string) error { } printRespJSON(info) + return nil } @@ -67,115 +76,179 @@ func CommandCreateFP() *cobra.Command { Use: "create-finality-provider", Aliases: []string{"cfp"}, Short: "Create a finality provider object and save it in database.", - Long: fmt.Sprintf(` - Create a new finality provider object and store it in the finality provider database. - It needs to have an operating EOTS manager available and running. - - If the flag %s is set, it will ask for the key record from the EOTS manager for the - corresponding EOTS public key. If it is not set, it will create a new EOTS key`, fpEotsPkFlag), - Example: fmt.Sprintf(`fpd create-finality-provider --daemon-address %s ...`, defaultFpdDaemonAddress), - Args: cobra.NoArgs, - RunE: fpcmd.RunEWithClientCtx(runCommandCreateFP), + Long: "Create a new finality provider object and store it in the finality provider database. " + + "It needs to have an operating EOTS manager available and running.", + Example: strings.TrimSpace( + fmt.Sprintf(` +Either by specifying all flags manually: + +$fpd create-finality-provider --daemon-address %s ... + +Or providing the path to finality-provider.json: +$fpd create-finality-provider --daemon-address %s --from-file /path/to/finality-provider.json + +Where finality-provider.json contains: + +{ + "keyName": "The unique key name of the finality provider's Babylon account", + "chainID": "The identifier of the consumer chain", + "passphrase": "The pass phrase used to encrypt the keys", + "commissionRate": "The commission rate for the finality provider, e.g., 0.05"", + "moniker": ""A human-readable name for the finality provider", + "identity": "A optional identity signature", + "website": "Validator's (optional) website", + "securityContract": "Validator's (optional) security contact email", + "details": "Validator's (optional) details", + "eotsPK": "The hex string of the finality provider's EOTS public key" +} +`, defaultFpdDaemonAddress, defaultFpdDaemonAddress)), + Args: cobra.NoArgs, + RunE: fpcmd.RunEWithClientCtx(runCommandCreateFP), } f := cmd.Flags() f.String(fpdDaemonAddressFlag, defaultFpdDaemonAddress, "The RPC server address of fpd") - f.String(keyNameFlag, "", "The unique name of the finality provider key") + f.String(keyNameFlag, "", "The unique key name of the finality provider's Babylon account") f.String(sdkflags.FlagHome, fpcfg.DefaultFpdDir, "The application home directory") - f.String(chainIdFlag, "", "The identifier of the consumer chain") + f.String(chainIDFlag, "", "The identifier of the consumer chain") f.String(passphraseFlag, "", "The pass phrase used to encrypt the keys") - f.String(hdPathFlag, "", "The hd path used to derive the private key") - f.String(commissionRateFlag, "0.05", "The commission rate for the finality provider, e.g., 0.05") + f.String(commissionRateFlag, "", "The commission rate for the finality provider, e.g., 0.05") f.String(monikerFlag, "", "A human-readable name for the finality provider") f.String(identityFlag, "", "An optional identity signature (ex. UPort or Keybase)") f.String(websiteFlag, "", "An optional website link") f.String(securityContactFlag, "", "An email for security contact") f.String(detailsFlag, "", "Other optional details") - f.String(fpEotsPkFlag, "", "Optional hex EOTS public key, if not provided a new one will be created") + f.String(fpEotsPkFlag, "", "The hex string of the finality provider's EOTS public key") + f.String(fromFile, "", "Path to a json file containing finality provider data") + + cmd.PreRunE = func(cmd *cobra.Command, _ []string) error { + fromFilePath, _ := cmd.Flags().GetString(fromFile) + if fromFilePath == "" { + // Mark flags as required only if --from-file is not provided + if err := cmd.MarkFlagRequired(chainIDFlag); err != nil { + return err + } + if err := cmd.MarkFlagRequired(keyNameFlag); err != nil { + return err + } + if err := cmd.MarkFlagRequired(monikerFlag); err != nil { + return err + } + if err := cmd.MarkFlagRequired(commissionRateFlag); err != nil { + return err + } + if err := cmd.MarkFlagRequired(fpEotsPkFlag); err != nil { + return err + } + } + + return nil + } return cmd } func runCommandCreateFP(ctx client.Context, cmd *cobra.Command, _ []string) error { flags := cmd.Flags() - daemonAddress, err := flags.GetString(fpdDaemonAddressFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) - } - commissionRateStr, err := flags.GetString(commissionRateFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", commissionRateFlag, err) - } - commissionRate, err := math.LegacyNewDecFromStr(commissionRateStr) + fpJSONPath, err := flags.GetString(fromFile) if err != nil { - return fmt.Errorf("invalid commission rate: %w", err) + return fmt.Errorf("failed to read flag %s: %w", fromFile, err) } - description, err := getDescriptionFromFlags(flags) - if err != nil { - return fmt.Errorf("invalid description: %w", err) + var fp *parsedFinalityProvider + if fpJSONPath != "" { + fp, err = parseFinalityProviderJSON(fpJSONPath, ctx.HomeDir) + if err != nil { + panic(err) + } + } else { + fp, err = parseFinalityProviderFlags(cmd, ctx.HomeDir) + if err != nil { + panic(err) + } } - keyName, err := loadKeyName(ctx.HomeDir, cmd) + daemonAddress, err := flags.GetString(fpdDaemonAddressFlag) if err != nil { - return fmt.Errorf("not able to load key name: %w", err) + return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) } client, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(daemonAddress) if err != nil { return err } - defer cleanUp() + defer func() { + if err := cleanUp(); err != nil { + fmt.Printf("Failed to clean up grpc client: %v\n", err) + } + }() - chainId, err := flags.GetString(chainIdFlag) + res, err := client.CreateFinalityProvider( + context.Background(), + fp.keyName, + fp.chainID, + fp.eotsPK, + fp.passphrase, + fp.description, + &fp.commissionRate, + ) if err != nil { - return fmt.Errorf("failed to read flag %s: %w", chainIdFlag, err) + return err } - passphrase, err := flags.GetString(passphraseFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", passphraseFlag, err) + printRespJSON(res) + + cmd.Println("Your finality provider is successfully created. Please restart your fpd.") + + return nil +} + +// CommandUnjailFP returns the unjail-finality-provider command by connecting to the fpd daemon. +func CommandUnjailFP() *cobra.Command { + var cmd = &cobra.Command{ + Use: "unjail-finality-provider", + Aliases: []string{"ufp"}, + Short: "Unjail the given finality provider.", + Example: fmt.Sprintf(`fpd unjail-finality-provider [eots-pk] --daemon-address %s ...`, defaultFpdDaemonAddress), + Args: cobra.ExactArgs(1), + RunE: fpcmd.RunEWithClientCtx(runCommandUnjailFP), } - hdPath, err := flags.GetString(hdPathFlag) + f := cmd.Flags() + f.String(fpdDaemonAddressFlag, defaultFpdDaemonAddress, "The RPC server address of fpd") + + return cmd +} + +func runCommandUnjailFP(_ client.Context, cmd *cobra.Command, args []string) error { + flags := cmd.Flags() + daemonAddress, err := flags.GetString(fpdDaemonAddressFlag) if err != nil { - return fmt.Errorf("failed to read flag %s: %w", hdPathFlag, err) + return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) } - eotsPkHex, err := flags.GetString(fpEotsPkFlag) + client, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(daemonAddress) if err != nil { - return fmt.Errorf("failed to read flag %s: %w", fpEotsPkFlag, err) + return err } - - if len(eotsPkHex) > 0 { - // if is set, validate before the creation request - _, err := types.NewBIP340PubKeyFromHex(eotsPkHex) - if err != nil { - return fmt.Errorf("invalid eots public key %s: %w", eotsPkHex, err) + defer func() { + if err := cleanUp(); err != nil { + fmt.Printf("Failed to clean up grpc client: %v\n", err) } - } + }() - info, err := client.CreateFinalityProvider( - context.Background(), - keyName, - chainId, - eotsPkHex, - passphrase, - hdPath, - description, - &commissionRate, - ) + _, err = client.UnjailFinalityProvider(context.Background(), args[0]) if err != nil { return err } - printRespJSON(info.FinalityProvider) return nil } -func getDescriptionFromFlags(f *pflag.FlagSet) (desc stakingtypes.Description, err error) { +func getDescriptionFromFlags(f *pflag.FlagSet) (stakingtypes.Description, error) { // get information for description + var desc stakingtypes.Description monikerStr, err := f.GetString(monikerFlag) if err != nil { return desc, fmt.Errorf("failed to read flag %s: %w", monikerFlag, err) @@ -198,6 +271,7 @@ func getDescriptionFromFlags(f *pflag.FlagSet) (desc stakingtypes.Description, e } description := stakingtypes.NewDescription(monikerStr, identityStr, websiteStr, securityContactStr, detailsStr) + return description.EnsureLength() } @@ -212,10 +286,11 @@ func CommandLsFP() *cobra.Command { RunE: runCommandLsFP, } cmd.Flags().String(fpdDaemonAddressFlag, defaultFpdDaemonAddress, "The RPC server address of fpd") + return cmd } -func runCommandLsFP(cmd *cobra.Command, args []string) error { +func runCommandLsFP(cmd *cobra.Command, _ []string) error { daemonAddress, err := cmd.Flags().GetString(fpdDaemonAddressFlag) if err != nil { return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) @@ -225,7 +300,11 @@ func runCommandLsFP(cmd *cobra.Command, args []string) error { if err != nil { return err } - defer cleanUp() + defer func() { + if err := cleanUp(); err != nil { + fmt.Printf("Failed to clean up grpc client: %v\n", err) + } + }() resp, err := client.QueryFinalityProviderList(context.Background()) if err != nil { @@ -247,11 +326,12 @@ func CommandInfoFP() *cobra.Command { RunE: runCommandInfoFP, } cmd.Flags().String(fpdDaemonAddressFlag, defaultFpdDaemonAddress, "The RPC server address of fpd") + return cmd } func runCommandInfoFP(cmd *cobra.Command, args []string) error { - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(args[0]) + fpPk, err := types.NewBIP340PubKeyFromHex(args[0]) if err != nil { return err } @@ -265,7 +345,11 @@ func runCommandInfoFP(cmd *cobra.Command, args []string) error { if err != nil { return err } - defer cleanUp() + defer func() { + if err := cleanUp(); err != nil { + fmt.Printf("Failed to clean up grpc client: %v\n", err) + } + }() resp, err := client.QueryFinalityProviderInfo(context.Background(), fpPk) if err != nil { @@ -276,24 +360,30 @@ func runCommandInfoFP(cmd *cobra.Command, args []string) error { return nil } -// CommandRegisterFP returns the register-finality-provider command by connecting to the fpd daemon. -func CommandRegisterFP() *cobra.Command { +// CommandAddFinalitySig returns the add-finality-sig command by connecting to the fpd daemon. +func CommandAddFinalitySig() *cobra.Command { var cmd = &cobra.Command{ - Use: "register-finality-provider [fp-eots-pk-hex]", - Aliases: []string{"rfp"}, - Short: "Register a created finality provider to Babylon.", - Example: fmt.Sprintf(`fpd register-finality-provider --daemon-address %s`, defaultFpdDaemonAddress), - Args: cobra.ExactArgs(1), - RunE: runCommandRegisterFP, + Use: "unsafe-add-finality-sig [fp-eots-pk-hex] [block-height]", + Aliases: []string{"unsafe-afs"}, + Short: "[UNSAFE] Send a finality signature to the consumer chain.", + Long: "[UNSAFE] Send a finality signature to the consumer chain. This command should only be used for presentation/testing purposes", + Example: fmt.Sprintf(`fpd unsafe-add-finality-sig [fp-eots-pk-hex] [block-height] --daemon-address %s`, defaultFpdDaemonAddress), + Args: cobra.ExactArgs(2), + RunE: runCommandAddFinalitySig, } - f := cmd.Flags() - f.String(fpdDaemonAddressFlag, defaultFpdDaemonAddress, "The RPC server address of fpd") - f.String(passphraseFlag, "", "The pass phrase used to encrypt the keys") + cmd.Flags().String(fpdDaemonAddressFlag, defaultFpdDaemonAddress, "The RPC server address of fpd") + cmd.Flags().String(appHashFlag, defaultAppHashStr, "The last commit hash of the chain block") + cmd.Flags().Bool(checkDoubleSignFlag, true, "If 'true', uses anti-slashing protection when doing EOTS sign") + return cmd } -func runCommandRegisterFP(cmd *cobra.Command, args []string) error { - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(args[0]) +func runCommandAddFinalitySig(cmd *cobra.Command, args []string) error { + fpPk, err := types.NewBIP340PubKeyFromHex(args[0]) + if err != nil { + return err + } + blkHeight, err := strconv.ParseUint(args[1], 10, 64) if err != nil { return err } @@ -304,18 +394,32 @@ func runCommandRegisterFP(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) } + appHashHex, err := flags.GetString(appHashFlag) + if err != nil { + return fmt.Errorf("failed to read flag %s: %w", appHashFlag, err) + } + + checkDoubleSign, err := flags.GetBool(checkDoubleSignFlag) + if err != nil { + return fmt.Errorf("failed to read flag %s: %w", checkDoubleSignFlag, err) + } + client, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(daemonAddress) if err != nil { return err } - defer cleanUp() + defer func() { + if err := cleanUp(); err != nil { + fmt.Printf("Failed to clean up grpc client: %v\n", err) + } + }() - passphrase, err := flags.GetString(passphraseFlag) + appHash, err := hex.DecodeString(appHashHex) if err != nil { - return fmt.Errorf("failed to read flag %s: %w", passphraseFlag, err) + return err } - res, err := client.RegisterFinalityProvider(context.Background(), fpPk, passphrase) + res, err := client.AddFinalitySignature(context.Background(), fpPk.MarshalHex(), blkHeight, appHash, checkDoubleSign) if err != nil { return err } @@ -324,28 +428,33 @@ func runCommandRegisterFP(cmd *cobra.Command, args []string) error { return nil } -// CommandAddFinalitySig returns the add-finality-sig command by connecting to the fpd daemon. -func CommandAddFinalitySig() *cobra.Command { +// CommandEditFinalityDescription edits description of finality provider +func CommandEditFinalityDescription() *cobra.Command { var cmd = &cobra.Command{ - Use: "unsafe-add-finality-sig [fp-eots-pk-hex] [block-height]", - Aliases: []string{"unsafe-afs"}, - Short: "[UNSAFE] Send a finality signature to the consumer chain.", - Long: "[UNSAFE] Send a finality signature to the consumer chain. This command should only be used for presentation/testing purposes", - Example: fmt.Sprintf(`fpd unsafe-add-finality-sig [fp-eots-pk-hex] [block-height] --daemon-address %s`, defaultFpdDaemonAddress), - Args: cobra.ExactArgs(2), - RunE: runCommandAddFinalitySig, + Use: "edit-finality-provider [eots_pk]", + Aliases: []string{"efp"}, + Short: "Edit finality provider data without resetting unchanged fields", + Long: "Edit the details of a finality provider using the specified EOTS public key. " + + "\nThe provided [eots_pk] must correspond to the Babylon address controlled by the key specified in fpd.conf. " + + "\nIf one or more optional flags are passed (such as --moniker, --website, etc.), " + + "the corresponding values are updated, while unchanged fields retain their current values from the Babylon Node.", + Example: fmt.Sprintf(`fpd edit-finality-provider [eots_pk] --daemon-address %s --moniker "new-moniker"`, defaultFpdDaemonAddress), + Args: cobra.ExactArgs(1), + RunE: runCommandEditFinalityDescription, } cmd.Flags().String(fpdDaemonAddressFlag, defaultFpdDaemonAddress, "The RPC server address of fpd") - cmd.Flags().String(appHashFlag, defaultAppHashStr, "The last commit hash of the chain block") + cmd.Flags().String(monikerFlag, "", "The finality provider's (optional) moniker") + cmd.Flags().String(websiteFlag, "", "The finality provider's (optional) website") + cmd.Flags().String(securityContactFlag, "", "The finality provider's (optional) security contact email") + cmd.Flags().String(detailsFlag, "", "The finality provider's (optional) details") + cmd.Flags().String(identityFlag, "", "The (optional) identity signature (ex. UPort or Keybase)") + cmd.Flags().String(commissionRateFlag, "", "The (optional) commission rate percentage (ex. 0.2)") + return cmd } -func runCommandAddFinalitySig(cmd *cobra.Command, args []string) error { - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(args[0]) - if err != nil { - return err - } - blkHeight, err := strconv.ParseUint(args[1], 10, 64) +func runCommandEditFinalityDescription(cmd *cobra.Command, args []string) error { + fpPk, err := types.NewBIP340PubKeyFromHex(args[0]) if err != nil { return err } @@ -356,27 +465,94 @@ func runCommandAddFinalitySig(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) } - appHashHex, err := flags.GetString(appHashFlag) + grpcClient, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(daemonAddress) if err != nil { - return fmt.Errorf("failed to read flag %s: %w", appHashFlag, err) + return err } + defer func() { + if err := cleanUp(); err != nil { + fmt.Printf("Failed to clean up grpc client: %v\n", err) + } + }() - client, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(daemonAddress) + moniker, _ := cmd.Flags().GetString(monikerFlag) + website, _ := cmd.Flags().GetString(websiteFlag) + securityContact, _ := cmd.Flags().GetString(securityContactFlag) + details, _ := cmd.Flags().GetString(detailsFlag) + identity, _ := cmd.Flags().GetString(identityFlag) + rate, _ := cmd.Flags().GetString(commissionRateFlag) + + desc := &proto.Description{ + Moniker: moniker, + Identity: identity, + Website: website, + SecurityContact: securityContact, + Details: details, + } + + if err := grpcClient.EditFinalityProvider(cmd.Context(), fpPk, desc, rate); err != nil { + return fmt.Errorf("failed to edit finality provider %v err %w", fpPk.MarshalHex(), err) + } + + return nil +} + +// CommandUnsafePruneMerkleProof prunes merkle proof +func CommandUnsafePruneMerkleProof() *cobra.Command { + var cmd = &cobra.Command{ + Use: "unsafe-prune-merkle-proof [eots_pk]", + Aliases: []string{"rmp"}, + Short: "Prunes merkle proofs up to the specified target height", + Long: strings.TrimSpace(`This command will prune all merkle proof up to the target height. The +operator of this command should ensure that finality provider has voted, or doesn't have voting power up to the target height.' +`), + Example: fmt.Sprintf(`fpd unsafe-prune-merkle-proof [eots_pk] --daemon-address %s`, defaultFpdDaemonAddress), + Args: cobra.ExactArgs(1), + RunE: runCommandUnsafePruneMerkleProof, + } + cmd.Flags().String(fpdDaemonAddressFlag, defaultFpdDaemonAddress, "The RPC server address of fpd") + cmd.Flags().String(chainIDFlag, "", "The identifier of the consumer chain") + cmd.Flags().Uint64(upToHeight, 0, "Target height to prune merkle proofs") + + if err := cmd.MarkFlagRequired(chainIDFlag); err != nil { + panic(err) + } + + if err := cmd.MarkFlagRequired(upToHeight); err != nil { + panic(err) + } + + return cmd +} + +func runCommandUnsafePruneMerkleProof(cmd *cobra.Command, args []string) error { + fpPk, err := types.NewBIP340PubKeyFromHex(args[0]) if err != nil { return err } - defer cleanUp() - appHash, err := hex.DecodeString(appHashHex) + flags := cmd.Flags() + daemonAddress, err := flags.GetString(fpdDaemonAddressFlag) if err != nil { - return err + return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) } - res, err := client.AddFinalitySignature(context.Background(), fpPk.MarshalHex(), blkHeight, appHash) + grpcClient, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(daemonAddress) if err != nil { return err } - printRespJSON(res) + defer func() { + if err := cleanUp(); err != nil { + fmt.Printf("Failed to clean up grpc client: %v\n", err) + } + }() + + chainID, _ := cmd.Flags().GetString(chainIDFlag) + targetHeight, _ := cmd.Flags().GetUint64(upToHeight) + + if err := grpcClient.UnsafeRemoveMerkleProof(cmd.Context(), fpPk, chainID, targetHeight); err != nil { + return fmt.Errorf("failed to edit finality provider %v err %w", fpPk.MarshalHex(), err) + } return nil } @@ -385,6 +561,7 @@ func printRespJSON(resp interface{}) { jsonBytes, err := json.MarshalIndent(resp, "", " ") if err != nil { fmt.Println("unable to decode response: ", err) + return } @@ -405,12 +582,153 @@ func loadKeyName(homeDir string, cmd *cobra.Command) (string, error) { // beforehand cfg, err := fpcfg.LoadConfig(homeDir) if err != nil { - return "", fmt.Errorf("failed to load config from %s: %w", fpcfg.ConfigFile(homeDir), err) + return "", fmt.Errorf("failed to load config from %s: %w", fpcfg.CfgFile(homeDir), err) } keyName = cfg.BabylonConfig.Key if keyName == "" { return "", fmt.Errorf("the key in config is empty") } + return keyName, nil } + +type parsedFinalityProvider struct { + keyName string + chainID string + eotsPK string + passphrase string + description stakingtypes.Description + commissionRate math.LegacyDec +} + +func parseFinalityProviderJSON(path string, homeDir string) (*parsedFinalityProvider, error) { + type internalFpJSON struct { + KeyName string `json:"keyName"` + ChainID string `json:"chainID"` + Passphrase string `json:"passphrase"` + CommissionRate string `json:"commissionRate"` + Moniker string `json:"moniker"` + Identity string `json:"identity"` + Website string `json:"website"` + SecurityContract string `json:"securityContract"` + Details string `json:"details"` + EotsPK string `json:"eotsPK"` + } + + // #nosec G304 - The log file path is provided by the user and not externally + contents, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var fp internalFpJSON + if err := json.Unmarshal(contents, &fp); err != nil { + return nil, err + } + + if fp.ChainID == "" { + return nil, fmt.Errorf("chainID is required") + } + + if fp.KeyName == "" { + cfg, err := fpcfg.LoadConfig(homeDir) + if err != nil { + return nil, fmt.Errorf("failed to load config from %s: %w", fpcfg.CfgFile(homeDir), err) + } + fp.KeyName = cfg.BabylonConfig.Key + if fp.KeyName == "" { + return nil, fmt.Errorf("the key is neither in config nor provided in the json file") + } + } + + if fp.Moniker == "" { + return nil, fmt.Errorf("moniker is required") + } + + if fp.CommissionRate == "" { + return nil, fmt.Errorf("commissionRate is required") + } + + if fp.EotsPK == "" { + return nil, fmt.Errorf("eotsPK is required") + } + + commissionRate, err := math.LegacyNewDecFromStr(fp.CommissionRate) + if err != nil { + return nil, fmt.Errorf("invalid commission rate: %w", err) + } + + description, err := stakingtypes.NewDescription(fp.Moniker, fp.Identity, fp.Website, fp.SecurityContract, fp.Details).EnsureLength() + if err != nil { + return nil, err + } + + return &parsedFinalityProvider{ + keyName: fp.KeyName, + chainID: fp.ChainID, + eotsPK: fp.EotsPK, + passphrase: fp.Passphrase, + description: description, + commissionRate: commissionRate, + }, nil +} + +func parseFinalityProviderFlags(cmd *cobra.Command, homeDir string) (*parsedFinalityProvider, error) { + flags := cmd.Flags() + + commissionRateStr, err := flags.GetString(commissionRateFlag) + if err != nil { + return nil, fmt.Errorf("failed to read flag %s: %w", commissionRateFlag, err) + } + commissionRate, err := math.LegacyNewDecFromStr(commissionRateStr) + if err != nil { + return nil, fmt.Errorf("invalid commission rate: %w", err) + } + + description, err := getDescriptionFromFlags(flags) + if err != nil { + return nil, fmt.Errorf("invalid description: %w", err) + } + + keyName, err := loadKeyName(homeDir, cmd) + if err != nil { + return nil, fmt.Errorf("not able to load key name: %w", err) + } + + if keyName == "" { + return nil, fmt.Errorf("keyname cannot be empty") + } + + chainID, err := flags.GetString(chainIDFlag) + if err != nil { + return nil, fmt.Errorf("failed to read flag %s: %w", chainIDFlag, err) + } + + if chainID == "" { + return nil, fmt.Errorf("chain-id cannot be empty") + } + + passphrase, err := flags.GetString(passphraseFlag) + if err != nil { + return nil, fmt.Errorf("failed to read flag %s: %w", passphraseFlag, err) + } + + eotsPkHex, err := flags.GetString(fpEotsPkFlag) + if err != nil { + return nil, fmt.Errorf("failed to read flag %s: %w", fpEotsPkFlag, err) + } + + if eotsPkHex == "" { + return nil, fmt.Errorf("eots-pk cannot be empty") + } + + return &parsedFinalityProvider{ + keyName: keyName, + chainID: chainID, + eotsPK: eotsPkHex, + passphrase: passphrase, + description: description, + commissionRate: commissionRate, + }, nil +} diff --git a/finality-provider/cmd/fpd/daemon/export.go b/finality-provider/cmd/fpd/daemon/export.go deleted file mode 100644 index 8686628b..00000000 --- a/finality-provider/cmd/fpd/daemon/export.go +++ /dev/null @@ -1,147 +0,0 @@ -package daemon - -import ( - "context" - "encoding/hex" - "fmt" - - "cosmossdk.io/math" - bbn "github.com/babylonlabs-io/babylon/types" - btcstktypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - "github.com/cosmos/cosmos-sdk/client" - sdkflags "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/spf13/cobra" - - fpcmd "github.com/babylonlabs-io/finality-provider/finality-provider/cmd" - fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" - dc "github.com/babylonlabs-io/finality-provider/finality-provider/service/client" -) - -// FinalityProviderSigned wraps the finality provider by adding the -// signature signed by the finality provider's Babylon key in hex -type FinalityProviderSigned struct { - btcstktypes.FinalityProvider - // FpSigHex is the finality provider cosmos sdk chain key - // can be verified with the pub key in btcstktypes.FinalityProvider.BabylonPk - FpSigHex string `json:"fp_sig_hex"` -} - -// CommandExportFP returns the export-finality-provider command by loading the fp and export the data. -func CommandExportFP() *cobra.Command { - var cmd = &cobra.Command{ - Use: "export-finality-provider [fp-eots-pk-hex]", - Aliases: []string{"exfp"}, - Short: "It exports the finality provider by the given EOTS public key.", - Example: fmt.Sprintf(`fpd export-finality-provider --daemon-address %s`, defaultFpdDaemonAddress), - Args: cobra.NoArgs, - RunE: fpcmd.RunEWithClientCtx(runCommandExportFP), - } - - f := cmd.Flags() - f.String(fpdDaemonAddressFlag, defaultFpdDaemonAddress, "The RPC server address of fpd") - f.Bool(signedFlag, false, - `Specify if the exported finality provider information should be signed, - if true, it will sign using the flag key-name, if not set it will load from the - babylon key on config.`, - ) - f.String(keyNameFlag, "", "The unique name of the finality provider key") - f.String(sdkflags.FlagHome, fpcfg.DefaultFpdDir, "The application home directory") - f.String(passphraseFlag, "", "The pass phrase used to encrypt the keys") - f.String(hdPathFlag, "", "The hd path used to derive the private key") - - return cmd -} - -func runCommandExportFP(ctx client.Context, cmd *cobra.Command, args []string) error { - flags := cmd.Flags() - daemonAddress, err := flags.GetString(fpdDaemonAddressFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) - } - - client, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(daemonAddress) - if err != nil { - return fmt.Errorf("failled to connect to daemon addr %s: %w", daemonAddress, err) - } - defer cleanUp() - - fpBtcPkHex := args[0] - fpPk, err := bbn.NewBIP340PubKeyFromHex(fpBtcPkHex) - if err != nil { - return fmt.Errorf("invalid fp btc pk hex %s: %w", fpBtcPkHex, err) - } - - fpInfoResp, err := client.QueryFinalityProviderInfo(context.Background(), fpPk) - if err != nil { - return fmt.Errorf("failed to query fp info from %s: %w", fpBtcPkHex, err) - } - - fpInfo := fpInfoResp.FinalityProvider - comm, err := math.LegacyNewDecFromStr(fpInfo.Commission) - if err != nil { - return fmt.Errorf("failed to parse fp commission %s: %w", fpInfo.Commission, err) - } - - desc := fpInfo.Description - fp := btcstktypes.FinalityProvider{ - Addr: fpInfo.FpAddr, - Description: &types.Description{ - Moniker: desc.Moniker, - Identity: desc.Identity, - Website: desc.Website, - SecurityContact: desc.SecurityContact, - Details: desc.Details, - }, - Commission: &comm, - BtcPk: fpPk, - Pop: nil, // TODO: fill PoP? - } - - signed, err := flags.GetBool(signedFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", signedFlag, err) - } - if !signed { - printRespJSON(fp) - return nil - } - - keyName, err := loadKeyName(ctx.HomeDir, cmd) - if err != nil { - return fmt.Errorf("not able to load key name: %w", err) - } - - // sign the finality provider data. - fpbz, err := fp.Marshal() - if err != nil { - return fmt.Errorf("failed to marshal finality provider %+v: %w", fp, err) - } - - passphrase, err := flags.GetString(passphraseFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", passphraseFlag, err) - } - - hdPath, err := flags.GetString(hdPathFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", hdPathFlag, err) - } - - resp, err := client.SignMessageFromChainKey( - context.Background(), - keyName, - passphrase, - hdPath, - fpbz, - ) - if err != nil { - return fmt.Errorf("failed to sign finality provider: %w", err) - } - - printRespJSON(FinalityProviderSigned{ - FinalityProvider: fp, - FpSigHex: hex.EncodeToString(resp.Signature), - }) - return nil -} diff --git a/finality-provider/cmd/fpd/daemon/flags.go b/finality-provider/cmd/fpd/daemon/flags.go index 0debceb2..6a1f7965 100644 --- a/finality-provider/cmd/fpd/daemon/flags.go +++ b/finality-provider/cmd/fpd/daemon/flags.go @@ -9,8 +9,11 @@ const ( appHashFlag = "app-hash" passphraseFlag = "passphrase" hdPathFlag = "hd-path" - chainIdFlag = "chain-id" + chainIDFlag = "chain-id" signedFlag = "signed" + checkDoubleSignFlag = "check-double-sign" + fromFile = "from-file" + upToHeight = "up-to-height" // flags for description monikerFlag = "moniker" @@ -18,5 +21,5 @@ const ( websiteFlag = "website" securityContactFlag = "security-contact" detailsFlag = "details" - commissionRateFlag = "commission" + commissionRateFlag = "commission-rate" ) diff --git a/finality-provider/cmd/fpd/daemon/init.go b/finality-provider/cmd/fpd/daemon/init.go index 99bb759a..e3bfafa6 100644 --- a/finality-provider/cmd/fpd/daemon/init.go +++ b/finality-provider/cmd/fpd/daemon/init.go @@ -24,10 +24,11 @@ func CommandInit() *cobra.Command { RunE: fpcmd.RunEWithClientCtx(runInitCmd), } cmd.Flags().Bool(forceFlag, false, "Override existing configuration") + return cmd } -func runInitCmd(ctx client.Context, cmd *cobra.Command, args []string) error { +func runInitCmd(ctx client.Context, cmd *cobra.Command, _ []string) error { homePath, err := filepath.Abs(ctx.HomeDir) if err != nil { return err @@ -55,5 +56,5 @@ func runInitCmd(ctx client.Context, cmd *cobra.Command, args []string) error { defaultConfig := fpcfg.DefaultConfigWithHome(homePath) fileParser := flags.NewParser(&defaultConfig, flags.Default) - return flags.NewIniParser(fileParser).WriteFile(fpcfg.ConfigFile(homePath), flags.IniIncludeComments|flags.IniIncludeDefaults) + return flags.NewIniParser(fileParser).WriteFile(fpcfg.CfgFile(homePath), flags.IniIncludeComments|flags.IniIncludeDefaults) } diff --git a/finality-provider/cmd/fpd/daemon/keys.go b/finality-provider/cmd/fpd/daemon/keys.go index d4893b20..fd667781 100644 --- a/finality-provider/cmd/fpd/daemon/keys.go +++ b/finality-provider/cmd/fpd/daemon/keys.go @@ -1,8 +1,7 @@ package daemon import ( - "strings" - + "github.com/babylonlabs-io/finality-provider/util" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/spf13/cobra" ) @@ -11,22 +10,12 @@ import ( // post run action to update the config if exists. func CommandKeys() *cobra.Command { keysCmd := keys.Commands() - keyAddCmd := GetSubCommand(keysCmd, "add") + keyAddCmd := util.GetSubCommand(keysCmd, "add") if keyAddCmd == nil { panic("failed to find keys add command") } keyAddCmd.Long += "\nIf this key is needed to run as the default for the finality-provider daemon, remind to update the fpd.conf" - return keysCmd -} -// GetSubCommand retuns the command if it finds, otherwise it returns nil -func GetSubCommand(cmd *cobra.Command, commandName string) *cobra.Command { - for _, c := range cmd.Commands() { - if !strings.EqualFold(c.Name(), commandName) { - continue - } - return c - } - return nil + return keysCmd } diff --git a/finality-provider/cmd/fpd/daemon/start.go b/finality-provider/cmd/fpd/daemon/start.go index 14b4dc24..c759331b 100644 --- a/finality-provider/cmd/fpd/daemon/start.go +++ b/finality-provider/cmd/fpd/daemon/start.go @@ -8,7 +8,7 @@ import ( "github.com/babylonlabs-io/babylon/types" "github.com/btcsuite/btcwallet/walletdb" "github.com/cosmos/cosmos-sdk/client" - "github.com/lightningnetwork/lnd/signal" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/spf13/cobra" "go.uber.org/zap" @@ -32,10 +32,12 @@ func CommandStart() *cobra.Command { cmd.Flags().String(fpEotsPkFlag, "", "The EOTS public key of the finality-provider to start") cmd.Flags().String(passphraseFlag, "", "The pass phrase used to decrypt the private key") cmd.Flags().String(rpcListenerFlag, "", "The address that the RPC server listens to") + cmd.Flags().String(flags.FlagHome, fpcfg.DefaultFpdDir, "The application home directory") + return cmd } -func runStartCmd(ctx client.Context, cmd *cobra.Command, args []string) error { +func runStartCmd(ctx client.Context, cmd *cobra.Command, _ []string) error { homePath, err := filepath.Abs(ctx.HomeDir) if err != nil { return err @@ -68,7 +70,7 @@ func runStartCmd(ctx client.Context, cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("invalid RPC listener address %s, %w", rpcListener, err) } - cfg.RpcListener = rpcListener + cfg.RPCListener = rpcListener } logger, err := log.NewRootLoggerWithFile(fpcfg.LogFile(homePath), cfg.LogLevel) @@ -76,7 +78,7 @@ func runStartCmd(ctx client.Context, cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to initialize the logger: %w", err) } - dbBackend, err := cfg.DatabaseConfig.GetDbBackend() + dbBackend, err := cfg.DatabaseConfig.GetDBBackend() if err != nil { return fmt.Errorf("failed to create db backend: %w", err) } @@ -90,14 +92,9 @@ func runStartCmd(ctx client.Context, cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to start app: %w", err) } - // Hook interceptor for os signals. - shutdownInterceptor, err := signal.Intercept() - if err != nil { - return err - } + fpServer := service.NewFinalityProviderServer(cfg, logger, fpApp, dbBackend) - fpServer := service.NewFinalityProviderServer(cfg, logger, fpApp, dbBackend, shutdownInterceptor) - return fpServer.RunUntilShutdown() + return fpServer.RunUntilShutdown(cmd.Context()) } // loadApp initialize an finality provider app based on config and flags set. @@ -108,7 +105,7 @@ func loadApp( ) (*service.FinalityProviderApp, error) { fpApp, err := service.NewFinalityProviderAppFromConfig(cfg, dbBackend, logger) if err != nil { - return nil, fmt.Errorf("failed to create finality-provider app: %v", err) + return nil, fmt.Errorf("failed to create finality-provider app: %w", err) } return fpApp, nil @@ -119,23 +116,38 @@ func startApp( fpApp *service.FinalityProviderApp, fpPkStr, passphrase string, ) error { - // only start the app without starting any finality-provider instance - // as there might be no finality-provider registered yet + // only start the app without starting any finality provider instance + // this is needed for new finality provider registration or unjailing + // finality providers if err := fpApp.Start(); err != nil { - return fmt.Errorf("failed to start the finality-provider app: %w", err) + return fmt.Errorf("failed to start the finality provider app: %w", err) } + // fp instance will be started if public key is specified if fpPkStr != "" { // start the finality-provider instance with the given public key fpPk, err := types.NewBIP340PubKeyFromHex(fpPkStr) if err != nil { - return fmt.Errorf("invalid finality-provider public key %s: %w", fpPkStr, err) + return fmt.Errorf("invalid finality provider public key %s: %w", fpPkStr, err) } - if err := fpApp.StartHandlingFinalityProvider(fpPk, passphrase); err != nil { - return fmt.Errorf("failed to start the finality-provider instance %s: %w", fpPkStr, err) - } + return fpApp.StartFinalityProvider(fpPk, passphrase) + } + + storedFps, err := fpApp.GetFinalityProviderStore().GetAllStoredFinalityProviders() + if err != nil { + return err + } + + if len(storedFps) == 1 { + return fpApp.StartFinalityProvider(types.NewBIP340PubKeyFromBTCPK(storedFps[0].BtcPk), passphrase) } - return fpApp.StartHandlingAll() + if len(storedFps) > 1 { + return fmt.Errorf("%d finality providers found in DB. Please specify the EOTS public key", len(storedFps)) + } + + fpApp.Logger().Info("No finality providers found in DB. Waiting for registration.") + + return nil } diff --git a/finality-provider/cmd/fpd/daemon/tx.go b/finality-provider/cmd/fpd/daemon/tx.go deleted file mode 100644 index ad96794e..00000000 --- a/finality-provider/cmd/fpd/daemon/tx.go +++ /dev/null @@ -1,118 +0,0 @@ -package daemon - -import ( - "fmt" - "strings" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/spf13/cobra" - - btcstakingcli "github.com/babylonlabs-io/babylon/x/btcstaking/client/cli" - btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - sdk "github.com/cosmos/cosmos-sdk/types" - authclient "github.com/cosmos/cosmos-sdk/x/auth/client" - authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" -) - -// CommandTxs returns the transaction commands for finality provider related msgs. -func CommandTxs() *cobra.Command { - cmd := &cobra.Command{ - Use: "tx", - Short: "transactions subcommands", - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, - } - - cmd.AddCommand( - authcli.GetSignCommand(), - btcstakingcli.NewCreateFinalityProviderCmd(), - // finalitycli.NewUnjailFinalityProviderCmd(), // TODO: add this back - NewValidateSignedFinalityProviderCmd(), - ) - - return cmd -} - -// NewValidateSignedFinalityProviderCmd returns the command line for -// tx validate-signed-finality-provider -func NewValidateSignedFinalityProviderCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "validate-signed-finality-provider [file_path_signed_msg]", - Args: cobra.ExactArgs(1), - Short: "Validates the signed MsgCreateFinalityProvider", - Long: strings.TrimSpace(` - Loads the signed MsgCreateFinalityProvider and checks if the basic - information is satisfied and the Proof of Possession is valid against the - signer of the msg and the finality provider's BTC public key - `), - Example: strings.TrimSpace( - `fdp tx validate-signed-finality-provider ./path/to/signed-msg.json`, - ), - RunE: func(cmd *cobra.Command, args []string) error { - ctx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - stdTx, err := authclient.ReadTxFromFile(ctx, args[0]) - if err != nil { - return err - } - - msgsV2, err := stdTx.GetMsgsV2() - if err != nil { - return err - } - - msgs := stdTx.GetMsgs() - if len(msgs) == 0 { - return fmt.Errorf("invalid msg, there is no msg inside %s file", args[0]) - } - - for i, sdkMsg := range msgs { - msgV2 := msgsV2[i] - msg, ok := sdkMsg.(*btcstakingtypes.MsgCreateFinalityProvider) - if !ok { - return fmt.Errorf("unable to parse %+v to MsgCreateFinalityProvider", msg) - } - - if err := msg.ValidateBasic(); err != nil { - return fmt.Errorf("error validating basic msg: %w", err) - } - - signers, err := ctx.Codec.GetMsgV2Signers(msgV2) - if err != nil { - return fmt.Errorf("failed to get signers from msg %+v: %w", msg, err) - } - - if len(signers) == 0 { - return fmt.Errorf("no signer at msg %+v", msgV2) - } - - signerAddrStr, err := ctx.Codec.InterfaceRegistry().SigningContext().AddressCodec().BytesToString(signers[0]) - if err != nil { - return err - } - - signerBbnAddr, err := sdk.AccAddressFromBech32(signerAddrStr) - if err != nil { - return fmt.Errorf("invalid signer address %s, please sign with a valid bbn address, err: %w", signerAddrStr, err) - } - - if !strings.EqualFold(msg.Addr, signerAddrStr) { - return fmt.Errorf("signer address: %s is different from finality provider address: %s", signerAddrStr, msg.Addr) - } - - if err := msg.Pop.VerifyBIP340(signerBbnAddr, msg.BtcPk); err != nil { - return fmt.Errorf("invalid Proof of Possession with signer %s: %w", signerBbnAddr.String(), err) - } - } - - _, err = cmd.OutOrStdout().Write([]byte("The signed MsgCreateFinalityProvider is valid")) - return err - }, - } - - return cmd -} diff --git a/finality-provider/cmd/fpd/daemon/tx_test.go b/finality-provider/cmd/fpd/daemon/tx_test.go deleted file mode 100644 index 9460ea3b..00000000 --- a/finality-provider/cmd/fpd/daemon/tx_test.go +++ /dev/null @@ -1,172 +0,0 @@ -package daemon_test - -import ( - "bytes" - "encoding/json" - "fmt" - "math/rand" - "os" - "path/filepath" - "testing" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/keys" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/spf13/cobra" - - "github.com/stretchr/testify/require" - - "github.com/babylonlabs-io/babylon/app" - "github.com/babylonlabs-io/babylon/testutil/datagen" - bbn "github.com/babylonlabs-io/babylon/types" - - btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - fpcmd "github.com/babylonlabs-io/finality-provider/finality-provider/cmd" - "github.com/babylonlabs-io/finality-provider/finality-provider/cmd/fpd/daemon" - fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" - "github.com/babylonlabs-io/finality-provider/testutil" -) - -func FuzzNewValidateSignedFinalityProviderCmd(f *testing.F) { - testutil.AddRandomSeedsToFuzzer(f, 10) - tempApp := app.NewTmpBabylonApp() - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - rootCmdBuff := new(bytes.Buffer) - root := rootCmd(rootCmdBuff) - - tDir := t.TempDir() - tempHome := filepath.Join(tDir, "homefpnew") - homeFlag := fmt.Sprintf("--home=%s", tempHome) - - exec(t, root, rootCmdBuff, "init", homeFlag) - - keyName := datagen.GenRandomHexStr(r, 5) - kbt := "--keyring-backend=test" - - keyOut := execUnmarshal[keys.KeyOutput](t, root, rootCmdBuff, "keys", "add", keyName, homeFlag, kbt) - require.Equal(t, keyName, keyOut.Name) - - fpAddr, err := sdk.AccAddressFromBech32(keyOut.Address) - require.NoError(t, err) - - btcSK, btcPK, err := datagen.GenRandomBTCKeyPair(r) - require.NoError(t, err) - - pop, err := btcstakingtypes.NewPoPBTC(fpAddr, btcSK) - require.NoError(t, err) - - popHex, err := pop.ToHexStr() - require.NoError(t, err) - - bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) - - _, unsignedMsgStr := exec( - t, root, rootCmdBuff, "tx", "create-finality-provider", bip340PK.MarshalHex(), popHex, homeFlag, kbt, - fmt.Sprintf("--from=%s", keyName), "--generate-only", "--gas-prices=10ubbn", - "--commission-rate=0.05", "--moniker='niceFP'", "--identity=x", "--website=test.com", - "--security-contact=niceEmail", "--details='no Details'", - ) - - tx, err := tempApp.TxConfig().TxJSONDecoder()([]byte(unsignedMsgStr)) - require.NoError(t, err) - - txMsgs := tx.GetMsgs() - require.Len(t, txMsgs, 1) - sdkMsg := txMsgs[0] - - msg, ok := sdkMsg.(*btcstakingtypes.MsgCreateFinalityProvider) - require.True(t, ok) - require.Equal(t, msg.Addr, fpAddr.String()) - - // sends the unsigned msg to a file to be signed. - unsignedMsgFilePath := writeToTempFile(t, r, unsignedMsgStr) - - _, signedMsgStr := exec(t, root, rootCmdBuff, "tx", "sign", unsignedMsgFilePath, homeFlag, kbt, - fmt.Sprintf("--from=%s", keyName), "--offline", "--account-number=0", "--sequence=0", - ) - // sends the signed msg to a file. - signedMsgFilePath := writeToTempFile(t, r, signedMsgStr) - - tx, err = tempApp.TxConfig().TxJSONDecoder()([]byte(signedMsgStr)) - require.NoError(t, err) - - msgSigned := tx.GetMsgs()[0].(*btcstakingtypes.MsgCreateFinalityProvider) - require.Equal(t, msgSigned.Addr, fpAddr.String()) - - // executes the verification. - _, outputValidate := exec(t, root, rootCmdBuff, "tx", "validate-signed-finality-provider", signedMsgFilePath, homeFlag) - require.Equal(t, "The signed MsgCreateFinalityProvider is valid", outputValidate) - }) -} - -func rootCmd(outputBuff *bytes.Buffer) *cobra.Command { - cmd := &cobra.Command{ - Use: "fpd", - PersistentPreRunE: fpcmd.PersistClientCtx(client.Context{}.WithOutput(outputBuff)), - } - cmd.PersistentFlags().String(flags.FlagHome, fpcfg.DefaultFpdDir, "The application home directory") - - cmd.AddCommand( - daemon.CommandInit(), daemon.CommandStart(), daemon.CommandKeys(), - daemon.CommandGetDaemonInfo(), daemon.CommandCreateFP(), daemon.CommandLsFP(), - daemon.CommandInfoFP(), daemon.CommandRegisterFP(), daemon.CommandAddFinalitySig(), - daemon.CommandExportFP(), daemon.CommandTxs(), - ) - - return cmd -} - -// exec executes a command based on the cmd passed, the args should only search for subcommands, not parent commands -// it also receives rootCmdBuf as a parameter, which is the buff set in the cmd context standard output for the commands -// that do print to the context stdout instead of the root stdout. -func exec(t *testing.T, root *cobra.Command, rootCmdBuf *bytes.Buffer, args ...string) (c *cobra.Command, output string) { - buf := new(bytes.Buffer) - root.SetOut(buf) - root.SetErr(buf) - root.SetArgs(args) - - c, err := root.ExecuteC() - require.NoError(t, err) - - outStr := buf.String() - if len(outStr) > 0 { - return c, outStr - } - - _, err = buf.Write(rootCmdBuf.Bytes()) - require.NoError(t, err) - - return c, buf.String() -} - -func execUnmarshal[structure any](t *testing.T, root *cobra.Command, rootCmdBuf *bytes.Buffer, args ...string) (output structure) { - var typed structure - _, outStr := exec(t, root, rootCmdBuf, args...) - - // helpfull for debug - // fmt.Printf("\nargs %s", strings.Join(args, " ")) - // fmt.Printf("\nout %s", outStr) - - err := json.Unmarshal([]byte(outStr), &typed) - require.NoError(t, err) - - return typed -} - -func writeToTempFile(t *testing.T, r *rand.Rand, str string) (outputFilePath string) { - outputFilePath = filepath.Join(t.TempDir(), datagen.GenRandomHexStr(r, 5)) - - outPutFile, err := os.Create(outputFilePath) - require.NoError(t, err) - defer outPutFile.Close() - - _, err = outPutFile.WriteString(str) - require.NoError(t, err) - - return outputFilePath -} diff --git a/finality-provider/cmd/fpd/main.go b/finality-provider/cmd/fpd/main.go index 78eb9d96..6b7c7b05 100644 --- a/finality-provider/cmd/fpd/main.go +++ b/finality-provider/cmd/fpd/main.go @@ -1,9 +1,13 @@ package main import ( + "context" "fmt" "os" + "os/signal" + "syscall" + incentivecli "github.com/babylonlabs-io/babylon/x/incentive/client/cli" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/spf13/cobra" @@ -11,6 +15,7 @@ import ( fpcmd "github.com/babylonlabs-io/finality-provider/finality-provider/cmd" "github.com/babylonlabs-io/finality-provider/finality-provider/cmd/fpd/daemon" fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" + "github.com/babylonlabs-io/finality-provider/version" ) // NewRootCmd creates a new root command for fpd. It is called once in the main function. @@ -32,13 +37,17 @@ func main() { cmd.AddCommand( daemon.CommandInit(), daemon.CommandStart(), daemon.CommandKeys(), daemon.CommandGetDaemonInfo(), daemon.CommandCreateFP(), daemon.CommandLsFP(), - daemon.CommandInfoFP(), daemon.CommandRegisterFP(), daemon.CommandAddFinalitySig(), - daemon.CommandExportFP(), daemon.CommandTxs(), - daemon.CommandCommitPubRand(), + daemon.CommandInfoFP(), daemon.CommandAddFinalitySig(), daemon.CommandUnjailFP(), + daemon.CommandEditFinalityDescription(), daemon.CommandCommitPubRand(), + incentivecli.NewWithdrawRewardCmd(), incentivecli.NewSetWithdrawAddressCmd(), + version.CommandVersion("fpd"), daemon.CommandUnsafePruneMerkleProof(), ) - if err := cmd.Execute(); err != nil { - fmt.Fprintf(os.Stderr, "Whoops. There was an error while executing your fpd CLI '%s'", err) - os.Exit(1) + ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer cancel() + + if err := cmd.ExecuteContext(ctx); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Whoops. There was an error while executing your fpd CLI '%s'", err) + os.Exit(1) //nolint:gocritic } } diff --git a/finality-provider/config/config.go b/finality-provider/config/config.go index a54ce9d8..c599c691 100644 --- a/finality-provider/config/config.go +++ b/finality-provider/config/config.go @@ -25,19 +25,16 @@ const ( defaultFinalityProviderKeyName = "finality-provider" DefaultRPCPort = 12581 defaultConfigFileName = "fpd.conf" - defaultNumPubRand = 70000 // support running of 1 week with block production time as 10s + defaultNumPubRand = 10000 // support running of roughly 1 day with block production time as 10s defaultNumPubRandMax = 100000 - defaultMinRandHeightGap = 35000 + defaultTimestampingDelayBlocks = 6000 // 100 BTC blocks * 600s / 10s defaultBatchSubmissionSize = 1000 - defaultStatusUpdateInterval = 20 * time.Second defaultRandomInterval = 30 * time.Second defaultSubmitRetryInterval = 1 * time.Second - defaultSyncFpStatusInterval = 30 * time.Second defaultSignatureSubmissionInterval = 1 * time.Second defaultMaxSubmissionRetries = 20 defaultBitcoinNetwork = "signet" defaultDataDirname = "data" - defaultMaxNumFinalityProviders = 3 ) var ( @@ -48,7 +45,7 @@ var ( defaultBTCNetParams = chaincfg.SigNetParams defaultEOTSManagerAddress = "127.0.0.1:" + strconv.Itoa(eotscfg.DefaultRPCPort) - DefaultRpcListener = "127.0.0.1:" + strconv.Itoa(DefaultRPCPort) + DefaultRPCListener = "127.0.0.1:" + strconv.Itoa(DefaultRPCPort) DefaultDataDir = DataDir(DefaultFpdDir) ) @@ -56,18 +53,15 @@ var ( type Config struct { LogLevel string `long:"loglevel" description:"Logging level for all subsystems" choice:"trace" choice:"debug" choice:"info" choice:"warn" choice:"error" choice:"fatal"` // ChainType and ChainID (if any) of the chain config identify a consumer chain - ChainType string `long:"chaintype" description:"the type of the consumer chain" choice:"babylon" choice:"OPStackL2" choice:"wasm"` + ChainType string `long:"chaintype" description:"the type of the consumer chain" choice:"babylon"` NumPubRand uint32 `long:"numPubRand" description:"The number of Schnorr public randomness for each commitment"` NumPubRandMax uint32 `long:"numpubrandmax" description:"The upper bound of the number of Schnorr public randomness for each commitment"` - MinRandHeightGap uint32 `long:"minrandheightgap" description:"The minimum gap between the last committed rand height and the current Babylon block height"` + TimestampingDelayBlocks uint32 `long:"timestampingdelayblocks" description:"The delay, measured in blocks, between a randomness commit submission and the randomness is BTC-timestamped"` MaxSubmissionRetries uint32 `long:"maxsubmissionretries" description:"The maximum number of retries to submit finality signature or public randomness"` EOTSManagerAddress string `long:"eotsmanageraddress" description:"The address of the remote EOTS manager; Empty if the EOTS manager is running locally"` BatchSubmissionSize uint32 `long:"batchsubmissionsize" description:"The size of a batch in one submission"` - MaxNumFinalityProviders uint32 `long:"maxnumfinalityproviders" description:"The maximum number of finality-provider instances running concurrently within the daemon"` - StatusUpdateInterval time.Duration `long:"statusupdateinterval" description:"The interval between each update of finality-provider status"` RandomnessCommitInterval time.Duration `long:"randomnesscommitinterval" description:"The interval between each attempt to commit public randomness"` SubmissionRetryInterval time.Duration `long:"submissionretryinterval" description:"The interval between each attempt to submit finality signature or public randomness after a failure"` - SyncFpStatusInterval time.Duration `long:"syncfpstatusinterval" description:"The duration of time that it should sync FP status with the client blockchain"` SignatureSubmissionInterval time.Duration `long:"signaturesubmissioninterval" description:"The interval between each finality signature(s) submission"` BitcoinNetwork string `long:"bitcoinnetwork" description:"Bitcoin network to run on" choise:"mainnet" choice:"regtest" choice:"testnet" choice:"simnet" choice:"signet"` @@ -84,7 +78,7 @@ type Config struct { CosmwasmConfig *CosmwasmConfig `group:"wasm" namespace:"wasm"` - RpcListener string `long:"rpclistener" description:"the listener for RPC connections, e.g., 127.0.0.1:1234"` + RPCListener string `long:"rpclistener" description:"the listener for RPC connections, e.g., 127.0.0.1:1234"` Metrics *metrics.Config `group:"metrics" namespace:"metrics"` } @@ -102,9 +96,8 @@ func DefaultConfigWithHome(homePath string) Config { PollerConfig: &pollerCfg, NumPubRand: defaultNumPubRand, NumPubRandMax: defaultNumPubRandMax, - MinRandHeightGap: defaultMinRandHeightGap, + TimestampingDelayBlocks: defaultTimestampingDelayBlocks, BatchSubmissionSize: defaultBatchSubmissionSize, - StatusUpdateInterval: defaultStatusUpdateInterval, RandomnessCommitInterval: defaultRandomInterval, SubmissionRetryInterval: defaultSubmitRetryInterval, SignatureSubmissionInterval: defaultSignatureSubmissionInterval, @@ -112,10 +105,8 @@ func DefaultConfigWithHome(homePath string) Config { BitcoinNetwork: defaultBitcoinNetwork, BTCNetParams: defaultBTCNetParams, EOTSManagerAddress: defaultEOTSManagerAddress, - RpcListener: DefaultRpcListener, + RPCListener: DefaultRPCListener, Metrics: metrics.DefaultFpConfig(), - SyncFpStatusInterval: defaultSyncFpStatusInterval, - MaxNumFinalityProviders: defaultMaxNumFinalityProviders, } if err := cfg.Validate(); err != nil { @@ -129,7 +120,7 @@ func DefaultConfig() Config { return DefaultConfigWithHome(DefaultFpdDir) } -func ConfigFile(homePath string) string { +func CfgFile(homePath string) string { return filepath.Join(homePath, defaultConfigFileName) } @@ -156,7 +147,7 @@ func DataDir(homePath string) string { func LoadConfig(homePath string) (*Config, error) { // The home directory is required to have a configuration file with a specific name // under it. - cfgFile := ConfigFile(homePath) + cfgFile := CfgFile(homePath) if !util.FileExists(cfgFile) { return nil, fmt.Errorf("specified config file does "+ "not exist in %s", cfgFile) @@ -194,9 +185,9 @@ func (cfg *Config) Validate() error { } cfg.BTCNetParams = btcNetConfig - _, err = net.ResolveTCPAddr("tcp", cfg.RpcListener) + _, err = net.ResolveTCPAddr("tcp", cfg.RPCListener) if err != nil { - return fmt.Errorf("invalid RPC listener address %s, %w", cfg.RpcListener, err) + return fmt.Errorf("invalid RPC listener address %s, %w", cfg.RPCListener, err) } if cfg.Metrics == nil { @@ -204,7 +195,11 @@ func (cfg *Config) Validate() error { } if err := cfg.Metrics.Validate(); err != nil { - return fmt.Errorf("invalid metrics config") + return fmt.Errorf("invalid metrics config: %w", err) + } + + if err := cfg.PollerConfig.Validate(); err != nil { + return fmt.Errorf("invalid poller config: %w", err) } // All good, return the sanitized result. @@ -212,7 +207,7 @@ func (cfg *Config) Validate() error { } // NetParamsBTC parses the BTC net params from config. -func NetParamsBTC(btcNet string) (p chaincfg.Params, err error) { +func NetParamsBTC(btcNet string) (chaincfg.Params, error) { switch btcNet { case "mainnet": return chaincfg.MainNetParams, nil @@ -225,6 +220,6 @@ func NetParamsBTC(btcNet string) (p chaincfg.Params, err error) { case "signet": return chaincfg.SigNetParams, nil default: - return p, fmt.Errorf("invalid network: %v", btcNet) + return chaincfg.Params{}, fmt.Errorf("invalid network: %v", btcNet) } } diff --git a/finality-provider/config/cosmwasm.go b/finality-provider/config/cosmwasm.go index 464ad23d..bce1300b 100644 --- a/finality-provider/config/cosmwasm.go +++ b/finality-provider/config/cosmwasm.go @@ -48,6 +48,7 @@ func (cfg *CosmwasmConfig) Validate() error { if !strings.HasPrefix(cfg.BtcStakingContractAddress, cfg.AccountPrefix) { return fmt.Errorf("babylon-contract-address: invalid address prefix: %w", err) } + return nil } diff --git a/finality-provider/config/db.go b/finality-provider/config/db.go index 171c2856..e6c0477c 100644 --- a/finality-provider/config/db.go +++ b/finality-provider/config/db.go @@ -7,7 +7,7 @@ import ( ) const ( - defaultDbName = "finality-provider.db" + defaultDBName = "finality-provider.db" ) type DBConfig struct { @@ -47,13 +47,12 @@ func DefaultDBConfig() *DBConfig { func DefaultDBConfigWithHomePath(homePath string) *DBConfig { return &DBConfig{ DBPath: DataDir(homePath), - DBFileName: defaultDbName, + DBFileName: defaultDBName, NoFreelistSync: true, AutoCompact: false, AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge, DBTimeout: kvdb.DefaultDBTimeout, } - } func (db *DBConfig) DBConfigToBoltBackendConfig() *kvdb.BoltBackendConfig { @@ -67,6 +66,6 @@ func (db *DBConfig) DBConfigToBoltBackendConfig() *kvdb.BoltBackendConfig { } } -func (db *DBConfig) GetDbBackend() (kvdb.Backend, error) { +func (db *DBConfig) GetDBBackend() (kvdb.Backend, error) { return kvdb.GetBoltBackend(db.DBConfigToBoltBackendConfig()) } diff --git a/finality-provider/config/opstackl2.go b/finality-provider/config/opstackl2.go index 5fc8c832..1d1640cc 100644 --- a/finality-provider/config/opstackl2.go +++ b/finality-provider/config/opstackl2.go @@ -14,7 +14,7 @@ import ( type OPStackL2Config struct { OPStackL2RPCAddress string `long:"opstackl2-rpc-address" description:"the rpc address of the op-stack-l2 node to connect to"` OPFinalityGadgetAddress string `long:"op-finality-gadget" description:"the contract address of the op-finality-gadget"` - BabylonFinalityGadgetRpc string `long:"babylon-finality-gadget-rpc" description:"the rpc address of babylon op finality gadget"` + BabylonFinalityGadgetRpc string `long:"babylon-finality-gadget-rpc" description:"the rpc address of babylon op finality gadget"` //nolint:stylecheck,revive // Below configurations are needed for the Babylon client Key string `long:"key" description:"name of the babylon key to sign transactions with"` ChainID string `long:"chain-id" description:"chain id of the babylon chain to connect to"` @@ -55,6 +55,7 @@ func (cfg *OPStackL2Config) Validate() error { if cfg.BlockTimeout < 0 { return fmt.Errorf("block-timeout can't be negative") } + return nil } diff --git a/finality-provider/config/poller.go b/finality-provider/config/poller.go index d0e1e0f1..b1d2f89e 100644 --- a/finality-provider/config/poller.go +++ b/finality-provider/config/poller.go @@ -1,11 +1,15 @@ package config -import "time" +import ( + "fmt" + "time" +) var ( defaultBufferSize = uint32(1000) defaultPollingInterval = 1 * time.Second defaultStaticStartHeight = uint64(1) + defaultPollSize = uint32(1000) ) type ChainPollerConfig struct { @@ -13,13 +17,27 @@ type ChainPollerConfig struct { PollInterval time.Duration `long:"pollinterval" description:"The interval between each polling of blocks; the value should be set depending on the block production time but could be set smaller for quick catching up"` StaticChainScanningStartHeight uint64 `long:"staticchainscanningstartheight" description:"The static height from which we start polling the chain"` AutoChainScanningMode bool `long:"autochainscanningmode" description:"Automatically discover the height from which to start polling the chain"` + PollSize uint32 `long:"pollsize" description:"The poll batch size when polling for blocks"` } func DefaultChainPollerConfig() ChainPollerConfig { return ChainPollerConfig{ BufferSize: defaultBufferSize, PollInterval: defaultPollingInterval, + PollSize: defaultPollSize, StaticChainScanningStartHeight: defaultStaticStartHeight, AutoChainScanningMode: true, } } + +func (c ChainPollerConfig) Validate() error { + if c.BufferSize == 0 { + return fmt.Errorf("invalid buffersize: %d", c.BufferSize) + } + + if c.PollSize == 0 { + return fmt.Errorf("invalid pollinterval: %d", c.PollInterval) + } + + return nil +} diff --git a/finality-provider/proto/finality_providers.go b/finality-provider/proto/finality_providers.go index fefa8111..172d9b1e 100644 --- a/finality-provider/proto/finality_providers.go +++ b/finality-provider/proto/finality_providers.go @@ -14,11 +14,13 @@ func (sfp *FinalityProvider) MustGetBTCPK() *btcec.PublicKey { if err != nil { panic(fmt.Errorf("failed to parse BTC PK: %w", err)) } + return btcPubKey } func (sfp *FinalityProvider) MustGetBIP340BTCPK() *bbn.BIP340PubKey { btcPK := sfp.MustGetBTCPK() + return bbn.NewBIP340PubKeyFromBTCPK(btcPK) } @@ -27,6 +29,7 @@ func NewFinalityProviderInfo(sfp *FinalityProvider) (*FinalityProviderInfo, erro if err := des.Unmarshal(sfp.Description); err != nil { return nil, err } + return &FinalityProviderInfo{ FpAddr: sfp.FpAddr, BtcPkHex: sfp.MustGetBIP340BTCPK().MarshalHex(), diff --git a/finality-provider/proto/finality_providers.pb.go b/finality-provider/proto/finality_providers.pb.go index fe2b59f3..c545ea01 100644 --- a/finality-provider/proto/finality_providers.pb.go +++ b/finality-provider/proto/finality_providers.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 +// protoc-gen-go v1.33.0 // protoc (unknown) // source: finality_providers.proto @@ -24,55 +24,54 @@ const ( ) // FinalityProviderStatus is the status of a finality provider -// a FinalityProvider object has 4 states: -// - Created - created and managed by finality provider client, not registered to -// the consumer chain yet +// a FinalityProvider object has 5 states: // - Registered - created and registered to the consumer chain, but not voting yet (No // delegated stake) -// - Active - created and registered to the consumer chain with stake to vote -// - Inactive - created and registered to the consumer chain with no stake to vote. +// - Inactive - the finality provider does not have voting power to vote +// - Active - the finality provider has voting power to vote // Finality Provider was already active. +// - Slashed - the finality provider looses all the voting power forever due to double voting +// - Jailed - the finality provider temporarily looses the voting power due to being sluggish // -// Valid State Transactions: -// - Created -> Registered -// - Registered -> Active -// - Active -> Inactive -// - Inactive -> Active +// Possible State Transactions: +// - Registered -> Inactive +// - Inactive -> Active +// - Active -> Inactive +// - Active -> Slashed +// - Active -> Jailed +// - Jailed -> Active +// - Jailed -> Inactive type FinalityProviderStatus int32 const ( - // CREATED defines a finality provider that is awaiting registration - FinalityProviderStatus_CREATED FinalityProviderStatus = 0 // REGISTERED defines a finality provider that has been registered // to the consumer chain but has no delegated stake - FinalityProviderStatus_REGISTERED FinalityProviderStatus = 1 + FinalityProviderStatus_REGISTERED FinalityProviderStatus = 0 // ACTIVE defines a finality provider that is delegated to vote - FinalityProviderStatus_ACTIVE FinalityProviderStatus = 2 + FinalityProviderStatus_ACTIVE FinalityProviderStatus = 1 // INACTIVE defines a finality provider whose delegations are reduced to zero but not slashed - FinalityProviderStatus_INACTIVE FinalityProviderStatus = 3 + FinalityProviderStatus_INACTIVE FinalityProviderStatus = 2 // SLASHED defines a finality provider that has been slashed - FinalityProviderStatus_SLASHED FinalityProviderStatus = 4 + FinalityProviderStatus_SLASHED FinalityProviderStatus = 3 // JAILED defines a finality provider that has been jailed - FinalityProviderStatus_JAILED FinalityProviderStatus = 5 + FinalityProviderStatus_JAILED FinalityProviderStatus = 4 ) // Enum value maps for FinalityProviderStatus. var ( FinalityProviderStatus_name = map[int32]string{ - 0: "CREATED", - 1: "REGISTERED", - 2: "ACTIVE", - 3: "INACTIVE", - 4: "SLASHED", - 5: "JAILED", + 0: "REGISTERED", + 1: "ACTIVE", + 2: "INACTIVE", + 3: "SLASHED", + 4: "JAILED", } FinalityProviderStatus_value = map[string]int32{ - "CREATED": 0, - "REGISTERED": 1, - "ACTIVE": 2, - "INACTIVE": 3, - "SLASHED": 4, - "JAILED": 5, + "REGISTERED": 0, + "ACTIVE": 1, + "INACTIVE": 2, + "SLASHED": 3, + "JAILED": 4, } ) @@ -197,18 +196,16 @@ type CreateFinalityProviderRequest struct { KeyName string `protobuf:"bytes,1,opt,name=key_name,json=keyName,proto3" json:"key_name,omitempty"` // passphrase is used to encrypt the keys Passphrase string `protobuf:"bytes,2,opt,name=passphrase,proto3" json:"passphrase,omitempty"` - // hd_path is the hd path for private key derivation - HdPath string `protobuf:"bytes,3,opt,name=hd_path,json=hdPath,proto3" json:"hd_path,omitempty"` // chain_id is the identifier of the consumer chain that the finality provider is connected to - ChainId string `protobuf:"bytes,4,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + ChainId string `protobuf:"bytes,3,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` // description defines the description terms for the finality provider - Description []byte `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` + Description []byte `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // commission defines the commission rate for the finality provider - Commission string `protobuf:"bytes,6,opt,name=commission,proto3" json:"commission,omitempty"` + Commission string `protobuf:"bytes,5,opt,name=commission,proto3" json:"commission,omitempty"` // eots_pk_hex it is the optional EOTS public key and used to ask for // the key record from the EOTS manager for the corresponding EOTS public key. // If this property is not set, it will create a new EOTS key. - EotsPkHex string `protobuf:"bytes,7,opt,name=eots_pk_hex,json=eotsPkHex,proto3" json:"eots_pk_hex,omitempty"` + EotsPkHex string `protobuf:"bytes,6,opt,name=eots_pk_hex,json=eotsPkHex,proto3" json:"eots_pk_hex,omitempty"` } func (x *CreateFinalityProviderRequest) Reset() { @@ -257,13 +254,6 @@ func (x *CreateFinalityProviderRequest) GetPassphrase() string { return "" } -func (x *CreateFinalityProviderRequest) GetHdPath() string { - if x != nil { - return x.HdPath - } - return "" -} - func (x *CreateFinalityProviderRequest) GetChainId() string { if x != nil { return x.ChainId @@ -298,6 +288,8 @@ type CreateFinalityProviderResponse struct { unknownFields protoimpl.UnknownFields FinalityProvider *FinalityProviderInfo `protobuf:"bytes,1,opt,name=finality_provider,json=finalityProvider,proto3" json:"finality_provider,omitempty"` + // hash of the successful finality provider creation transaction + TxHash string `protobuf:"bytes,2,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` } func (x *CreateFinalityProviderResponse) Reset() { @@ -339,19 +331,30 @@ func (x *CreateFinalityProviderResponse) GetFinalityProvider() *FinalityProvider return nil } -type RegisterFinalityProviderRequest struct { +func (x *CreateFinalityProviderResponse) GetTxHash() string { + if x != nil { + return x.TxHash + } + return "" +} + +type AddFinalitySignatureRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // btc_pk is hex string of the BTC secp256k1 public key of the finality provider encoded in BIP-340 spec BtcPk string `protobuf:"bytes,1,opt,name=btc_pk,json=btcPk,proto3" json:"btc_pk,omitempty"` - // passphrase is used to encrypt the keys - Passphrase string `protobuf:"bytes,2,opt,name=passphrase,proto3" json:"passphrase,omitempty"` + // height is the height of the chain block + Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` + // app_hash is the AppHash of the chain block + AppHash []byte `protobuf:"bytes,3,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` + // check_double_sign use double sign protection when doing EOTS sign + CheckDoubleSign bool `protobuf:"varint,4,opt,name=check_double_sign,json=checkDoubleSign,proto3" json:"check_double_sign,omitempty"` } -func (x *RegisterFinalityProviderRequest) Reset() { - *x = RegisterFinalityProviderRequest{} +func (x *AddFinalitySignatureRequest) Reset() { + *x = AddFinalitySignatureRequest{} if protoimpl.UnsafeEnabled { mi := &file_finality_providers_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -359,13 +362,13 @@ func (x *RegisterFinalityProviderRequest) Reset() { } } -func (x *RegisterFinalityProviderRequest) String() string { +func (x *AddFinalitySignatureRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RegisterFinalityProviderRequest) ProtoMessage() {} +func (*AddFinalitySignatureRequest) ProtoMessage() {} -func (x *RegisterFinalityProviderRequest) ProtoReflect() protoreflect.Message { +func (x *AddFinalitySignatureRequest) ProtoReflect() protoreflect.Message { mi := &file_finality_providers_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -377,36 +380,54 @@ func (x *RegisterFinalityProviderRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RegisterFinalityProviderRequest.ProtoReflect.Descriptor instead. -func (*RegisterFinalityProviderRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use AddFinalitySignatureRequest.ProtoReflect.Descriptor instead. +func (*AddFinalitySignatureRequest) Descriptor() ([]byte, []int) { return file_finality_providers_proto_rawDescGZIP(), []int{4} } -func (x *RegisterFinalityProviderRequest) GetBtcPk() string { +func (x *AddFinalitySignatureRequest) GetBtcPk() string { if x != nil { return x.BtcPk } return "" } -func (x *RegisterFinalityProviderRequest) GetPassphrase() string { +func (x *AddFinalitySignatureRequest) GetHeight() uint64 { if x != nil { - return x.Passphrase + return x.Height } - return "" + return 0 +} + +func (x *AddFinalitySignatureRequest) GetAppHash() []byte { + if x != nil { + return x.AppHash + } + return nil +} + +func (x *AddFinalitySignatureRequest) GetCheckDoubleSign() bool { + if x != nil { + return x.CheckDoubleSign + } + return false } -type RegisterFinalityProviderResponse struct { +type AddFinalitySignatureResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // hash of the successful chain registration transaction + // hash of the successful chain finality signature submission transaction TxHash string `protobuf:"bytes,1,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` + // the hex string of the extracted Bitcoin secp256k1 private key + ExtractedSkHex string `protobuf:"bytes,2,opt,name=extracted_sk_hex,json=extractedSkHex,proto3" json:"extracted_sk_hex,omitempty"` + // the hex string of the local Bitcoin secp256k1 private key + LocalSkHex string `protobuf:"bytes,3,opt,name=local_sk_hex,json=localSkHex,proto3" json:"local_sk_hex,omitempty"` } -func (x *RegisterFinalityProviderResponse) Reset() { - *x = RegisterFinalityProviderResponse{} +func (x *AddFinalitySignatureResponse) Reset() { + *x = AddFinalitySignatureResponse{} if protoimpl.UnsafeEnabled { mi := &file_finality_providers_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -414,13 +435,13 @@ func (x *RegisterFinalityProviderResponse) Reset() { } } -func (x *RegisterFinalityProviderResponse) String() string { +func (x *AddFinalitySignatureResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RegisterFinalityProviderResponse) ProtoMessage() {} +func (*AddFinalitySignatureResponse) ProtoMessage() {} -func (x *RegisterFinalityProviderResponse) ProtoReflect() protoreflect.Message { +func (x *AddFinalitySignatureResponse) ProtoReflect() protoreflect.Message { mi := &file_finality_providers_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -432,33 +453,43 @@ func (x *RegisterFinalityProviderResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RegisterFinalityProviderResponse.ProtoReflect.Descriptor instead. -func (*RegisterFinalityProviderResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use AddFinalitySignatureResponse.ProtoReflect.Descriptor instead. +func (*AddFinalitySignatureResponse) Descriptor() ([]byte, []int) { return file_finality_providers_proto_rawDescGZIP(), []int{5} } -func (x *RegisterFinalityProviderResponse) GetTxHash() string { +func (x *AddFinalitySignatureResponse) GetTxHash() string { if x != nil { return x.TxHash } return "" } -type AddFinalitySignatureRequest struct { +func (x *AddFinalitySignatureResponse) GetExtractedSkHex() string { + if x != nil { + return x.ExtractedSkHex + } + return "" +} + +func (x *AddFinalitySignatureResponse) GetLocalSkHex() string { + if x != nil { + return x.LocalSkHex + } + return "" +} + +type UnjailFinalityProviderRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // btc_pk is hex string of the BTC secp256k1 public key of the finality provider encoded in BIP-340 spec BtcPk string `protobuf:"bytes,1,opt,name=btc_pk,json=btcPk,proto3" json:"btc_pk,omitempty"` - // height is the height of the chain block - Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` - // app_hash is the AppHash of the chain block - AppHash []byte `protobuf:"bytes,3,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` } -func (x *AddFinalitySignatureRequest) Reset() { - *x = AddFinalitySignatureRequest{} +func (x *UnjailFinalityProviderRequest) Reset() { + *x = UnjailFinalityProviderRequest{} if protoimpl.UnsafeEnabled { mi := &file_finality_providers_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -466,13 +497,13 @@ func (x *AddFinalitySignatureRequest) Reset() { } } -func (x *AddFinalitySignatureRequest) String() string { +func (x *UnjailFinalityProviderRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*AddFinalitySignatureRequest) ProtoMessage() {} +func (*UnjailFinalityProviderRequest) ProtoMessage() {} -func (x *AddFinalitySignatureRequest) ProtoReflect() protoreflect.Message { +func (x *UnjailFinalityProviderRequest) ProtoReflect() protoreflect.Message { mi := &file_finality_providers_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -484,47 +515,29 @@ func (x *AddFinalitySignatureRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use AddFinalitySignatureRequest.ProtoReflect.Descriptor instead. -func (*AddFinalitySignatureRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use UnjailFinalityProviderRequest.ProtoReflect.Descriptor instead. +func (*UnjailFinalityProviderRequest) Descriptor() ([]byte, []int) { return file_finality_providers_proto_rawDescGZIP(), []int{6} } -func (x *AddFinalitySignatureRequest) GetBtcPk() string { +func (x *UnjailFinalityProviderRequest) GetBtcPk() string { if x != nil { return x.BtcPk } return "" } -func (x *AddFinalitySignatureRequest) GetHeight() uint64 { - if x != nil { - return x.Height - } - return 0 -} - -func (x *AddFinalitySignatureRequest) GetAppHash() []byte { - if x != nil { - return x.AppHash - } - return nil -} - -type AddFinalitySignatureResponse struct { +type UnjailFinalityProviderResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // hash of the successful chain finality signature submission transaction + // hash of the successful chain unjail finality provider transaction TxHash string `protobuf:"bytes,1,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` - // the hex string of the extracted Bitcoin secp256k1 private key - ExtractedSkHex string `protobuf:"bytes,2,opt,name=extracted_sk_hex,json=extractedSkHex,proto3" json:"extracted_sk_hex,omitempty"` - // the hex string of the local Bitcoin secp256k1 private key - LocalSkHex string `protobuf:"bytes,3,opt,name=local_sk_hex,json=localSkHex,proto3" json:"local_sk_hex,omitempty"` } -func (x *AddFinalitySignatureResponse) Reset() { - *x = AddFinalitySignatureResponse{} +func (x *UnjailFinalityProviderResponse) Reset() { + *x = UnjailFinalityProviderResponse{} if protoimpl.UnsafeEnabled { mi := &file_finality_providers_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -532,13 +545,13 @@ func (x *AddFinalitySignatureResponse) Reset() { } } -func (x *AddFinalitySignatureResponse) String() string { +func (x *UnjailFinalityProviderResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*AddFinalitySignatureResponse) ProtoMessage() {} +func (*UnjailFinalityProviderResponse) ProtoMessage() {} -func (x *AddFinalitySignatureResponse) ProtoReflect() protoreflect.Message { +func (x *UnjailFinalityProviderResponse) ProtoReflect() protoreflect.Message { mi := &file_finality_providers_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -550,32 +563,18 @@ func (x *AddFinalitySignatureResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use AddFinalitySignatureResponse.ProtoReflect.Descriptor instead. -func (*AddFinalitySignatureResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use UnjailFinalityProviderResponse.ProtoReflect.Descriptor instead. +func (*UnjailFinalityProviderResponse) Descriptor() ([]byte, []int) { return file_finality_providers_proto_rawDescGZIP(), []int{7} } -func (x *AddFinalitySignatureResponse) GetTxHash() string { +func (x *UnjailFinalityProviderResponse) GetTxHash() string { if x != nil { return x.TxHash } return "" } -func (x *AddFinalitySignatureResponse) GetExtractedSkHex() string { - if x != nil { - return x.ExtractedSkHex - } - return "" -} - -func (x *AddFinalitySignatureResponse) GetLocalSkHex() string { - if x != nil { - return x.LocalSkHex - } - return "" -} - type QueryFinalityProviderRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -714,7 +713,7 @@ type QueryFinalityProviderListResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - FinalityProviders []*FinalityProviderInfo `protobuf:"bytes,1,rep,name=finality_providers,json=finalityProviders,proto3" json:"finality_providers,omitempty"` // TODO add pagination in case the list gets large + FinalityProviders []*FinalityProviderInfo `protobuf:"bytes,1,rep,name=finality_providers,json=finalityProviders,proto3" json:"finality_providers,omitempty"` } func (x *QueryFinalityProviderListResponse) Reset() { @@ -770,19 +769,12 @@ type FinalityProvider struct { Description []byte `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // commission defines the commission rate for the finality provider Commission string `protobuf:"bytes,4,opt,name=commission,proto3" json:"commission,omitempty"` - // pop is the proof of possession of chain_pk and btc_pk - Pop *ProofOfPossession `protobuf:"bytes,5,opt,name=pop,proto3" json:"pop,omitempty"` - // key_name is the identifier of the keyring - KeyName string `protobuf:"bytes,6,opt,name=key_name,json=keyName,proto3" json:"key_name,omitempty"` // chain_id is the identifier of the consumer chain that the finality provider connected to - ChainId string `protobuf:"bytes,7,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + ChainId string `protobuf:"bytes,5,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` // last_voted_height defines the height of the last voted chain block - LastVotedHeight uint64 `protobuf:"varint,8,opt,name=last_voted_height,json=lastVotedHeight,proto3" json:"last_voted_height,omitempty"` - // last_processed_height defines the height of the last successfully processed block - // even though the vote is not cast - LastProcessedHeight uint64 `protobuf:"varint,9,opt,name=last_processed_height,json=lastProcessedHeight,proto3" json:"last_processed_height,omitempty"` + LastVotedHeight uint64 `protobuf:"varint,6,opt,name=last_voted_height,json=lastVotedHeight,proto3" json:"last_voted_height,omitempty"` // status defines the current finality provider status - Status FinalityProviderStatus `protobuf:"varint,10,opt,name=status,proto3,enum=proto.FinalityProviderStatus" json:"status,omitempty"` + Status FinalityProviderStatus `protobuf:"varint,7,opt,name=status,proto3,enum=proto.FinalityProviderStatus" json:"status,omitempty"` } func (x *FinalityProvider) Reset() { @@ -845,20 +837,6 @@ func (x *FinalityProvider) GetCommission() string { return "" } -func (x *FinalityProvider) GetPop() *ProofOfPossession { - if x != nil { - return x.Pop - } - return nil -} - -func (x *FinalityProvider) GetKeyName() string { - if x != nil { - return x.KeyName - } - return "" -} - func (x *FinalityProvider) GetChainId() string { if x != nil { return x.ChainId @@ -873,18 +851,11 @@ func (x *FinalityProvider) GetLastVotedHeight() uint64 { return 0 } -func (x *FinalityProvider) GetLastProcessedHeight() uint64 { - if x != nil { - return x.LastProcessedHeight - } - return 0 -} - func (x *FinalityProvider) GetStatus() FinalityProviderStatus { if x != nil { return x.Status } - return FinalityProviderStatus_CREATED + return FinalityProviderStatus_REGISTERED } // FinalityProviderInfo is the basic information of a finality provider mainly for external usage @@ -1300,6 +1271,178 @@ func (x *SignMessageFromChainKeyResponse) GetSignature() []byte { return nil } +// FinalityProviderInfo is the basic information of a finality provider mainly for external usage +type EditFinalityProviderRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // btc_pk is the hex string of the BTC secp256k1 PK of the finality provider encoded in BIP-340 spec + BtcPk string `protobuf:"bytes,1,opt,name=btc_pk,json=btcPk,proto3" json:"btc_pk,omitempty"` + // description defines the description terms for the finality provider + Description *Description `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + // commission defines the updated commission rate of the finality provider + Commission string `protobuf:"bytes,4,opt,name=commission,proto3" json:"commission,omitempty"` +} + +func (x *EditFinalityProviderRequest) Reset() { + *x = EditFinalityProviderRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_finality_providers_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EditFinalityProviderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EditFinalityProviderRequest) ProtoMessage() {} + +func (x *EditFinalityProviderRequest) ProtoReflect() protoreflect.Message { + mi := &file_finality_providers_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EditFinalityProviderRequest.ProtoReflect.Descriptor instead. +func (*EditFinalityProviderRequest) Descriptor() ([]byte, []int) { + return file_finality_providers_proto_rawDescGZIP(), []int{19} +} + +func (x *EditFinalityProviderRequest) GetBtcPk() string { + if x != nil { + return x.BtcPk + } + return "" +} + +func (x *EditFinalityProviderRequest) GetDescription() *Description { + if x != nil { + return x.Description + } + return nil +} + +func (x *EditFinalityProviderRequest) GetCommission() string { + if x != nil { + return x.Commission + } + return "" +} + +type RemoveMerkleProofRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // btc_pk_hex is the hex string of the BTC secp256k1 PK of the finality provider encoded in BIP-340 spec + BtcPkHex string `protobuf:"bytes,1,opt,name=btc_pk_hex,json=btcPkHex,proto3" json:"btc_pk_hex,omitempty"` + // chain_id is the identifier of the consumer chain + ChainId string `protobuf:"bytes,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // target_height to to delete all proofs + TargetHeight uint64 `protobuf:"varint,3,opt,name=target_height,json=targetHeight,proto3" json:"target_height,omitempty"` +} + +func (x *RemoveMerkleProofRequest) Reset() { + *x = RemoveMerkleProofRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_finality_providers_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveMerkleProofRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveMerkleProofRequest) ProtoMessage() {} + +func (x *RemoveMerkleProofRequest) ProtoReflect() protoreflect.Message { + mi := &file_finality_providers_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveMerkleProofRequest.ProtoReflect.Descriptor instead. +func (*RemoveMerkleProofRequest) Descriptor() ([]byte, []int) { + return file_finality_providers_proto_rawDescGZIP(), []int{20} +} + +func (x *RemoveMerkleProofRequest) GetBtcPkHex() string { + if x != nil { + return x.BtcPkHex + } + return "" +} + +func (x *RemoveMerkleProofRequest) GetChainId() string { + if x != nil { + return x.ChainId + } + return "" +} + +func (x *RemoveMerkleProofRequest) GetTargetHeight() uint64 { + if x != nil { + return x.TargetHeight + } + return 0 +} + +// Define an empty response message +type EmptyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *EmptyResponse) Reset() { + *x = EmptyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_finality_providers_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EmptyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EmptyResponse) ProtoMessage() {} + +func (x *EmptyResponse) ProtoReflect() protoreflect.Message { + mi := &file_finality_providers_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EmptyResponse.ProtoReflect.Descriptor instead. +func (*EmptyResponse) Descriptor() ([]byte, []int) { + return file_finality_providers_proto_rawDescGZIP(), []int{21} +} + var File_finality_providers_proto protoreflect.FileDescriptor var file_finality_providers_proto_rawDesc = []byte{ @@ -1314,210 +1457,225 @@ var file_finality_providers_proto_rawDesc = []byte{ 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x95, 0x02, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xfc, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, - 0x72, 0x61, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, - 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x23, - 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x1b, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, - 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, - 0x44, 0x65, 0x63, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x1e, 0x0a, 0x0b, 0x65, 0x6f, 0x74, 0x73, 0x5f, 0x70, 0x6b, 0x5f, 0x68, 0x65, 0x78, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x6f, 0x74, 0x73, 0x50, 0x6b, 0x48, 0x65, 0x78, 0x22, - 0x6a, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, - 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x48, 0x0a, 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, - 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x58, 0x0a, 0x1f, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, - 0x0a, 0x06, 0x62, 0x74, 0x63, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x62, 0x74, 0x63, 0x50, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, - 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x73, 0x73, 0x70, - 0x68, 0x72, 0x61, 0x73, 0x65, 0x22, 0x3b, 0x0a, 0x20, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, - 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, - 0x73, 0x68, 0x22, 0x67, 0x0a, 0x1b, 0x41, 0x64, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, - 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x74, 0x63, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x62, 0x74, 0x63, 0x50, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x12, 0x19, 0x0a, 0x08, 0x61, 0x70, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x07, 0x61, 0x70, 0x70, 0x48, 0x61, 0x73, 0x68, 0x22, 0x83, 0x01, 0x0a, 0x1c, - 0x41, 0x64, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, - 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, - 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, - 0x65, 0x64, 0x5f, 0x73, 0x6b, 0x5f, 0x68, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x65, 0x64, 0x53, 0x6b, 0x48, 0x65, 0x78, 0x12, - 0x20, 0x0a, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x6b, 0x5f, 0x68, 0x65, 0x78, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x53, 0x6b, 0x48, 0x65, - 0x78, 0x22, 0x35, 0x0a, 0x1c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, - 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x74, 0x63, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x62, 0x74, 0x63, 0x50, 0x6b, 0x22, 0x69, 0x0a, 0x1d, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x61, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, + 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x23, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x1b, 0x63, + 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x6d, 0x61, 0x74, 0x68, + 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x44, 0x65, 0x63, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0b, 0x65, 0x6f, 0x74, 0x73, 0x5f, 0x70, + 0x6b, 0x5f, 0x68, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x6f, 0x74, + 0x73, 0x50, 0x6b, 0x48, 0x65, 0x78, 0x22, 0x83, 0x01, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x22, 0x22, 0x0a, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, + 0x64, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, 0x93, 0x01, 0x0a, + 0x1b, 0x41, 0x64, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, + 0x62, 0x74, 0x63, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x62, 0x74, + 0x63, 0x50, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, + 0x70, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, + 0x70, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, + 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x53, 0x69, + 0x67, 0x6e, 0x22, 0x83, 0x01, 0x0a, 0x1c, 0x41, 0x64, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x28, 0x0a, 0x10, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x6b, 0x5f, 0x68, 0x65, 0x78, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x65, + 0x64, 0x53, 0x6b, 0x48, 0x65, 0x78, 0x12, 0x20, 0x0a, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, + 0x73, 0x6b, 0x5f, 0x68, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x53, 0x6b, 0x48, 0x65, 0x78, 0x22, 0x36, 0x0a, 0x1d, 0x55, 0x6e, 0x6a, 0x61, + 0x69, 0x6c, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x74, 0x63, + 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x62, 0x74, 0x63, 0x50, 0x6b, + 0x22, 0x39, 0x0a, 0x1e, 0x55, 0x6e, 0x6a, 0x61, 0x69, 0x6c, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, 0x35, 0x0a, 0x1c, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x62, + 0x74, 0x63, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x62, 0x74, 0x63, + 0x50, 0x6b, 0x22, 0x69, 0x0a, 0x1d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x5f, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x66, 0x69, 0x6e, + 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x22, 0x0a, + 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x6f, 0x0a, 0x21, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x12, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x74, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x73, 0x22, 0xc1, 0x02, 0x0a, 0x10, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x07, 0x66, 0x70, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, + 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x52, 0x06, 0x66, 0x70, 0x41, 0x64, 0x64, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x74, + 0x63, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x74, 0x63, 0x50, + 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x23, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, + 0x1b, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x6d, 0x61, + 0x74, 0x68, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x44, 0x65, 0x63, 0x52, 0x0a, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x76, 0x6f, 0x74, 0x65, + 0x64, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, + 0x6c, 0x61, 0x73, 0x74, 0x56, 0x6f, 0x74, 0x65, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, + 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xc5, 0x02, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x31, 0x0a, 0x07, 0x66, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x66, 0x70, 0x41, 0x64, + 0x64, 0x72, 0x12, 0x1c, 0x0a, 0x0a, 0x62, 0x74, 0x63, 0x5f, 0x70, 0x6b, 0x5f, 0x68, 0x65, 0x78, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x74, 0x63, 0x50, 0x6b, 0x48, 0x65, 0x78, + 0x12, 0x34, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x23, 0xc8, 0xde, 0x1f, 0x00, + 0xda, 0xde, 0x1f, 0x1b, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, + 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x44, 0x65, 0x63, 0x52, + 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x56, 0x6f, 0x74, 0x65, + 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0xa2, + 0x01, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x77, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x29, + 0x0a, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, + 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x22, 0x2c, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x4f, 0x66, 0x50, 0x6f, + 0x73, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x62, 0x74, 0x63, 0x5f, + 0x73, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x74, 0x63, 0x53, 0x69, + 0x67, 0x22, 0x47, 0x0a, 0x0f, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x52, 0x61, 0x6e, 0x64, + 0x50, 0x61, 0x69, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x75, 0x62, 0x5f, 0x72, 0x61, 0x6e, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x75, 0x62, 0x52, 0x61, 0x6e, 0x64, 0x12, + 0x19, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x5f, 0x72, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x73, 0x65, 0x63, 0x52, 0x61, 0x6e, 0x64, 0x22, 0x94, 0x01, 0x0a, 0x1e, 0x53, + 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, + 0x0b, 0x6d, 0x73, 0x67, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x73, 0x67, 0x54, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x19, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x61, 0x73, 0x73, + 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, + 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x64, 0x5f, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x64, 0x50, 0x61, 0x74, + 0x68, 0x22, 0x3f, 0x0a, 0x1f, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x1b, 0x45, 0x64, 0x69, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x74, 0x63, 0x5f, 0x70, 0x6b, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x62, 0x74, 0x63, 0x50, 0x6b, 0x12, 0x34, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x4d, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x2d, 0xda, 0xde, 0x1f, 0x1b, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, + 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, + 0x79, 0x44, 0x65, 0x63, 0xd2, 0xb4, 0x2d, 0x0a, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x44, + 0x65, 0x63, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x78, + 0x0a, 0x18, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x62, 0x74, + 0x63, 0x5f, 0x70, 0x6b, 0x5f, 0x68, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x62, 0x74, 0x63, 0x50, 0x6b, 0x48, 0x65, 0x78, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0xa4, 0x01, 0x0a, 0x16, 0x46, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, + 0x45, 0x44, 0x10, 0x00, 0x1a, 0x0e, 0x8a, 0x9d, 0x20, 0x0a, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, + 0x45, 0x52, 0x45, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, + 0x1a, 0x0a, 0x8a, 0x9d, 0x20, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x12, 0x1a, 0x0a, 0x08, + 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x1a, 0x0c, 0x8a, 0x9d, 0x20, 0x08, + 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x4c, 0x41, 0x53, + 0x48, 0x45, 0x44, 0x10, 0x03, 0x1a, 0x0b, 0x8a, 0x9d, 0x20, 0x07, 0x53, 0x4c, 0x41, 0x53, 0x48, + 0x45, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x4a, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x1a, 0x0a, + 0x8a, 0x9d, 0x20, 0x06, 0x4a, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x1a, 0x04, 0x88, 0xa3, 0x1e, 0x00, + 0x32, 0xf4, 0x05, 0x0a, 0x11, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x65, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, + 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, + 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x46, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, + 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x64, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x64, 0x64, 0x46, + 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x16, 0x55, 0x6e, 0x6a, 0x61, + 0x69, 0x6c, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x6e, 0x6a, 0x61, 0x69, + 0x6c, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x55, 0x6e, 0x6a, 0x61, 0x69, 0x6c, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x62, 0x0a, 0x15, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x19, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x6f, 0x0a, 0x21, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x12, - 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0xbc, 0x03, 0x0a, 0x10, 0x46, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x31, 0x0a, - 0x07, 0x66, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, - 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x66, 0x70, 0x41, 0x64, 0x64, 0x72, - 0x12, 0x15, 0x0a, 0x06, 0x62, 0x74, 0x63, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x05, 0x62, 0x74, 0x63, 0x50, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x23, 0xc8, - 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x1b, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, - 0x2e, 0x69, 0x6f, 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x44, - 0x65, 0x63, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, - 0x0a, 0x03, 0x70, 0x6f, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x4f, 0x66, 0x50, 0x6f, 0x73, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x70, 0x6f, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, - 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, - 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, - 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6c, 0x61, 0x73, - 0x74, 0x56, 0x6f, 0x74, 0x65, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x32, 0x0a, 0x15, - 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x6c, 0x61, 0x73, - 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x12, 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, - 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xc5, 0x02, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x61, - 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x31, 0x0a, 0x07, 0x66, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x66, 0x70, 0x41, - 0x64, 0x64, 0x72, 0x12, 0x1c, 0x0a, 0x0a, 0x62, 0x74, 0x63, 0x5f, 0x70, 0x6b, 0x5f, 0x68, 0x65, - 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x74, 0x63, 0x50, 0x6b, 0x48, 0x65, - 0x78, 0x12, 0x34, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x23, 0xc8, 0xde, 0x1f, - 0x00, 0xda, 0xde, 0x1f, 0x1b, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, - 0x6f, 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x44, 0x65, 0x63, - 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, - 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x56, 0x6f, 0x74, - 0x65, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x22, - 0xa2, 0x01, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x77, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, - 0x29, 0x0a, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x22, 0x2c, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x4f, 0x66, 0x50, - 0x6f, 0x73, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x62, 0x74, 0x63, - 0x5f, 0x73, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x74, 0x63, 0x53, - 0x69, 0x67, 0x22, 0x47, 0x0a, 0x0f, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x52, 0x61, 0x6e, - 0x64, 0x50, 0x61, 0x69, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x75, 0x62, 0x5f, 0x72, 0x61, 0x6e, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x75, 0x62, 0x52, 0x61, 0x6e, 0x64, - 0x12, 0x19, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x5f, 0x72, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x07, 0x73, 0x65, 0x63, 0x52, 0x61, 0x6e, 0x64, 0x22, 0x94, 0x01, 0x0a, 0x1e, - 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, - 0x0a, 0x0b, 0x6d, 0x73, 0x67, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x73, 0x67, 0x54, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x19, - 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x61, 0x73, - 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, - 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x64, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x64, 0x50, 0x61, - 0x74, 0x68, 0x22, 0x3f, 0x0a, 0x1f, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x2a, 0xbe, 0x01, 0x0a, 0x16, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, - 0x0a, 0x07, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x1a, 0x0b, 0x8a, 0x9d, 0x20, - 0x07, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x52, 0x45, 0x47, 0x49, - 0x53, 0x54, 0x45, 0x52, 0x45, 0x44, 0x10, 0x01, 0x1a, 0x0e, 0x8a, 0x9d, 0x20, 0x0a, 0x52, 0x45, - 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x45, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, - 0x56, 0x45, 0x10, 0x02, 0x1a, 0x0a, 0x8a, 0x9d, 0x20, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, - 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x03, 0x1a, 0x0c, - 0x8a, 0x9d, 0x20, 0x08, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x12, 0x18, 0x0a, 0x07, - 0x53, 0x4c, 0x41, 0x53, 0x48, 0x45, 0x44, 0x10, 0x04, 0x1a, 0x0b, 0x8a, 0x9d, 0x20, 0x07, 0x53, - 0x4c, 0x41, 0x53, 0x48, 0x45, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x4a, 0x41, 0x49, 0x4c, 0x45, 0x44, - 0x10, 0x05, 0x1a, 0x0a, 0x8a, 0x9d, 0x20, 0x06, 0x4a, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x1a, 0x04, - 0x88, 0xa3, 0x1e, 0x00, 0x32, 0xc0, 0x05, 0x0a, 0x11, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, - 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, - 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x24, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x18, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, + 0x12, 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x14, 0x45, 0x64, 0x69, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x46, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x12, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x64, 0x64, 0x46, 0x69, 0x6e, 0x61, - 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x64, 0x64, - 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x15, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, - 0x19, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, - 0x17, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x52, + 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x17, 0x55, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x12, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, + 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x62, 0x79, 0x6c, 0x6f, 0x6e, 0x6c, 0x61, 0x62, 0x73, 0x2d, 0x69, 0x6f, 0x2f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x70, 0x72, @@ -1539,17 +1697,17 @@ func file_finality_providers_proto_rawDescGZIP() []byte { } var file_finality_providers_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_finality_providers_proto_msgTypes = make([]protoimpl.MessageInfo, 19) +var file_finality_providers_proto_msgTypes = make([]protoimpl.MessageInfo, 22) var file_finality_providers_proto_goTypes = []interface{}{ (FinalityProviderStatus)(0), // 0: proto.FinalityProviderStatus (*GetInfoRequest)(nil), // 1: proto.GetInfoRequest (*GetInfoResponse)(nil), // 2: proto.GetInfoResponse (*CreateFinalityProviderRequest)(nil), // 3: proto.CreateFinalityProviderRequest (*CreateFinalityProviderResponse)(nil), // 4: proto.CreateFinalityProviderResponse - (*RegisterFinalityProviderRequest)(nil), // 5: proto.RegisterFinalityProviderRequest - (*RegisterFinalityProviderResponse)(nil), // 6: proto.RegisterFinalityProviderResponse - (*AddFinalitySignatureRequest)(nil), // 7: proto.AddFinalitySignatureRequest - (*AddFinalitySignatureResponse)(nil), // 8: proto.AddFinalitySignatureResponse + (*AddFinalitySignatureRequest)(nil), // 5: proto.AddFinalitySignatureRequest + (*AddFinalitySignatureResponse)(nil), // 6: proto.AddFinalitySignatureResponse + (*UnjailFinalityProviderRequest)(nil), // 7: proto.UnjailFinalityProviderRequest + (*UnjailFinalityProviderResponse)(nil), // 8: proto.UnjailFinalityProviderResponse (*QueryFinalityProviderRequest)(nil), // 9: proto.QueryFinalityProviderRequest (*QueryFinalityProviderResponse)(nil), // 10: proto.QueryFinalityProviderResponse (*QueryFinalityProviderListRequest)(nil), // 11: proto.QueryFinalityProviderListRequest @@ -1561,30 +1719,35 @@ var file_finality_providers_proto_goTypes = []interface{}{ (*SchnorrRandPair)(nil), // 17: proto.SchnorrRandPair (*SignMessageFromChainKeyRequest)(nil), // 18: proto.SignMessageFromChainKeyRequest (*SignMessageFromChainKeyResponse)(nil), // 19: proto.SignMessageFromChainKeyResponse + (*EditFinalityProviderRequest)(nil), // 20: proto.EditFinalityProviderRequest + (*RemoveMerkleProofRequest)(nil), // 21: proto.RemoveMerkleProofRequest + (*EmptyResponse)(nil), // 22: proto.EmptyResponse } var file_finality_providers_proto_depIdxs = []int32{ 14, // 0: proto.CreateFinalityProviderResponse.finality_provider:type_name -> proto.FinalityProviderInfo 14, // 1: proto.QueryFinalityProviderResponse.finality_provider:type_name -> proto.FinalityProviderInfo 14, // 2: proto.QueryFinalityProviderListResponse.finality_providers:type_name -> proto.FinalityProviderInfo - 16, // 3: proto.FinalityProvider.pop:type_name -> proto.ProofOfPossession - 0, // 4: proto.FinalityProvider.status:type_name -> proto.FinalityProviderStatus - 15, // 5: proto.FinalityProviderInfo.description:type_name -> proto.Description + 0, // 3: proto.FinalityProvider.status:type_name -> proto.FinalityProviderStatus + 15, // 4: proto.FinalityProviderInfo.description:type_name -> proto.Description + 15, // 5: proto.EditFinalityProviderRequest.description:type_name -> proto.Description 1, // 6: proto.FinalityProviders.GetInfo:input_type -> proto.GetInfoRequest 3, // 7: proto.FinalityProviders.CreateFinalityProvider:input_type -> proto.CreateFinalityProviderRequest - 5, // 8: proto.FinalityProviders.RegisterFinalityProvider:input_type -> proto.RegisterFinalityProviderRequest - 7, // 9: proto.FinalityProviders.AddFinalitySignature:input_type -> proto.AddFinalitySignatureRequest + 5, // 8: proto.FinalityProviders.AddFinalitySignature:input_type -> proto.AddFinalitySignatureRequest + 7, // 9: proto.FinalityProviders.UnjailFinalityProvider:input_type -> proto.UnjailFinalityProviderRequest 9, // 10: proto.FinalityProviders.QueryFinalityProvider:input_type -> proto.QueryFinalityProviderRequest 11, // 11: proto.FinalityProviders.QueryFinalityProviderList:input_type -> proto.QueryFinalityProviderListRequest - 18, // 12: proto.FinalityProviders.SignMessageFromChainKey:input_type -> proto.SignMessageFromChainKeyRequest - 2, // 13: proto.FinalityProviders.GetInfo:output_type -> proto.GetInfoResponse - 4, // 14: proto.FinalityProviders.CreateFinalityProvider:output_type -> proto.CreateFinalityProviderResponse - 6, // 15: proto.FinalityProviders.RegisterFinalityProvider:output_type -> proto.RegisterFinalityProviderResponse - 8, // 16: proto.FinalityProviders.AddFinalitySignature:output_type -> proto.AddFinalitySignatureResponse - 10, // 17: proto.FinalityProviders.QueryFinalityProvider:output_type -> proto.QueryFinalityProviderResponse - 12, // 18: proto.FinalityProviders.QueryFinalityProviderList:output_type -> proto.QueryFinalityProviderListResponse - 19, // 19: proto.FinalityProviders.SignMessageFromChainKey:output_type -> proto.SignMessageFromChainKeyResponse - 13, // [13:20] is the sub-list for method output_type - 6, // [6:13] is the sub-list for method input_type + 20, // 12: proto.FinalityProviders.EditFinalityProvider:input_type -> proto.EditFinalityProviderRequest + 21, // 13: proto.FinalityProviders.UnsafeRemoveMerkleProof:input_type -> proto.RemoveMerkleProofRequest + 2, // 14: proto.FinalityProviders.GetInfo:output_type -> proto.GetInfoResponse + 4, // 15: proto.FinalityProviders.CreateFinalityProvider:output_type -> proto.CreateFinalityProviderResponse + 6, // 16: proto.FinalityProviders.AddFinalitySignature:output_type -> proto.AddFinalitySignatureResponse + 8, // 17: proto.FinalityProviders.UnjailFinalityProvider:output_type -> proto.UnjailFinalityProviderResponse + 10, // 18: proto.FinalityProviders.QueryFinalityProvider:output_type -> proto.QueryFinalityProviderResponse + 12, // 19: proto.FinalityProviders.QueryFinalityProviderList:output_type -> proto.QueryFinalityProviderListResponse + 22, // 20: proto.FinalityProviders.EditFinalityProvider:output_type -> proto.EmptyResponse + 22, // 21: proto.FinalityProviders.UnsafeRemoveMerkleProof:output_type -> proto.EmptyResponse + 14, // [14:22] is the sub-list for method output_type + 6, // [6:14] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name 6, // [6:6] is the sub-list for extension extendee 0, // [0:6] is the sub-list for field type_name @@ -1645,7 +1808,7 @@ func file_finality_providers_proto_init() { } } file_finality_providers_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterFinalityProviderRequest); i { + switch v := v.(*AddFinalitySignatureRequest); i { case 0: return &v.state case 1: @@ -1657,7 +1820,7 @@ func file_finality_providers_proto_init() { } } file_finality_providers_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterFinalityProviderResponse); i { + switch v := v.(*AddFinalitySignatureResponse); i { case 0: return &v.state case 1: @@ -1669,7 +1832,7 @@ func file_finality_providers_proto_init() { } } file_finality_providers_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddFinalitySignatureRequest); i { + switch v := v.(*UnjailFinalityProviderRequest); i { case 0: return &v.state case 1: @@ -1681,7 +1844,7 @@ func file_finality_providers_proto_init() { } } file_finality_providers_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddFinalitySignatureResponse); i { + switch v := v.(*UnjailFinalityProviderResponse); i { case 0: return &v.state case 1: @@ -1824,6 +1987,42 @@ func file_finality_providers_proto_init() { return nil } } + file_finality_providers_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EditFinalityProviderRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_finality_providers_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveMerkleProofRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_finality_providers_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EmptyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1831,7 +2030,7 @@ func file_finality_providers_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_finality_providers_proto_rawDesc, NumEnums: 1, - NumMessages: 19, + NumMessages: 22, NumExtensions: 0, NumServices: 1, }, diff --git a/finality-provider/proto/finality_providers.proto b/finality-provider/proto/finality_providers.proto index 66083d1a..87e0704b 100644 --- a/finality-provider/proto/finality_providers.proto +++ b/finality-provider/proto/finality_providers.proto @@ -16,16 +16,16 @@ service FinalityProviders { rpc CreateFinalityProvider (CreateFinalityProviderRequest) returns (CreateFinalityProviderResponse); - // RegisterFinalityProvider sends a transactions to the consumer chain to register a BTC - // finality provider - rpc RegisterFinalityProvider (RegisterFinalityProviderRequest) - returns (RegisterFinalityProviderResponse); - // AddFinalitySignature sends a transactions to the consumer chain to add a Finality // signature for a block rpc AddFinalitySignature(AddFinalitySignatureRequest) returns (AddFinalitySignatureResponse); + // UnjailFinalityProvider sends a transactions to the consumer chain to unjail a given + // finality provider + rpc UnjailFinalityProvider(UnjailFinalityProviderRequest) + returns (UnjailFinalityProviderResponse); + // QueryFinalityProvider queries the finality provider rpc QueryFinalityProvider (QueryFinalityProviderRequest) returns (QueryFinalityProviderResponse); @@ -33,9 +33,11 @@ service FinalityProviders { rpc QueryFinalityProviderList (QueryFinalityProviderListRequest) returns (QueryFinalityProviderListResponse); - // SignMessageFromChainKey signs a message from the chain keyring. - rpc SignMessageFromChainKey (SignMessageFromChainKeyRequest) - returns (SignMessageFromChainKeyResponse); + // EditFinalityProvider edits finality provider + rpc EditFinalityProvider (EditFinalityProviderRequest) returns (EmptyResponse); + + // UnsafeRemoveMerkleProof removes merkle proofs up to target height + rpc UnsafeRemoveMerkleProof (RemoveMerkleProofRequest) returns (EmptyResponse); } message GetInfoRequest { @@ -50,37 +52,25 @@ message CreateFinalityProviderRequest { string key_name = 1; // passphrase is used to encrypt the keys string passphrase = 2; - // hd_path is the hd path for private key derivation - string hd_path = 3; // chain_id is the identifier of the consumer chain that the finality provider is connected to - string chain_id = 4; + string chain_id = 3; // description defines the description terms for the finality provider - bytes description = 5; + bytes description = 4; // commission defines the commission rate for the finality provider - string commission = 6 [ + string commission = 5 [ (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false ]; // eots_pk_hex it is the optional EOTS public key and used to ask for // the key record from the EOTS manager for the corresponding EOTS public key. // If this property is not set, it will create a new EOTS key. - string eots_pk_hex = 7; + string eots_pk_hex = 6; } message CreateFinalityProviderResponse { FinalityProviderInfo finality_provider = 1; -} - -message RegisterFinalityProviderRequest { - // btc_pk is hex string of the BTC secp256k1 public key of the finality provider encoded in BIP-340 spec - string btc_pk = 1; - // passphrase is used to encrypt the keys - string passphrase = 2; -} - -message RegisterFinalityProviderResponse { - // hash of the successful chain registration transaction - string tx_hash = 1; + // hash of the successful finality provider creation transaction + string tx_hash = 2; } message AddFinalitySignatureRequest { @@ -90,6 +80,8 @@ message AddFinalitySignatureRequest { uint64 height = 2; // app_hash is the AppHash of the chain block bytes app_hash = 3; + // check_double_sign use double sign protection when doing EOTS sign + bool check_double_sign = 4; } message AddFinalitySignatureResponse { @@ -101,6 +93,16 @@ message AddFinalitySignatureResponse { string local_sk_hex = 3; } +message UnjailFinalityProviderRequest { + // btc_pk is hex string of the BTC secp256k1 public key of the finality provider encoded in BIP-340 spec + string btc_pk = 1; +} + +message UnjailFinalityProviderResponse { + // hash of the successful chain unjail finality provider transaction + string tx_hash = 1; +} + message QueryFinalityProviderRequest { // btc_pk is hex string of the BTC secp256k1 public key of the finality provider encoded in BIP-340 spec string btc_pk = 1; @@ -111,12 +113,10 @@ message QueryFinalityProviderResponse { } message QueryFinalityProviderListRequest { - // TODO add pagination in case the list gets large } message QueryFinalityProviderListResponse { repeated FinalityProviderInfo finality_providers = 1; - // TODO add pagination in case the list gets large } // FinalityProvider defines current state of finality provider. @@ -132,19 +132,12 @@ message FinalityProvider { (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false ]; - // pop is the proof of possession of chain_pk and btc_pk - ProofOfPossession pop = 5; - // key_name is the identifier of the keyring - string key_name = 6; // chain_id is the identifier of the consumer chain that the finality provider connected to - string chain_id = 7; + string chain_id = 5; // last_voted_height defines the height of the last voted chain block - uint64 last_voted_height = 8; - // last_processed_height defines the height of the last successfully processed block - // even though the vote is not cast - uint64 last_processed_height = 9; + uint64 last_voted_height = 6; // status defines the current finality provider status - FinalityProviderStatus status = 10; + FinalityProviderStatus status = 7; } // FinalityProviderInfo is the basic information of a finality provider mainly for external usage @@ -192,35 +185,36 @@ message SchnorrRandPair { } // FinalityProviderStatus is the status of a finality provider -// a FinalityProvider object has 4 states: -// - Created - created and managed by finality provider client, not registered to -// the consumer chain yet +// a FinalityProvider object has 5 states: // - Registered - created and registered to the consumer chain, but not voting yet (No // delegated stake) -// - Active - created and registered to the consumer chain with stake to vote -// - Inactive - created and registered to the consumer chain with no stake to vote. +// - Inactive - the finality provider does not have voting power to vote +// - Active - the finality provider has voting power to vote // Finality Provider was already active. -// Valid State Transactions: -// - Created -> Registered -// - Registered -> Active -// - Active -> Inactive -// - Inactive -> Active +// - Slashed - the finality provider looses all the voting power forever due to double voting +// - Jailed - the finality provider temporarily looses the voting power due to being sluggish +// Possible State Transactions: +// - Registered -> Inactive +// - Inactive -> Active +// - Active -> Inactive +// - Active -> Slashed +// - Active -> Jailed +// - Jailed -> Active +// - Jailed -> Inactive enum FinalityProviderStatus { option (gogoproto.goproto_enum_prefix) = false; - // CREATED defines a finality provider that is awaiting registration - CREATED = 0 [(gogoproto.enumvalue_customname) = "CREATED"]; // REGISTERED defines a finality provider that has been registered // to the consumer chain but has no delegated stake - REGISTERED = 1 [(gogoproto.enumvalue_customname) = "REGISTERED"]; + REGISTERED = 0 [(gogoproto.enumvalue_customname) = "REGISTERED"]; // ACTIVE defines a finality provider that is delegated to vote - ACTIVE = 2 [(gogoproto.enumvalue_customname) = "ACTIVE"]; + ACTIVE = 1 [(gogoproto.enumvalue_customname) = "ACTIVE"]; // INACTIVE defines a finality provider whose delegations are reduced to zero but not slashed - INACTIVE = 3 [(gogoproto.enumvalue_customname) = "INACTIVE"]; + INACTIVE = 2 [(gogoproto.enumvalue_customname) = "INACTIVE"]; // SLASHED defines a finality provider that has been slashed - SLASHED = 4 [(gogoproto.enumvalue_customname) = "SLASHED"]; + SLASHED = 3 [(gogoproto.enumvalue_customname) = "SLASHED"]; // JAILED defines a finality provider that has been jailed - JAILED = 5 [(gogoproto.enumvalue_customname) = "JAILED"]; + JAILED = 4 [(gogoproto.enumvalue_customname) = "JAILED"]; } message SignMessageFromChainKeyRequest { @@ -238,3 +232,28 @@ message SignMessageFromChainKeyRequest { message SignMessageFromChainKeyResponse { bytes signature = 1; } + +// FinalityProviderInfo is the basic information of a finality provider mainly for external usage +message EditFinalityProviderRequest { + // btc_pk is the hex string of the BTC secp256k1 PK of the finality provider encoded in BIP-340 spec + string btc_pk = 1; + // description defines the description terms for the finality provider + Description description = 2; + // commission defines the updated commission rate of the finality provider + string commission = 4 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec" + ]; +} + +message RemoveMerkleProofRequest { + // btc_pk_hex is the hex string of the BTC secp256k1 PK of the finality provider encoded in BIP-340 spec + string btc_pk_hex = 1; + // chain_id is the identifier of the consumer chain + string chain_id = 2; + // target_height to to delete all proofs + uint64 target_height = 3; +} + +// Define an empty response message +message EmptyResponse {} diff --git a/finality-provider/proto/finality_providers_grpc.pb.go b/finality-provider/proto/finality_providers_grpc.pb.go index e2d7bdca..c8726775 100644 --- a/finality-provider/proto/finality_providers_grpc.pb.go +++ b/finality-provider/proto/finality_providers_grpc.pb.go @@ -21,11 +21,12 @@ const _ = grpc.SupportPackageIsVersion7 const ( FinalityProviders_GetInfo_FullMethodName = "/proto.FinalityProviders/GetInfo" FinalityProviders_CreateFinalityProvider_FullMethodName = "/proto.FinalityProviders/CreateFinalityProvider" - FinalityProviders_RegisterFinalityProvider_FullMethodName = "/proto.FinalityProviders/RegisterFinalityProvider" FinalityProviders_AddFinalitySignature_FullMethodName = "/proto.FinalityProviders/AddFinalitySignature" + FinalityProviders_UnjailFinalityProvider_FullMethodName = "/proto.FinalityProviders/UnjailFinalityProvider" FinalityProviders_QueryFinalityProvider_FullMethodName = "/proto.FinalityProviders/QueryFinalityProvider" FinalityProviders_QueryFinalityProviderList_FullMethodName = "/proto.FinalityProviders/QueryFinalityProviderList" - FinalityProviders_SignMessageFromChainKey_FullMethodName = "/proto.FinalityProviders/SignMessageFromChainKey" + FinalityProviders_EditFinalityProvider_FullMethodName = "/proto.FinalityProviders/EditFinalityProvider" + FinalityProviders_UnsafeRemoveMerkleProof_FullMethodName = "/proto.FinalityProviders/UnsafeRemoveMerkleProof" ) // FinalityProvidersClient is the client API for FinalityProviders service. @@ -36,18 +37,20 @@ type FinalityProvidersClient interface { GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) // CreateFinalityProvider generates and saves a finality provider object CreateFinalityProvider(ctx context.Context, in *CreateFinalityProviderRequest, opts ...grpc.CallOption) (*CreateFinalityProviderResponse, error) - // RegisterFinalityProvider sends a transactions to the consumer chain to register a BTC - // finality provider - RegisterFinalityProvider(ctx context.Context, in *RegisterFinalityProviderRequest, opts ...grpc.CallOption) (*RegisterFinalityProviderResponse, error) // AddFinalitySignature sends a transactions to the consumer chain to add a Finality // signature for a block AddFinalitySignature(ctx context.Context, in *AddFinalitySignatureRequest, opts ...grpc.CallOption) (*AddFinalitySignatureResponse, error) + // UnjailFinalityProvider sends a transactions to the consumer chain to unjail a given + // finality provider + UnjailFinalityProvider(ctx context.Context, in *UnjailFinalityProviderRequest, opts ...grpc.CallOption) (*UnjailFinalityProviderResponse, error) // QueryFinalityProvider queries the finality provider QueryFinalityProvider(ctx context.Context, in *QueryFinalityProviderRequest, opts ...grpc.CallOption) (*QueryFinalityProviderResponse, error) // QueryFinalityProviderList queries a list of finality providers QueryFinalityProviderList(ctx context.Context, in *QueryFinalityProviderListRequest, opts ...grpc.CallOption) (*QueryFinalityProviderListResponse, error) - // SignMessageFromChainKey signs a message from the chain keyring. - SignMessageFromChainKey(ctx context.Context, in *SignMessageFromChainKeyRequest, opts ...grpc.CallOption) (*SignMessageFromChainKeyResponse, error) + // EditFinalityProvider edits finality provider + EditFinalityProvider(ctx context.Context, in *EditFinalityProviderRequest, opts ...grpc.CallOption) (*EmptyResponse, error) + // UnsafeRemoveMerkleProof removes merkle proofs up to target height + UnsafeRemoveMerkleProof(ctx context.Context, in *RemoveMerkleProofRequest, opts ...grpc.CallOption) (*EmptyResponse, error) } type finalityProvidersClient struct { @@ -76,18 +79,18 @@ func (c *finalityProvidersClient) CreateFinalityProvider(ctx context.Context, in return out, nil } -func (c *finalityProvidersClient) RegisterFinalityProvider(ctx context.Context, in *RegisterFinalityProviderRequest, opts ...grpc.CallOption) (*RegisterFinalityProviderResponse, error) { - out := new(RegisterFinalityProviderResponse) - err := c.cc.Invoke(ctx, FinalityProviders_RegisterFinalityProvider_FullMethodName, in, out, opts...) +func (c *finalityProvidersClient) AddFinalitySignature(ctx context.Context, in *AddFinalitySignatureRequest, opts ...grpc.CallOption) (*AddFinalitySignatureResponse, error) { + out := new(AddFinalitySignatureResponse) + err := c.cc.Invoke(ctx, FinalityProviders_AddFinalitySignature_FullMethodName, in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *finalityProvidersClient) AddFinalitySignature(ctx context.Context, in *AddFinalitySignatureRequest, opts ...grpc.CallOption) (*AddFinalitySignatureResponse, error) { - out := new(AddFinalitySignatureResponse) - err := c.cc.Invoke(ctx, FinalityProviders_AddFinalitySignature_FullMethodName, in, out, opts...) +func (c *finalityProvidersClient) UnjailFinalityProvider(ctx context.Context, in *UnjailFinalityProviderRequest, opts ...grpc.CallOption) (*UnjailFinalityProviderResponse, error) { + out := new(UnjailFinalityProviderResponse) + err := c.cc.Invoke(ctx, FinalityProviders_UnjailFinalityProvider_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -112,9 +115,18 @@ func (c *finalityProvidersClient) QueryFinalityProviderList(ctx context.Context, return out, nil } -func (c *finalityProvidersClient) SignMessageFromChainKey(ctx context.Context, in *SignMessageFromChainKeyRequest, opts ...grpc.CallOption) (*SignMessageFromChainKeyResponse, error) { - out := new(SignMessageFromChainKeyResponse) - err := c.cc.Invoke(ctx, FinalityProviders_SignMessageFromChainKey_FullMethodName, in, out, opts...) +func (c *finalityProvidersClient) EditFinalityProvider(ctx context.Context, in *EditFinalityProviderRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, FinalityProviders_EditFinalityProvider_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *finalityProvidersClient) UnsafeRemoveMerkleProof(ctx context.Context, in *RemoveMerkleProofRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, FinalityProviders_UnsafeRemoveMerkleProof_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -129,18 +141,20 @@ type FinalityProvidersServer interface { GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) // CreateFinalityProvider generates and saves a finality provider object CreateFinalityProvider(context.Context, *CreateFinalityProviderRequest) (*CreateFinalityProviderResponse, error) - // RegisterFinalityProvider sends a transactions to the consumer chain to register a BTC - // finality provider - RegisterFinalityProvider(context.Context, *RegisterFinalityProviderRequest) (*RegisterFinalityProviderResponse, error) // AddFinalitySignature sends a transactions to the consumer chain to add a Finality // signature for a block AddFinalitySignature(context.Context, *AddFinalitySignatureRequest) (*AddFinalitySignatureResponse, error) + // UnjailFinalityProvider sends a transactions to the consumer chain to unjail a given + // finality provider + UnjailFinalityProvider(context.Context, *UnjailFinalityProviderRequest) (*UnjailFinalityProviderResponse, error) // QueryFinalityProvider queries the finality provider QueryFinalityProvider(context.Context, *QueryFinalityProviderRequest) (*QueryFinalityProviderResponse, error) // QueryFinalityProviderList queries a list of finality providers QueryFinalityProviderList(context.Context, *QueryFinalityProviderListRequest) (*QueryFinalityProviderListResponse, error) - // SignMessageFromChainKey signs a message from the chain keyring. - SignMessageFromChainKey(context.Context, *SignMessageFromChainKeyRequest) (*SignMessageFromChainKeyResponse, error) + // EditFinalityProvider edits finality provider + EditFinalityProvider(context.Context, *EditFinalityProviderRequest) (*EmptyResponse, error) + // UnsafeRemoveMerkleProof removes merkle proofs up to target height + UnsafeRemoveMerkleProof(context.Context, *RemoveMerkleProofRequest) (*EmptyResponse, error) mustEmbedUnimplementedFinalityProvidersServer() } @@ -154,20 +168,23 @@ func (UnimplementedFinalityProvidersServer) GetInfo(context.Context, *GetInfoReq func (UnimplementedFinalityProvidersServer) CreateFinalityProvider(context.Context, *CreateFinalityProviderRequest) (*CreateFinalityProviderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateFinalityProvider not implemented") } -func (UnimplementedFinalityProvidersServer) RegisterFinalityProvider(context.Context, *RegisterFinalityProviderRequest) (*RegisterFinalityProviderResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RegisterFinalityProvider not implemented") -} func (UnimplementedFinalityProvidersServer) AddFinalitySignature(context.Context, *AddFinalitySignatureRequest) (*AddFinalitySignatureResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddFinalitySignature not implemented") } +func (UnimplementedFinalityProvidersServer) UnjailFinalityProvider(context.Context, *UnjailFinalityProviderRequest) (*UnjailFinalityProviderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnjailFinalityProvider not implemented") +} func (UnimplementedFinalityProvidersServer) QueryFinalityProvider(context.Context, *QueryFinalityProviderRequest) (*QueryFinalityProviderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryFinalityProvider not implemented") } func (UnimplementedFinalityProvidersServer) QueryFinalityProviderList(context.Context, *QueryFinalityProviderListRequest) (*QueryFinalityProviderListResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryFinalityProviderList not implemented") } -func (UnimplementedFinalityProvidersServer) SignMessageFromChainKey(context.Context, *SignMessageFromChainKeyRequest) (*SignMessageFromChainKeyResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SignMessageFromChainKey not implemented") +func (UnimplementedFinalityProvidersServer) EditFinalityProvider(context.Context, *EditFinalityProviderRequest) (*EmptyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EditFinalityProvider not implemented") +} +func (UnimplementedFinalityProvidersServer) UnsafeRemoveMerkleProof(context.Context, *RemoveMerkleProofRequest) (*EmptyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnsafeRemoveMerkleProof not implemented") } func (UnimplementedFinalityProvidersServer) mustEmbedUnimplementedFinalityProvidersServer() {} @@ -218,38 +235,38 @@ func _FinalityProviders_CreateFinalityProvider_Handler(srv interface{}, ctx cont return interceptor(ctx, in, info, handler) } -func _FinalityProviders_RegisterFinalityProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RegisterFinalityProviderRequest) +func _FinalityProviders_AddFinalitySignature_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddFinalitySignatureRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(FinalityProvidersServer).RegisterFinalityProvider(ctx, in) + return srv.(FinalityProvidersServer).AddFinalitySignature(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: FinalityProviders_RegisterFinalityProvider_FullMethodName, + FullMethod: FinalityProviders_AddFinalitySignature_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FinalityProvidersServer).RegisterFinalityProvider(ctx, req.(*RegisterFinalityProviderRequest)) + return srv.(FinalityProvidersServer).AddFinalitySignature(ctx, req.(*AddFinalitySignatureRequest)) } return interceptor(ctx, in, info, handler) } -func _FinalityProviders_AddFinalitySignature_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AddFinalitySignatureRequest) +func _FinalityProviders_UnjailFinalityProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UnjailFinalityProviderRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(FinalityProvidersServer).AddFinalitySignature(ctx, in) + return srv.(FinalityProvidersServer).UnjailFinalityProvider(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: FinalityProviders_AddFinalitySignature_FullMethodName, + FullMethod: FinalityProviders_UnjailFinalityProvider_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FinalityProvidersServer).AddFinalitySignature(ctx, req.(*AddFinalitySignatureRequest)) + return srv.(FinalityProvidersServer).UnjailFinalityProvider(ctx, req.(*UnjailFinalityProviderRequest)) } return interceptor(ctx, in, info, handler) } @@ -290,20 +307,38 @@ func _FinalityProviders_QueryFinalityProviderList_Handler(srv interface{}, ctx c return interceptor(ctx, in, info, handler) } -func _FinalityProviders_SignMessageFromChainKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SignMessageFromChainKeyRequest) +func _FinalityProviders_EditFinalityProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EditFinalityProviderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FinalityProvidersServer).EditFinalityProvider(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FinalityProviders_EditFinalityProvider_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FinalityProvidersServer).EditFinalityProvider(ctx, req.(*EditFinalityProviderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FinalityProviders_UnsafeRemoveMerkleProof_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveMerkleProofRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(FinalityProvidersServer).SignMessageFromChainKey(ctx, in) + return srv.(FinalityProvidersServer).UnsafeRemoveMerkleProof(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: FinalityProviders_SignMessageFromChainKey_FullMethodName, + FullMethod: FinalityProviders_UnsafeRemoveMerkleProof_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FinalityProvidersServer).SignMessageFromChainKey(ctx, req.(*SignMessageFromChainKeyRequest)) + return srv.(FinalityProvidersServer).UnsafeRemoveMerkleProof(ctx, req.(*RemoveMerkleProofRequest)) } return interceptor(ctx, in, info, handler) } @@ -323,14 +358,14 @@ var FinalityProviders_ServiceDesc = grpc.ServiceDesc{ MethodName: "CreateFinalityProvider", Handler: _FinalityProviders_CreateFinalityProvider_Handler, }, - { - MethodName: "RegisterFinalityProvider", - Handler: _FinalityProviders_RegisterFinalityProvider_Handler, - }, { MethodName: "AddFinalitySignature", Handler: _FinalityProviders_AddFinalitySignature_Handler, }, + { + MethodName: "UnjailFinalityProvider", + Handler: _FinalityProviders_UnjailFinalityProvider_Handler, + }, { MethodName: "QueryFinalityProvider", Handler: _FinalityProviders_QueryFinalityProvider_Handler, @@ -340,8 +375,12 @@ var FinalityProviders_ServiceDesc = grpc.ServiceDesc{ Handler: _FinalityProviders_QueryFinalityProviderList_Handler, }, { - MethodName: "SignMessageFromChainKey", - Handler: _FinalityProviders_SignMessageFromChainKey_Handler, + MethodName: "EditFinalityProvider", + Handler: _FinalityProviders_EditFinalityProvider_Handler, + }, + { + MethodName: "UnsafeRemoveMerkleProof", + Handler: _FinalityProviders_UnsafeRemoveMerkleProof_Handler, }, }, Streams: []grpc.StreamDesc{}, diff --git a/finality-provider/service/app.go b/finality-provider/service/app.go index df8b5204..359b7979 100644 --- a/finality-provider/service/app.go +++ b/finality-provider/service/app.go @@ -1,17 +1,17 @@ package service import ( + "errors" "fmt" "strings" "sync" - "time" sdkmath "cosmossdk.io/math" bbntypes "github.com/babylonlabs-io/babylon/types" bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/btcsuite/btcd/btcec/v2" + "github.com/cometbft/cometbft/crypto/tmhash" "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/lightningnetwork/lnd/kvdb" @@ -26,15 +26,13 @@ import ( "github.com/babylonlabs-io/finality-provider/finality-provider/store" fpkr "github.com/babylonlabs-io/finality-provider/keyring" "github.com/babylonlabs-io/finality-provider/metrics" - "github.com/babylonlabs-io/finality-provider/types" ) type FinalityProviderApp struct { startOnce sync.Once stopOnce sync.Once - - wg sync.WaitGroup - quit chan struct{} + wg sync.WaitGroup + quit chan struct{} cc ccapi.ClientController consumerCon ccapi.ConsumerController @@ -45,29 +43,36 @@ type FinalityProviderApp struct { logger *zap.Logger input *strings.Reader - fpManager *FinalityProviderManager + fpInsMu sync.RWMutex // Protects fpIns + fpIns *FinalityProviderInstance eotsManager eotsmanager.EOTSManager metrics *metrics.FpMetrics - createFinalityProviderRequestChan chan *createFinalityProviderRequest - registerFinalityProviderRequestChan chan *registerFinalityProviderRequest - finalityProviderRegisteredEventChan chan *finalityProviderRegisteredEvent + createFinalityProviderRequestChan chan *CreateFinalityProviderRequest + unjailFinalityProviderRequestChan chan *UnjailFinalityProviderRequest + criticalErrChan chan *CriticalError } +// NewFinalityProviderAppFromConfig creates a new FinalityProviderApp instance from the given configuration. func NewFinalityProviderAppFromConfig( cfg *fpcfg.Config, db kvdb.Backend, logger *zap.Logger, ) (*FinalityProviderApp, error) { - cc, err := fpcc.NewClientController(cfg, logger) + cc, err := fpcc.NewBabylonController(cfg, logger) if err != nil { - return nil, fmt.Errorf("failed to create rpc client for the Babylon chain: %v", err) + return nil, fmt.Errorf("failed to create rpc client for the Babylon chain: %w", err) + } + if err := cc.Start(); err != nil { + return nil, fmt.Errorf("failed to start rpc client for the Babylon chain: %w", err) } + consumerCon, err := fpcc.NewConsumerController(cfg, logger) if err != nil { - return nil, fmt.Errorf("failed to create rpc client for the consumer chain %s: %v", cfg.ChainType, err) + return nil, fmt.Errorf("failed to create rpc client for the consumer chain %s: %w", cfg.ChainType, err) } + // if the EOTSManagerAddress is empty, run a local EOTS manager; // otherwise connect a remote one with a gRPC client em, err := client.NewEOTSManagerGRpcClient(cfg.EOTSManagerAddress) @@ -82,7 +87,7 @@ func NewFinalityProviderAppFromConfig( func NewFinalityProviderApp( config *fpcfg.Config, - cc ccapi.ClientController, // TODO: this should be renamed as client controller is always going to be babylon + cc ccapi.ClientController, consumerCon ccapi.ConsumerController, em eotsmanager.EOTSManager, db kvdb.Backend, @@ -110,27 +115,21 @@ func NewFinalityProviderApp( fpMetrics := metrics.NewFpMetrics() - fpm, err := NewFinalityProviderManager(fpStore, pubRandStore, config, cc, consumerCon, em, fpMetrics, logger) - if err != nil { - return nil, fmt.Errorf("failed to create finality-provider manager: %w", err) - } - return &FinalityProviderApp{ - cc: cc, - consumerCon: consumerCon, - fps: fpStore, - pubRandStore: pubRandStore, - kr: kr, - config: config, - logger: logger, - input: input, - fpManager: fpm, - eotsManager: em, - metrics: fpMetrics, - quit: make(chan struct{}), - createFinalityProviderRequestChan: make(chan *createFinalityProviderRequest), - registerFinalityProviderRequestChan: make(chan *registerFinalityProviderRequest), - finalityProviderRegisteredEventChan: make(chan *finalityProviderRegisteredEvent), + cc: cc, + consumerCon: consumerCon, + fps: fpStore, + pubRandStore: pubRandStore, + kr: kr, + config: config, + logger: logger, + input: input, + eotsManager: em, + metrics: fpMetrics, + quit: make(chan struct{}), + unjailFinalityProviderRequestChan: make(chan *UnjailFinalityProviderRequest), + createFinalityProviderRequestChan: make(chan *CreateFinalityProviderRequest), + criticalErrChan: make(chan *CriticalError), }, nil } @@ -154,166 +153,144 @@ func (app *FinalityProviderApp) GetPubRandProofStore() *store.PubRandProofStore return app.pubRandStore } -func (app *FinalityProviderApp) GetKeyring() keyring.Keyring { - return app.kr -} - -func (app *FinalityProviderApp) GetInput() *strings.Reader { - return app.input -} - -// Logger returns the current logger of FP app. -func (app *FinalityProviderApp) Logger() *zap.Logger { - return app.logger -} - -func (app *FinalityProviderApp) ListFinalityProviderInstances() []*FinalityProviderInstance { - return app.fpManager.ListFinalityProviderInstances() -} - -func (app *FinalityProviderApp) ListFinalityProviderInstancesForChain(chainID string) []*FinalityProviderInstance { - return app.fpManager.ListFinalityProviderInstancesForChain(chainID) -} - -func (app *FinalityProviderApp) ListAllFinalityProvidersInfo() ([]*proto.FinalityProviderInfo, error) { - return app.fpManager.AllFinalityProviders() -} - func (app *FinalityProviderApp) GetFinalityProviderInfo(fpPk *bbntypes.BIP340PubKey) (*proto.FinalityProviderInfo, error) { - return app.fpManager.FinalityProviderInfo(fpPk) -} - -// GetFinalityProviderInstance returns the finality-provider instance with the given Babylon public key -func (app *FinalityProviderApp) GetFinalityProviderInstance(fpPk *bbntypes.BIP340PubKey) (*FinalityProviderInstance, error) { - return app.fpManager.GetFinalityProviderInstance(fpPk) -} - -func (app *FinalityProviderApp) RegisterFinalityProvider(fpPkStr string) (*RegisterFinalityProviderResponse, error) { - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(fpPkStr) + storedFp, err := app.fps.GetFinalityProvider(fpPk.MustToBTCPK()) if err != nil { return nil, err } - fp, err := app.fps.GetFinalityProvider(fpPk.MustToBTCPK()) - if err != nil { - return nil, err - } + fpInfo := storedFp.ToFinalityProviderInfo() - if fp.Status != proto.FinalityProviderStatus_CREATED { - return nil, fmt.Errorf("finality-provider is already registered") + if app.IsFinalityProviderRunning(fpPk) { + fpInfo.IsRunning = true } - btcSig, err := bbntypes.NewBIP340Signature(fp.Pop.BtcSig) + return fpInfo, nil +} + +func (app *FinalityProviderApp) ListAllFinalityProvidersInfo() ([]*proto.FinalityProviderInfo, error) { + storedFps, err := app.fps.GetAllStoredFinalityProviders() if err != nil { return nil, err } - pop := &bstypes.ProofOfPossessionBTC{ - BtcSig: btcSig.MustMarshal(), - BtcSigType: bstypes.BTCSigType_BIP340, - } + fpsInfo := make([]*proto.FinalityProviderInfo, 0, len(storedFps)) + for _, fp := range storedFps { + fpInfo := fp.ToFinalityProviderInfo() - fpAddr, err := sdk.AccAddressFromBech32(fp.FPAddr) - if err != nil { - return nil, err - } + if app.IsFinalityProviderRunning(fp.GetBIP340BTCPK()) { + fpInfo.IsRunning = true + } - request := ®isterFinalityProviderRequest{ - chainID: fp.ChainID, - fpAddr: fpAddr, - btcPubKey: bbntypes.NewBIP340PubKeyFromBTCPK(fp.BtcPk), - pop: pop, - description: fp.Description, - commission: fp.Commission, - errResponse: make(chan error, 1), - successResponse: make(chan *RegisterFinalityProviderResponse, 1), + fpsInfo = append(fpsInfo, fpInfo) } - app.registerFinalityProviderRequestChan <- request + return fpsInfo, nil +} - select { - case err := <-request.errResponse: - return nil, err - case successResponse := <-request.successResponse: - return successResponse, nil - case <-app.quit: - return nil, fmt.Errorf("finality-provider app is shutting down") +// GetFinalityProviderInstance returns the finality-provider instance with the given Babylon public key +func (app *FinalityProviderApp) GetFinalityProviderInstance() (*FinalityProviderInstance, error) { + app.fpInsMu.RLock() + defer app.fpInsMu.RUnlock() + + if app.fpIns == nil { + return nil, fmt.Errorf("finality provider does not exist") } -} -// StartHandlingFinalityProvider starts a finality-provider instance with the given Babylon public key -// Note: this should be called right after the finality-provider is registered -func (app *FinalityProviderApp) StartHandlingFinalityProvider(fpPk *bbntypes.BIP340PubKey, passphrase string) error { - return app.fpManager.StartFinalityProvider(fpPk, passphrase) + return app.fpIns, nil } -func (app *FinalityProviderApp) StartHandlingAll() error { - return app.fpManager.StartAll() +func (app *FinalityProviderApp) Logger() *zap.Logger { + return app.logger } -// NOTE: this is not safe in production, so only used for testing purpose -func (app *FinalityProviderApp) getFpPrivKey(fpPk []byte) (*btcec.PrivateKey, error) { - record, err := app.eotsManager.KeyRecord(fpPk, "") - if err != nil { - return nil, err +// StartFinalityProvider starts a finality provider instance with the given EOTS public key +// Note: this should be called right after the finality-provider is registered +func (app *FinalityProviderApp) StartFinalityProvider(fpPk *bbntypes.BIP340PubKey, passphrase string) error { + app.logger.Info("starting finality provider", zap.String("pk", fpPk.MarshalHex())) + + if err := app.startFinalityProviderInstance(fpPk, passphrase); err != nil { + return err } - return record.PrivKey, nil -} + app.logger.Info("finality provider is started", zap.String("pk", fpPk.MarshalHex())) -// SyncFinalityProviderStatus syncs the status of the finality-providers with the chain. -func (app *FinalityProviderApp) SyncFinalityProviderStatus() (fpInstanceRunning bool, err error) { - latestBlockHeight, err := app.consumerCon.QueryLatestBlockHeight() - if err != nil { - return false, err - } + return nil +} +// SyncAllFinalityProvidersStatus syncs the status of all the stored finality providers with the chain. +// it should be called before a fp instance is started +func (app *FinalityProviderApp) SyncAllFinalityProvidersStatus() error { fps, err := app.fps.GetAllStoredFinalityProviders() if err != nil { - return false, err + return err } for _, fp := range fps { - hasPower, err := app.consumerCon.QueryFinalityProviderHasPower(fp.BtcPk, latestBlockHeight) + latestBlockHeight, err := app.consumerCon.QueryLatestBlockHeight() if err != nil { - continue + return err } - bip340PubKey := fp.GetBIP340BTCPK() - if app.fpManager.IsFinalityProviderRunning(bip340PubKey) { - // there is a instance running, no need to keep syncing - fpInstanceRunning = true - // if it is already running, no need to update status - continue + pkHex := fp.GetBIP340BTCPK().MarshalHex() + hasPower, err := app.consumerCon.QueryFinalityProviderHasPower(fp.BtcPk, latestBlockHeight) + if err != nil { + return fmt.Errorf("failed to query voting power for finality provider %s at height %d: %w", + fp.GetBIP340BTCPK().MarshalHex(), latestBlockHeight, err) } + // power > 0 (slashed_height must > 0), set status to ACTIVE oldStatus := fp.Status - newStatus, err := app.fps.UpdateFpStatusFromVotingPower(hasPower, fp) + if hasPower { + if oldStatus != proto.FinalityProviderStatus_ACTIVE { + fp.Status = proto.FinalityProviderStatus_ACTIVE + app.fps.MustSetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_ACTIVE) + app.logger.Debug( + "the finality-provider status is changed to ACTIVE", + zap.String("fp_btc_pk", pkHex), + zap.String("old_status", oldStatus.String()), + ) + } + + continue + } + slashed, jailed, err := app.consumerCon.QueryFinalityProviderSlashedOrJailed(fp.BtcPk) if err != nil { - return false, err + return err } + if slashed { + app.fps.MustSetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_SLASHED) - if oldStatus != newStatus { - app.logger.Info( - "Update FP status", - zap.String("fp_addr", fp.FPAddr), + app.logger.Debug( + "the finality-provider status is changed to SLAHED", + zap.String("fp_btc_pk", pkHex), zap.String("old_status", oldStatus.String()), - zap.String("new_status", newStatus.String()), ) - fp.Status = newStatus - } - if !fp.ShouldStart() { continue } + if jailed { + app.fps.MustSetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_JAILED) + + app.logger.Debug( + "the finality-provider status is changed to JAILED", + zap.String("fp_btc_pk", pkHex), + zap.String("old_status", oldStatus.String()), + ) - if err := app.fpManager.StartFinalityProvider(bip340PubKey, ""); err != nil { - return false, err + continue } - fpInstanceRunning = true + + app.fps.MustSetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_INACTIVE) + + app.logger.Debug( + "the finality-provider status is changed to INACTIVE", + zap.String("fp_btc_pk", pkHex), + zap.String("old_status", oldStatus.String()), + ) } - return fpInstanceRunning, nil + return nil } // Start starts only the finality-provider daemon without any finality-provider instances @@ -322,29 +299,16 @@ func (app *FinalityProviderApp) Start() error { app.startOnce.Do(func() { app.logger.Info("Starting FinalityProviderApp") + startErr = app.SyncAllFinalityProvidersStatus() + if startErr != nil { + return + } + app.wg.Add(4) - go app.syncChainFpStatusLoop() - go app.eventLoop() - go app.registrationLoop() go app.metricsUpdateLoop() - }) - - return startErr -} - -// StartWithoutSyncFpStatus starts only the finality-provider daemon without any finality-provider instances -// and without syncing the finality-provider status loop for testing purpose -// b/c this loop detects FP status and then automatically starts the FP instance when it is ACTIVE -// Note: this is only for testing purposes -func (app *FinalityProviderApp) StartWithoutSyncFpStatus() error { - var startErr error - app.startOnce.Do(func() { - app.logger.Info("Starting FinalityProviderApp") - - app.wg.Add(3) - go app.eventLoop() + go app.monitorCriticalErr() go app.registrationLoop() - go app.metricsUpdateLoop() + go app.unjailFpLoop() }) return startErr @@ -355,394 +319,333 @@ func (app *FinalityProviderApp) Stop() error { app.stopOnce.Do(func() { app.logger.Info("Stopping FinalityProviderApp") - // Always stop the submission loop first to not generate additional events and actions - app.logger.Debug("Stopping submission loop") close(app.quit) app.wg.Wait() - app.logger.Debug("Stopping finality providers") - if app.fpManager.isStarted.Swap(true) { - if err := app.fpManager.Stop(); err != nil { - stopErr = err + if app.fpIns != nil && app.fpIns.IsRunning() { + pkHex := app.fpIns.GetBtcPkHex() + app.logger.Info("stopping finality provider", zap.String("pk", pkHex)) + + if err := app.fpIns.Stop(); err != nil { + stopErr = fmt.Errorf("failed to close the fp instance: %w", err) + return } + + app.logger.Info("finality provider is stopped", zap.String("pk", pkHex)) } app.logger.Debug("Stopping EOTS manager") if err := app.eotsManager.Close(); err != nil { - stopErr = err + stopErr = fmt.Errorf("failed to close the EOTS manager: %w", err) + return } app.logger.Debug("FinalityProviderApp successfully stopped") - }) + return stopErr } func (app *FinalityProviderApp) CreateFinalityProvider( - keyName, chainID, passPhrase, hdPath string, + keyName, chainID, passPhrase string, eotsPk *bbntypes.BIP340PubKey, description *stakingtypes.Description, commission *sdkmath.LegacyDec, ) (*CreateFinalityProviderResult, error) { + // 1. check if the chain key exists + kr, err := fpkr.NewChainKeyringControllerWithKeyring(app.kr, keyName, app.input) + if err != nil { + return nil, err + } + + fpAddr, err := kr.Address(passPhrase) + if err != nil { + // the chain key does not exist, should create the chain key first + return nil, fmt.Errorf("the keyname %s does not exist, add the key first: %w", keyName, err) + } + + // 2. create proof-of-possession + if eotsPk == nil { + return nil, fmt.Errorf("eots pk cannot be nil") + } + pop, err := app.CreatePop(fpAddr, eotsPk, passPhrase) + if err != nil { + return nil, fmt.Errorf("failed to create proof-of-possession of the finality-provider: %w", err) + } + + // Query the consumer chain to check if the fp is already registered + // if true, update db with the fp info from the consumer chain + // otherwise, proceed registration + resp, err := app.cc.QueryFinalityProvider(eotsPk.MustToBTCPK()) + if err != nil { + if !strings.Contains(err.Error(), "the finality provider is not found") { + return nil, fmt.Errorf("err getting finality provider: %w", err) + } + } + if resp != nil { + app.logger.Info("finality-provider already registered on the consumer chain", + zap.String("eots_pk", resp.FinalityProvider.BtcPk.MarshalHex()), + zap.String("addr", resp.FinalityProvider.Addr), + ) + + if err := app.putFpFromResponse(resp.FinalityProvider, chainID); err != nil { + return nil, err + } + + // get updated fp from db + storedFp, err := app.fps.GetFinalityProvider(eotsPk.MustToBTCPK()) + if err != nil { + return nil, err + } + + return &CreateFinalityProviderResult{ + FpInfo: storedFp.ToFinalityProviderInfo(), + }, nil + } - req := &createFinalityProviderRequest{ - keyName: keyName, + request := &CreateFinalityProviderRequest{ chainID: chainID, - passPhrase: passPhrase, - hdPath: hdPath, - eotsPk: eotsPk, + fpAddr: fpAddr, + btcPubKey: eotsPk, + pop: pop, description: description, commission: commission, errResponse: make(chan error, 1), - successResponse: make(chan *createFinalityProviderResponse, 1), + successResponse: make(chan *RegisterFinalityProviderResponse, 1), } - app.createFinalityProviderRequestChan <- req + app.createFinalityProviderRequestChan <- request select { - case err := <-req.errResponse: + case err := <-request.errResponse: return nil, err - case successResponse := <-req.successResponse: + case successResponse := <-request.successResponse: + pkHex := eotsPk.MarshalHex() + btcPk := eotsPk.MustToBTCPK() + // save the fp info to db after successful registration + // this ensures the data saved in db is consistent with that on the consumer chain + // if the program crashes in the middle, the user can retry registration + // which will update db use the information from the consumer chain without + // submitting a registration again + if err := app.fps.CreateFinalityProvider(fpAddr, btcPk, description, commission, chainID); err != nil { + return nil, fmt.Errorf("failed to save finality-provider: %w", err) + } + + app.metrics.RecordFpStatus(pkHex, proto.FinalityProviderStatus_REGISTERED) + + app.logger.Info("successfully saved the finality-provider", + zap.String("eots_pk", pkHex), + zap.String("addr", fpAddr.String()), + ) + + storedFp, err := app.fps.GetFinalityProvider(btcPk) + if err != nil { + return nil, err + } + return &CreateFinalityProviderResult{ - FpInfo: successResponse.FpInfo, + FpInfo: storedFp.ToFinalityProviderInfo(), + TxHash: successResponse.txHash, }, nil case <-app.quit: return nil, fmt.Errorf("finality-provider app is shutting down") } } -func (app *FinalityProviderApp) handleCreateFinalityProviderRequest(req *createFinalityProviderRequest) (*createFinalityProviderResponse, error) { - // 1. check if the chain key exists - kr, err := fpkr.NewChainKeyringControllerWithKeyring(app.kr, req.keyName, app.input) - if err != nil { - return nil, err +// UnjailFinalityProvider sends a transaction to unjail a finality-provider +func (app *FinalityProviderApp) UnjailFinalityProvider(fpPk *bbntypes.BIP340PubKey) (*UnjailFinalityProviderResponse, error) { + // send request to the loop to avoid blocking the main thread + request := &UnjailFinalityProviderRequest{ + btcPubKey: fpPk, + errResponse: make(chan error, 1), + successResponse: make(chan *UnjailFinalityProviderResponse, 1), } - fpAddr, err := kr.Address(req.passPhrase) - if err != nil { - // the chain key does not exist, should create the chain key first - keyInfo, err := kr.CreateChainKey(req.passPhrase, req.hdPath, "") - if err != nil { - return nil, fmt.Errorf("failed to create chain key %s: %w", req.keyName, err) - } - fpAddr = keyInfo.AccAddress - } + app.unjailFinalityProviderRequestChan <- request - // 2. create EOTS key - fpPk := req.eotsPk - if req.eotsPk == nil { - fpPkBytes, err := app.eotsManager.CreateKey(req.keyName, req.passPhrase, req.hdPath) + select { + case err := <-request.errResponse: + return nil, err + case successResponse := <-request.successResponse: + _, err := app.fps.GetFinalityProvider(fpPk.MustToBTCPK()) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get finality provider from db: %w", err) } - fpPk, err = bbntypes.NewBIP340PubKey(fpPkBytes) + + // Update finality-provider status in the local store + // set it to INACTIVE for now and it will be updated to + // ACTIVE if the fp has voting power + err = app.fps.SetFpStatus(fpPk.MustToBTCPK(), proto.FinalityProviderStatus_INACTIVE) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to update finality-provider status after unjailing: %w", err) } - } - fpRecord, err := app.eotsManager.KeyRecord(fpPk.MustMarshal(), req.passPhrase) - if err != nil { - return nil, fmt.Errorf("failed to get finality-provider record: %w", err) - } + app.metrics.RecordFpStatus(fpPk.MarshalHex(), proto.FinalityProviderStatus_INACTIVE) - // 3. create proof-of-possession - pop, err := kr.CreatePop(fpAddr, fpRecord.PrivKey) - if err != nil { - return nil, fmt.Errorf("failed to create proof-of-possession of the finality-provider: %w", err) + return successResponse, nil + case <-app.quit: + return nil, fmt.Errorf("finality-provider app is shutting down") } +} - if err := app.fps.CreateFinalityProvider(fpAddr, fpPk.MustToBTCPK(), req.description, req.commission, req.keyName, req.chainID, pop.BtcSig); err != nil { - return nil, fmt.Errorf("failed to save finality-provider: %w", err) +func (app *FinalityProviderApp) CreatePop(fpAddress sdk.AccAddress, fpPk *bbntypes.BIP340PubKey, passphrase string) (*bstypes.ProofOfPossessionBTC, error) { + pop := &bstypes.ProofOfPossessionBTC{ + BtcSigType: bstypes.BTCSigType_BIP340, // by default, we use BIP-340 encoding for BTC signature } - app.fpManager.metrics.RecordFpStatus(fpPk.MarshalHex(), proto.FinalityProviderStatus_CREATED) - app.logger.Info("successfully created a finality-provider", - zap.String("btc_pk", fpPk.MarshalHex()), - zap.String("addr", fpAddr.String()), - zap.String("key_name", req.keyName), - ) + // generate pop.BtcSig = schnorr_sign(sk_BTC, hash(bbnAddress)) + // NOTE: *schnorr.Sign has to take the hash of the message. + // So we have to hash the address before signing + hash := tmhash.Sum(fpAddress.Bytes()) - storedFp, err := app.fps.GetFinalityProvider(fpPk.MustToBTCPK()) + sig, err := app.eotsManager.SignSchnorrSig(fpPk.MustMarshal(), hash, passphrase) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get schnorr signature from the EOTS manager: %w", err) } - return &createFinalityProviderResponse{ - FpInfo: storedFp.ToFinalityProviderInfo(), - }, nil -} - -// SignRawMsg loads the keyring private key and signs a message. -func (app *FinalityProviderApp) SignRawMsg( - keyName, passPhrase, hdPath string, - rawMsgToSign []byte, -) ([]byte, error) { - _, chainSk, err := app.loadChainKeyring(keyName, passPhrase, hdPath) - if err != nil { - return nil, err - } + pop.BtcSig = bbntypes.NewBIP340SignatureFromBTCSig(sig).MustMarshal() - return chainSk.Sign(rawMsgToSign) + return pop, nil } -// loadChainKeyring checks the keyring by loading or creating a chain key. -func (app *FinalityProviderApp) loadChainKeyring( - keyName, passPhrase, hdPath string, -) (*fpkr.ChainKeyringController, *secp256k1.PrivKey, error) { - kr, err := fpkr.NewChainKeyringControllerWithKeyring(app.kr, keyName, app.input) - if err != nil { - return nil, nil, err - } - chainSk, err := kr.GetChainPrivKey(passPhrase) - if err != nil { - // the chain key does not exist, should create the chain key first - keyInfo, err := kr.CreateChainKey(passPhrase, hdPath, "") +func (app *FinalityProviderApp) startFinalityProviderInstance( + pk *bbntypes.BIP340PubKey, + passphrase string, +) error { + pkHex := pk.MarshalHex() + app.fpInsMu.Lock() + defer app.fpInsMu.Unlock() + + if app.fpIns == nil { + fpIns, err := NewFinalityProviderInstance( + pk, app.config, app.fps, app.pubRandStore, app.cc, app.consumerCon, app.eotsManager, + app.metrics, passphrase, app.criticalErrChan, app.logger, + ) if err != nil { - return nil, nil, fmt.Errorf("failed to create chain key %s: %w", keyName, err) + return fmt.Errorf("failed to create finality provider instance %s: %w", pkHex, err) } - chainSk = &secp256k1.PrivKey{Key: keyInfo.PrivateKey.Serialize()} + + app.fpIns = fpIns + } else if !pk.Equals(app.fpIns.btcPk) { + return fmt.Errorf("the finality provider daemon is already bonded with the finality provider %s,"+ + "please restart the daemon to switch to another instance", app.fpIns.btcPk.MarshalHex()) } - return kr, chainSk, nil + return app.fpIns.Start() } -// UpdateClientController sets a new client controoller in the App. -// Usefull for testing with multiples PKs with different keys, it needs -// to update who is the signer -func (app *FinalityProviderApp) UpdateClientController(cc ccapi.ClientController) { - app.cc = cc -} +func (app *FinalityProviderApp) IsFinalityProviderRunning(fpPk *bbntypes.BIP340PubKey) bool { + app.fpInsMu.RLock() + defer app.fpInsMu.RUnlock() -// StoreFinalityProvider stores a new finality provider in the fp store. -func (app *FinalityProviderApp) StoreFinalityProvider( - keyName, passPhrase, hdPath, chainID string, - description *stakingtypes.Description, - commission *sdkmath.LegacyDec, -) (*store.StoredFinalityProvider, error) { - // 1. check if the chain key exists - kr, _, err := app.loadChainKeyring(keyName, passPhrase, hdPath) - if err != nil { - return nil, err - } - fpAddr, err := kr.Address(passPhrase) - if err != nil { - return nil, err - } - - // 2. create EOTS key - fpPkBytes, err := app.eotsManager.CreateKey(keyName, passPhrase, hdPath) - if err != nil { - return nil, err - } - fpPk, err := bbntypes.NewBIP340PubKey(fpPkBytes) - if err != nil { - return nil, err - } - fpRecord, err := app.eotsManager.KeyRecord(fpPk.MustMarshal(), passPhrase) - if err != nil { - return nil, fmt.Errorf("failed to get finality-provider record: %w", err) + if app.fpIns == nil { + return false } - // 3. create proof-of-possession - pop, err := kr.CreatePop(fpAddr, fpRecord.PrivKey) - if err != nil { - return nil, fmt.Errorf("failed to create proof-of-possession of the finality provider: %w", err) + if app.fpIns.GetBtcPkHex() != fpPk.MarshalHex() { + return false } - if err := app.fps.CreateFinalityProvider(fpAddr, fpPk.MustToBTCPK(), description, commission, keyName, chainID, pop.BtcSig); err != nil { - return nil, fmt.Errorf("failed to save finality-provider: %w", err) - } - app.fpManager.metrics.RecordFpStatus(fpPk.MarshalHex(), proto.FinalityProviderStatus_CREATED) + return app.fpIns.IsRunning() +} - app.logger.Info("successfully created a finality-provider", - zap.String("btc_pk", fpPk.MarshalHex()), - zap.String("fp_addr", fpAddr.String()), - zap.String("key_name", keyName), - ) +func (app *FinalityProviderApp) removeFinalityProviderInstance() error { + app.fpInsMu.Lock() + defer app.fpInsMu.Unlock() - storedFp, err := app.fps.GetFinalityProvider(fpPk.MustToBTCPK()) - if err != nil { - return nil, err + fpi := app.fpIns + if fpi == nil { + return fmt.Errorf("the finality provider instance does not exist") + } + if fpi.IsRunning() { + if err := fpi.Stop(); err != nil { + return fmt.Errorf("failed to stop the finality provider instance %s", fpi.GetBtcPkHex()) + } } - return storedFp, nil + app.fpIns = nil + + return nil } -func CreateChainKey(keyringDir, chainID, keyName, backend, passphrase, hdPath, mnemonic string) (*types.ChainKeyInfo, error) { - sdkCtx, err := fpkr.CreateClientCtx( - keyringDir, chainID, - ) - if err != nil { - return nil, err +func (app *FinalityProviderApp) setFinalityProviderSlashed(fpi *FinalityProviderInstance) { + fpi.MustSetStatus(proto.FinalityProviderStatus_SLASHED) + if err := app.removeFinalityProviderInstance(); err != nil { + panic(fmt.Errorf("failed to terminate a slashed finality-provider %s: %w", fpi.GetBtcPkHex(), err)) } +} - krController, err := fpkr.NewChainKeyringController( - sdkCtx, - keyName, - backend, - ) +// NOTE: this is not safe in production, so only used for testing purpose +func (app *FinalityProviderApp) getFpPrivKey(fpPk []byte) (*btcec.PrivateKey, error) { + record, err := app.eotsManager.KeyRecord(fpPk, "") if err != nil { return nil, err } - return krController.CreateChainKey(passphrase, hdPath, mnemonic) + return record.PrivKey, nil } -// main event loop for the finality-provider app -func (app *FinalityProviderApp) eventLoop() { - defer app.wg.Done() - - for { - select { - case req := <-app.createFinalityProviderRequestChan: - res, err := app.handleCreateFinalityProviderRequest(req) +// putFpFromResponse creates or updates finality-provider in the local store +func (app *FinalityProviderApp) putFpFromResponse(fp *bstypes.FinalityProviderResponse, chainID string) error { + btcPk := fp.BtcPk.MustToBTCPK() + _, err := app.fps.GetFinalityProvider(btcPk) + if err != nil { + if errors.Is(err, store.ErrFinalityProviderNotFound) { + addr, err := sdk.AccAddressFromBech32(fp.Addr) if err != nil { - req.errResponse <- err - continue + return fmt.Errorf("err converting fp addr: %w", err) } - - req.successResponse <- &createFinalityProviderResponse{FpInfo: res.FpInfo} - - case ev := <-app.finalityProviderRegisteredEventChan: - // change the status of the finality-provider to registered - err := app.fps.SetFpStatus(ev.btcPubKey.MustToBTCPK(), proto.FinalityProviderStatus_REGISTERED) - if err != nil { - app.logger.Fatal("failed to set finality-provider status to REGISTERED", - zap.String("pk", ev.btcPubKey.MarshalHex()), - zap.Error(err), - ) + if err := app.fps.CreateFinalityProvider(addr, btcPk, fp.Description, fp.Commission, chainID); err != nil { + return fmt.Errorf("failed to save finality-provider: %w", err) } - app.fpManager.metrics.RecordFpStatus(ev.btcPubKey.MarshalHex(), proto.FinalityProviderStatus_REGISTERED) - // return to the caller - ev.successResponse <- &RegisterFinalityProviderResponse{ - bbnAddress: ev.bbnAddress, - btcPubKey: ev.btcPubKey, - TxHash: ev.txHash, - } + app.logger.Info("finality-provider successfully saved the local db", + zap.String("eots_pk", fp.BtcPk.MarshalHex()), + zap.String("addr", fp.Addr), + ) - case <-app.quit: - app.logger.Debug("exiting main event loop") - return + return nil } - } -} - -func (app *FinalityProviderApp) registrationLoop() { - defer app.wg.Done() - for { - select { - case req := <-app.registerFinalityProviderRequestChan: - // we won't do any retries here to not block the loop for more important messages. - // Most probably it fails due so some user error so we just return the error to the user. - // TODO: need to start passing context here to be able to cancel the request in case of app quiting - popBytes, err := req.pop.Marshal() - if err != nil { - req.errResponse <- err - continue - } - - desBytes, err := req.description.Marshal() - if err != nil { - req.errResponse <- err - continue - } - res, err := app.cc.RegisterFinalityProvider( - req.chainID, - req.btcPubKey.MustToBTCPK(), - popBytes, - req.commission, - desBytes, - ) - if err != nil { - app.logger.Error( - "failed to register finality-provider", - zap.String("pk", req.btcPubKey.MarshalHex()), - zap.Error(err), - ) - req.errResponse <- err - continue - } + return err + } - app.logger.Info( - "successfully registered finality-provider on babylon", - zap.String("btc_pk", req.btcPubKey.MarshalHex()), - zap.String("fp_addr", req.fpAddr.String()), - zap.String("txHash", res.TxHash), - ) + if err := app.fps.SetFpDescription(btcPk, fp.Description, fp.Commission); err != nil { + return err + } - app.finalityProviderRegisteredEventChan <- &finalityProviderRegisteredEvent{ - btcPubKey: req.btcPubKey, - bbnAddress: req.fpAddr, - txHash: res.TxHash, - // pass the channel to the event so that we can send the response to the user which requested - // the registration - successResponse: req.successResponse, - } - case <-app.quit: - app.logger.Debug("exiting registration loop") - return - } + if err := app.fps.SetFpLastVotedHeight(btcPk, uint64(fp.HighestVotedHeight)); err != nil { + return err } -} -func (app *FinalityProviderApp) metricsUpdateLoop() { - defer app.wg.Done() + hasPower, err := app.consumerCon.QueryFinalityProviderHasPower(btcPk, fp.Height) + if err != nil { + return fmt.Errorf("failed to query voting power for finality provider %s: %w", + fp.BtcPk.MarshalHex(), err) + } - interval := app.config.Metrics.UpdateInterval - app.logger.Info("starting metrics update loop", - zap.Float64("interval seconds", interval.Seconds())) - updateTicker := time.NewTicker(interval) + var status proto.FinalityProviderStatus + switch { + case hasPower: + status = proto.FinalityProviderStatus_ACTIVE + case fp.SlashedBtcHeight > 0: + status = proto.FinalityProviderStatus_SLASHED + case fp.Jailed: + status = proto.FinalityProviderStatus_JAILED + default: + status = proto.FinalityProviderStatus_INACTIVE + } - for { - select { - case <-updateTicker.C: - fps, err := app.fps.GetAllStoredFinalityProviders() - if err != nil { - app.logger.Error("failed to get finality-providers from the store", zap.Error(err)) - continue - } - app.metrics.UpdateFpMetrics(fps) - case <-app.quit: - updateTicker.Stop() - app.logger.Info("exiting metrics update loop") - return - } + if err := app.fps.SetFpStatus(btcPk, status); err != nil { + return fmt.Errorf("failed to update status for finality provider %s: %w", fp.BtcPk.MarshalHex(), err) } -} -// syncChainFpStatusLoop keeps querying the chain for the finality -// provider voting power and update the FP status accordingly. -// If there is some voting power it sets to active, for zero voting power -// it goes from: CREATED -> REGISTERED or ACTIVE -> INACTIVE. -func (app *FinalityProviderApp) syncChainFpStatusLoop() { - defer app.wg.Done() - - interval := app.config.SyncFpStatusInterval - app.logger.Info( - "starting sync FP status loop", - zap.Float64("interval seconds", interval.Seconds()), + app.logger.Info("finality-provider successfully updated the local db", + zap.String("eots_pk", fp.BtcPk.MarshalHex()), + zap.String("addr", fp.Addr), ) - syncFpStatusTicker := time.NewTicker(interval) - defer syncFpStatusTicker.Stop() - for { - select { - case <-syncFpStatusTicker.C: - fpInstanceStarted, err := app.SyncFinalityProviderStatus() - if err != nil { - app.Logger().Error("failed to sync finality-provider status", zap.Error(err)) - } - if fpInstanceStarted { - return - } - case <-app.quit: - app.logger.Info("exiting sync FP status loop") - return - } - } + return nil } diff --git a/finality-provider/service/app_test.go b/finality-provider/service/app_test.go index 62f3a314..4d024084 100644 --- a/finality-provider/service/app_test.go +++ b/finality-provider/service/app_test.go @@ -10,38 +10,46 @@ import ( "testing" "time" + btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + "github.com/babylonlabs-io/babylon/testutil/datagen" bbntypes "github.com/babylonlabs-io/babylon/types" - bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + finalitytypes "github.com/babylonlabs-io/babylon/x/finality/types" + sdkkeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - "go.uber.org/zap" + "github.com/babylonlabs-io/finality-provider/clientcontroller/api" "github.com/babylonlabs-io/finality-provider/eotsmanager" eotscfg "github.com/babylonlabs-io/finality-provider/eotsmanager/config" "github.com/babylonlabs-io/finality-provider/finality-provider/config" "github.com/babylonlabs-io/finality-provider/finality-provider/proto" "github.com/babylonlabs-io/finality-provider/finality-provider/service" + fpstore "github.com/babylonlabs-io/finality-provider/finality-provider/store" + "github.com/babylonlabs-io/finality-provider/keyring" "github.com/babylonlabs-io/finality-provider/testutil" "github.com/babylonlabs-io/finality-provider/types" + "github.com/babylonlabs-io/finality-provider/util" ) -var ( - passphrase = "testpass" - hdPath = "" +const ( + passphrase = "testpass" + hdPath = "" + eventuallyWaitTimeOut = 5 * time.Second + eventuallyPollTime = 10 * time.Millisecond ) -func FuzzRegisterFinalityProvider(f *testing.F) { +func FuzzCreateFinalityProvider(f *testing.F) { testutil.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { + t.Parallel() r := rand.New(rand.NewSource(seed)) - logger, err := zap.NewDevelopment() - require.NoError(t, err) + logger := testutil.GetTestLogger(t) // create an EOTS manager eotsHomeDir := filepath.Join(t.TempDir(), "eots-home") eotsCfg := eotscfg.DefaultConfigWithHomePath(eotsHomeDir) - dbBackend, err := eotsCfg.DatabaseConfig.GetDbBackend() + dbBackend, err := eotsCfg.DatabaseConfig.GetDBBackend() require.NoError(t, err) em, err := eotsmanager.NewLocalEOTSManager(eotsHomeDir, eotsCfg.KeyringBackend, dbBackend, logger) require.NoError(t, err) @@ -58,6 +66,8 @@ func FuzzRegisterFinalityProvider(f *testing.F) { mockConsumerController.EXPECT().QueryLatestFinalizedBlock().Return(nil, nil).AnyTimes() mockConsumerController.EXPECT().QueryFinalityProviderHasPower(gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockConsumerController.EXPECT().QueryBlocks(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockConsumerController.EXPECT().QueryLastPublicRandCommit(gomock.Any()).Return(nil, nil).AnyTimes() mockBabylonController := testutil.PrepareMockedBabylonController(t) // Create randomized config @@ -66,7 +76,7 @@ func FuzzRegisterFinalityProvider(f *testing.F) { fpCfg.NumPubRand = testutil.TestPubRandNum fpCfg.PollerConfig.AutoChainScanningMode = false fpCfg.PollerConfig.StaticChainScanningStartHeight = randomStartingHeight - fpdb, err := fpCfg.DatabaseConfig.GetDbBackend() + fpdb, err := fpCfg.DatabaseConfig.GetDBBackend() require.NoError(t, err) app, err := service.NewFinalityProviderApp(&fpCfg, mockBabylonController, mockConsumerController, em, fpdb, logger) require.NoError(t, err) @@ -85,153 +95,319 @@ func FuzzRegisterFinalityProvider(f *testing.F) { }() var eotsPk *bbntypes.BIP340PubKey - eotsPk = nil - generateEotsKeyBefore := r.Int31n(10) > 5 - if generateEotsKeyBefore { - // sometimes uses the previously generated EOTS pk - eotsKeyName := testutil.GenRandomHexStr(r, 4) - eotsPkBz, err := em.CreateKey(eotsKeyName, passphrase, hdPath) - require.NoError(t, err) - eotsPk, err = bbntypes.NewBIP340PubKey(eotsPkBz) - require.NoError(t, err) - } - - // create a finality-provider object and save it to db - fp := testutil.GenStoredFinalityProvider(r, t, app, passphrase, hdPath, eotsPk) - if generateEotsKeyBefore { - require.Equal(t, eotsPk, bbntypes.NewBIP340PubKeyFromBTCPK(fp.BtcPk)) - } - - btcSig := new(bbntypes.BIP340Signature) - err = btcSig.Unmarshal(fp.Pop.BtcSig) + eotsKeyName := testutil.GenRandomHexStr(r, 4) require.NoError(t, err) - pop := &bstypes.ProofOfPossessionBTC{ - BtcSig: btcSig.MustMarshal(), - BtcSigType: bstypes.BTCSigType_BIP340, - } - popBytes, err := pop.Marshal() + eotsPkBz, err := em.CreateKey(eotsKeyName, passphrase, hdPath) require.NoError(t, err) - fpInfo, err := app.GetFinalityProviderInfo(fp.GetBIP340BTCPK()) + eotsPk, err = bbntypes.NewBIP340PubKey(eotsPkBz) require.NoError(t, err) - require.Equal(t, proto.FinalityProviderStatus_name[0], fpInfo.Status) - require.Equal(t, false, fpInfo.IsRunning) - fpListInfo, err := app.ListAllFinalityProvidersInfo() + + // generate keyring + keyName := testutil.GenRandomHexStr(r, 4) + chainID := testutil.GenRandomHexStr(r, 4) + + cfg := app.GetConfig() + _, err = testutil.CreateChainKey(cfg.BabylonConfig.KeyDirectory, cfg.BabylonConfig.ChainID, keyName, sdkkeyring.BackendTest, passphrase, hdPath, "") require.NoError(t, err) - require.Equal(t, fpInfo.BtcPkHex, fpListInfo[0].BtcPkHex) txHash := testutil.GenRandomHexStr(r, 32) mockBabylonController.EXPECT(). RegisterFinalityProvider( - fp.ChainID, - fp.BtcPk, - popBytes, + chainID, + eotsPk.MustToBTCPK(), + gomock.Any(), testutil.ZeroCommissionRate(), gomock.Any(), ).Return(&types.TxResponse{TxHash: txHash}, nil).AnyTimes() - - res, err := app.RegisterFinalityProvider(fp.GetBIP340BTCPK().MarshalHex()) + mockBabylonController.EXPECT().QueryFinalityProvider(gomock.Any()).Return(nil, nil).AnyTimes() + res, err := app.CreateFinalityProvider(keyName, chainID, passphrase, eotsPk, testutil.RandomDescription(r), testutil.ZeroCommissionRate()) require.NoError(t, err) require.Equal(t, txHash, res.TxHash) - mockConsumerController.EXPECT().QueryLastPublicRandCommit(gomock.Any()).Return(nil, nil).AnyTimes() - err = app.StartHandlingFinalityProvider(fp.GetBIP340BTCPK(), passphrase) + fpInfo, err := app.GetFinalityProviderInfo(eotsPk) require.NoError(t, err) + require.Equal(t, eotsPk.MarshalHex(), fpInfo.BtcPkHex) + }) +} - fpAfterReg, err := app.GetFinalityProviderInstance(fp.GetBIP340BTCPK()) - require.NoError(t, err) - require.Equal(t, proto.FinalityProviderStatus_REGISTERED, fpAfterReg.GetStoreFinalityProvider().Status) +func FuzzSyncFinalityProviderStatus(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + mockBabylonController := testutil.PrepareMockedBabylonController(t) + randomStartingHeight := uint64(r.Int63n(100) + 1) + currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2) + mockConsumerController := testutil.PrepareMockedConsumerController(t, r, randomStartingHeight, currentHeight) + + mockConsumerController.EXPECT().QueryLastPublicRandCommit(gomock.Any()).Return(nil, nil).AnyTimes() + mockConsumerController.EXPECT().QueryLatestFinalizedBlock().Return(nil, nil).AnyTimes() + mockConsumerController.EXPECT().QueryLatestBlockHeight().Return(currentHeight, nil).AnyTimes() + mockConsumerController.EXPECT().QueryBlock(gomock.Any()).Return(nil, errors.New("chain not online")).AnyTimes() + + noVotingPowerTable := r.Int31n(10) > 5 + if noVotingPowerTable { + allowedErr := fmt.Sprintf("failed to query Finality Voting Power at Height %d: rpc error: code = Unknown desc = %s: unknown request", + currentHeight, finalitytypes.ErrVotingPowerTableNotUpdated.Wrapf("height: %d", currentHeight).Error()) + mockConsumerController.EXPECT().QueryFinalityProviderHasPower(gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockConsumerController.EXPECT().QueryActivatedHeight().Return(uint64(0), errors.New(allowedErr)).AnyTimes() + } else { + mockConsumerController.EXPECT().QueryFinalityProviderHasPower(gomock.Any(), gomock.Any()).Return(true, nil).AnyTimes() + mockConsumerController.EXPECT().QueryActivatedHeight().Return(currentHeight, nil).AnyTimes() + } + mockConsumerController.EXPECT().QueryFinalityProviderHighestVotedHeight(gomock.Any()).Return(uint64(0), nil).AnyTimes() + var isSlashedOrJailed int + if noVotingPowerTable { + // 0 means is slashed, 1 means is jailed, 2 means neither slashed nor jailed + isSlashedOrJailed = r.Intn(3) + switch isSlashedOrJailed { + case 0: + mockConsumerController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(true, false, nil).AnyTimes() + case 1: + mockConsumerController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(false, true, nil).AnyTimes() + case 2: + mockConsumerController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(false, false, nil).AnyTimes() + } + } + + // Create randomized config + pathSuffix := datagen.GenRandomHexStr(r, 10) + fpHomeDir := filepath.Join(t.TempDir(), "fp-home", pathSuffix) + fpCfg := config.DefaultConfigWithHome(fpHomeDir) + // no need for other intervals to run + fpCfg.SubmissionRetryInterval = time.Minute * 10 - fpInfo, err = app.GetFinalityProviderInfo(fp.GetBIP340BTCPK()) + // Create fp app + app, fpPk, cleanup := startFPAppWithRegisteredFp(t, r, fpHomeDir, &fpCfg, mockBabylonController, mockConsumerController) + defer cleanup() + + fpInfo, err := app.GetFinalityProviderInfo(fpPk) require.NoError(t, err) - require.Equal(t, proto.FinalityProviderStatus_name[1], fpInfo.Status) - require.Equal(t, true, fpInfo.IsRunning) + + expectedStatus := proto.FinalityProviderStatus_ACTIVE + if noVotingPowerTable { + switch isSlashedOrJailed { + case 0: + expectedStatus = proto.FinalityProviderStatus_SLASHED + case 1: + expectedStatus = proto.FinalityProviderStatus_JAILED + case 2: + expectedStatus = proto.FinalityProviderStatus_INACTIVE + } + } + + require.Equal(t, fpInfo.Status, expectedStatus.String()) }) } -func FuzzSyncFinalityProviderStatus(f *testing.F) { - testutil.AddRandomSeedsToFuzzer(f, 14) +func FuzzUnjailFinalityProvider(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { + t.Parallel() r := rand.New(rand.NewSource(seed)) - logger, err := zap.NewDevelopment() - require.NoError(t, err) + mockBabylonController := testutil.PrepareMockedBabylonController(t) + randomStartingHeight := uint64(r.Int63n(100) + 1) + currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2) + mockConsumerController := testutil.PrepareMockedConsumerController(t, r, randomStartingHeight, currentHeight) + // Create randomized config pathSuffix := datagen.GenRandomHexStr(r, 10) + fpHomeDir := filepath.Join(t.TempDir(), "fp-home", pathSuffix) + fpCfg := config.DefaultConfigWithHome(fpHomeDir) + // use shorter interval for the test to end faster + fpCfg.SubmissionRetryInterval = time.Millisecond * 10 + fpCfg.SignatureSubmissionInterval = time.Millisecond * 10 + + mockConsumerController.EXPECT().QueryLastPublicRandCommit(gomock.Any()).Return(nil, nil).AnyTimes() + mockConsumerController.EXPECT().QueryLatestFinalizedBlock().Return(nil, nil).AnyTimes() + mockConsumerController.EXPECT().QueryLatestBlockHeight().Return(currentHeight, nil).AnyTimes() + mockConsumerController.EXPECT().QueryBlocks(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("chain not online")).AnyTimes() + + // set voting power to be positive so that the fp should eventually become ACTIVE + mockConsumerController.EXPECT().QueryFinalityProviderHasPower(gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockConsumerController.EXPECT().QueryFinalityActivationBlockHeight().Return(uint64(0), nil).AnyTimes() + mockConsumerController.EXPECT().QueryActivatedHeight().Return(uint64(1), nil).AnyTimes() + mockConsumerController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(false, true, nil).AnyTimes() + mockConsumerController.EXPECT().QueryFinalityProviderHighestVotedHeight(gomock.Any()).Return(uint64(0), nil).AnyTimes() + + // Create fp app + app, fpPk, cleanup := startFPAppWithRegisteredFp(t, r, fpHomeDir, &fpCfg, mockBabylonController, mockConsumerController) + defer cleanup() + + expectedTxHash := datagen.GenRandomHexStr(r, 32) + mockConsumerController.EXPECT().UnjailFinalityProvider(fpPk.MustToBTCPK()).Return(&types.TxResponse{TxHash: expectedTxHash}, nil).AnyTimes() + err := app.StartFinalityProvider(fpPk, "") + require.NoError(t, err) + fpIns, err := app.GetFinalityProviderInstance() + require.NoError(t, err) + require.True(t, fpIns.IsJailed()) + res, err := app.UnjailFinalityProvider(fpPk) + require.NoError(t, err) + require.Equal(t, expectedTxHash, res.TxHash) + require.Eventually(t, func() bool { + return !fpIns.IsJailed() + }, eventuallyWaitTimeOut, eventuallyPollTime) + }) +} + +func FuzzSaveAlreadyRegisteredFinalityProvider(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + t.Parallel() + r := rand.New(rand.NewSource(seed)) + + logger := testutil.GetTestLogger(t) // create an EOTS manager - eotsHomeDir := filepath.Join(t.TempDir(), "eots-home", pathSuffix) + eotsHomeDir := filepath.Join(t.TempDir(), "eots-home") eotsCfg := eotscfg.DefaultConfigWithHomePath(eotsHomeDir) - dbBackend, err := eotsCfg.DatabaseConfig.GetDbBackend() + dbBackend, err := eotsCfg.DatabaseConfig.GetDBBackend() require.NoError(t, err) em, err := eotsmanager.NewLocalEOTSManager(eotsHomeDir, eotsCfg.KeyringBackend, dbBackend, logger) require.NoError(t, err) - - // clean up after the test defer func() { dbBackend.Close() err = os.RemoveAll(eotsHomeDir) require.NoError(t, err) }() - // Create randomized config - fpHomeDir := filepath.Join(t.TempDir(), "fp-home", pathSuffix) - fpCfg := config.DefaultConfigWithHome(fpHomeDir) - fpCfg.SyncFpStatusInterval = time.Millisecond * 100 - fpCfg.NumPubRand = testutil.TestPubRandNum - // no need for other intervals to run - fpCfg.StatusUpdateInterval = time.Minute * 10 - fpCfg.SubmissionRetryInterval = time.Minute * 10 - fpdb, err := fpCfg.DatabaseConfig.GetDbBackend() - require.NoError(t, err) - randomStartingHeight := uint64(r.Int63n(100) + 1) currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2) - mockConsumerController := testutil.PrepareMockedConsumerController(t, r, randomStartingHeight, currentHeight) mockBabylonController := testutil.PrepareMockedBabylonController(t) + mockConsumerController := testutil.PrepareMockedConsumerController(t, r, randomStartingHeight, currentHeight) + rndFp, err := datagen.GenRandomFinalityProvider(r) + require.NoError(t, err) - mockConsumerController.EXPECT().QueryLatestFinalizedBlock().Return(nil, nil).AnyTimes() - mockConsumerController.EXPECT().QueryLatestBlockHeight().Return(currentHeight, nil).AnyTimes() - mockConsumerController.EXPECT().QueryBlock(gomock.Any()).Return(nil, errors.New("chain not online")).AnyTimes() - mockConsumerController.EXPECT().QueryLastPublicRandCommit(gomock.Any()).Return(nil, nil).AnyTimes() - - noVotingPowerTable := r.Int31n(10) > 5 - if noVotingPowerTable { - allowedErr := fmt.Sprintf("failed to query Finality Voting Power at Height %d: rpc error: code = Unknown desc = %s: unknown request", - currentHeight, bstypes.ErrVotingPowerTableNotUpdated.Wrapf("height: %d", currentHeight).Error()) - mockConsumerController.EXPECT().QueryFinalityProviderHasPower(gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() - mockConsumerController.EXPECT().QueryActivatedHeight().Return(uint64(0), errors.New(allowedErr)).AnyTimes() - } else { - mockConsumerController.EXPECT().QueryFinalityProviderHasPower(gomock.Any(), gomock.Any()).Return(true, nil).AnyTimes() - mockConsumerController.EXPECT().QueryActivatedHeight().Return(currentHeight, nil).AnyTimes() - } + // Create randomized config + fpHomeDir := filepath.Join(t.TempDir(), "fp-home") + fpCfg := config.DefaultConfigWithHome(fpHomeDir) + fpCfg.PollerConfig.AutoChainScanningMode = false + fpCfg.PollerConfig.StaticChainScanningStartHeight = randomStartingHeight + fpdb, err := fpCfg.DatabaseConfig.GetDBBackend() + require.NoError(t, err) app, err := service.NewFinalityProviderApp(&fpCfg, mockBabylonController, mockConsumerController, em, fpdb, logger) require.NoError(t, err) + defer func() { + err = fpdb.Close() + require.NoError(t, err) + err = os.RemoveAll(fpHomeDir) + require.NoError(t, err) + }() + err = app.Start() require.NoError(t, err) + defer func() { + err = app.Stop() + require.NoError(t, err) + }() - fp := testutil.GenStoredFinalityProvider(r, t, app, "", hdPath, nil) + var eotsPk *bbntypes.BIP340PubKey + eotsKeyName := testutil.GenRandomHexStr(r, 4) + require.NoError(t, err) + eotsPkBz, err := em.CreateKey(eotsKeyName, passphrase, hdPath) + require.NoError(t, err) + eotsPk, err = bbntypes.NewBIP340PubKey(eotsPkBz) + require.NoError(t, err) - require.Eventually(t, func() bool { - fpPk := fp.GetBIP340BTCPK() - fpInfo, err := app.GetFinalityProviderInfo(fpPk) - if err != nil { - return false - } + // generate keyring + keyName := testutil.GenRandomHexStr(r, 4) + chainID := testutil.GenRandomHexStr(r, 4) - expectedStatus := proto.FinalityProviderStatus_ACTIVE - if noVotingPowerTable { - expectedStatus = proto.FinalityProviderStatus_REGISTERED - } - fpInstance, err := app.GetFinalityProviderInstance(fpPk) - if err != nil { - return false - } + cfg := app.GetConfig() + _, err = testutil.CreateChainKey(cfg.BabylonConfig.KeyDirectory, cfg.BabylonConfig.ChainID, keyName, sdkkeyring.BackendTest, passphrase, hdPath, "") + require.NoError(t, err) + + fpRes := &btcstakingtypes.QueryFinalityProviderResponse{FinalityProvider: &btcstakingtypes.FinalityProviderResponse{ + Description: rndFp.Description, + Commission: rndFp.Commission, + Addr: rndFp.Addr, + BtcPk: eotsPk, + Pop: rndFp.Pop, + SlashedBabylonHeight: rndFp.SlashedBabylonHeight, + SlashedBtcHeight: rndFp.SlashedBtcHeight, + Jailed: rndFp.Jailed, + HighestVotedHeight: rndFp.HighestVotedHeight, + }} + + mockBabylonController.EXPECT().QueryFinalityProvider(gomock.Any()).Return(fpRes, nil).AnyTimes() + + res, err := app.CreateFinalityProvider(keyName, chainID, passphrase, eotsPk, testutil.RandomDescription(r), testutil.ZeroCommissionRate()) + require.NoError(t, err) + require.Equal(t, res.FpInfo.BtcPkHex, eotsPk.MarshalHex()) - // TODO: verify why mocks are failing - btcPkEqual := fpInstance.GetBtcPk().IsEqual(fp.BtcPk) - statusEqual := strings.EqualFold(fpInfo.Status, expectedStatus.String()) - return statusEqual && btcPkEqual - }, time.Second*5, time.Millisecond*200, "should eventually be registered or active") + fpInfo, err := app.GetFinalityProviderInfo(eotsPk) + require.NoError(t, err) + require.Equal(t, eotsPk.MarshalHex(), fpInfo.BtcPkHex) }) } + +func startFPAppWithRegisteredFp(t *testing.T, r *rand.Rand, homePath string, cfg *config.Config, cc api.ClientController, consumerCon api.ConsumerController) (*service.FinalityProviderApp, *bbntypes.BIP340PubKey, func()) { + logger := testutil.GetTestLogger(t) + // create an EOTS manager + eotsHomeDir := filepath.Join(t.TempDir(), "eots-home") + eotsCfg := eotscfg.DefaultConfigWithHomePath(eotsHomeDir) + eotsdb, err := eotsCfg.DatabaseConfig.GetDBBackend() + require.NoError(t, err) + em, err := eotsmanager.NewLocalEOTSManager(eotsHomeDir, eotsCfg.KeyringBackend, eotsdb, logger) + require.NoError(t, err) + + // create finality-provider app with randomized config + input := strings.NewReader("") + require.NoError(t, err) + err = util.MakeDirectory(config.DataDir(homePath)) + require.NoError(t, err) + db, err := cfg.DatabaseConfig.GetDBBackend() + require.NoError(t, err) + fpStore, err := fpstore.NewFinalityProviderStore(db) + require.NoError(t, err) + app, err := service.NewFinalityProviderApp(cfg, cc, consumerCon, em, db, logger) + require.NoError(t, err) + + // create registered finality-provider + keyName := datagen.GenRandomHexStr(r, 10) + chainID := datagen.GenRandomHexStr(r, 10) + kr, err := keyring.CreateKeyring( + cfg.BabylonConfig.KeyDirectory, + cfg.BabylonConfig.ChainID, + cfg.BabylonConfig.KeyringBackend, + input, + ) + require.NoError(t, err) + kc, err := keyring.NewChainKeyringControllerWithKeyring(kr, keyName, input) + require.NoError(t, err) + btcPkBytes, err := em.CreateKey(keyName, passphrase, hdPath) + require.NoError(t, err) + btcPk, err := bbntypes.NewBIP340PubKey(btcPkBytes) + require.NoError(t, err) + keyInfo, err := kc.CreateChainKey(passphrase, hdPath, "") + require.NoError(t, err) + fpAddr := keyInfo.AccAddress + + err = fpStore.CreateFinalityProvider( + fpAddr, + btcPk.MustToBTCPK(), + testutil.RandomDescription(r), + testutil.ZeroCommissionRate(), + chainID, + ) + require.NoError(t, err) + err = app.Start() + require.NoError(t, err) + + cleanUp := func() { + err = app.Stop() + require.NoError(t, err) + err = eotsdb.Close() + require.NoError(t, err) + err = db.Close() + require.NoError(t, err) + err = os.RemoveAll(eotsHomeDir) + require.NoError(t, err) + err = os.RemoveAll(homePath) + require.NoError(t, err) + } + + return app, btcPk, cleanUp +} diff --git a/finality-provider/service/benchmark_helper.go b/finality-provider/service/benchmark_helper.go new file mode 100644 index 00000000..672cf610 --- /dev/null +++ b/finality-provider/service/benchmark_helper.go @@ -0,0 +1,91 @@ +package service + +import ( + "fmt" + "time" + + "go.uber.org/zap" + + "github.com/babylonlabs-io/finality-provider/types" +) + +// CommitPubRandTiming - helper struct used to capture times for benchmark +type CommitPubRandTiming struct { + GetPubRandListTime time.Duration + AddPubRandProofListTime time.Duration + CommitPubRandListTime time.Duration +} + +// HelperCommitPubRand used for benchmark +func (fp *FinalityProviderInstance) HelperCommitPubRand(tipHeight uint64) (*types.TxResponse, *CommitPubRandTiming, error) { + lastCommittedHeight, err := fp.GetLastCommittedHeight() + if err != nil { + return nil, nil, err + } + + var startHeight uint64 + switch { + case lastCommittedHeight == uint64(0): + // the finality-provider has never submitted public rand before + startHeight = tipHeight + 1 + case lastCommittedHeight < uint64(fp.cfg.TimestampingDelayBlocks)+tipHeight: + // (should not use subtraction because they are in the type of uint64) + // we are running out of the randomness + startHeight = lastCommittedHeight + 1 + default: + fp.logger.Debug( + "the finality-provider has sufficient public randomness, skip committing more", + zap.String("pk", fp.GetBtcPkHex()), + zap.Uint64("block_height", tipHeight), + zap.Uint64("last_committed_height", lastCommittedHeight), + ) + + return nil, nil, nil + } + + return fp.commitPubRandPairsWithTiming(startHeight) +} + +func (fp *FinalityProviderInstance) commitPubRandPairsWithTiming(startHeight uint64) (*types.TxResponse, *CommitPubRandTiming, error) { + timing := &CommitPubRandTiming{} + + activationBlkHeight, err := fp.consumerCon.QueryFinalityActivationBlockHeight() + if err != nil { + return nil, timing, err + } + + startHeight = max(startHeight, activationBlkHeight) + + // Measure getPubRandList + pubRandListStart := time.Now() + pubRandList, err := fp.GetPubRandList(startHeight, fp.cfg.NumPubRand) + if err != nil { + return nil, timing, fmt.Errorf("failed to generate randomness: %w", err) + } + timing.GetPubRandListTime = time.Since(pubRandListStart) + + numPubRand := uint64(len(pubRandList)) + commitment, proofList := types.GetPubRandCommitAndProofs(pubRandList) + + // Measure addPubRandProofList + addProofStart := time.Now() + if err := fp.pubRandState.addPubRandProofList(fp.GetChainID(), fp.btcPk.MustMarshal(), startHeight, uint64(fp.cfg.NumPubRand), proofList); err != nil { + return nil, timing, fmt.Errorf("failed to save public randomness to DB: %w", err) + } + timing.AddPubRandProofListTime = time.Since(addProofStart) + + // Measure CommitPubRandList + commitListStart := time.Now() + schnorrSig, err := fp.SignPubRandCommit(startHeight, numPubRand, commitment) + if err != nil { + return nil, timing, fmt.Errorf("failed to sign the Schnorr signature: %w", err) + } + + res, err := fp.consumerCon.CommitPubRandList(fp.GetBtcPk(), startHeight, numPubRand, commitment, schnorrSig) + if err != nil { + return nil, timing, fmt.Errorf("failed to commit public randomness to the consumer chain: %w", err) + } + timing.CommitPubRandListTime = time.Since(commitListStart) + + return res, timing, nil +} diff --git a/finality-provider/service/chain_poller.go b/finality-provider/service/chain_poller.go index ec969ffe..0e1b5121 100644 --- a/finality-provider/service/chain_poller.go +++ b/finality-provider/service/chain_poller.go @@ -16,7 +16,6 @@ import ( ) var ( - // TODO: Maybe configurable? RtyAttNum = uint(5) RtyAtt = retry.Attempts(RtyAttNum) RtyDel = retry.Delay(time.Millisecond * 400) @@ -24,51 +23,36 @@ var ( ) const ( - // TODO: Maybe configurable? maxFailedCycles = 20 ) -type skipHeightRequest struct { - height uint64 - resp chan *skipHeightResponse -} - -type skipHeightResponse struct { - err error -} - type ChainPoller struct { - isStarted *atomic.Bool - wg sync.WaitGroup - quit chan struct{} - - cc ccapi.ClientController - consumerCon ccapi.ConsumerController - cfg *cfg.ChainPollerConfig - metrics *metrics.FpMetrics - blockInfoChan chan *types.BlockInfo - skipHeightChan chan *skipHeightRequest - nextHeight uint64 - logger *zap.Logger + mu sync.RWMutex + wg sync.WaitGroup + isStarted *atomic.Bool + quit chan struct{} + consumerCon ccapi.ConsumerController + cfg *cfg.ChainPollerConfig + metrics *metrics.FpMetrics + blockInfoChan chan *types.BlockInfo + logger *zap.Logger + nextHeight uint64 } func NewChainPoller( logger *zap.Logger, cfg *cfg.ChainPollerConfig, - cc ccapi.ClientController, consumerCon ccapi.ConsumerController, metrics *metrics.FpMetrics, ) *ChainPoller { return &ChainPoller{ - isStarted: atomic.NewBool(false), - logger: logger, - cfg: cfg, - cc: cc, - consumerCon: consumerCon, - metrics: metrics, - blockInfoChan: make(chan *types.BlockInfo, cfg.BufferSize), - skipHeightChan: make(chan *skipHeightRequest), - quit: make(chan struct{}), + isStarted: atomic.NewBool(false), + logger: logger, + cfg: cfg, + consumerCon: consumerCon, + metrics: metrics, + blockInfoChan: make(chan *types.BlockInfo, cfg.BufferSize), + quit: make(chan struct{}), } } @@ -79,15 +63,9 @@ func (cp *ChainPoller) Start(startHeight uint64) error { cp.logger.Info("starting the chain poller") - err := cp.validateStartHeight(startHeight) - if err != nil { - return fmt.Errorf("invalid starting height %d: %w", startHeight, err) - } - cp.nextHeight = startHeight cp.wg.Add(1) - go cp.pollChain() cp.metrics.RecordPollerStartingHeight(startHeight) @@ -102,7 +80,7 @@ func (cp *ChainPoller) Stop() error { } cp.logger.Info("stopping the chain poller") - err := cp.cc.Close() + err := cp.consumerCon.Close() if err != nil { return err } @@ -118,90 +96,69 @@ func (cp *ChainPoller) IsRunning() bool { return cp.isStarted.Load() } -// Return read only channel for incoming blocks -// TODO: Handle the case when there is more than one consumer. Currently with more than -// one consumer blocks most probably will be received out of order to those consumers. +// GetBlockInfoChan returns the read-only channel for incoming blocks func (cp *ChainPoller) GetBlockInfoChan() <-chan *types.BlockInfo { return cp.blockInfoChan } -func (cp *ChainPoller) latestBlockHeightWithRetry() (uint64, error) { +func (cp *ChainPoller) blocksWithRetry(start, end uint64, limit uint32) ([]*types.BlockInfo, error) { var ( - latestBlockHeight uint64 - err error + block []*types.BlockInfo + err error ) - if err := retry.Do(func() error { - latestBlockHeight, err = cp.consumerCon.QueryLatestBlockHeight() + block, err = cp.consumerCon.QueryBlocks(start, end, limit) if err != nil { return err } + + if len(block) == 0 { + return fmt.Errorf("no blocks found for range %d-%d", start, end) + } + return nil }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { cp.logger.Debug( - "failed to query the consumer chain for the latest block", + "failed to query the consumer chain for block range", zap.Uint("attempt", n+1), zap.Uint("max_attempts", RtyAttNum), + zap.Uint64("start_height", start), + zap.Uint64("end_height", end), + zap.Uint32("limit", limit), zap.Error(err), ) })); err != nil { - return 0, err + return nil, err } - return latestBlockHeight, nil + + return block, nil } -func (cp *ChainPoller) blockWithRetry(height uint64) (*types.BlockInfo, error) { +func (cp *ChainPoller) latestBlockHeightWithRetry() (uint64, error) { var ( - block *types.BlockInfo - err error + latestBlockHeight uint64 + err error ) + if err := retry.Do(func() error { - block, err = cp.consumerCon.QueryBlock(height) + latestBlockHeight, err = cp.consumerCon.QueryLatestBlockHeight() if err != nil { return err } + return nil }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { cp.logger.Debug( "failed to query the consumer chain for the latest block", zap.Uint("attempt", n+1), zap.Uint("max_attempts", RtyAttNum), - zap.Uint64("height", height), zap.Error(err), ) })); err != nil { - return nil, err - } - - return block, nil -} - -func (cp *ChainPoller) validateStartHeight(startHeight uint64) error { - // Infinite retry to get initial latest height - // TODO: Add possible cancellation or timeout for starting node - - if startHeight == 0 { - return fmt.Errorf("start height can't be 0") - } - - var currentBestChainHeight uint64 - for { - lastestBlockHeight, err := cp.latestBlockHeightWithRetry() - if err != nil { - cp.logger.Debug("failed to query babylon for the latest status", zap.Error(err)) - continue - } - - currentBestChainHeight = lastestBlockHeight - break - } - - // Allow the start height to be the next chain height - if startHeight > currentBestChainHeight+1 { - return fmt.Errorf("start height %d is more than the next chain tip height %d", startHeight, currentBestChainHeight+1) + return 0, err } - return nil + return latestBlockHeight, nil } // waitForActivation waits until BTC staking is activated @@ -216,17 +173,16 @@ func (cp *ChainPoller) waitForActivation() { if cp.nextHeight < activatedHeight { cp.nextHeight = activatedHeight } + return } - select { case <-time.After(cp.cfg.PollInterval): - + continue case <-cp.quit: return } } - } func (cp *ChainPoller) pollChain() { @@ -234,108 +190,90 @@ func (cp *ChainPoller) pollChain() { cp.waitForActivation() + ticker := time.NewTicker(cp.cfg.PollInterval) + defer ticker.Stop() + var failedCycles uint32 for { - // TODO: Handlig of request cancellation, as otherwise shutdown will be blocked - // until request is finished - blockToRetrieve := cp.nextHeight - block, err := cp.blockWithRetry(blockToRetrieve) + latestBlockHeight, err := cp.latestBlockHeightWithRetry() if err != nil { failedCycles++ cp.logger.Debug( - "failed to query the consumer chain for the block", + "failed to query the consumer chain for the latest block", zap.Uint32("current_failures", failedCycles), - zap.Uint64("block_to_retrieve", blockToRetrieve), zap.Error(err), ) } else { - // no error and we got the header we wanted to get, bump the state and push - // notification about data - cp.nextHeight = blockToRetrieve + 1 - failedCycles = 0 - cp.metrics.RecordLastPolledHeight(block.Height) - - cp.logger.Info("the poller retrieved the block from the consumer chain", - zap.Uint64("height", block.Height)) - - // push the data to the channel - // Note: if the consumer is too slow -- the buffer is full - // the channel will block, and we will stop retrieving data from the node - cp.blockInfoChan <- block + // start polling in the first iteration + blockToRetrieve := cp.NextHeight() + var blocks []*types.BlockInfo + var err error + if blockToRetrieve == latestBlockHeight { + var latestBlock *types.BlockInfo + latestBlock, err = cp.consumerCon.QueryBlock(latestBlockHeight) + blocks = []*types.BlockInfo{latestBlock} + } else { + blocks, err = cp.blocksWithRetry(blockToRetrieve, latestBlockHeight, cp.cfg.PollSize) + } + + if err != nil { + failedCycles++ + cp.logger.Debug( + "failed to query the consumer chain for the block range", + zap.Uint32("current_failures", failedCycles), + zap.Uint64("start_height", blockToRetrieve), + zap.Uint64("end_height", latestBlockHeight), + zap.Error(err), + ) + } else { + // no error and we got the header we wanted to get, bump the state and push + // notification about data + failedCycles = 0 + if len(blocks) == 0 { + continue + } + + lb := blocks[len(blocks)-1] + cp.setNextHeight(lb.Height + 1) + + cp.metrics.RecordLastPolledHeight(lb.Height) + + cp.logger.Info("the poller retrieved the blocks from the consumer chain", + zap.Uint64("start_height", blockToRetrieve), + zap.Uint64("end_height", lb.Height)) + + // push the data to the channel + // Note: if the consumer is too slow -- the buffer is full + // the channel will block, and we will stop retrieving data from the node + for _, block := range blocks { + cp.blockInfoChan <- block + } + } } if failedCycles > maxFailedCycles { cp.logger.Fatal("the poller has reached the max failed cycles, exiting") } - select { - case <-time.After(cp.cfg.PollInterval): - - case req := <-cp.skipHeightChan: - // no need to skip heights if the target height is not higher - // than the next height to retrieve - targetHeight := req.height - if targetHeight <= cp.nextHeight { - resp := &skipHeightResponse{ - err: fmt.Errorf( - "the target height %d is not higher than the next height %d to retrieve", - targetHeight, cp.nextHeight)} - req.resp <- resp - continue - } - - // drain blocks that can be skipped from blockInfoChan - cp.clearChanBufferUpToHeight(targetHeight) - - // set the next height to the skip height - cp.nextHeight = targetHeight - - cp.logger.Debug("the poller has skipped height(s)", - zap.Uint64("next_height", req.height)) - - req.resp <- &skipHeightResponse{} - + case <-ticker.C: + continue case <-cp.quit: return } } } -func (cp *ChainPoller) SkipToHeight(height uint64) error { - if !cp.IsRunning() { - return fmt.Errorf("the chain poller is stopped") - } - - respChan := make(chan *skipHeightResponse, 1) - - // this handles the case when the poller is stopped before the - // skip height request is sent - select { - case <-cp.quit: - return fmt.Errorf("the chain poller is stopped") - case cp.skipHeightChan <- &skipHeightRequest{height: height, resp: respChan}: - } - - // this handles the case when the poller is stopped before - // the skip height request is returned - select { - case <-cp.quit: - return fmt.Errorf("the chain poller is stopped") - case resp := <-respChan: - return resp.err - } -} - func (cp *ChainPoller) NextHeight() uint64 { + cp.mu.RLock() + defer cp.mu.RUnlock() + return cp.nextHeight } -func (cp *ChainPoller) clearChanBufferUpToHeight(upToHeight uint64) { - for len(cp.blockInfoChan) > 0 { - block := <-cp.blockInfoChan - if block.Height+1 >= upToHeight { - break - } - } +func (cp *ChainPoller) setNextHeight(height uint64) { + cp.mu.Lock() + defer cp.mu.Unlock() + + cp.nextHeight = height } diff --git a/finality-provider/service/chain_poller_test.go b/finality-provider/service/chain_poller_test.go index da4b700e..f1970523 100644 --- a/finality-provider/service/chain_poller_test.go +++ b/finality-provider/service/chain_poller_test.go @@ -2,13 +2,11 @@ package service_test import ( "math/rand" - "sync" "testing" "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - "go.uber.org/zap" fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" "github.com/babylonlabs-io/finality-provider/finality-provider/service" @@ -23,36 +21,36 @@ import ( func FuzzChainPoller_Start(f *testing.F) { testutil.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { + t.Parallel() r := rand.New(rand.NewSource(seed)) currentHeight := uint64(r.Int63n(100) + 1) startHeight := currentHeight + 1 endHeight := startHeight + uint64(r.Int63n(10)+1) + currentBlockRes := &types.BlockInfo{ + Height: endHeight, + } ctl := gomock.NewController(t) - mockBabylonController := mocks.NewMockClientController(ctl) - mockBabylonController.EXPECT().Close().Return(nil).AnyTimes() mockConsumerController := mocks.NewMockConsumerController(ctl) + mockConsumerController.EXPECT().Close().Return(nil).AnyTimes() mockConsumerController.EXPECT().QueryActivatedHeight().Return(uint64(1), nil).AnyTimes() - - mockConsumerController.EXPECT().QueryLatestBlockHeight().Return(currentHeight, nil).AnyTimes() + mockConsumerController.EXPECT().QueryLatestBlockHeight().Return(endHeight, nil).AnyTimes() + mockConsumerController.EXPECT().QueryBlock(endHeight).Return(currentBlockRes, nil).AnyTimes() + pollerCfg := fpcfg.DefaultChainPollerConfig() for i := startHeight; i <= endHeight; i++ { - resBlock := &types.BlockInfo{ + resBlocks := []*types.BlockInfo{{ Height: i, - } - mockConsumerController.EXPECT().QueryBlock(i).Return(resBlock, nil).AnyTimes() - } + }} - logger, err := zap.NewDevelopment() - require.NoError(t, err) + mockConsumerController.EXPECT().QueryBlocks(i, endHeight, pollerCfg.PollSize).Return(resBlocks, nil).AnyTimes() + } - // TODO: use mock metrics m := metrics.NewFpMetrics() - pollerCfg := fpcfg.DefaultChainPollerConfig() pollerCfg.PollInterval = 10 * time.Millisecond - poller := service.NewChainPoller(logger, &pollerCfg, mockBabylonController, mockConsumerController, m) - err = poller.Start(startHeight) + poller := service.NewChainPoller(testutil.GetTestLogger(t), &pollerCfg, mockConsumerController, m) + err := poller.Start(startHeight) require.NoError(t, err) defer func() { err := poller.Stop() @@ -69,87 +67,3 @@ func FuzzChainPoller_Start(f *testing.F) { } }) } - -// FuzzChainPoller_SkipHeight tests the functionality of SkipHeight -func FuzzChainPoller_SkipHeight(f *testing.F) { - testutil.AddRandomSeedsToFuzzer(f, 10) - - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - currentHeight := uint64(r.Int63n(100) + 1) - startHeight := currentHeight + 1 - endHeight := startHeight + uint64(r.Int63n(10)+2) - skipHeight := endHeight + uint64(r.Int63n(10)+1) - - ctl := gomock.NewController(t) - mockBabylonController := mocks.NewMockClientController(ctl) - mockConsumerController := mocks.NewMockConsumerController(ctl) - mockBabylonController.EXPECT().Close().Return(nil).AnyTimes() - mockConsumerController.EXPECT().QueryActivatedHeight().Return(uint64(1), nil).AnyTimes() - mockConsumerController.EXPECT().QueryLatestBlockHeight().Return(currentHeight, nil).AnyTimes() - - for i := startHeight; i <= skipHeight; i++ { - resBlock := &types.BlockInfo{ - Height: i, - } - mockConsumerController.EXPECT().QueryBlock(i).Return(resBlock, nil).AnyTimes() - } - - logger, err := zap.NewDevelopment() - require.NoError(t, err) - - // TODO: use mock metrics - m := metrics.NewFpMetrics() - pollerCfg := fpcfg.DefaultChainPollerConfig() - pollerCfg.PollInterval = 1 * time.Second - poller := service.NewChainPoller(logger, &pollerCfg, mockBabylonController, mockConsumerController, m) - // should expect error if the poller is not started - err = poller.SkipToHeight(skipHeight) - require.Error(t, err) - err = poller.Start(startHeight) - require.NoError(t, err) - defer func() { - err := poller.Stop() - require.NoError(t, err) - // should expect error if the poller is stopped - err = poller.SkipToHeight(skipHeight) - require.Error(t, err) - }() - - var wg sync.WaitGroup - wg.Add(1) - go func() { - wg.Done() - // insert a skipToHeight request with height lower than the next - // height to retrieve, expecting an error - err = poller.SkipToHeight(poller.NextHeight() - 1) - require.Error(t, err) - // insert a skipToHeight request with a height higher than the - // next height to retrieve - err = poller.SkipToHeight(skipHeight) - require.NoError(t, err) - }() - - skipped := false - for i := startHeight; i <= endHeight; i++ { - if skipped { - break - } - select { - case info := <-poller.GetBlockInfoChan(): - if info.Height == skipHeight { - skipped = true - } else { - require.Equal(t, i, info.Height) - } - case <-time.After(10 * time.Second): - t.Fatalf("Failed to get block info") - } - } - - wg.Wait() - - require.Equal(t, skipHeight+1, poller.NextHeight()) - }) -} diff --git a/finality-provider/service/client/rpcclient.go b/finality-provider/service/client/rpcclient.go index 6d5d47f2..a5cb15c2 100644 --- a/finality-provider/service/client/rpcclient.go +++ b/finality-provider/service/client/rpcclient.go @@ -18,14 +18,18 @@ type FinalityProviderServiceGRpcClient struct { } // NewFinalityProviderServiceGRpcClient creates a new GRPC connection with finality provider daemon. -func NewFinalityProviderServiceGRpcClient(remoteAddr string) (client *FinalityProviderServiceGRpcClient, cleanUp func(), err error) { +func NewFinalityProviderServiceGRpcClient(remoteAddr string) (*FinalityProviderServiceGRpcClient, func() error, error) { conn, err := grpc.NewClient(remoteAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, nil, fmt.Errorf("failed to build gRPC connection to %s: %w", remoteAddr, err) } - cleanUp = func() { - conn.Close() + cleanUp := func() error { + if conn == nil { + return nil + } + + return conn.Close() } return &FinalityProviderServiceGRpcClient{ @@ -43,28 +47,12 @@ func (c *FinalityProviderServiceGRpcClient) GetInfo(ctx context.Context) (*proto return res, nil } -func (c *FinalityProviderServiceGRpcClient) RegisterFinalityProvider( - ctx context.Context, - fpPk *bbntypes.BIP340PubKey, - passphrase string, -) (*proto.RegisterFinalityProviderResponse, error) { - - req := &proto.RegisterFinalityProviderRequest{BtcPk: fpPk.MarshalHex(), Passphrase: passphrase} - res, err := c.client.RegisterFinalityProvider(ctx, req) - if err != nil { - return nil, err - } - - return res, nil -} - func (c *FinalityProviderServiceGRpcClient) CreateFinalityProvider( ctx context.Context, - keyName, chainID, eotsPkHex, passphrase, hdPath string, + keyName, chainID, eotsPkHex, passphrase string, description types.Description, commission *sdkmath.LegacyDec, ) (*proto.CreateFinalityProviderResponse, error) { - descBytes, err := description.Marshal() if err != nil { return nil, err @@ -74,7 +62,6 @@ func (c *FinalityProviderServiceGRpcClient) CreateFinalityProvider( KeyName: keyName, ChainId: chainID, Passphrase: passphrase, - HdPath: hdPath, Description: descBytes, Commission: commission.String(), EotsPkHex: eotsPkHex, @@ -88,11 +75,18 @@ func (c *FinalityProviderServiceGRpcClient) CreateFinalityProvider( return res, nil } -func (c *FinalityProviderServiceGRpcClient) AddFinalitySignature(ctx context.Context, fpPk string, height uint64, appHash []byte) (*proto.AddFinalitySignatureResponse, error) { +func (c *FinalityProviderServiceGRpcClient) AddFinalitySignature( + ctx context.Context, + fpPk string, + height uint64, + appHash []byte, + checkDoubleSign bool, +) (*proto.AddFinalitySignatureResponse, error) { req := &proto.AddFinalitySignatureRequest{ - BtcPk: fpPk, - Height: height, - AppHash: appHash, + BtcPk: fpPk, + Height: height, + AppHash: appHash, + CheckDoubleSign: checkDoubleSign, } res, err := c.client.AddFinalitySignature(ctx, req) @@ -103,6 +97,19 @@ func (c *FinalityProviderServiceGRpcClient) AddFinalitySignature(ctx context.Con return res, nil } +func (c *FinalityProviderServiceGRpcClient) UnjailFinalityProvider(ctx context.Context, fpPk string) (*proto.UnjailFinalityProviderResponse, error) { + req := &proto.UnjailFinalityProviderRequest{ + BtcPk: fpPk, + } + + res, err := c.client.UnjailFinalityProvider(ctx, req) + if err != nil { + return nil, err + } + + return res, nil +} + func (c *FinalityProviderServiceGRpcClient) QueryFinalityProviderList(ctx context.Context) (*proto.QueryFinalityProviderListResponse, error) { req := &proto.QueryFinalityProviderListRequest{} res, err := c.client.QueryFinalityProviderList(ctx, req) @@ -113,6 +120,7 @@ func (c *FinalityProviderServiceGRpcClient) QueryFinalityProviderList(ctx contex return res, nil } +// QueryFinalityProviderInfo - gets the finality provider data from local store func (c *FinalityProviderServiceGRpcClient) QueryFinalityProviderInfo(ctx context.Context, fpPk *bbntypes.BIP340PubKey) (*proto.QueryFinalityProviderResponse, error) { req := &proto.QueryFinalityProviderRequest{BtcPk: fpPk.MarshalHex()} res, err := c.client.QueryFinalityProvider(ctx, req) @@ -123,16 +131,35 @@ func (c *FinalityProviderServiceGRpcClient) QueryFinalityProviderInfo(ctx contex return res, nil } -func (c *FinalityProviderServiceGRpcClient) SignMessageFromChainKey( - ctx context.Context, - keyName, passphrase, hdPath string, - rawMsgToSign []byte, -) (*proto.SignMessageFromChainKeyResponse, error) { - req := &proto.SignMessageFromChainKeyRequest{ - MsgToSign: rawMsgToSign, - KeyName: keyName, - Passphrase: passphrase, - HdPath: hdPath, +// EditFinalityProvider - edit the finality provider data. +func (c *FinalityProviderServiceGRpcClient) EditFinalityProvider( + ctx context.Context, fpPk *bbntypes.BIP340PubKey, desc *proto.Description, rate string) error { + if rate == "" { + currentProvider, err := c.QueryFinalityProviderInfo(ctx, fpPk) + if err != nil { + return fmt.Errorf("failed to get current provider info: %w", err) + } + rate = currentProvider.FinalityProvider.Commission + } + + req := &proto.EditFinalityProviderRequest{BtcPk: fpPk.MarshalHex(), Description: desc, Commission: rate} + + _, err := c.client.EditFinalityProvider(ctx, req) + if err != nil { + return fmt.Errorf("failed to edit finality provider: %w", err) + } + + return nil +} + +// UnsafeRemoveMerkleProof - remove all proofs up to target height +func (c *FinalityProviderServiceGRpcClient) UnsafeRemoveMerkleProof( + ctx context.Context, fpPk *bbntypes.BIP340PubKey, chainID string, targetHeight uint64) error { + req := &proto.RemoveMerkleProofRequest{BtcPkHex: fpPk.MarshalHex(), ChainId: chainID, TargetHeight: targetHeight} + _, err := c.client.UnsafeRemoveMerkleProof(ctx, req) + if err != nil { + return err } - return c.client.SignMessageFromChainKey(ctx, req) + + return nil } diff --git a/finality-provider/service/eots_manager_adapter.go b/finality-provider/service/eots_manager_adapter.go index e5c20dad..882dec33 100644 --- a/finality-provider/service/eots_manager_adapter.go +++ b/finality-provider/service/eots_manager_adapter.go @@ -4,19 +4,20 @@ import ( "fmt" bbntypes "github.com/babylonlabs-io/babylon/types" - "github.com/babylonlabs-io/finality-provider/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/cometbft/cometbft/crypto/tmhash" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/babylonlabs-io/finality-provider/types" ) -func (fp *FinalityProviderInstance) GetPubRandList(startHeight uint64, numPubRand uint64) ([]*btcec.FieldVal, error) { +func (fp *FinalityProviderInstance) GetPubRandList(startHeight uint64, numPubRand uint32) ([]*btcec.FieldVal, error) { pubRandList, err := fp.em.CreateRandomnessPairList( fp.btcPk.MustMarshal(), fp.GetChainID(), startHeight, - uint32(numPubRand), + numPubRand, fp.passphrase, ) if err != nil { @@ -26,7 +27,6 @@ func (fp *FinalityProviderInstance) GetPubRandList(startHeight uint64, numPubRan return pubRandList, nil } -// TODO: have this function in Babylon side func getHashToSignForCommitPubRand(startHeight uint64, numPubRand uint64, commitment []byte) ([]byte, error) { hasher := tmhash.New() if _, err := hasher.Write(sdk.Uint64ToBigEndian(startHeight)); err != nil { @@ -38,6 +38,7 @@ func getHashToSignForCommitPubRand(startHeight uint64, numPubRand uint64, commit if _, err := hasher.Write(commitment); err != nil { return nil, err } + return hasher.Sum(nil), nil } @@ -51,7 +52,6 @@ func (fp *FinalityProviderInstance) SignPubRandCommit(startHeight uint64, numPub return fp.em.SignSchnorrSig(fp.btcPk.MustMarshal(), hash, fp.passphrase) } -// TODO: have this function in Babylon side func getMsgToSignForVote(blockHeight uint64, blockHash []byte) []byte { return append(sdk.Uint64ToBigEndian(blockHeight), blockHash...) } diff --git a/finality-provider/service/errors.go b/finality-provider/service/errors.go index c499215e..3f261ab4 100644 --- a/finality-provider/service/errors.go +++ b/finality-provider/service/errors.go @@ -1,7 +1,25 @@ package service -import "errors" +import ( + "errors" + "fmt" + + bbntypes "github.com/babylonlabs-io/babylon/types" +) + +const instanceTerminatingMsg = "terminating the finality-provider instance due to critical error" + +type CriticalError struct { + err error + fpBtcPk *bbntypes.BIP340PubKey +} + +func (ce *CriticalError) Error() string { + return fmt.Sprintf("critical err on finality-provider %s: %s", ce.fpBtcPk.MarshalHex(), ce.err.Error()) +} var ( ErrFinalityProviderShutDown = errors.New("the finality provider instance is shutting down") + ErrFinalityProviderJailed = errors.New("the finality provider instance is jailed") + ErrFinalityProviderSlashed = errors.New("the finality provider instance is slashed") ) diff --git a/finality-provider/service/event_loops.go b/finality-provider/service/event_loops.go new file mode 100644 index 00000000..b01c20ce --- /dev/null +++ b/finality-provider/service/event_loops.go @@ -0,0 +1,230 @@ +package service + +import ( + "errors" + "fmt" + "time" + + sdkmath "cosmossdk.io/math" + bbntypes "github.com/babylonlabs-io/babylon/types" + btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "go.uber.org/zap" + + "github.com/babylonlabs-io/finality-provider/finality-provider/proto" +) + +type CreateFinalityProviderRequest struct { + chainID string + fpAddr sdk.AccAddress + btcPubKey *bbntypes.BIP340PubKey + pop *btcstakingtypes.ProofOfPossessionBTC + description *stakingtypes.Description + commission *sdkmath.LegacyDec + errResponse chan error + successResponse chan *RegisterFinalityProviderResponse +} + +type RegisterFinalityProviderResponse struct { + txHash string +} + +type CreateFinalityProviderResult struct { + FpInfo *proto.FinalityProviderInfo + TxHash string +} + +type UnjailFinalityProviderRequest struct { + btcPubKey *bbntypes.BIP340PubKey + errResponse chan error + successResponse chan *UnjailFinalityProviderResponse +} + +type UnjailFinalityProviderResponse struct { + TxHash string +} + +// event loop for critical errors +func (app *FinalityProviderApp) monitorCriticalErr() { + defer app.wg.Done() + + var criticalErr *CriticalError + + for { + select { + case criticalErr = <-app.criticalErrChan: + fpi, err := app.GetFinalityProviderInstance() + if err != nil { + app.logger.Debug("the finality-provider instance is already shutdown", + zap.String("pk", criticalErr.fpBtcPk.MarshalHex())) + + continue + } + if errors.Is(criticalErr.err, ErrFinalityProviderSlashed) { + app.setFinalityProviderSlashed(fpi) + app.logger.Debug("the finality-provider has been slashed", + zap.String("pk", criticalErr.fpBtcPk.MarshalHex())) + + continue + } + app.logger.Fatal(instanceTerminatingMsg, + zap.String("pk", criticalErr.fpBtcPk.MarshalHex()), zap.Error(criticalErr.err)) + case <-app.quit: + app.logger.Info("exiting monitor critical error loop") + + return + } + } +} + +// event loop for handling fp registration +func (app *FinalityProviderApp) registrationLoop() { + defer app.wg.Done() + for { + select { + case req := <-app.createFinalityProviderRequestChan: + // we won't do any retries here to not block the loop for more important messages. + // Most probably it fails due so some user error so we just return the error to the user. + // TODO: need to start passing context here to be able to cancel the request in case of app quiting + popBytes, err := req.pop.Marshal() + if err != nil { + req.errResponse <- err + + continue + } + + desBytes, err := req.description.Marshal() + if err != nil { + req.errResponse <- err + + continue + } + res, err := app.cc.RegisterFinalityProvider( + req.chainID, + req.btcPubKey.MustToBTCPK(), + popBytes, + req.commission, + desBytes, + ) + + if err != nil { + app.logger.Error( + "failed to register finality-provider", + zap.String("pk", req.btcPubKey.MarshalHex()), + zap.Error(err), + ) + req.errResponse <- err + + continue + } + + app.logger.Info( + "successfully registered finality-provider on babylon", + zap.String("btc_pk", req.btcPubKey.MarshalHex()), + zap.String("fp_addr", req.fpAddr.String()), + zap.String("txHash", res.TxHash), + ) + + app.metrics.RecordFpStatus(req.btcPubKey.MarshalHex(), proto.FinalityProviderStatus_REGISTERED) + + req.successResponse <- &RegisterFinalityProviderResponse{ + txHash: res.TxHash, + } + case <-app.quit: + app.logger.Info("exiting registration loop") + + return + } + } +} + +// event loop for unjailing fp +func (app *FinalityProviderApp) unjailFpLoop() { + defer app.wg.Done() + for { + select { + case req := <-app.unjailFinalityProviderRequestChan: + pkHex := req.btcPubKey.MarshalHex() + isSlashed, isJailed, err := app.consumerCon.QueryFinalityProviderSlashedOrJailed(req.btcPubKey.MustToBTCPK()) + if err != nil { + req.errResponse <- fmt.Errorf("failed to query jailing status of the finality provider %s: %w", pkHex, err) + + continue + } + if isSlashed { + req.errResponse <- fmt.Errorf("the finality provider %s is already slashed", pkHex) + + continue + } + if !isJailed { + req.errResponse <- fmt.Errorf("the finality provider %s is not jailed", pkHex) + + continue + } + + res, err := app.consumerCon.UnjailFinalityProvider( + req.btcPubKey.MustToBTCPK(), + ) + + if err != nil { + app.logger.Error( + "failed to unjail finality-provider", + zap.String("pk", req.btcPubKey.MarshalHex()), + zap.Error(err), + ) + req.errResponse <- err + + continue + } + + app.logger.Info( + "successfully unjailed finality-provider on babylon", + zap.String("btc_pk", req.btcPubKey.MarshalHex()), + zap.String("txHash", res.TxHash), + ) + + // set the status to INACTIVE by default + // the status will be changed to ACTIVE + // if it has voting power for the next height + app.fps.MustSetFpStatus(req.btcPubKey.MustToBTCPK(), proto.FinalityProviderStatus_INACTIVE) + + req.successResponse <- &UnjailFinalityProviderResponse{ + TxHash: res.TxHash, + } + case <-app.quit: + app.logger.Info("exiting unjailing fp loop") + + return + } + } +} + +// event loop for metrics update +func (app *FinalityProviderApp) metricsUpdateLoop() { + defer app.wg.Done() + + interval := app.config.Metrics.UpdateInterval + app.logger.Info("starting metrics update loop", + zap.Float64("interval seconds", interval.Seconds())) + + updateTicker := time.NewTicker(interval) + defer updateTicker.Stop() + + for { + select { + case <-updateTicker.C: + app.fpInsMu.RLock() + if app.fpIns != nil { + if fp := app.fpIns.GetStoreFinalityProvider(); fp != nil { + app.metrics.UpdateFpMetrics(fp) + } + } + app.fpInsMu.RUnlock() + case <-app.quit: + app.logger.Info("exiting metrics update loop") + + return + } + } +} diff --git a/finality-provider/service/fp_instance.go b/finality-provider/service/fp_instance.go index e250a129..4f933879 100644 --- a/finality-provider/service/fp_instance.go +++ b/finality-provider/service/fp_instance.go @@ -1,9 +1,9 @@ package service import ( - "encoding/json" "errors" "fmt" + "math" "strings" "sync" "time" @@ -12,7 +12,6 @@ import ( bbntypes "github.com/babylonlabs-io/babylon/types" ftypes "github.com/babylonlabs-io/babylon/x/finality/types" "github.com/btcsuite/btcd/btcec/v2" - "github.com/cosmos/relayer/v2/relayer/provider" "github.com/gogo/protobuf/jsonpb" "go.uber.org/atomic" "go.uber.org/zap" @@ -47,8 +46,6 @@ type FinalityProviderInstance struct { criticalErrChan chan<- *CriticalError isStarted *atomic.Bool - inSync *atomic.Bool - isLagging *atomic.Bool wg sync.WaitGroup quit chan struct{} @@ -71,35 +68,11 @@ func NewFinalityProviderInstance( ) (*FinalityProviderInstance, error) { sfp, err := s.GetFinalityProvider(fpPk.MustToBTCPK()) if err != nil { - return nil, fmt.Errorf("failed to retrive the finality-provider %s from DB: %w", fpPk.MarshalHex(), err) + return nil, fmt.Errorf("failed to retrieve the finality provider %s from DB: %w", fpPk.MarshalHex(), err) } - // ensure the finality-provider has been registered - if sfp.Status < proto.FinalityProviderStatus_REGISTERED { - return nil, fmt.Errorf("the finality-provider %s has not been registered", sfp.KeyName) - } - - return newFinalityProviderInstanceFromStore(sfp, cfg, s, prStore, cc, consumerCon, em, metrics, passphrase, errChan, logger) -} - -// TestNewUnregisteredFinalityProviderInstance creates a FinalityProviderInstance without checking registration status -// Note: this is only for testing purposes -func TestNewUnregisteredFinalityProviderInstance( - fpPk *bbntypes.BIP340PubKey, - cfg *fpcfg.Config, - s *store.FinalityProviderStore, - prStore *store.PubRandProofStore, - cc ccapi.ClientController, - consumerCon ccapi.ConsumerController, - em eotsmanager.EOTSManager, - metrics *metrics.FpMetrics, - passphrase string, - errChan chan<- *CriticalError, - logger *zap.Logger, -) (*FinalityProviderInstance, error) { - sfp, err := s.GetFinalityProvider(fpPk.MustToBTCPK()) - if err != nil { - return nil, fmt.Errorf("failed to retrive the finality-provider %s from DB: %w", fpPk.MarshalHex(), err) + if sfp.Status == proto.FinalityProviderStatus_SLASHED { + return nil, fmt.Errorf("the finality provider instance is already slashed") } return newFinalityProviderInstanceFromStore(sfp, cfg, s, prStore, cc, consumerCon, em, metrics, passphrase, errChan, logger) @@ -121,13 +94,11 @@ func newFinalityProviderInstanceFromStore( ) (*FinalityProviderInstance, error) { return &FinalityProviderInstance{ btcPk: bbntypes.NewBIP340PubKeyFromBTCPK(sfp.BtcPk), - fpState: NewFpState(sfp, s), - pubRandState: NewPubRandState(prStore), + fpState: newFpState(sfp, s), + pubRandState: newPubRandState(prStore), cfg: cfg, logger: logger, isStarted: atomic.NewBool(false), - inSync: atomic.NewBool(false), - isLagging: atomic.NewBool(false), criticalErrChan: errChan, passphrase: passphrase, em: em, @@ -142,17 +113,20 @@ func (fp *FinalityProviderInstance) Start() error { return fmt.Errorf("the finality-provider instance %s is already started", fp.GetBtcPkHex()) } - fp.logger.Info("Starting finality-provider instance", zap.String("pk", fp.GetBtcPkHex())) + if fp.IsJailed() { + fp.logger.Warn("the finality provider is jailed", + zap.String("pk", fp.GetBtcPkHex())) + } - startHeight, err := fp.getPollerStartingHeight() + startHeight, err := fp.DetermineStartHeight() if err != nil { return fmt.Errorf("failed to get the start height: %w", err) } - fp.logger.Info("starting the finality provider", + fp.logger.Info("starting the finality provider instance", zap.String("pk", fp.GetBtcPkHex()), zap.Uint64("height", startHeight)) - poller := NewChainPoller(fp.logger, fp.cfg.PollerConfig, fp.cc, fp.consumerCon, fp.metrics) + poller := NewChainPoller(fp.logger, fp.cfg.PollerConfig, fp.consumerCon, fp.metrics) if err := poller.Start(startHeight); err != nil { return fmt.Errorf("failed to start the poller with start height %d: %w", startHeight, err) @@ -160,10 +134,10 @@ func (fp *FinalityProviderInstance) Start() error { fp.poller = poller fp.quit = make(chan struct{}) - fp.wg.Add(1) + + fp.wg.Add(2) go fp.finalitySigSubmissionLoop() - fp.wg.Add(1) - go fp.randomnessCommitmentLoop(startHeight) + go fp.randomnessCommitmentLoop() return nil } @@ -187,32 +161,82 @@ func (fp *FinalityProviderInstance) Stop() error { return nil } +func (fp *FinalityProviderInstance) GetConfig() *fpcfg.Config { + return fp.cfg +} + func (fp *FinalityProviderInstance) IsRunning() bool { return fp.isStarted.Load() } +// IsJailed returns true if fp is JAILED +// NOTE: it retrieves the the status from the db to +// ensure status is up-to-date +func (fp *FinalityProviderInstance) IsJailed() bool { + storedFp, err := fp.fpState.s.GetFinalityProvider(fp.GetBtcPk()) + if err != nil { + panic(fmt.Errorf("failed to retrieve the finality provider %s from db: %w", fp.GetBtcPkHex(), err)) + } + + if storedFp.Status != fp.GetStatus() { + fp.MustSetStatus(storedFp.Status) + } + + return fp.GetStatus() == proto.FinalityProviderStatus_JAILED +} + func (fp *FinalityProviderInstance) finalitySigSubmissionLoop() { defer fp.wg.Done() for { select { case <-time.After(fp.cfg.SignatureSubmissionInterval): - pollerBlocks := fp.getAllBlocksFromChan() + // start submission in the first iteration + pollerBlocks := fp.getBatchBlocksFromChan() if len(pollerBlocks) == 0 { continue } + + if fp.IsJailed() { + fp.logger.Warn("the finality-provider is jailed", + zap.String("pk", fp.GetBtcPkHex()), + ) + + continue + } + targetHeight := pollerBlocks[len(pollerBlocks)-1].Height fp.logger.Debug("the finality-provider received new block(s), start processing", zap.String("pk", fp.GetBtcPkHex()), zap.Uint64("start_height", pollerBlocks[0].Height), zap.Uint64("end_height", targetHeight), ) - res, err := fp.retrySubmitSigsUntilFinalized(pollerBlocks) + + processedBlocks, err := fp.processBlocksToVote(pollerBlocks) + if err != nil { + fp.reportCriticalErr(err) + + continue + } + + if len(processedBlocks) == 0 { + continue + } + + res, err := fp.retrySubmitSigsUntilFinalized(processedBlocks) if err != nil { fp.metrics.IncrementFpTotalFailedVotes(fp.GetBtcPkHex()) + if errors.Is(err, ErrFinalityProviderJailed) { + fp.MustSetStatus(proto.FinalityProviderStatus_JAILED) + fp.logger.Debug("the finality-provider has been jailed", + zap.String("pk", fp.GetBtcPkHex())) + + continue + } if !errors.Is(err, ErrFinalityProviderShutDown) { fp.reportCriticalErr(err) } + continue } if res == nil { @@ -229,35 +253,80 @@ func (fp *FinalityProviderInstance) finalitySigSubmissionLoop() { zap.Uint64("end_height", targetHeight), zap.String("tx_hash", res.TxHash), ) - case <-fp.quit: fp.logger.Info("the finality signature submission loop is closing") + return } } } -func (fp *FinalityProviderInstance) getAllBlocksFromChan() []*types.BlockInfo { +// processBlocksToVote processes a batch a blocks and picks ones that need to vote +// it also updates the fp instance status according to the block's voting power +func (fp *FinalityProviderInstance) processBlocksToVote(blocks []*types.BlockInfo) ([]*types.BlockInfo, error) { + processedBlocks := make([]*types.BlockInfo, 0, len(blocks)) + + var hasPower bool + var err error + for _, b := range blocks { + blk := *b + if blk.Height <= fp.GetLastVotedHeight() { + fp.logger.Debug( + "the block height is lower than last processed height", + zap.String("pk", fp.GetBtcPkHex()), + zap.Uint64("block_height", blk.Height), + zap.Uint64("last_voted_height", fp.GetLastVotedHeight()), + ) + + continue + } + + // check whether the finality provider has voting power + hasPower, err = fp.GetVotingPowerWithRetry(blk.Height) + if err != nil { + return nil, fmt.Errorf("failed to get voting power for height %d: %w", blk.Height, err) + } + if !hasPower { + fp.logger.Debug( + "the finality-provider does not have voting power", + zap.String("pk", fp.GetBtcPkHex()), + zap.Uint64("block_height", blk.Height), + ) + + // the finality provider does not have voting power + // and it will never will at this block, so continue + fp.metrics.IncrementFpTotalBlocksWithoutVotingPower(fp.GetBtcPkHex()) + + continue + } + + processedBlocks = append(processedBlocks, &blk) + } + + // update fp status according to the power for the last block + if hasPower && fp.GetStatus() != proto.FinalityProviderStatus_ACTIVE { + fp.MustSetStatus(proto.FinalityProviderStatus_ACTIVE) + } + + if !hasPower && fp.GetStatus() == proto.FinalityProviderStatus_ACTIVE { + fp.MustSetStatus(proto.FinalityProviderStatus_INACTIVE) + } + + return processedBlocks, nil +} + +func (fp *FinalityProviderInstance) getBatchBlocksFromChan() []*types.BlockInfo { var pollerBlocks []*types.BlockInfo for { select { case b := <-fp.poller.GetBlockInfoChan(): - // TODO: in cases of catching up, this could issue frequent RPC calls - shouldProcess, err := fp.shouldProcessBlock(b) - if err != nil { - if !errors.Is(err, ErrFinalityProviderShutDown) { - fp.reportCriticalErr(err) - } - break - } - if shouldProcess { - pollerBlocks = append(pollerBlocks, b) - } + pollerBlocks = append(pollerBlocks, b) if len(pollerBlocks) == int(fp.cfg.BatchSubmissionSize) { return pollerBlocks } case <-fp.quit: fp.logger.Info("the get all blocks loop is closing") + return nil default: return pollerBlocks @@ -265,75 +334,28 @@ func (fp *FinalityProviderInstance) getAllBlocksFromChan() []*types.BlockInfo { } } -func (fp *FinalityProviderInstance) shouldProcessBlock(b *types.BlockInfo) (bool, error) { - // check whether the block has been processed before - if fp.hasProcessed(b) { - return false, nil - } - - // check whether the finality provider has voting power - hasVp, err := fp.hasVotingPower(b.Height) - if err != nil { - return false, err - } - if !hasVp { - // the finality provider does not have voting power - // and it will never will at this block - fp.MustSetLastProcessedHeight(b.Height) - fp.metrics.IncrementFpTotalBlocksWithoutVotingPower(fp.GetBtcPkHex()) - return false, nil - } - - return true, nil -} - -func (fp *FinalityProviderInstance) randomnessCommitmentLoop(startHeight uint64) { +func (fp *FinalityProviderInstance) randomnessCommitmentLoop() { defer fp.wg.Done() - commitRandTicker := time.NewTicker(fp.cfg.RandomnessCommitInterval) - defer commitRandTicker.Stop() - - lastCommittedHeight, err := fp.GetLastCommittedHeight() - if err != nil { - fp.logger.Fatal("Error getting last committed height while starting the randomness commitment loop", zap.Error(err)) - return - } - - // if there is no committed randomness, we need to commit the first randomness - if lastCommittedHeight == uint64(0) { - txRes, err := fp.retryCommitPubRandUntilMaxRetry(startHeight) - if err != nil { - fp.metrics.IncrementFpTotalFailedRandomness(fp.GetBtcPkHex()) - fp.reportCriticalErr(err) - return - } - if txRes == nil { - fp.logger.Fatal( - "Error submitting the first randomness", - zap.String("consumer_id", string(fp.GetChainID())), - zap.String("pk", fp.GetBtcPkHex()), - ) - return - } - fp.logger.Info( - "successfully committed public randomness to the consumer chain", - zap.String("consumer_id", string(fp.GetChainID())), - zap.String("pk", fp.GetBtcPkHex()), - ) - } - for { select { - case <-commitRandTicker.C: - tipBlockHeight, err := fp.getLatestBlockHeightWithRetry() + case <-time.After(fp.cfg.RandomnessCommitInterval): + // start randomness commit in the first iteration + should, startHeight, err := fp.ShouldCommitRandomness() if err != nil { fp.reportCriticalErr(err) + continue } - txRes, err := fp.retryCommitPubRandUntilBlockFinalized(tipBlockHeight) + if !should { + continue + } + + txRes, err := fp.CommitPubRand(startHeight) if err != nil { fp.metrics.IncrementFpTotalFailedRandomness(fp.GetBtcPkHex()) fp.reportCriticalErr(err) + continue } // txRes could be nil if no need to commit more randomness @@ -345,45 +367,69 @@ func (fp *FinalityProviderInstance) randomnessCommitmentLoop(startHeight uint64) zap.String("tx_hash", txRes.TxHash), ) } - case <-fp.quit: fp.logger.Info("the randomness commitment loop is closing") + return } } } -func (fp *FinalityProviderInstance) hasProcessed(b *types.BlockInfo) bool { - if b.Height <= fp.GetLastProcessedHeight() { - fp.logger.Debug( - "the block has been processed before, skip processing", - zap.String("pk", fp.GetBtcPkHex()), - zap.Uint64("block_height", b.Height), - zap.Uint64("last_processed_height", fp.GetLastProcessedHeight()), - ) - return true +// ShouldCommitRandomness determines whether a new randomness commit should be made +// Note: there's a delay from the commit is submitted to it is available to use due +// to timestamping. Therefore, the start height of the commit should consider an +// estimated delay. +// If randomness should be committed, start height of the commit will be returned +func (fp *FinalityProviderInstance) ShouldCommitRandomness() (bool, uint64, error) { + lastCommittedHeight, err := fp.GetLastCommittedHeight() + if err != nil { + return false, 0, fmt.Errorf("failed to get last committed height: %w", err) } - return false -} - -// hasVotingPower checks whether the finality provider has voting power for the given block -func (fp *FinalityProviderInstance) hasVotingPower(blockHeight uint64) (bool, error) { - hasPower, err := fp.GetVotingPowerWithRetry(blockHeight) + tipHeight, err := fp.consumerCon.QueryLatestBlockHeight() if err != nil { - return false, err + return false, 0, fmt.Errorf("failed to get the last block: %w", err) } - if !hasPower { + + tipHeightWithDelay := tipHeight + uint64(fp.cfg.TimestampingDelayBlocks) + + var startHeight uint64 + switch { + case lastCommittedHeight < tipHeightWithDelay: + // the start height should consider the timestamping delay + // as it is only available to use after tip height + estimated timestamping delay + startHeight = tipHeightWithDelay + case lastCommittedHeight < tipHeightWithDelay+uint64(fp.cfg.NumPubRand): + startHeight = lastCommittedHeight + 1 + default: + // the randomness is sufficient, no need to make another commit fp.logger.Debug( - "the finality-provider does not have voting power", + "the finality-provider has sufficient public randomness, skip committing more", zap.String("pk", fp.GetBtcPkHex()), - zap.Uint64("block_height", blockHeight), + zap.Uint64("tip_height", tipHeight), + zap.Uint64("last_committed_height", lastCommittedHeight), ) - return false, nil + return false, 0, nil + } + + fp.logger.Debug( + "the finality-provider should commit randomness", + zap.String("pk", fp.GetBtcPkHex()), + zap.Uint64("tip_height", tipHeight), + zap.Uint64("last_committed_height", lastCommittedHeight), + ) + + activationBlkHeight, err := fp.consumerCon.QueryFinalityActivationBlockHeight() + if err != nil { + return false, 0, err } - return true, nil + // make sure that the start height is at least the finality activation height + // and updated to generate the list with the same as the committed height. + startHeight = max(startHeight, activationBlkHeight) + + return true, startHeight, nil } func (fp *FinalityProviderInstance) reportCriticalErr(err error) { @@ -406,7 +452,10 @@ func (fp *FinalityProviderInstance) retrySubmitSigsUntilFinalized(targetBlocks [ // First iteration happens before the loop for { // Attempt submission immediately - res, err := fp.SubmitBatchFinalitySignatures(targetBlocks) + // error will be returned if max retries have been reached + var res *types.TxResponse + var err error + res, err = fp.SubmitBatchFinalitySignatures(targetBlocks) if err != nil { fp.logger.Debug( "failed to submit finality signature to the consumer chain", @@ -434,8 +483,8 @@ func (fp *FinalityProviderInstance) retrySubmitSigsUntilFinalized(targetBlocks [ return res, nil } - // Periodically query the index block to check whether it is finalized - finalized, err := fp.consumerCon.QueryIsBlockFinalized(targetHeight) + // periodically query the index block to be later checked whether it is Finalized + finalized, err := fp.checkBlockFinalization(targetHeight) if err != nil { return nil, fmt.Errorf("failed to query block finalization at height %v: %w", targetHeight, err) } @@ -445,165 +494,43 @@ func (fp *FinalityProviderInstance) retrySubmitSigsUntilFinalized(targetBlocks [ zap.String("pk", fp.GetBtcPkHex()), zap.Uint64("target_height", targetHeight), ) - return nil, nil - } - - // Wait for the retry interval - select { - case <-time.After(fp.cfg.SubmissionRetryInterval): - // Continue to next retry iteration - case <-fp.quit: - fp.logger.Debug("the finality-provider instance is closing", zap.String("pk", fp.GetBtcPkHex())) - return nil, ErrFinalityProviderShutDown - } - } -} - -// retryCommitPubRandUntilBlockFinalized periodically tries to commit public rand until success or the block is finalized -// error will be returned if maximum retries have been reached or the query to the consumer chain fails -func (fp *FinalityProviderInstance) retryCommitPubRandUntilBlockFinalized(targetBlockHeight uint64) (*types.TxResponse, error) { - var failedCycles uint32 - - // we break the for loop if the block is finalized or the public rand is successfully committed - // error will be returned if maximum retries have been reached or the query to the consumer chain fails - for { - // error will be returned if max retries have been reached - // TODO: CommitPubRand also includes saving all inclusion proofs of public randomness - // this part should not be retried here. We need to separate the function into - // 1) determining the starting height to commit, 2) generating pub rand and inclusion - // proofs, and 3) committing public randomness. - // TODO: make 3) a part of `select` statement. The function terminates upon either the block - // is finalised or the pub rand is committed successfully - res, err := fp.CommitPubRand(targetBlockHeight) - if err != nil { - if fpcc.IsUnrecoverable(err) { - return nil, err - } - fp.logger.Debug( - "failed to commit public randomness to the consumer chain", - zap.String("pk", fp.GetBtcPkHex()), - zap.Uint32("current_failures", failedCycles), - zap.Uint64("target_block_height", targetBlockHeight), - zap.Error(err), - ) - failedCycles += 1 - if failedCycles > uint32(fp.cfg.MaxSubmissionRetries) { - return nil, fmt.Errorf("reached max failed cycles with err: %w", err) - } - } else { - // the public randomness has been successfully submitted - return res, nil - } - select { - case <-time.After(fp.cfg.SubmissionRetryInterval): - // periodically query the index block to be later checked whether it is Finalized - finalized, err := fp.consumerCon.QueryIsBlockFinalized(targetBlockHeight) - if err != nil { - return nil, fmt.Errorf("failed to query block finalization at height %v: %w", targetBlockHeight, err) - } - if finalized { - fp.logger.Debug( - "the block is already finalized, skip submission", - zap.String("pk", fp.GetBtcPkHex()), - zap.Uint64("target_height", targetBlockHeight), - ) - // TODO: returning nil here is to safely break the loop - // the error still exists - return nil, nil - } + fp.metrics.IncrementFpTotalFailedVotes(fp.GetBtcPkHex()) - case <-fp.quit: - fp.logger.Debug("the finality-provider instance is closing", zap.String("pk", fp.GetBtcPkHex())) + // TODO: returning nil here is to safely break the loop + // the error still exists return nil, nil } - } -} - -func (fp *FinalityProviderInstance) retryCommitPubRandUntilMaxRetry(targetBlockHeight uint64) (*types.TxResponse, error) { - var failedCycles uint32 - - // we break the for loop if the public rand is successfully committed - // error will be returned if maximum retries have been reached - for { - // error will be returned if max retries have been reached - // TODO: CommitPubRand also includes saving all inclusion proofs of public randomness - // this part should not be retried here. We need to separate the function into - // 1) determining the starting height to commit, 2) generating pub rand and inclusion - // proofs, and 3) committing public randomness. - // TODO: make 3) a part of `select` statement. The function terminates upon either the block - // is finalised or the pub rand is committed successfully - res, err := fp.CommitPubRand(targetBlockHeight) - if err != nil { - if fpcc.IsUnrecoverable(err) { - return nil, err - } - fp.logger.Debug( - "failed to commit public randomness to the consumer chain", - zap.String("pk", fp.GetBtcPkHex()), - zap.Uint32("current_failures", failedCycles), - zap.Uint64("target_block_height", targetBlockHeight), - zap.Error(err), - ) - failedCycles += 1 - if failedCycles > uint32(fp.cfg.MaxSubmissionRetries) { - return nil, fmt.Errorf("reached max failed cycles with err: %w", err) - } - } else { - // the public randomness has been successfully submitted - return res, nil - } + // Wait for the retry interval select { case <-time.After(fp.cfg.SubmissionRetryInterval): + // Continue to next retry iteration continue case <-fp.quit: fp.logger.Debug("the finality-provider instance is closing", zap.String("pk", fp.GetBtcPkHex())) - return nil, nil + + return nil, ErrFinalityProviderShutDown } } } -// CommitPubRand generates a list of Schnorr rand pairs, -// commits the public randomness for the managed finality providers, -// and save the randomness pair to DB -// Note: -// - if there is no pubrand committed before, it will start from the targetBlockHeight -// - if the targetBlockHeight is too large, it will only commit fp.cfg.NumPubRand pairs -func (fp *FinalityProviderInstance) CommitPubRand(targetBlockHeight uint64) (*types.TxResponse, error) { - lastCommittedHeight, err := fp.GetLastCommittedHeight() +func (fp *FinalityProviderInstance) checkBlockFinalization(height uint64) (bool, error) { + b, err := fp.consumerCon.QueryBlock(height) if err != nil { - return nil, err - } - - var startHeight uint64 - if lastCommittedHeight == uint64(0) { - // the finality-provider has never submitted public rand before - startHeight = targetBlockHeight - } else if lastCommittedHeight < uint64(fp.cfg.MinRandHeightGap)+targetBlockHeight { - // (should not use subtraction because they are in the type of uint64) - // we are running out of the randomness - startHeight = lastCommittedHeight + 1 - } else { - fp.logger.Debug( - "the finality-provider has sufficient public randomness, skip committing more", - zap.String("pk", fp.GetBtcPkHex()), - zap.Uint64("block_height", targetBlockHeight), - zap.Uint64("last_committed_height", lastCommittedHeight), - ) - return nil, nil + return false, err } - return fp.commitPubRandPairs(startHeight) + return b.Finalized, nil } -// it will commit fp.cfg.NumPubRand pairs of public randomness starting from startHeight -func (fp *FinalityProviderInstance) commitPubRandPairs(startHeight uint64) (*types.TxResponse, error) { +// CommitPubRand commits a list of randomness from given start height +func (fp *FinalityProviderInstance) CommitPubRand(startHeight uint64) (*types.TxResponse, error) { // generate a list of Schnorr randomness pairs // NOTE: currently, calling this will create and save a list of randomness // in case of failure, randomness that has been created will be overwritten // for safety reason as the same randomness must not be used twice - pubRandList, err := fp.GetPubRandList(startHeight, uint64(fp.cfg.NumPubRand)) + pubRandList, err := fp.GetPubRandList(startHeight, fp.cfg.NumPubRand) if err != nil { return nil, fmt.Errorf("failed to generate randomness: %w", err) } @@ -613,7 +540,7 @@ func (fp *FinalityProviderInstance) commitPubRandPairs(startHeight uint64) (*typ commitment, proofList := types.GetPubRandCommitAndProofs(pubRandList) // store them to database - if err := fp.pubRandState.AddPubRandProofList(pubRandList, proofList); err != nil { + if err := fp.pubRandState.addPubRandProofList(fp.btcPk.MustMarshal(), fp.GetChainID(), startHeight, uint64(fp.cfg.NumPubRand), proofList); err != nil { return nil, fmt.Errorf("failed to save public randomness to DB: %w", err) } @@ -630,6 +557,7 @@ func (fp *FinalityProviderInstance) commitPubRandPairs(startHeight uint64) (*typ // Update metrics fp.metrics.RecordFpRandomnessTime(fp.GetBtcPkHex()) + fp.metrics.RecordFpLastCommittedRandomnessHeight(fp.GetBtcPkHex(), startHeight+numPubRand-1) fp.metrics.AddToFpTotalCommittedRandomness(fp.GetBtcPkHex(), float64(len(pubRandList))) fp.metrics.RecordFpLastCommittedRandomnessHeight(fp.GetBtcPkHex(), startHeight+numPubRand-1) @@ -650,13 +578,8 @@ func (fp *FinalityProviderInstance) TestCommitPubRand(targetBlockHeight uint64) if err != nil { return err } - if lastCommittedHeight == uint64(0) { - // Note: it can also be the case that the finality-provider has committed 1 pubrand before (but in practice, we - // will never set cfg.NumPubRand to 1. so we can safely assume it has never committed before) - startHeight = 0 - } else if lastCommittedHeight < targetBlockHeight { - startHeight = lastCommittedHeight + 1 - } else { + + if lastCommittedHeight >= targetBlockHeight { return fmt.Errorf( "finality provider has already committed pubrand to target block height (pk: %s, target: %d, last committed: %d)", fp.GetBtcPkHex(), @@ -665,6 +588,14 @@ func (fp *FinalityProviderInstance) TestCommitPubRand(targetBlockHeight uint64) ) } + if lastCommittedHeight == uint64(0) { + // Note: it can also be the case that the finality-provider has committed 1 pubrand before (but in practice, we + // will never set cfg.NumPubRand to 1. so we can safely assume it has never committed before) + startHeight = 0 + } else { + startHeight = lastCommittedHeight + 1 + } + return fp.TestCommitPubRandWithStartHeight(startHeight, targetBlockHeight) } @@ -691,10 +622,8 @@ func (fp *FinalityProviderInstance) TestCommitPubRandWithStartHeight(startHeight fp.logger.Info("Start committing pubrand from block height", zap.Uint64("start_height", startHeight)) - // TODO: instead of sending multiple txs, a better way is to bundle all the commit messages into - // one like we do for batch finality signatures. see discussion https://bit.ly/3OmbjkN for startHeight <= targetBlockHeight { - _, err = fp.commitPubRandPairs(startHeight) + _, err = fp.CommitPubRand(startHeight) if err != nil { return err } @@ -719,16 +648,27 @@ func (fp *FinalityProviderInstance) SubmitBatchFinalitySignatures(blocks []*type return nil, fmt.Errorf("should not submit batch finality signature with zero block") } + if len(blocks) > math.MaxUint32 { + return nil, fmt.Errorf("should not submit batch finality signature with too many blocks") + } + // get public randomness list - prList, err := fp.GetPubRandList(blocks[0].Height, uint64(len(blocks))) + numPubRand := len(blocks) + // #nosec G115 -- performed the conversion check above + prList, err := fp.GetPubRandList(blocks[0].Height, uint32(numPubRand)) if err != nil { - return nil, fmt.Errorf("failed to get public randomness list: %v", err) + return nil, fmt.Errorf("failed to get public randomness list: %w", err) } // get proof list - // TODO: how to recover upon having an error in GetPubRandProofList? - proofBytesList, err := fp.pubRandState.GetPubRandProofList(prList) + // TODO: how to recover upon having an error in getPubRandProofList? + proofBytesList, err := fp.pubRandState.getPubRandProofList( + fp.btcPk.MustMarshal(), + fp.GetChainID(), + blocks[0].Height, + uint64(numPubRand), + ) if err != nil { - return nil, fmt.Errorf("failed to get public randomness inclusion proof list: %v", err) + return nil, fmt.Errorf("failed to get public randomness inclusion proof list: %w", err) } // sign blocks @@ -744,7 +684,14 @@ func (fp *FinalityProviderInstance) SubmitBatchFinalitySignatures(blocks []*type // send finality signature to the consumer chain res, err := fp.consumerCon.SubmitBatchFinalitySigs(fp.GetBtcPk(), blocks, prList, proofBytesList, sigList) if err != nil { - return nil, fmt.Errorf("failed to send a batch of finality signatures to the consumer chain: %w", err) + if strings.Contains(err.Error(), "jailed") { + return nil, ErrFinalityProviderJailed + } + if strings.Contains(err.Error(), "slashed") { + return nil, ErrFinalityProviderSlashed + } + + return nil, err } // update DB @@ -757,22 +704,38 @@ func (fp *FinalityProviderInstance) SubmitBatchFinalitySignatures(blocks []*type // TestSubmitFinalitySignatureAndExtractPrivKey is exposed for presentation/testing purpose to allow manual sending finality signature // this API is the same as SubmitBatchFinalitySignatures except that we don't constraint the voting height and update status // Note: this should not be used in the submission loop -func (fp *FinalityProviderInstance) TestSubmitFinalitySignatureAndExtractPrivKey(b *types.BlockInfo) (*types.TxResponse, *btcec.PrivateKey, error) { +func (fp *FinalityProviderInstance) TestSubmitFinalitySignatureAndExtractPrivKey( + b *types.BlockInfo, useSafeEOTSFunc bool, +) (*types.TxResponse, *btcec.PrivateKey, error) { // get public randomness prList, err := fp.GetPubRandList(b.Height, 1) if err != nil { - return nil, nil, fmt.Errorf("failed to get public randomness list: %v", err) + return nil, nil, fmt.Errorf("failed to get public randomness list: %w", err) } pubRand := prList[0] // get proof - proofBytes, err := fp.pubRandState.GetPubRandProof(pubRand) + proofBytes, err := fp.pubRandState.getPubRandProof(fp.btcPk.MustMarshal(), fp.GetChainID(), b.Height) if err != nil { - return nil, nil, fmt.Errorf("failed to get public randomness inclusion proof: %v", err) + return nil, nil, fmt.Errorf("failed to get public randomness inclusion proof: %w", err) + } + + eotsSignerFunc := func(b *types.BlockInfo) (*bbntypes.SchnorrEOTSSig, error) { + msgToSign := getMsgToSignForVote(b.Height, b.Hash) + sig, err := fp.em.UnsafeSignEOTS(fp.btcPk.MustMarshal(), fp.GetChainID(), msgToSign, b.Height, fp.passphrase) + if err != nil { + return nil, fmt.Errorf("failed to sign EOTS: %w", err) + } + + return bbntypes.NewSchnorrEOTSSigFromModNScalar(sig), nil + } + + if useSafeEOTSFunc { + eotsSignerFunc = fp.SignFinalitySig } // sign block - eotsSig, err := fp.SignFinalitySig(b) + eotsSig, err := eotsSignerFunc(b) if err != nil { return nil, nil, err } @@ -783,13 +746,13 @@ func (fp *FinalityProviderInstance) TestSubmitFinalitySignatureAndExtractPrivKey return nil, nil, fmt.Errorf("failed to send finality signature to the consumer chain: %w", err) } + if res.TxHash == "" { + return res, nil, nil + } + // try to extract the private key var privKey *btcec.PrivateKey - events, err := parseCosmosEvents(res.Events) - if err != nil { - return nil, nil, fmt.Errorf("failed to decode bytes to RelayerEvent: %s", err.Error()) - } - for _, ev := range events { + for _, ev := range res.Events { if strings.Contains(ev.EventType, "EventSlashedFinalityProvider") { evidenceStr := ev.Attributes["evidence"] fp.logger.Debug("found slashing evidence") @@ -801,6 +764,7 @@ func (fp *FinalityProviderInstance) TestSubmitFinalitySignatureAndExtractPrivKey if err != nil { return nil, nil, fmt.Errorf("failed to extract private key: %s", err.Error()) } + break } } @@ -808,41 +772,72 @@ func (fp *FinalityProviderInstance) TestSubmitFinalitySignatureAndExtractPrivKey return res, privKey, nil } -func parseCosmosEvents(eventsData []byte) ([]provider.RelayerEvent, error) { - var events []provider.RelayerEvent - if err := json.Unmarshal(eventsData, &events); err != nil { - return nil, fmt.Errorf("failed to decode bytes to RelayerEvent: %s", err.Error()) - } - return events, nil -} - -func (fp *FinalityProviderInstance) getPollerStartingHeight() (uint64, error) { +// DetermineStartHeight determines start height for block processing by: +// +// If AutoChainScanningMode is disabled: +// - Returns StaticChainScanningStartHeight from config +// +// If AutoChainScanningMode is enabled: +// - Gets finalityActivationHeight from chain +// - Gets lastFinalizedHeight from chain +// - Gets lastVotedHeight from local state +// - Gets highestVotedHeight from chain +// - Sets startHeight = max(lastVotedHeight, highestVotedHeight, lastFinalizedHeight) + 1 +// - Returns max(startHeight, finalityActivationHeight) to ensure startHeight is not +// lower than the finality activation height +// +// This ensures that: +// 1. The FP will not vote for heights below the finality activation height +// 2. The FP will resume from its last voting position or the chain's last finalized height +// 3. The FP will not process blocks it has already voted on +// +// Note: Starting from lastFinalizedHeight when there's a gap to the last processed height +// may result in missed rewards, depending on the consumer chain's reward distribution mechanism. +func (fp *FinalityProviderInstance) DetermineStartHeight() (uint64, error) { + // start from a height from config if AutoChainScanningMode is disabled if !fp.cfg.PollerConfig.AutoChainScanningMode { + fp.logger.Info("using static chain scanning mode", + zap.String("pk", fp.GetBtcPkHex()), + zap.Uint64("start_height", fp.cfg.PollerConfig.StaticChainScanningStartHeight)) + return fp.cfg.PollerConfig.StaticChainScanningStartHeight, nil } - // Set initial block to the maximum of - // - last processed height + 1 - // - the latest Babylon finalised height + 1 - // The above is to ensure that: - // - // (1) Any finality-provider that is eligible to vote for a block, - // doesn't miss submitting a vote for it. - // (2) The finality providers do not submit signatures for any already - // finalised blocks. - initialBlockToGet := fp.GetLastProcessedHeight() - latestFinalizedBlock, err := fp.latestFinalizedBlockWithRetry() + highestVotedHeight, err := fp.highestVotedHeightWithRetry() if err != nil { - return 0, err + return 0, fmt.Errorf("failed to get the highest voted height: %w", err) } - // find max(initialBlockToGet, latestFinalizedBlock.Height) - maxHeight := initialBlockToGet - if latestFinalizedBlock != nil && latestFinalizedBlock.Height > initialBlockToGet { - maxHeight = latestFinalizedBlock.Height + lastFinalizedHeight, err := fp.latestFinalizedHeightWithRetry() + if err != nil { + return 0, fmt.Errorf("failed to get the last finalized height: %w", err) + } + + // determine start height to be the max height among local last voted height, highest voted height + // from Babylon, and the last finalized height + // NOTE: if highestVotedHeight is selected, it could lead issues when there are missed blocks between + // the gap due to bugs. A potential solution is to check if the fp has voted for each block within + // the gap. This issue is not critical if we can assume the votes are sent in the monotonically + // increasing order. + startHeight := max(fp.GetLastVotedHeight(), highestVotedHeight, lastFinalizedHeight) + 1 + + finalityActivationHeight, err := fp.getFinalityActivationHeightWithRetry() + if err != nil { + return 0, fmt.Errorf("failed to get finality activation height: %w", err) } - return maxHeight + 1, nil + // ensure start height is not lower than the finality activation height + startHeight = max(startHeight, finalityActivationHeight) + + fp.logger.Info("determined poller starting height", + zap.String("pk", fp.GetBtcPkHex()), + zap.Uint64("start_height", startHeight), + zap.Uint64("finality_activation_height", finalityActivationHeight), + zap.Uint64("last_voted_height", fp.GetLastVotedHeight()), + zap.Uint64("last_finalized_height", lastFinalizedHeight), + zap.Uint64("highest_voted_height", highestVotedHeight)) + + return startHeight, nil } func (fp *FinalityProviderInstance) GetLastCommittedHeight() (uint64, error) { @@ -872,6 +867,7 @@ func (fp *FinalityProviderInstance) lastCommittedPublicRandWithRetry() (*types.P } } response = resp + return nil }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { fp.logger.Debug( @@ -883,32 +879,87 @@ func (fp *FinalityProviderInstance) lastCommittedPublicRandWithRetry() (*types.P })); err != nil { return nil, err } + return response, nil } -// nil will be returned if the finalized block does not exist -func (fp *FinalityProviderInstance) latestFinalizedBlockWithRetry() (*types.BlockInfo, error) { - var response *types.BlockInfo +func (fp *FinalityProviderInstance) latestFinalizedHeightWithRetry() (uint64, error) { + var height uint64 if err := retry.Do(func() error { - latestFinalizedBlock, err := fp.consumerCon.QueryLatestFinalizedBlock() + block, err := fp.consumerCon.QueryLatestFinalizedBlock() if err != nil { return err } - response = latestFinalizedBlock + if block == nil { + // no finalized block yet + return nil + } + height = block.Height + return nil }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { fp.logger.Debug( - "failed to query babylon for the latest finalised blocks", + "failed to query babylon for the latest finalised height", zap.Uint("attempt", n+1), zap.Uint("max_attempts", RtyAttNum), zap.Error(err), ) })); err != nil { - return nil, err + return 0, err } + + return height, nil +} + +func (fp *FinalityProviderInstance) highestVotedHeightWithRetry() (uint64, error) { + var height uint64 + if err := retry.Do(func() error { + h, err := fp.consumerCon.QueryFinalityProviderHighestVotedHeight(fp.GetBtcPk()) + if err != nil { + return err + } + height = h + + return nil + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + fp.logger.Debug( + "failed to query babylon for the highest voted height", + zap.Uint("attempt", n+1), + zap.Uint("max_attempts", RtyAttNum), + zap.Error(err), + ) + })); err != nil { + return 0, err + } + + return height, nil +} + +func (fp *FinalityProviderInstance) getFinalityActivationHeightWithRetry() (uint64, error) { + var response uint64 + if err := retry.Do(func() error { + finalityActivationHeight, err := fp.consumerCon.QueryFinalityActivationBlockHeight() + if err != nil { + return err + } + response = finalityActivationHeight + + return nil + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + fp.logger.Debug( + "failed to query babylon for the finality activation height", + zap.Uint("attempt", n+1), + zap.Uint("max_attempts", RtyAttNum), + zap.Error(err), + ) + })); err != nil { + return 0, err + } + return response, nil } +// nolint:unused func (fp *FinalityProviderInstance) getLatestBlockHeightWithRetry() (uint64, error) { var ( latestBlockHeight uint64 @@ -920,6 +971,7 @@ func (fp *FinalityProviderInstance) getLatestBlockHeightWithRetry() (uint64, err if err != nil { return err } + return nil }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { fp.logger.Debug( @@ -947,6 +999,7 @@ func (fp *FinalityProviderInstance) GetVotingPowerWithRetry(height uint64) (bool if err != nil { return err } + return nil }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { fp.logger.Debug( @@ -970,10 +1023,11 @@ func (fp *FinalityProviderInstance) GetFinalityProviderSlashedOrJailedWithRetry( ) if err := retry.Do(func() error { - slashed, jailed, err = fp.cc.QueryFinalityProviderSlashedOrJailed(fp.GetBtcPk()) + slashed, jailed, err = fp.consumerCon.QueryFinalityProviderSlashedOrJailed(fp.GetBtcPk()) if err != nil { return err } + return nil }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { fp.logger.Debug( diff --git a/finality-provider/service/fp_instance_test.go b/finality-provider/service/fp_instance_test.go index 3d1def64..f0d18389 100644 --- a/finality-provider/service/fp_instance_test.go +++ b/finality-provider/service/fp_instance_test.go @@ -1,23 +1,24 @@ package service_test import ( + "fmt" "math/rand" "os" "path/filepath" + "strings" "testing" + "github.com/babylonlabs-io/babylon/testutil/datagen" + bbntypes "github.com/babylonlabs-io/babylon/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/babylonlabs-io/babylon/testutil/datagen" - ccapi "github.com/babylonlabs-io/finality-provider/clientcontroller/api" + "github.com/babylonlabs-io/finality-provider/clientcontroller/api" "github.com/babylonlabs-io/finality-provider/eotsmanager" eotscfg "github.com/babylonlabs-io/finality-provider/eotsmanager/config" "github.com/babylonlabs-io/finality-provider/finality-provider/config" - "github.com/babylonlabs-io/finality-provider/finality-provider/proto" "github.com/babylonlabs-io/finality-provider/finality-provider/service" + fpkr "github.com/babylonlabs-io/finality-provider/keyring" "github.com/babylonlabs-io/finality-provider/metrics" "github.com/babylonlabs-io/finality-provider/testutil" "github.com/babylonlabs-io/finality-provider/types" @@ -31,18 +32,14 @@ func FuzzCommitPubRandList(f *testing.F) { randomStartingHeight := uint64(r.Int63n(100) + 1) currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2) startingBlock := &types.BlockInfo{Height: randomStartingHeight, Hash: testutil.GenRandomByteArray(r, 32)} - expectedTxHash := testutil.GenRandomHexStr(r, 32) mockBabylonController := testutil.PrepareMockedBabylonController(t) + expectedTxHash := testutil.GenRandomHexStr(r, 32) mockConsumerController := testutil.PrepareMockedConsumerControllerWithTxHash(t, r, randomStartingHeight, currentHeight, expectedTxHash) mockConsumerController.EXPECT().QueryFinalityProviderHasPower(gomock.Any(), gomock.Any()). Return(false, nil).AnyTimes() - _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockBabylonController, mockConsumerController, randomStartingHeight) + _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockBabylonController, mockConsumerController, true, randomStartingHeight, testutil.TestPubRandNum) defer cleanUp() - mockConsumerController.EXPECT(). - CommitPubRandList(fpIns.GetBtcPk(), startingBlock.Height+1, gomock.Any(), gomock.Any(), gomock.Any()). - Return(&types.TxResponse{TxHash: expectedTxHash}, nil).AnyTimes() - mockConsumerController.EXPECT().QueryLastPublicRandCommit(gomock.Any()).Return(nil, nil).AnyTimes() res, err := fpIns.CommitPubRand(startingBlock.Height) require.NoError(t, err) require.Equal(t, expectedTxHash, res.TxHash) @@ -57,15 +54,13 @@ func FuzzSubmitFinalitySigs(f *testing.F) { randomStartingHeight := uint64(r.Int63n(100) + 1) currentHeight := randomStartingHeight + uint64(r.Int63n(10)+1) startingBlock := &types.BlockInfo{Height: randomStartingHeight, Hash: testutil.GenRandomByteArray(r, 32)} - mockConsumerController := testutil.PrepareMockedConsumerController(t, r, randomStartingHeight, currentHeight) mockBabylonController := testutil.PrepareMockedBabylonController(t) - - mockConsumerController.EXPECT().QueryLatestFinalizedBlock().Return(nil, nil).AnyTimes() - _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockBabylonController, mockConsumerController, randomStartingHeight) + mockConsumerController := testutil.PrepareMockedConsumerController(t, r, randomStartingHeight, currentHeight) + mockConsumerController.EXPECT().QueryLatestBlockHeight().Return(uint64(0), nil).AnyTimes() + _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockBabylonController, mockConsumerController, true, randomStartingHeight, testutil.TestPubRandNum) defer cleanUp() // commit pub rand - mockConsumerController.EXPECT().QueryLastPublicRandCommit(gomock.Any()).Return(nil, nil).Times(1) _, err := fpIns.CommitPubRand(startingBlock.Height) require.NoError(t, err) @@ -96,17 +91,61 @@ func FuzzSubmitFinalitySigs(f *testing.F) { // check the last_voted_height require.Equal(t, nextBlock.Height, fpIns.GetLastVotedHeight()) - require.Equal(t, nextBlock.Height, fpIns.GetLastProcessedHeight()) }) } -func startFinalityProviderAppWithRegisteredFp(t *testing.T, r *rand.Rand, cc ccapi.ClientController, consumerCon ccapi.ConsumerController, startingHeight uint64) (*service.FinalityProviderApp, *service.FinalityProviderInstance, func()) { - logger, err := zap.NewDevelopment() - require.NoError(t, err) +func FuzzDetermineStartHeight(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // generate random heights + finalityActivationHeight := uint64(r.Int63n(1000) + 1) + lastVotedHeight := uint64(r.Int63n(1000)) + highestVotedHeight := uint64(r.Int63n(1000)) + lastFinalizedHeight := uint64(r.Int63n(1000) + 1) + + randomStartingHeight := uint64(r.Int63n(100) + 1) + currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2) + mockBabylonController := testutil.PrepareMockedBabylonController(t) + mockConsumerController := testutil.PrepareMockedConsumerController(t, r, randomStartingHeight, currentHeight) + + // setup mocks + mockConsumerController.EXPECT().QueryFinalityActivationBlockHeight().Return(finalityActivationHeight, nil).AnyTimes() + mockConsumerController.EXPECT(). + QueryFinalityProviderHighestVotedHeight(gomock.Any()). + Return(highestVotedHeight, nil). + AnyTimes() + finalizedBlock := &types.BlockInfo{ + Height: lastFinalizedHeight, + } + mockConsumerController.EXPECT().QueryLatestFinalizedBlock().Return(finalizedBlock, nil).AnyTimes() + + _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockBabylonController, mockConsumerController, false, randomStartingHeight, testutil.TestPubRandNum) + defer cleanUp() + fpIns.MustUpdateStateAfterFinalitySigSubmission(lastVotedHeight) + + startHeight, err := fpIns.DetermineStartHeight() + require.NoError(t, err) + + require.Equal(t, startHeight, max(finalityActivationHeight, highestVotedHeight+1, lastFinalizedHeight+1, lastVotedHeight+1)) + }) +} + +func startFinalityProviderAppWithRegisteredFp( + t *testing.T, + r *rand.Rand, + cc api.ClientController, + consumerCon api.ConsumerController, + isStaticStartHeight bool, + startingHeight uint64, + numPubRand uint32, +) (*service.FinalityProviderApp, *service.FinalityProviderInstance, func()) { + logger := testutil.GetTestLogger(t) // create an EOTS manager eotsHomeDir := filepath.Join(t.TempDir(), "eots-home") eotsCfg := eotscfg.DefaultConfigWithHomePath(eotsHomeDir) - eotsdb, err := eotsCfg.DatabaseConfig.GetDbBackend() + eotsdb, err := eotsCfg.DatabaseConfig.GetDBBackend() require.NoError(t, err) em, err := eotsmanager.NewLocalEOTSManager(eotsHomeDir, eotsCfg.KeyringBackend, eotsdb, logger) require.NoError(t, err) @@ -114,27 +153,50 @@ func startFinalityProviderAppWithRegisteredFp(t *testing.T, r *rand.Rand, cc cca // create finality-provider app with randomized config fpHomeDir := filepath.Join(t.TempDir(), "fp-home") fpCfg := config.DefaultConfigWithHome(fpHomeDir) - fpCfg.NumPubRand = testutil.TestPubRandNum - fpCfg.PollerConfig.AutoChainScanningMode = false + fpCfg.NumPubRand = numPubRand + fpCfg.PollerConfig.AutoChainScanningMode = !isStaticStartHeight fpCfg.PollerConfig.StaticChainScanningStartHeight = startingHeight - db, err := fpCfg.DatabaseConfig.GetDbBackend() + db, err := fpCfg.DatabaseConfig.GetDBBackend() require.NoError(t, err) app, err := service.NewFinalityProviderApp(&fpCfg, cc, consumerCon, em, db, logger) require.NoError(t, err) err = app.Start() require.NoError(t, err) - err = app.StartHandlingAll() - require.NoError(t, err) // create registered finality-provider - fp := testutil.GenStoredFinalityProvider(r, t, app, passphrase, hdPath, nil) + eotsKeyName := testutil.GenRandomHexStr(r, 4) + require.NoError(t, err) + eotsPkBz, err := em.CreateKey(eotsKeyName, passphrase, hdPath) + require.NoError(t, err) + eotsPk, err := bbntypes.NewBIP340PubKey(eotsPkBz) + require.NoError(t, err) pubRandProofStore := app.GetPubRandProofStore() fpStore := app.GetFinalityProviderStore() - err = fpStore.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_REGISTERED) + keyName := datagen.GenRandomHexStr(r, 10) + chainID := datagen.GenRandomHexStr(r, 10) + input := strings.NewReader("") + kr, err := fpkr.CreateKeyring( + fpCfg.BabylonConfig.KeyDirectory, + fpCfg.BabylonConfig.ChainID, + fpCfg.BabylonConfig.KeyringBackend, + input, + ) + require.NoError(t, err) + kc, err := fpkr.NewChainKeyringControllerWithKeyring(kr, keyName, input) + require.NoError(t, err) + keyInfo, err := kc.CreateChainKey("", "", "") + require.NoError(t, err) + fpAddr := keyInfo.AccAddress + err = fpStore.CreateFinalityProvider( + fpAddr, + eotsPk.MustToBTCPK(), + testutil.RandomDescription(r), + testutil.ZeroCommissionRate(), + chainID, + ) require.NoError(t, err) - // TODO: use mock metrics m := metrics.NewFpMetrics() - fpIns, err := service.NewFinalityProviderInstance(fp.GetBIP340BTCPK(), &fpCfg, app.GetFinalityProviderStore(), pubRandProofStore, cc, consumerCon, em, m, passphrase, make(chan *service.CriticalError), logger) + fpIns, err := service.NewFinalityProviderInstance(eotsPk, &fpCfg, fpStore, pubRandProofStore, cc, consumerCon, em, m, passphrase, make(chan *service.CriticalError), logger) require.NoError(t, err) cleanUp := func() { @@ -152,3 +214,64 @@ func startFinalityProviderAppWithRegisteredFp(t *testing.T, r *rand.Rand, cc cca return app, fpIns, cleanUp } + +func setupBenchmarkEnvironment(t *testing.T, seed int64, numPubRand uint32) (*types.BlockInfo, *service.FinalityProviderInstance, func()) { + r := rand.New(rand.NewSource(seed)) + + randomStartingHeight := uint64(r.Int63n(100) + 1) + currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2) + startingBlock := &types.BlockInfo{ + Height: randomStartingHeight, + Hash: testutil.GenRandomByteArray(r, 32), + } + + // Mock client controller setup + mockBabylonController := testutil.PrepareMockedBabylonController(t) + mockConsumerController := testutil.PrepareMockedConsumerController(t, r, randomStartingHeight, currentHeight) + mockConsumerController.EXPECT().QueryFinalityProviderHasPower(gomock.Any(), gomock.Any()). + Return(false, nil).AnyTimes() + + // Set up finality provider app + _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockBabylonController, mockConsumerController, true, randomStartingHeight, numPubRand) + + // Configure additional mocks + expectedTxHash := testutil.GenRandomHexStr(r, 32) + mockConsumerController.EXPECT(). + CommitPubRandList(fpIns.GetBtcPk(), startingBlock.Height+1, gomock.Any(), gomock.Any(), gomock.Any()). + Return(&types.TxResponse{TxHash: expectedTxHash}, nil).AnyTimes() + mockConsumerController.EXPECT().QueryLastPublicRandCommit(gomock.Any()).Return(nil, nil).AnyTimes() + + return startingBlock, fpIns, cleanUp +} + +func BenchmarkCommitPubRand(b *testing.B) { + for _, numPubRand := range []uint32{10, 50, 100, 200, 500, 1000, 5000, 10000, 25000, 50000, 75000, 100000} { + b.Run(fmt.Sprintf("numPubRand=%d", numPubRand), func(b *testing.B) { + t := &testing.T{} + startingBlock, fpIns, cleanUp := setupBenchmarkEnvironment(t, 42, numPubRand) + defer cleanUp() + + // exclude setup time + b.ResetTimer() + + var totalTiming service.CommitPubRandTiming + for i := 0; i < b.N; i++ { + res, timing, err := fpIns.HelperCommitPubRand(startingBlock.Height) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + + if res == nil { + b.Fatalf("unexpected result") + } + // Accumulate timings for averaging + totalTiming.GetPubRandListTime += timing.GetPubRandListTime + totalTiming.AddPubRandProofListTime += timing.AddPubRandProofListTime + totalTiming.CommitPubRandListTime += timing.CommitPubRandListTime + } + b.ReportMetric(float64(totalTiming.GetPubRandListTime.Nanoseconds())/float64(b.N), "ns/GetPubRandList") + b.ReportMetric(float64(totalTiming.AddPubRandProofListTime.Nanoseconds())/float64(b.N), "ns/AddPubRandProofList") + b.ReportMetric(float64(totalTiming.CommitPubRandListTime.Nanoseconds())/float64(b.N), "ns/CommitPubRandList") + }) + } +} diff --git a/finality-provider/service/fp_manager.go b/finality-provider/service/fp_manager.go deleted file mode 100644 index 7a37ca6f..00000000 --- a/finality-provider/service/fp_manager.go +++ /dev/null @@ -1,475 +0,0 @@ -package service - -import ( - "fmt" - "strings" - "sync" - "time" - - "github.com/avast/retry-go/v4" - bbntypes "github.com/babylonlabs-io/babylon/types" - btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - "go.uber.org/atomic" - "go.uber.org/zap" - - ccapi "github.com/babylonlabs-io/finality-provider/clientcontroller/api" - "github.com/babylonlabs-io/finality-provider/eotsmanager" - fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" - "github.com/babylonlabs-io/finality-provider/finality-provider/proto" - "github.com/babylonlabs-io/finality-provider/finality-provider/store" - "github.com/babylonlabs-io/finality-provider/metrics" -) - -const instanceTerminatingMsg = "terminating the finality-provider instance due to critical error" - -type CriticalError struct { - err error - fpBtcPk *bbntypes.BIP340PubKey -} - -func (ce *CriticalError) Error() string { - return fmt.Sprintf("critical err on finality-provider %s: %s", ce.fpBtcPk.MarshalHex(), ce.err.Error()) -} - -type FinalityProviderManager struct { - isStarted *atomic.Bool - - // mutex to acess map of fp instances (fpis) - mu sync.Mutex - wg sync.WaitGroup - - // running finality-provider instances map keyed by the hex string of the BTC public key - fpis map[string]*FinalityProviderInstance - - // needed for initiating finality-provider instances - fps *store.FinalityProviderStore - pubRandStore *store.PubRandProofStore - config *fpcfg.Config - cc ccapi.ClientController - consumerCon ccapi.ConsumerController - em eotsmanager.EOTSManager - logger *zap.Logger - - metrics *metrics.FpMetrics - - criticalErrChan chan *CriticalError - - quit chan struct{} -} - -func NewFinalityProviderManager( - fps *store.FinalityProviderStore, - pubRandStore *store.PubRandProofStore, - config *fpcfg.Config, - cc ccapi.ClientController, - consumerCon ccapi.ConsumerController, - em eotsmanager.EOTSManager, - metrics *metrics.FpMetrics, - logger *zap.Logger, -) (*FinalityProviderManager, error) { - return &FinalityProviderManager{ - fpis: make(map[string]*FinalityProviderInstance), - criticalErrChan: make(chan *CriticalError), - isStarted: atomic.NewBool(false), - fps: fps, - pubRandStore: pubRandStore, - config: config, - cc: cc, - consumerCon: consumerCon, - em: em, - metrics: metrics, - logger: logger, - quit: make(chan struct{}), - }, nil -} - -// monitorCriticalErr takes actions when it receives critical errors from a finality-provider instance -// if the finality-provider is slashed, it will be terminated and the program keeps running in case -// new finality providers join -// otherwise, the program will panic -func (fpm *FinalityProviderManager) monitorCriticalErr() { - defer fpm.wg.Done() - - var criticalErr *CriticalError - for { - select { - case criticalErr = <-fpm.criticalErrChan: - fpi, err := fpm.GetFinalityProviderInstance(criticalErr.fpBtcPk) - if err != nil { - fpm.logger.Debug("the finality-provider instance is already shutdown", - zap.String("pk", criticalErr.fpBtcPk.MarshalHex())) - continue - } - // cannot use error.Is because the unwrapped error - // is not the expected error type - if strings.Contains(criticalErr.err.Error(), btcstakingtypes.ErrFpAlreadySlashed.Error()) { - fpm.setFinalityProviderSlashed(fpi) - fpm.logger.Debug("the finality-provider has been slashed", - zap.String("pk", criticalErr.fpBtcPk.MarshalHex())) - continue - } - if strings.Contains(criticalErr.err.Error(), btcstakingtypes.ErrFpAlreadyJailed.Error()) { - fpm.setFinalityProviderJailed(fpi) - fpm.logger.Debug("the finality-provider has been jailed", - zap.String("pk", criticalErr.fpBtcPk.MarshalHex())) - continue - } - fpm.logger.Fatal(instanceTerminatingMsg, - zap.String("pk", criticalErr.fpBtcPk.MarshalHex()), zap.Error(criticalErr.err)) - case <-fpm.quit: - return - } - } -} - -// monitorStatusUpdate periodically check the status of each managed finality providers and update -// it accordingly. We update the status by querying the latest voting power and the slashed_height. -// In particular, we perform the following status transitions (REGISTERED, ACTIVE, INACTIVE, SLASHED): -// 1. if power == 0 and slashed_height == 0, if status == ACTIVE, change to INACTIVE, otherwise remain the same -// 2. if power == 0 and slashed_height > 0, set status to SLASHED and stop and remove the finality-provider instance -// 3. if power > 0 (slashed_height must > 0), set status to ACTIVE -// NOTE: once error occurs, we log and continue as the status update is not critical to the entire program -func (fpm *FinalityProviderManager) monitorStatusUpdate() { - defer fpm.wg.Done() - - if fpm.config.StatusUpdateInterval == 0 { - fpm.logger.Info("the status update is disabled") - return - } - - statusUpdateTicker := time.NewTicker(fpm.config.StatusUpdateInterval) - defer statusUpdateTicker.Stop() - - for { - select { - case <-statusUpdateTicker.C: - latestBlockHeight, err := fpm.getLatestBlockHeightWithRetry() - if err != nil { - fpm.logger.Debug("failed to get the latest block", zap.Error(err)) - continue - } - fpis := fpm.ListFinalityProviderInstances() - for _, fpi := range fpis { - oldStatus := fpi.GetStatus() - hasPower, err := fpi.GetVotingPowerWithRetry(latestBlockHeight) - if err != nil { - fpm.logger.Debug( - "failed to query the voting power", - zap.String("fp_btc_pk", fpi.GetBtcPkHex()), - zap.Uint64("height", latestBlockHeight), - zap.Error(err), - ) - continue - } - // hasPower == true (slashed_height must > 0), set status to ACTIVE - if hasPower { - if oldStatus != proto.FinalityProviderStatus_ACTIVE { - fpi.MustSetStatus(proto.FinalityProviderStatus_ACTIVE) - fpm.logger.Debug( - "the finality-provider status is changed to ACTIVE", - zap.String("fp_btc_pk", fpi.GetBtcPkHex()), - zap.String("old_status", oldStatus.String()), - ) - } - continue - } - slashed, jailed, err := fpi.GetFinalityProviderSlashedOrJailedWithRetry() - if err != nil { - fpm.logger.Debug( - "failed to get the slashed or jailed status", - zap.String("fp_btc_pk", fpi.GetBtcPkHex()), - zap.Error(err), - ) - continue - } - // power == 0 and slashed == true, set status to SLASHED, stop, and remove the finality-provider instance - if slashed { - fpm.setFinalityProviderSlashed(fpi) - fpm.logger.Warn( - "the finality-provider is slashed", - zap.String("fp_btc_pk", fpi.GetBtcPkHex()), - zap.String("old_status", oldStatus.String()), - ) - continue - } - // power == 0 and jailed == true, set status to JAILED, stop, and remove the finality-provider instance - if jailed { - fpm.setFinalityProviderJailed(fpi) - fpm.logger.Warn( - "the finality-provider is jailed", - zap.String("fp_btc_pk", fpi.GetBtcPkHex()), - zap.String("old_status", oldStatus.String()), - ) - continue - } - // power == 0 and slashed_height == 0, change to INACTIVE if the current status is ACTIVE - if oldStatus == proto.FinalityProviderStatus_ACTIVE { - fpi.MustSetStatus(proto.FinalityProviderStatus_INACTIVE) - fpm.logger.Debug( - "the finality-provider status is changed to INACTIVE", - zap.String("fp_btc_pk", fpi.GetBtcPkHex()), - zap.String("old_status", oldStatus.String()), - ) - } - } - case <-fpm.quit: - return - } - } -} - -func (fpm *FinalityProviderManager) setFinalityProviderSlashed(fpi *FinalityProviderInstance) { - fpi.MustSetStatus(proto.FinalityProviderStatus_SLASHED) - if err := fpm.removeFinalityProviderInstance(fpi.GetBtcPkBIP340()); err != nil { - panic(fmt.Errorf("failed to terminate a slashed finality-provider %s: %w", fpi.GetBtcPkHex(), err)) - } -} - -func (fpm *FinalityProviderManager) setFinalityProviderJailed(fpi *FinalityProviderInstance) { - fpi.MustSetStatus(proto.FinalityProviderStatus_JAILED) - if err := fpm.removeFinalityProviderInstance(fpi.GetBtcPkBIP340()); err != nil { - panic(fmt.Errorf("failed to terminate a jailed finality-provider %s: %w", fpi.GetBtcPkHex(), err)) - } -} - -func (fpm *FinalityProviderManager) StartFinalityProvider(fpPk *bbntypes.BIP340PubKey, passphrase string) error { - if !fpm.isStarted.Load() { - fpm.isStarted.Store(true) - - fpm.wg.Add(1) - go fpm.monitorCriticalErr() - - fpm.wg.Add(1) - go fpm.monitorStatusUpdate() - } - - if fpm.numOfRunningFinalityProviders() >= int(fpm.config.MaxNumFinalityProviders) { - return fmt.Errorf("reaching maximum number of running finality providers %v", fpm.config.MaxNumFinalityProviders) - } - - if err := fpm.addFinalityProviderInstance(fpPk, passphrase); err != nil { - return err - } - - return nil -} - -func (fpm *FinalityProviderManager) StartAll() error { - if !fpm.isStarted.Load() { - fpm.isStarted.Store(true) - - fpm.wg.Add(1) - go fpm.monitorCriticalErr() - - fpm.wg.Add(1) - go fpm.monitorStatusUpdate() - } - - storedFps, err := fpm.fps.GetAllStoredFinalityProviders() - if err != nil { - return err - } - - for _, fp := range storedFps { - fpBtcPk := fp.GetBIP340BTCPK() - if !fp.ShouldStart() { - fpm.logger.Info( - "the finality provider cannot be started with status", - zap.String("eots-pk", fpBtcPk.MarshalHex()), - zap.String("status", fp.Status.String()), - ) - continue - } - if err := fpm.StartFinalityProvider(fpBtcPk, ""); err != nil { - return err - } - } - - return nil -} - -func (fpm *FinalityProviderManager) Stop() error { - if !fpm.isStarted.Swap(false) { - return fmt.Errorf("the finality-provider manager has already stopped") - } - - var stopErr error - for _, fpi := range fpm.fpis { - if !fpi.IsRunning() { - continue - } - if err := fpi.Stop(); err != nil { - stopErr = err - break - } - fpm.metrics.DecrementRunningFpGauge() - } - - close(fpm.quit) - fpm.wg.Wait() - - return stopErr -} - -func (fpm *FinalityProviderManager) ListFinalityProviderInstances() []*FinalityProviderInstance { - fpm.mu.Lock() - defer fpm.mu.Unlock() - - fpisList := make([]*FinalityProviderInstance, 0, len(fpm.fpis)) - for _, fpi := range fpm.fpis { - fpisList = append(fpisList, fpi) - } - - return fpisList -} - -func (fpm *FinalityProviderManager) ListFinalityProviderInstancesForChain(chainID string) []*FinalityProviderInstance { - fpm.mu.Lock() - defer fpm.mu.Unlock() - - fpisList := make([]*FinalityProviderInstance, 0, len(fpm.fpis)) - for _, fpi := range fpm.fpis { - if string(fpi.GetChainID()) == chainID { - fpisList = append(fpisList, fpi) - } - } - - return fpisList -} - -func (fpm *FinalityProviderManager) AllFinalityProviders() ([]*proto.FinalityProviderInfo, error) { - storedFps, err := fpm.fps.GetAllStoredFinalityProviders() - if err != nil { - return nil, err - } - - fpsInfo := make([]*proto.FinalityProviderInfo, 0, len(storedFps)) - for _, fp := range storedFps { - fpInfo := fp.ToFinalityProviderInfo() - - if fpm.IsFinalityProviderRunning(fp.GetBIP340BTCPK()) { - fpInfo.IsRunning = true - } - - fpsInfo = append(fpsInfo, fpInfo) - } - - return fpsInfo, nil -} - -func (fpm *FinalityProviderManager) FinalityProviderInfo(fpPk *bbntypes.BIP340PubKey) (*proto.FinalityProviderInfo, error) { - storedFp, err := fpm.fps.GetFinalityProvider(fpPk.MustToBTCPK()) - if err != nil { - return nil, err - } - - fpInfo := storedFp.ToFinalityProviderInfo() - - if fpm.IsFinalityProviderRunning(fpPk) { - fpInfo.IsRunning = true - } - - return fpInfo, nil -} - -func (fpm *FinalityProviderManager) IsFinalityProviderRunning(fpPk *bbntypes.BIP340PubKey) bool { - fpm.mu.Lock() - defer fpm.mu.Unlock() - - _, exists := fpm.fpis[fpPk.MarshalHex()] - return exists -} - -func (fpm *FinalityProviderManager) GetFinalityProviderInstance(fpPk *bbntypes.BIP340PubKey) (*FinalityProviderInstance, error) { - fpm.mu.Lock() - defer fpm.mu.Unlock() - - keyHex := fpPk.MarshalHex() - v, exists := fpm.fpis[keyHex] - if !exists { - return nil, fmt.Errorf("cannot find the finality-provider instance with PK: %s", keyHex) - } - - return v, nil -} - -func (fpm *FinalityProviderManager) removeFinalityProviderInstance(fpPk *bbntypes.BIP340PubKey) error { - fpm.mu.Lock() - defer fpm.mu.Unlock() - - keyHex := fpPk.MarshalHex() - fpi, exists := fpm.fpis[keyHex] - if !exists { - return fmt.Errorf("cannot find the finality-provider instance with PK: %s", keyHex) - } - if fpi.IsRunning() { - if err := fpi.Stop(); err != nil { - return fmt.Errorf("failed to stop the finality-provider instance %s", keyHex) - } - } - - delete(fpm.fpis, keyHex) - fpm.metrics.DecrementRunningFpGauge() - return nil -} - -func (fpm *FinalityProviderManager) numOfRunningFinalityProviders() int { - fpm.mu.Lock() - defer fpm.mu.Unlock() - - return len(fpm.fpis) -} - -// addFinalityProviderInstance creates a finality-provider instance, starts it and adds it into the finality-provider manager -func (fpm *FinalityProviderManager) addFinalityProviderInstance( - pk *bbntypes.BIP340PubKey, - passphrase string, -) error { - fpm.mu.Lock() - defer fpm.mu.Unlock() - - pkHex := pk.MarshalHex() - if _, exists := fpm.fpis[pkHex]; exists { - return fmt.Errorf("finality-provider instance already exists") - } - - fpIns, err := NewFinalityProviderInstance(pk, fpm.config, fpm.fps, fpm.pubRandStore, fpm.cc, fpm.consumerCon, fpm.em, fpm.metrics, passphrase, fpm.criticalErrChan, fpm.logger) - if err != nil { - return fmt.Errorf("failed to create finality-provider %s instance: %w", pkHex, err) - } - - if err := fpIns.Start(); err != nil { - return fmt.Errorf("failed to start finality-provider %s instance: %w", pkHex, err) - } - - fpm.fpis[pkHex] = fpIns - fpm.metrics.IncrementRunningFpGauge() - - return nil -} - -func (fpm *FinalityProviderManager) getLatestBlockHeightWithRetry() (uint64, error) { - var ( - latestBlockHeight uint64 - err error - ) - - if err := retry.Do(func() error { - latestBlockHeight, err = fpm.consumerCon.QueryLatestBlockHeight() - if err != nil { - return err - } - return nil - }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { - fpm.logger.Debug( - "failed to query the consumer chain for the latest block", - zap.Uint("attempt", n+1), - zap.Uint("max_attempts", RtyAttNum), - zap.Error(err), - ) - })); err != nil { - return 0, err - } - - return latestBlockHeight, nil -} diff --git a/finality-provider/service/fp_manager_test.go b/finality-provider/service/fp_manager_test.go deleted file mode 100644 index 0c6c78b9..00000000 --- a/finality-provider/service/fp_manager_test.go +++ /dev/null @@ -1,185 +0,0 @@ -package service_test - -import ( - "math/rand" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/babylonlabs-io/babylon/testutil/datagen" - bbntypes "github.com/babylonlabs-io/babylon/types" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - ccapi "github.com/babylonlabs-io/finality-provider/clientcontroller/api" - "github.com/babylonlabs-io/finality-provider/eotsmanager" - eotscfg "github.com/babylonlabs-io/finality-provider/eotsmanager/config" - fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" - "github.com/babylonlabs-io/finality-provider/finality-provider/proto" - "github.com/babylonlabs-io/finality-provider/finality-provider/service" - fpstore "github.com/babylonlabs-io/finality-provider/finality-provider/store" - "github.com/babylonlabs-io/finality-provider/keyring" - "github.com/babylonlabs-io/finality-provider/metrics" - "github.com/babylonlabs-io/finality-provider/testutil" - "github.com/babylonlabs-io/finality-provider/types" - "github.com/babylonlabs-io/finality-provider/util" -) - -var ( - eventuallyWaitTimeOut = 5 * time.Second - eventuallyPollTime = 10 * time.Millisecond -) - -func FuzzStatusUpdate(f *testing.F) { - testutil.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - mockBabylonController := testutil.PrepareMockedBabylonController(t) - randomStartingHeight := uint64(r.Int63n(100) + 100) - currentHeight := randomStartingHeight + uint64(r.Int63n(10)+1) - mockConsumerController := testutil.PrepareMockedConsumerController(t, r, randomStartingHeight, currentHeight) - vm, fpPk, cleanUp := newFinalityProviderManagerWithRegisteredFp(t, r, mockBabylonController, mockConsumerController) - defer cleanUp() - - // setup mocks - currentBlockRes := &types.BlockInfo{ - Height: currentHeight, - Hash: datagen.GenRandomByteArray(r, 32), - } - mockConsumerController.EXPECT().QueryLatestBlockHeight().Return(currentHeight, nil).AnyTimes() - mockConsumerController.EXPECT().Close().Return(nil).AnyTimes() - mockConsumerController.EXPECT().QueryLatestFinalizedBlock().Return(nil, nil).AnyTimes() - mockConsumerController.EXPECT().QueryActivatedHeight().Return(uint64(1), nil).AnyTimes() - mockConsumerController.EXPECT().QueryBlock(gomock.Any()).Return(currentBlockRes, nil).AnyTimes() - mockConsumerController.EXPECT().QueryLastPublicRandCommit(gomock.Any()).Return(nil, nil).AnyTimes() - - hasPower := r.Intn(2) != 0 - mockConsumerController.EXPECT().QueryFinalityProviderHasPower(gomock.Any(), currentHeight).Return(hasPower, nil).AnyTimes() - mockConsumerController.EXPECT().SubmitFinalitySig(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.TxResponse{TxHash: ""}, nil).AnyTimes() - var isSlashedOrJailed int - if !hasPower { - // 0 means is slashed, 1 means is jailed, 2 means neither slashed nor jailed - isSlashedOrJailed = r.Intn(3) - if isSlashedOrJailed == 0 { - mockBabylonController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(true, false, nil).AnyTimes() - } else if isSlashedOrJailed == 1 { - mockBabylonController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(false, true, nil).AnyTimes() - } else { - mockBabylonController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(false, false, nil).AnyTimes() - } - } - - err := vm.StartFinalityProvider(fpPk, passphrase) - require.NoError(t, err) - fpIns := vm.ListFinalityProviderInstances()[0] - // stop the finality-provider as we are testing static functionalities - err = fpIns.Stop() - require.NoError(t, err) - - if hasPower { - waitForStatus(t, fpIns, proto.FinalityProviderStatus_ACTIVE) - } else { - if isSlashedOrJailed == 2 && fpIns.GetStatus() == proto.FinalityProviderStatus_ACTIVE { - waitForStatus(t, fpIns, proto.FinalityProviderStatus_INACTIVE) - } else if isSlashedOrJailed == 1 { - waitForStatus(t, fpIns, proto.FinalityProviderStatus_JAILED) - } else if isSlashedOrJailed == 0 { - waitForStatus(t, fpIns, proto.FinalityProviderStatus_SLASHED) - } - } - }) -} - -func waitForStatus(t *testing.T, fpIns *service.FinalityProviderInstance, s proto.FinalityProviderStatus) { - require.Eventually(t, - func() bool { - return fpIns.GetStatus() == s - }, eventuallyWaitTimeOut, eventuallyPollTime) -} - -func newFinalityProviderManagerWithRegisteredFp(t *testing.T, r *rand.Rand, cc ccapi.ClientController, consumerCon ccapi.ConsumerController) (*service.FinalityProviderManager, *bbntypes.BIP340PubKey, func()) { - logger, err := zap.NewDevelopment() - require.NoError(t, err) - // create an EOTS manager - eotsHomeDir := filepath.Join(t.TempDir(), "eots-home") - eotsCfg := eotscfg.DefaultConfigWithHomePath(eotsHomeDir) - eotsdb, err := eotsCfg.DatabaseConfig.GetDbBackend() - require.NoError(t, err) - em, err := eotsmanager.NewLocalEOTSManager(eotsHomeDir, eotsCfg.KeyringBackend, eotsdb, logger) - require.NoError(t, err) - - // create finality-provider app with randomized config - fpHomeDir := filepath.Join(t.TempDir(), "fp-home") - fpCfg := fpcfg.DefaultConfigWithHome(fpHomeDir) - fpCfg.StatusUpdateInterval = 10 * time.Millisecond - fpCfg.NumPubRand = testutil.TestPubRandNum - input := strings.NewReader("") - kr, err := keyring.CreateKeyring( - fpCfg.BabylonConfig.KeyDirectory, - fpCfg.BabylonConfig.ChainID, - fpCfg.BabylonConfig.KeyringBackend, - input, - ) - require.NoError(t, err) - err = util.MakeDirectory(fpcfg.DataDir(fpHomeDir)) - require.NoError(t, err) - db, err := fpCfg.DatabaseConfig.GetDbBackend() - require.NoError(t, err) - fpStore, err := fpstore.NewFinalityProviderStore(db) - require.NoError(t, err) - pubRandStore, err := fpstore.NewPubRandProofStore(db) - require.NoError(t, err) - - metricsCollectors := metrics.NewFpMetrics() - vm, err := service.NewFinalityProviderManager(fpStore, pubRandStore, &fpCfg, cc, consumerCon, em, metricsCollectors, logger) - require.NoError(t, err) - - // create registered finality-provider - keyName := datagen.GenRandomHexStr(r, 10) - chainID := datagen.GenRandomHexStr(r, 10) - kc, err := keyring.NewChainKeyringControllerWithKeyring(kr, keyName, input) - require.NoError(t, err) - btcPkBytes, err := em.CreateKey(keyName, passphrase, hdPath) - require.NoError(t, err) - btcPk, err := bbntypes.NewBIP340PubKey(btcPkBytes) - require.NoError(t, err) - keyInfo, err := kc.CreateChainKey(passphrase, hdPath, "") - require.NoError(t, err) - fpAddr := keyInfo.AccAddress - fpRecord, err := em.KeyRecord(btcPk.MustMarshal(), passphrase) - require.NoError(t, err) - pop, err := kc.CreatePop(fpAddr, fpRecord.PrivKey) - require.NoError(t, err) - - err = fpStore.CreateFinalityProvider( - fpAddr, - btcPk.MustToBTCPK(), - testutil.RandomDescription(r), - testutil.ZeroCommissionRate(), - keyName, - chainID, - pop.BtcSig, - ) - require.NoError(t, err) - err = fpStore.SetFpStatus(btcPk.MustToBTCPK(), proto.FinalityProviderStatus_REGISTERED) - require.NoError(t, err) - - cleanUp := func() { - err = vm.Stop() - require.NoError(t, err) - err = eotsdb.Close() - require.NoError(t, err) - err = db.Close() - require.NoError(t, err) - err = os.RemoveAll(eotsHomeDir) - require.NoError(t, err) - err = os.RemoveAll(fpHomeDir) - require.NoError(t, err) - } - - return vm, btcPk, cleanUp -} diff --git a/finality-provider/service/fp_store_adapter.go b/finality-provider/service/fp_store_adapter.go index c131455f..f3545f16 100644 --- a/finality-provider/service/fp_store_adapter.go +++ b/finality-provider/service/fp_store_adapter.go @@ -3,70 +3,21 @@ package service import ( "sync" - sdkmath "cosmossdk.io/math" bbntypes "github.com/babylonlabs-io/babylon/types" - btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/btcsuite/btcd/btcec/v2" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "go.uber.org/zap" "github.com/babylonlabs-io/finality-provider/finality-provider/proto" "github.com/babylonlabs-io/finality-provider/finality-provider/store" ) -type createFinalityProviderResponse struct { - FpInfo *proto.FinalityProviderInfo -} - -type createFinalityProviderRequest struct { - keyName string - passPhrase string - hdPath string - chainID string - eotsPk *bbntypes.BIP340PubKey - description *stakingtypes.Description - commission *sdkmath.LegacyDec - errResponse chan error - successResponse chan *createFinalityProviderResponse -} - -type registerFinalityProviderRequest struct { - chainID string - fpAddr sdk.AccAddress - btcPubKey *bbntypes.BIP340PubKey - // TODO we should have our own representation of PoP - pop *btcstakingtypes.ProofOfPossessionBTC - description *stakingtypes.Description - commission *sdkmath.LegacyDec - errResponse chan error - successResponse chan *RegisterFinalityProviderResponse -} - -type finalityProviderRegisteredEvent struct { - bbnAddress sdk.AccAddress - btcPubKey *bbntypes.BIP340PubKey - txHash string - successResponse chan *RegisterFinalityProviderResponse -} - -type RegisterFinalityProviderResponse struct { - bbnAddress sdk.AccAddress - btcPubKey *bbntypes.BIP340PubKey - TxHash string -} - -type CreateFinalityProviderResult struct { - FpInfo *proto.FinalityProviderInfo -} - type fpState struct { mu sync.Mutex sfp *store.StoredFinalityProvider s *store.FinalityProviderStore } -func NewFpState( +func newFpState( fp *store.StoredFinalityProvider, s *store.FinalityProviderStore, ) *fpState { @@ -79,33 +30,35 @@ func NewFpState( func (fps *fpState) withLock(action func()) { fps.mu.Lock() defer fps.mu.Unlock() + action() } func (fps *fpState) setStatus(s proto.FinalityProviderStatus) error { - fps.withLock(func() { - fps.sfp.Status = s - }) + fps.mu.Lock() + fps.sfp.Status = s + fps.mu.Unlock() + return fps.s.SetFpStatus(fps.sfp.BtcPk, s) } -func (fps *fpState) setLastProcessedHeight(height uint64) error { - fps.withLock(func() { - fps.sfp.LastProcessedHeight = height - }) - return fps.s.SetFpLastProcessedHeight(fps.sfp.BtcPk, height) -} +func (fps *fpState) setLastVotedHeight(height uint64) error { + fps.mu.Lock() + fps.sfp.LastVotedHeight = height + fps.mu.Unlock() -func (fps *fpState) setLastProcessedAndVotedHeight(height uint64) error { - fps.withLock(func() { - fps.sfp.LastVotedHeight = height - fps.sfp.LastProcessedHeight = height - }) return fps.s.SetFpLastVotedHeight(fps.sfp.BtcPk, height) } func (fp *FinalityProviderInstance) GetStoreFinalityProvider() *store.StoredFinalityProvider { - return fp.fpState.sfp + var sfp *store.StoredFinalityProvider + fp.fpState.withLock(func() { + // Create a copy of the stored finality provider to prevent data races + sfpCopy := *fp.fpState.sfp + sfp = &sfpCopy + }) + + return sfp } func (fp *FinalityProviderInstance) GetBtcPkBIP340() *bbntypes.BIP340PubKey { @@ -113,6 +66,7 @@ func (fp *FinalityProviderInstance) GetBtcPkBIP340() *bbntypes.BIP340PubKey { fp.fpState.withLock(func() { pk = fp.fpState.sfp.GetBIP340BTCPK() }) + return pk } @@ -121,6 +75,7 @@ func (fp *FinalityProviderInstance) GetBtcPk() *btcec.PublicKey { fp.fpState.withLock(func() { pk = fp.fpState.sfp.BtcPk }) + return pk } @@ -133,6 +88,7 @@ func (fp *FinalityProviderInstance) GetStatus() proto.FinalityProviderStatus { fp.fpState.withLock(func() { status = fp.fpState.sfp.Status }) + return status } @@ -141,15 +97,8 @@ func (fp *FinalityProviderInstance) GetLastVotedHeight() uint64 { fp.fpState.withLock(func() { lastVotedHeight = fp.fpState.sfp.LastVotedHeight }) - return lastVotedHeight -} -func (fp *FinalityProviderInstance) GetLastProcessedHeight() uint64 { - var lastProcessedHeight uint64 - fp.fpState.withLock(func() { - lastProcessedHeight = fp.fpState.sfp.LastProcessedHeight - }) - return lastProcessedHeight + return lastVotedHeight } func (fp *FinalityProviderInstance) GetChainID() []byte { @@ -157,6 +106,7 @@ func (fp *FinalityProviderInstance) GetChainID() []byte { fp.fpState.withLock(func() { chainID = fp.fpState.sfp.ChainID }) + return []byte(chainID) } @@ -171,20 +121,8 @@ func (fp *FinalityProviderInstance) MustSetStatus(s proto.FinalityProviderStatus } } -func (fp *FinalityProviderInstance) SetLastProcessedHeight(height uint64) error { - return fp.fpState.setLastProcessedHeight(height) -} - -func (fp *FinalityProviderInstance) MustSetLastProcessedHeight(height uint64) { - if err := fp.SetLastProcessedHeight(height); err != nil { - fp.logger.Fatal("failed to set last processed height", - zap.String("pk", fp.GetBtcPkHex()), zap.Uint64("last_processed_height", height)) - } - fp.metrics.RecordFpLastProcessedHeight(fp.GetBtcPkHex(), height) -} - func (fp *FinalityProviderInstance) updateStateAfterFinalitySigSubmission(height uint64) error { - return fp.fpState.setLastProcessedAndVotedHeight(height) + return fp.fpState.setLastVotedHeight(height) } func (fp *FinalityProviderInstance) MustUpdateStateAfterFinalitySigSubmission(height uint64) { diff --git a/finality-provider/service/pub_rand_store_adapter.go b/finality-provider/service/pub_rand_store_adapter.go index f03a2836..3ab30891 100644 --- a/finality-provider/service/pub_rand_store_adapter.go +++ b/finality-provider/service/pub_rand_store_adapter.go @@ -2,7 +2,6 @@ package service import ( "github.com/babylonlabs-io/finality-provider/finality-provider/store" - "github.com/btcsuite/btcd/btcec/v2" "github.com/cometbft/cometbft/crypto/merkle" ) @@ -10,21 +9,21 @@ type pubRandState struct { s *store.PubRandProofStore } -func NewPubRandState(s *store.PubRandProofStore) *pubRandState { +func newPubRandState(s *store.PubRandProofStore) *pubRandState { return &pubRandState{s: s} } -func (st *pubRandState) AddPubRandProofList( - pubRandList []*btcec.FieldVal, +func (st *pubRandState) addPubRandProofList( + pk, chainID []byte, height uint64, numPubRand uint64, proofList []*merkle.Proof, ) error { - return st.s.AddPubRandProofList(pubRandList, proofList) + return st.s.AddPubRandProofList(chainID, pk, height, numPubRand, proofList) } -func (st *pubRandState) GetPubRandProof(pubRand *btcec.FieldVal) ([]byte, error) { - return st.s.GetPubRandProof(pubRand) +func (st *pubRandState) getPubRandProof(pk, chainID []byte, height uint64) ([]byte, error) { + return st.s.GetPubRandProof(chainID, pk, height) } -func (st *pubRandState) GetPubRandProofList(pubRandList []*btcec.FieldVal) ([][]byte, error) { - return st.s.GetPubRandProofList(pubRandList) +func (st *pubRandState) getPubRandProofList(pk, chainID []byte, height uint64, numPubRand uint64) ([][]byte, error) { + return st.s.GetPubRandProofList(chainID, pk, height, numPubRand) } diff --git a/finality-provider/service/rpcserver.go b/finality-provider/service/rpcserver.go index b792491d..38e80d9b 100644 --- a/finality-provider/service/rpcserver.go +++ b/finality-provider/service/rpcserver.go @@ -2,14 +2,16 @@ package service import ( "context" + "errors" "fmt" "sync" "sync/atomic" - "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" bbntypes "github.com/babylonlabs-io/babylon/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "google.golang.org/grpc" + protobuf "google.golang.org/protobuf/proto" "github.com/babylonlabs-io/finality-provider/finality-provider/proto" "github.com/babylonlabs-io/finality-provider/types" @@ -34,7 +36,6 @@ type rpcServer struct { func newRPCServer( fpa *FinalityProviderApp, ) *rpcServer { - return &rpcServer{ quit: make(chan struct{}), app: fpa, @@ -69,23 +70,23 @@ func (r *rpcServer) Stop() error { func (r *rpcServer) RegisterWithGrpcServer(grpcServer *grpc.Server) error { // Register the main RPC server. proto.RegisterFinalityProvidersServer(grpcServer, r) + return nil } // GetInfo returns general information relating to the active daemon func (r *rpcServer) GetInfo(context.Context, *proto.GetInfoRequest) (*proto.GetInfoResponse, error) { - return &proto.GetInfoResponse{ - Version: version.Version(), + Version: version.RPC(), }, nil } // CreateFinalityProvider generates a finality-provider object and saves it in the database func (r *rpcServer) CreateFinalityProvider( - ctx context.Context, + _ context.Context, req *proto.CreateFinalityProviderRequest, ) (*proto.CreateFinalityProviderResponse, error) { - commissionRate, err := math.LegacyNewDecFromStr(req.Commission) + commissionRate, err := sdkmath.LegacyNewDecFromStr(req.Commission) if err != nil { return nil, err } @@ -95,7 +96,7 @@ func (r *rpcServer) CreateFinalityProvider( return nil, err } - eotsPk, err := parseOptEotsPk(req.EotsPkHex) + eotsPk, err := parseEotsPk(req.EotsPkHex) if err != nil { return nil, err } @@ -104,7 +105,6 @@ func (r *rpcServer) CreateFinalityProvider( req.KeyName, req.ChainId, req.Passphrase, - req.HdPath, eotsPk, &description, &commissionRate, @@ -116,97 +116,152 @@ func (r *rpcServer) CreateFinalityProvider( return &proto.CreateFinalityProviderResponse{ FinalityProvider: result.FpInfo, + TxHash: result.TxHash, }, nil } -// RegisterFinalityProvider sends a transactions to Babylon to register a BTC finality-provider -func (r *rpcServer) RegisterFinalityProvider(ctx context.Context, req *proto.RegisterFinalityProviderRequest) ( - *proto.RegisterFinalityProviderResponse, error) { +// AddFinalitySignature adds a manually constructed finality signature to Babylon +// NOTE: this is only used for presentation/testing purposes +func (r *rpcServer) AddFinalitySignature(_ context.Context, req *proto.AddFinalitySignatureRequest) ( + *proto.AddFinalitySignatureResponse, + error, +) { + r.app.wg.Add(1) + defer r.app.wg.Done() + + var res *proto.AddFinalitySignatureResponse + + select { + case <-r.app.quit: + r.app.logger.Info("exiting metrics update loop") + + return res, nil + default: + fpPk, err := bbntypes.NewBIP340PubKeyFromHex(req.BtcPk) + if err != nil { + return nil, err + } + + fpi, err := r.app.GetFinalityProviderInstance() + if err != nil { + return nil, err + } + + if fpi.GetBtcPkHex() != req.BtcPk { + errMsg := fmt.Sprintf("the finality provider running does not match the request, got: %s, expected: %s", + req.BtcPk, fpi.GetBtcPkHex()) + + return nil, errors.New(errMsg) + } + + b := &types.BlockInfo{ + Height: req.Height, + Hash: req.AppHash, + } + + txRes, privKey, err := fpi.TestSubmitFinalitySignatureAndExtractPrivKey(b, req.CheckDoubleSign) + if err != nil { + return nil, err + } + + res = &proto.AddFinalitySignatureResponse{TxHash: txRes.TxHash} + + // if privKey is not empty, then this BTC finality-provider + // has voted for a fork and will be slashed + if privKey != nil { + localPrivKey, err := r.app.getFpPrivKey(fpPk.MustMarshal()) + if err != nil { + r.app.logger.Error(fmt.Sprintf("err get priv key %s", err.Error())) + + return nil, err + } + + res.ExtractedSkHex = privKey.Key.String() + localSkHex := localPrivKey.Key.String() + localSkNegateHex := localPrivKey.Key.Negate().String() + switch { + case res.ExtractedSkHex == localSkHex: + res.LocalSkHex = localSkHex + case res.ExtractedSkHex == localSkNegateHex: + res.LocalSkHex = localSkNegateHex + default: + msg := fmt.Sprintf( + "the finality-provider's BTC private key is extracted but does not match the local key,"+ + " extracted: %s, local: %s, local-negated: %s", + res.ExtractedSkHex, localSkHex, localSkNegateHex, + ) + + return nil, errors.New(msg) + } + } + + return res, nil + } +} - txRes, err := r.app.RegisterFinalityProvider(req.BtcPk) +// UnjailFinalityProvider unjails a finality-provider +func (r *rpcServer) UnjailFinalityProvider(_ context.Context, req *proto.UnjailFinalityProviderRequest) ( + *proto.UnjailFinalityProviderResponse, error) { + fpPk, err := bbntypes.NewBIP340PubKeyFromHex(req.BtcPk) if err != nil { - return nil, fmt.Errorf("failed to register the finality-provider to Babylon: %w", err) + return nil, err } - // the finality-provider instance should be started right after registration - if err := r.app.StartHandlingFinalityProvider(txRes.btcPubKey, req.Passphrase); err != nil { - return nil, fmt.Errorf("failed to start the registered finality-provider %s: %w", txRes.bbnAddress.String(), err) + res, err := r.app.UnjailFinalityProvider(fpPk) + if err != nil { + return nil, fmt.Errorf("failed to unjail the finality-provider: %w", err) } - return &proto.RegisterFinalityProviderResponse{TxHash: txRes.TxHash}, nil + return &proto.UnjailFinalityProviderResponse{TxHash: res.TxHash}, nil } -// AddFinalitySignature adds a manually constructed finality signature to Babylon -// NOTE: this is only used for presentation/testing purposes -func (r *rpcServer) AddFinalitySignature(ctx context.Context, req *proto.AddFinalitySignatureRequest) ( - *proto.AddFinalitySignatureResponse, error) { - +// QueryFinalityProvider queries the information of the finality-provider +func (r *rpcServer) QueryFinalityProvider(_ context.Context, req *proto.QueryFinalityProviderRequest) ( + *proto.QueryFinalityProviderResponse, error) { fpPk, err := bbntypes.NewBIP340PubKeyFromHex(req.BtcPk) if err != nil { return nil, err } - - fpi, err := r.app.GetFinalityProviderInstance(fpPk) + fp, err := r.app.GetFinalityProviderInfo(fpPk) if err != nil { return nil, err } - b := &types.BlockInfo{ - Height: req.Height, - Hash: req.AppHash, - } + return &proto.QueryFinalityProviderResponse{FinalityProvider: fp}, nil +} - txRes, privKey, err := fpi.TestSubmitFinalitySignatureAndExtractPrivKey(b) +func (r *rpcServer) EditFinalityProvider(_ context.Context, req *proto.EditFinalityProviderRequest) (*proto.EmptyResponse, error) { + fpPk, err := bbntypes.NewBIP340PubKeyFromHex(req.BtcPk) if err != nil { return nil, err } - res := &proto.AddFinalitySignatureResponse{TxHash: txRes.TxHash} - - // if privKey is not empty, then this BTC finality-provider - // has voted for a fork and will be slashed - if privKey != nil { - localPrivKey, err := r.app.getFpPrivKey(fpPk.MustMarshal()) - res.ExtractedSkHex = privKey.Key.String() - if err != nil { - return nil, err - } - localSkHex := localPrivKey.Key.String() - localSkNegateHex := localPrivKey.Key.Negate().String() - if res.ExtractedSkHex == localSkHex { - res.LocalSkHex = localSkHex - } else if res.ExtractedSkHex == localSkNegateHex { - res.LocalSkHex = localSkNegateHex - } else { - return nil, fmt.Errorf("the finality-provider's BTC private key is extracted but does not match the local key,"+ - "extrated: %s, local: %s, local-negated: %s", - res.ExtractedSkHex, localSkHex, localSkNegateHex) - } + rate, err := sdkmath.LegacyNewDecFromStr(req.Commission) + if err != nil { + return nil, err } - return res, nil -} - -// QueryFinalityProvider queries the information of the finality-provider -func (r *rpcServer) QueryFinalityProvider(ctx context.Context, req *proto.QueryFinalityProviderRequest) ( - *proto.QueryFinalityProviderResponse, error) { - - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(req.BtcPk) + descBytes, err := protobuf.Marshal(req.Description) if err != nil { return nil, err } - fp, err := r.app.GetFinalityProviderInfo(fpPk) + + fpPub := fpPk.MustToBTCPK() + updatedMsg, err := r.app.cc.EditFinalityProvider(fpPub, &rate, descBytes) if err != nil { return nil, err } - return &proto.QueryFinalityProviderResponse{FinalityProvider: fp}, nil + if err := r.app.fps.SetFpDescription(fpPub, updatedMsg.Description, updatedMsg.Commission); err != nil { + return nil, err + } + + return nil, nil } // QueryFinalityProviderList queries the information of a list of finality providers -func (r *rpcServer) QueryFinalityProviderList(ctx context.Context, req *proto.QueryFinalityProviderListRequest) ( +func (r *rpcServer) QueryFinalityProviderList(_ context.Context, _ *proto.QueryFinalityProviderListRequest) ( *proto.QueryFinalityProviderListResponse, error) { - fps, err := r.app.ListAllFinalityProvidersInfo() if err != nil { return nil, err @@ -215,20 +270,24 @@ func (r *rpcServer) QueryFinalityProviderList(ctx context.Context, req *proto.Qu return &proto.QueryFinalityProviderListResponse{FinalityProviders: fps}, nil } -// SignMessageFromChainKey signs a message from the chain keyring. -func (r *rpcServer) SignMessageFromChainKey(ctx context.Context, req *proto.SignMessageFromChainKeyRequest) ( - *proto.SignMessageFromChainKeyResponse, error) { - signature, err := r.app.SignRawMsg(req.KeyName, req.Passphrase, req.HdPath, req.MsgToSign) +// UnsafeRemoveMerkleProof - removes proofs up to target height +func (r *rpcServer) UnsafeRemoveMerkleProof(_ context.Context, req *proto.RemoveMerkleProofRequest) (*proto.EmptyResponse, error) { + fpPk, err := parseEotsPk(req.BtcPkHex) if err != nil { return nil, err } - return &proto.SignMessageFromChainKeyResponse{Signature: signature}, nil + if err := r.app.pubRandStore.RemovePubRandProofList([]byte(req.ChainId), fpPk.MustMarshal(), req.TargetHeight); err != nil { + return nil, err + } + + return nil, nil } -func parseOptEotsPk(eotsPkHex string) (*bbntypes.BIP340PubKey, error) { - if len(eotsPkHex) > 0 { - return bbntypes.NewBIP340PubKeyFromHex(eotsPkHex) +func parseEotsPk(eotsPkHex string) (*bbntypes.BIP340PubKey, error) { + if eotsPkHex == "" { + return nil, fmt.Errorf("eots-pk cannot be empty") } - return nil, nil + + return bbntypes.NewBIP340PubKeyFromHex(eotsPkHex) } diff --git a/finality-provider/service/server.go b/finality-provider/service/server.go index c6eb2743..7b6423b3 100644 --- a/finality-provider/service/server.go +++ b/finality-provider/service/server.go @@ -8,7 +8,6 @@ import ( "sync/atomic" "github.com/lightningnetwork/lnd/kvdb" - "github.com/lightningnetwork/lnd/signal" "go.uber.org/zap" "google.golang.org/grpc" @@ -25,28 +24,26 @@ type Server struct { cfg *fpcfg.Config logger *zap.Logger - rpcServer *rpcServer - db kvdb.Backend - interceptor signal.Interceptor + rpcServer *rpcServer + db kvdb.Backend quit chan struct{} } -// NewFinalityproviderServer creates a new server with the given config. -func NewFinalityProviderServer(cfg *fpcfg.Config, l *zap.Logger, fpa *FinalityProviderApp, db kvdb.Backend, sig signal.Interceptor) *Server { +// NewFinalityProviderServer creates a new server with the given config. +func NewFinalityProviderServer(cfg *fpcfg.Config, l *zap.Logger, fpa *FinalityProviderApp, db kvdb.Backend) *Server { return &Server{ - cfg: cfg, - logger: l, - rpcServer: newRPCServer(fpa), - db: db, - interceptor: sig, - quit: make(chan struct{}, 1), + cfg: cfg, + logger: l, + rpcServer: newRPCServer(fpa), + db: db, + quit: make(chan struct{}, 1), } } // RunUntilShutdown runs the main EOTS manager server loop until a signal is // received to shut down the process. -func (s *Server) RunUntilShutdown() error { +func (s *Server) RunUntilShutdown(ctx context.Context) error { if atomic.AddInt32(&s.started, 1) != 1 { return nil } @@ -64,20 +61,25 @@ func (s *Server) RunUntilShutdown() error { defer func() { s.logger.Info("Closing database...") - s.db.Close() - s.logger.Info("Database closed") - metricsServer.Stop(context.Background()) + if err := s.db.Close(); err != nil { + s.logger.Error("Failed to close database", zap.Error(err)) + } else { + s.logger.Info("Database closed") + } + metricsServer.Stop(ctx) s.logger.Info("Metrics server stopped") }() - listenAddr := s.cfg.RpcListener + listenAddr := s.cfg.RPCListener // we create listeners from the RPCListeners defined // in the config. lis, err := net.Listen("tcp", listenAddr) if err != nil { return fmt.Errorf("failed to listen on %s: %w", listenAddr, err) } - defer lis.Close() + defer func() { + _ = lis.Close() + }() grpcServer := grpc.NewServer() defer grpcServer.Stop() @@ -86,25 +88,22 @@ func (s *Server) RunUntilShutdown() error { return fmt.Errorf("failed to register gRPC server: %w", err) } - // All the necessary components have been registered, so we can + // All the necessary parts have been registered, so we can // actually start listening for requests. - if err := s.startGrpcListen(grpcServer, []net.Listener{lis}); err != nil { - return fmt.Errorf("failed to start gRPC listener: %v", err) - } + s.startGrpcListen(grpcServer, []net.Listener{lis}) s.logger.Info("Finality Provider Daemon is fully active!") // Wait for shutdown signal from either a graceful server stop or from // the interrupt handler. - <-s.interceptor.ShutdownChannel() + <-ctx.Done() return nil } // startGrpcListen starts the GRPC server on the passed listeners. -func (s *Server) startGrpcListen(grpcServer *grpc.Server, listeners []net.Listener) error { - - // Use a WaitGroup so we can be sure the instructions on how to input the +func (s *Server) startGrpcListen(grpcServer *grpc.Server, listeners []net.Listener) { + // Use a WaitGroup, so we can be sure the instructions on how to input the // password is the last thing to be printed to the console. var wg sync.WaitGroup @@ -123,6 +122,4 @@ func (s *Server) startGrpcListen(grpcServer *grpc.Server, listeners []net.Listen // Wait for gRPC servers to be up running. wg.Wait() - - return nil } diff --git a/finality-provider/store/errors.go b/finality-provider/store/errors.go index ad31420d..3d856da7 100644 --- a/finality-provider/store/errors.go +++ b/finality-provider/store/errors.go @@ -3,17 +3,17 @@ package store import "errors" var ( - // ErrCorruptedFinalityProviderDb For some reason, db on disk representation have changed - ErrCorruptedFinalityProviderDb = errors.New("finality provider db is corrupted") + // ErrCorruptedFinalityProviderDB For some reason, db on disk representation have changed + ErrCorruptedFinalityProviderDB = errors.New("finality provider db is corrupted") // ErrFinalityProviderNotFound The finality provider we try update is not found in db ErrFinalityProviderNotFound = errors.New("finality provider not found") - // ErrDuplicateFinalityProvider The finality provider we try to add already exists in db + // ErrDuplicateFinalityProvider, The finality provider we try to add already exists in db ErrDuplicateFinalityProvider = errors.New("finality provider already exists") - // ErrCorruptedPubRandProofDb For some reason, db on disk representation have changed - ErrCorruptedPubRandProofDb = errors.New("public randomness proof db is corrupted") + // ErrCorruptedPubRandProofDB For some reason, db on disk representation have changed + ErrCorruptedPubRandProofDB = errors.New("public randomness proof db is corrupted") // ErrPubRandProofNotFound The finality provider we try update is not found in db ErrPubRandProofNotFound = errors.New("public randomness proof not found") diff --git a/finality-provider/store/fpstore.go b/finality-provider/store/fpstore.go index 2b6a85a0..4993976c 100644 --- a/finality-provider/store/fpstore.go +++ b/finality-provider/store/fpstore.go @@ -37,6 +37,7 @@ func NewFinalityProviderStore(db kvdb.Backend) (*FinalityProviderStore, error) { func (s *FinalityProviderStore) initBuckets() error { return kvdb.Batch(s.db, func(tx kvdb.RwTx) error { _, err := tx.CreateTopLevelBucket(finalityProviderBucketName) + return err }) } @@ -46,8 +47,7 @@ func (s *FinalityProviderStore) CreateFinalityProvider( btcPk *btcec.PublicKey, description *stakingtypes.Description, commission *sdkmath.LegacyDec, - keyName, chainId string, - btcSig []byte, + chainID string, ) error { desBytes, err := description.Marshal() if err != nil { @@ -58,12 +58,8 @@ func (s *FinalityProviderStore) CreateFinalityProvider( BtcPk: schnorr.SerializePubKey(btcPk), Description: desBytes, Commission: commission.String(), - Pop: &proto.ProofOfPossession{ - BtcSig: btcSig, - }, - KeyName: keyName, - ChainId: chainId, - Status: proto.FinalityProviderStatus_CREATED, + ChainId: chainID, + Status: proto.FinalityProviderStatus_REGISTERED, } return s.createFinalityProviderInternal(fp) @@ -75,7 +71,7 @@ func (s *FinalityProviderStore) createFinalityProviderInternal( return kvdb.Batch(s.db, func(tx kvdb.RwTx) error { fpBucket := tx.ReadWriteBucket(finalityProviderBucketName) if fpBucket == nil { - return ErrCorruptedFinalityProviderDb + return ErrCorruptedFinalityProviderDB } // check btc pk first to avoid duplicates @@ -106,18 +102,25 @@ func saveFinalityProvider( func (s *FinalityProviderStore) SetFpStatus(btcPk *btcec.PublicKey, status proto.FinalityProviderStatus) error { setFpStatus := func(fp *proto.FinalityProvider) error { fp.Status = status + return nil } return s.setFinalityProviderState(btcPk, setFpStatus) } +func (s *FinalityProviderStore) MustSetFpStatus(btcPk *btcec.PublicKey, status proto.FinalityProviderStatus) { + if err := s.SetFpStatus(btcPk, status); err != nil { + panic(err) + } +} + // UpdateFpStatusFromVotingPower based on the current voting power of the finality provider // updates the status, if it has some voting power, sets to active func (s *FinalityProviderStore) UpdateFpStatusFromVotingPower( hasPower bool, fp *StoredFinalityProvider, -) (newStatus proto.FinalityProviderStatus, err error) { +) (proto.FinalityProviderStatus, error) { if fp.Status == proto.FinalityProviderStatus_SLASHED { // Slashed FP should not update status return proto.FinalityProviderStatus_SLASHED, nil @@ -128,15 +131,11 @@ func (s *FinalityProviderStore) UpdateFpStatusFromVotingPower( return proto.FinalityProviderStatus_ACTIVE, s.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_ACTIVE) } - // voting power == 0 then set status depending on previous status - switch fp.Status { - case proto.FinalityProviderStatus_CREATED: - // previous status is CREATED then set to REGISTERED - return proto.FinalityProviderStatus_REGISTERED, s.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_REGISTERED) - case proto.FinalityProviderStatus_ACTIVE: + if fp.Status == proto.FinalityProviderStatus_ACTIVE { // previous status is ACTIVE then set to INACTIVE return proto.FinalityProviderStatus_INACTIVE, s.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_INACTIVE) } + return fp.Status, nil } @@ -147,9 +146,6 @@ func (s *FinalityProviderStore) SetFpLastVotedHeight(btcPk *btcec.PublicKey, las if fp.LastVotedHeight < lastVotedHeight { fp.LastVotedHeight = lastVotedHeight } - if fp.LastProcessedHeight < lastVotedHeight { - fp.LastProcessedHeight = lastVotedHeight - } return nil } @@ -157,39 +153,26 @@ func (s *FinalityProviderStore) SetFpLastVotedHeight(btcPk *btcec.PublicKey, las return s.setFinalityProviderState(btcPk, setFpLastVotedHeight) } -// SetFpLastProcessedHeight sets the last processed height to the stored last processed height -// only if it is larger than the stored one. This is to ensure the stored state to increase monotonically -func (s *FinalityProviderStore) SetFpLastProcessedHeight(btcPk *btcec.PublicKey, lastProcessedHeight uint64) error { - setFpLastProcessedHeight := func(fp *proto.FinalityProvider) error { - if fp.LastProcessedHeight < lastProcessedHeight { - fp.LastProcessedHeight = lastProcessedHeight - } - - return nil - } - - return s.setFinalityProviderState(btcPk, setFpLastProcessedHeight) -} - func (s *FinalityProviderStore) setFinalityProviderState( btcPk *btcec.PublicKey, stateTransitionFn func(provider *proto.FinalityProvider) error, ) error { pkBytes := schnorr.SerializePubKey(btcPk) + return kvdb.Batch(s.db, func(tx kvdb.RwTx) error { fpBucket := tx.ReadWriteBucket(finalityProviderBucketName) if fpBucket == nil { - return ErrCorruptedFinalityProviderDb + return ErrCorruptedFinalityProviderDB } - fpFromDb := fpBucket.Get(pkBytes) - if fpFromDb == nil { + fpFromDB := fpBucket.Get(pkBytes) + if fpFromDB == nil { return ErrFinalityProviderNotFound } var storedFp proto.FinalityProvider - if err := pm.Unmarshal(fpFromDb, &storedFp); err != nil { - return ErrCorruptedFinalityProviderDb + if err := pm.Unmarshal(fpFromDB, &storedFp); err != nil { + return ErrCorruptedFinalityProviderDB } if err := stateTransitionFn(&storedFp); err != nil { @@ -207,7 +190,7 @@ func (s *FinalityProviderStore) GetFinalityProvider(btcPk *btcec.PublicKey) (*St err := s.db.View(func(tx kvdb.RTx) error { fpBucket := tx.ReadBucket(finalityProviderBucketName) if fpBucket == nil { - return ErrCorruptedFinalityProviderDb + return ErrCorruptedFinalityProviderDB } fpBytes := fpBucket.Get(pkBytes) @@ -217,15 +200,16 @@ func (s *FinalityProviderStore) GetFinalityProvider(btcPk *btcec.PublicKey) (*St var fpProto proto.FinalityProvider if err := pm.Unmarshal(fpBytes, &fpProto); err != nil { - return ErrCorruptedFinalityProviderDb + return ErrCorruptedFinalityProviderDB } - fpFromDb, err := protoFpToStoredFinalityProvider(&fpProto) + fpFromDB, err := protoFpToStoredFinalityProvider(&fpProto) if err != nil { return err } - storedFp = fpFromDb + storedFp = fpFromDB + return nil }, func() {}) @@ -245,20 +229,20 @@ func (s *FinalityProviderStore) GetAllStoredFinalityProviders() ([]*StoredFinali err := s.db.View(func(tx kvdb.RTx) error { fpBucket := tx.ReadBucket(finalityProviderBucketName) if fpBucket == nil { - return ErrCorruptedFinalityProviderDb + return ErrCorruptedFinalityProviderDB } - return fpBucket.ForEach(func(k, v []byte) error { + return fpBucket.ForEach(func(_, v []byte) error { var fpProto proto.FinalityProvider if err := pm.Unmarshal(v, &fpProto); err != nil { - return ErrCorruptedFinalityProviderDb + return ErrCorruptedFinalityProviderDB } - fpFromDb, err := protoFpToStoredFinalityProvider(&fpProto) + fpFromDB, err := protoFpToStoredFinalityProvider(&fpProto) if err != nil { return err } - storedFps = append(storedFps, fpFromDb) + storedFps = append(storedFps, fpFromDB) return nil }) @@ -270,3 +254,20 @@ func (s *FinalityProviderStore) GetAllStoredFinalityProviders() ([]*StoredFinali return storedFps, nil } + +// SetFpDescription updates description of finality provider +func (s *FinalityProviderStore) SetFpDescription(btcPk *btcec.PublicKey, desc *stakingtypes.Description, rate *sdkmath.LegacyDec) error { + setDescription := func(fp *proto.FinalityProvider) error { + descBytes, err := desc.Marshal() + if err != nil { + return err + } + + fp.Description = descBytes + fp.Commission = rate.String() + + return nil + } + + return s.setFinalityProviderState(btcPk, setDescription) +} diff --git a/finality-provider/store/fpstore_test.go b/finality-provider/store/fpstore_test.go index befe47f1..ddbde70f 100644 --- a/finality-provider/store/fpstore_test.go +++ b/finality-provider/store/fpstore_test.go @@ -8,23 +8,25 @@ import ( "github.com/babylonlabs-io/babylon/testutil/datagen" "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/babylonlabs-io/finality-provider/finality-provider/config" "github.com/babylonlabs-io/finality-provider/finality-provider/proto" fpstore "github.com/babylonlabs-io/finality-provider/finality-provider/store" "github.com/babylonlabs-io/finality-provider/testutil" - sdk "github.com/cosmos/cosmos-sdk/types" ) // FuzzFinalityProvidersStore tests save and list finality providers properly func FuzzFinalityProvidersStore(f *testing.F) { testutil.AddRandomSeedsToFuzzer(f, 10) f.Fuzz(func(t *testing.T, seed int64) { + t.Parallel() r := rand.New(rand.NewSource(seed)) homePath := t.TempDir() cfg := config.DefaultDBConfigWithHomePath(homePath) - fpdb, err := cfg.GetDbBackend() + fpdb, err := cfg.GetDBBackend() require.NoError(t, err) vs, err := fpstore.NewFinalityProviderStore(fpdb) require.NoError(t, err) @@ -46,9 +48,7 @@ func FuzzFinalityProvidersStore(f *testing.F) { fp.BtcPk, fp.Description, fp.Commission, - fp.KeyName, fp.ChainID, - fp.Pop.BtcSig, ) require.NoError(t, err) @@ -59,9 +59,7 @@ func FuzzFinalityProvidersStore(f *testing.F) { fp.BtcPk, fp.Description, fp.Commission, - fp.KeyName, fp.ChainID, - fp.Pop.BtcSig, ) require.ErrorIs(t, err, fpstore.ErrDuplicateFinalityProvider) @@ -81,6 +79,7 @@ func FuzzFinalityProvidersStore(f *testing.F) { } func TestUpdateFpStatusFromVotingPower(t *testing.T) { + t.Parallel() r := rand.New(rand.NewSource(10)) anyFpStatus := proto.FinalityProviderStatus(100) @@ -91,13 +90,6 @@ func TestUpdateFpStatusFromVotingPower(t *testing.T) { expStatus proto.FinalityProviderStatus expErr error }{ - { - "zero vp: Created to Registered", - proto.FinalityProviderStatus_CREATED, - 0, - proto.FinalityProviderStatus_REGISTERED, - nil, - }, { "zero vp: Active to Inactive", proto.FinalityProviderStatus_ACTIVE, @@ -126,13 +118,6 @@ func TestUpdateFpStatusFromVotingPower(t *testing.T) { proto.FinalityProviderStatus_SLASHED, nil, }, - { - "vp > 0: Created to Active", - proto.FinalityProviderStatus_CREATED, - 1, - proto.FinalityProviderStatus_ACTIVE, - nil, - }, { "vp > 0: Registered to Active", proto.FinalityProviderStatus_REGISTERED, @@ -154,13 +139,6 @@ func TestUpdateFpStatusFromVotingPower(t *testing.T) { anyFpStatus, fpstore.ErrFinalityProviderNotFound, }, - { - "err: fp not found and vp == 0 && created", - proto.FinalityProviderStatus_CREATED, - 0, - anyFpStatus, - fpstore.ErrFinalityProviderNotFound, - }, { "err: fp not found and vp == 0 && active", proto.FinalityProviderStatus_ACTIVE, @@ -173,20 +151,22 @@ func TestUpdateFpStatusFromVotingPower(t *testing.T) { homePath := t.TempDir() cfg := config.DefaultDBConfigWithHomePath(homePath) - fpdb, err := cfg.GetDbBackend() + fpdb, err := cfg.GetDBBackend() require.NoError(t, err) fps, err := fpstore.NewFinalityProviderStore(fpdb) require.NoError(t, err) - defer func() { + t.Cleanup(func() { err := fpdb.Close() require.NoError(t, err) err = os.RemoveAll(homePath) require.NoError(t, err) - }() + }) for _, tc := range tcs { + tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() fp := testutil.GenRandomFinalityProvider(r, t) fp.Status = tc.fpStoredStatus if tc.expErr == nil { @@ -195,9 +175,7 @@ func TestUpdateFpStatusFromVotingPower(t *testing.T) { fp.BtcPk, fp.Description, fp.Commission, - fp.KeyName, fp.ChainID, - fp.Pop.BtcSig, ) require.NoError(t, err) @@ -208,6 +186,7 @@ func TestUpdateFpStatusFromVotingPower(t *testing.T) { actStatus, err := fps.UpdateFpStatusFromVotingPower(tc.votingPowerOnChain > 0, fp) if tc.expErr != nil { require.EqualError(t, err, tc.expErr.Error()) + return } require.NoError(t, err) diff --git a/finality-provider/store/pub_rand.go b/finality-provider/store/pub_rand.go index 678c8f53..89c77a87 100644 --- a/finality-provider/store/pub_rand.go +++ b/finality-provider/store/pub_rand.go @@ -1,9 +1,12 @@ package store import ( + "bytes" "fmt" - "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcwallet/walletdb" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cometbft/cometbft/crypto/merkle" "github.com/lightningnetwork/lnd/kvdb" ) @@ -30,24 +33,61 @@ func NewPubRandProofStore(db kvdb.Backend) (*PubRandProofStore, error) { func (s *PubRandProofStore) initBuckets() error { return kvdb.Batch(s.db, func(tx kvdb.RwTx) error { _, err := tx.CreateTopLevelBucket(pubRandProofBucketName) + return err }) } +// getKey key is (chainID || pk || height) +func getKey(chainID, pk []byte, height uint64) []byte { + // Convert height to bytes + heightBytes := sdk.Uint64ToBigEndian(height) + + // Concatenate all components to create the key + key := make([]byte, 0, len(pk)+len(chainID)+len(heightBytes)) + key = append(key, chainID...) + key = append(key, pk...) + key = append(key, heightBytes...) + + return key +} + +func getPrefixKey(chainID, pk []byte) []byte { + // Concatenate chainID and pk to form the prefix + prefix := make([]byte, 0, len(chainID)+len(pk)) + prefix = append(prefix, chainID...) + prefix = append(prefix, pk...) + + return prefix +} + +func buildKeys(chainID, pk []byte, height uint64, num uint64) [][]byte { + keys := make([][]byte, 0, num) + + for i := uint64(0); i < num; i++ { + key := getKey(chainID, pk, height+i) + keys = append(keys, key) + } + + return keys +} + func (s *PubRandProofStore) AddPubRandProofList( - pubRandList []*btcec.FieldVal, + chainID []byte, + pk []byte, + height uint64, + numPubRand uint64, proofList []*merkle.Proof, ) error { - if len(pubRandList) != len(proofList) { + keys := buildKeys(chainID, pk, height, numPubRand) + + if len(keys) != len(proofList) { return fmt.Errorf("the number of public randomness is not same as the number of proofs") } - pubRandBytesList := [][]byte{} - proofBytesList := [][]byte{} - for i := range pubRandList { - pubRandBytes := *pubRandList[i].Bytes() - pubRandBytesList = append(pubRandBytesList, pubRandBytes[:]) - proofBytes, err := proofList[i].ToProto().Marshal() + var proofBytesList [][]byte + for _, proof := range proofList { + proofBytes, err := proof.ToProto().Marshal() if err != nil { return fmt.Errorf("invalid proof: %w", err) } @@ -57,16 +97,16 @@ func (s *PubRandProofStore) AddPubRandProofList( return kvdb.Batch(s.db, func(tx kvdb.RwTx) error { bucket := tx.ReadWriteBucket(pubRandProofBucketName) if bucket == nil { - return ErrCorruptedPubRandProofDb + return ErrCorruptedPubRandProofDB } - for i := range pubRandBytesList { + for i, key := range keys { // skip if already committed - if bucket.Get(pubRandBytesList[i]) != nil { + if bucket.Get(key) != nil { continue } // set to DB - if err := bucket.Put(pubRandBytesList[i], proofBytesList[i]); err != nil { + if err := bucket.Put(key, proofBytesList[i]); err != nil { return err } } @@ -75,17 +115,17 @@ func (s *PubRandProofStore) AddPubRandProofList( }) } -func (s *PubRandProofStore) GetPubRandProof(pubRand *btcec.FieldVal) ([]byte, error) { - pubRandBytes := *pubRand.Bytes() +func (s *PubRandProofStore) GetPubRandProof(chainID []byte, pk []byte, height uint64) ([]byte, error) { + key := getKey(chainID, pk, height) var proofBytes []byte err := s.db.View(func(tx kvdb.RTx) error { bucket := tx.ReadBucket(pubRandProofBucketName) if bucket == nil { - return ErrCorruptedPubRandProofDb + return ErrCorruptedPubRandProofDB } - proofBytes = bucket.Get(pubRandBytes[:]) + proofBytes = bucket.Get(key) if proofBytes == nil { return ErrPubRandProofNotFound } @@ -100,23 +140,23 @@ func (s *PubRandProofStore) GetPubRandProof(pubRand *btcec.FieldVal) ([]byte, er return proofBytes, nil } -func (s *PubRandProofStore) GetPubRandProofList(pubRandList []*btcec.FieldVal) ([][]byte, error) { - pubRandBytesList := [][]byte{} - for i := range pubRandList { - pubRandBytes := *pubRandList[i].Bytes() - pubRandBytesList = append(pubRandBytesList, pubRandBytes[:]) - } +func (s *PubRandProofStore) GetPubRandProofList(chainID []byte, + pk []byte, + height uint64, + numPubRand uint64, +) ([][]byte, error) { + keys := buildKeys(chainID, pk, height, numPubRand) - proofBytesList := [][]byte{} + var proofBytesList [][]byte err := s.db.View(func(tx kvdb.RTx) error { bucket := tx.ReadBucket(pubRandProofBucketName) if bucket == nil { - return ErrCorruptedPubRandProofDb + return ErrCorruptedPubRandProofDB } - for i := range pubRandBytesList { - proofBytes := bucket.Get(pubRandBytesList[i]) + for _, key := range keys { + proofBytes := bucket.Get(key) if proofBytes == nil { return ErrPubRandProofNotFound } @@ -133,4 +173,38 @@ func (s *PubRandProofStore) GetPubRandProofList(pubRandList []*btcec.FieldVal) ( return proofBytesList, nil } -// TODO: delete function? +// RemovePubRandProofList removes all proofs up to the target height +func (s *PubRandProofStore) RemovePubRandProofList(chainID []byte, pk []byte, targetHeight uint64) error { + prefix := getPrefixKey(chainID, pk) + + err := s.db.Update(func(tx walletdb.ReadWriteTx) error { + bucket := tx.ReadWriteBucket(pubRandProofBucketName) + if bucket == nil { + return walletdb.ErrBucketNotFound + } + + cursor := bucket.ReadWriteCursor() + + for k, _ := cursor.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, _ = cursor.Next() { + heightBytes := k[len(k)-8:] + height := sdk.BigEndianToUint64(heightBytes) + + // no need to keep iterating, keys are sorted in lexicographical order upon insert + if height > targetHeight { + break + } + + if err := cursor.Delete(); err != nil { + return err + } + } + + return nil + }, func() {}) + + if err != nil { + return err + } + + return nil +} diff --git a/finality-provider/store/pub_rand_test.go b/finality-provider/store/pub_rand_test.go new file mode 100644 index 00000000..5090bbbf --- /dev/null +++ b/finality-provider/store/pub_rand_test.go @@ -0,0 +1,50 @@ +package store_test + +import ( + "github.com/babylonlabs-io/babylon/testutil/datagen" + "github.com/babylonlabs-io/finality-provider/finality-provider/config" + "github.com/babylonlabs-io/finality-provider/finality-provider/store" + "github.com/babylonlabs-io/finality-provider/testutil" + "github.com/stretchr/testify/require" + "math/rand" + "testing" +) + +// FuzzRemoveMerkleProof removal of proofs +func FuzzRemoveMerkleProof(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + t.Parallel() + r := rand.New(rand.NewSource(seed)) + + homePath := t.TempDir() + cfg := config.DefaultDBConfigWithHomePath(homePath) + + db, err := cfg.GetDBBackend() + require.NoError(t, err) + vs, err := store.NewPubRandProofStore(db) + require.NoError(t, err) + + numPubRand := uint64(r.Intn(1000)) + chainID := []byte("test-chain") + rl, err := datagen.GenRandomPubRandList(r, numPubRand) + require.NoError(t, err) + fp := testutil.GenRandomFinalityProvider(r, t) + + defer func() { + err := db.Close() + require.NoError(t, err) + }() + + startHeight := uint64(1) + err = vs.AddPubRandProofList(chainID, fp.GetBIP340BTCPK().MustMarshal(), startHeight, numPubRand, rl.ProofList) + require.NoError(t, err) + + targetHeight := uint64(r.Intn(1000)) + err = vs.RemovePubRandProofList(chainID, fp.GetBIP340BTCPK().MustMarshal(), targetHeight) + require.NoError(t, err) + + _, err = vs.GetPubRandProofList(chainID, fp.GetBIP340BTCPK().MustMarshal(), startHeight, targetHeight) + require.ErrorIs(t, err, store.ErrPubRandProofNotFound) + }) +} diff --git a/finality-provider/store/storedfp.go b/finality-provider/store/storedfp.go index 84c2c4b0..acf9df3c 100644 --- a/finality-provider/store/storedfp.go +++ b/finality-provider/store/storedfp.go @@ -13,16 +13,13 @@ import ( ) type StoredFinalityProvider struct { - FPAddr string - BtcPk *btcec.PublicKey - Description *stakingtypes.Description - Commission *sdkmath.LegacyDec - Pop *proto.ProofOfPossession - KeyName string - ChainID string - LastVotedHeight uint64 - LastProcessedHeight uint64 - Status proto.FinalityProviderStatus + FPAddr string + BtcPk *btcec.PublicKey + Description *stakingtypes.Description + Commission *sdkmath.LegacyDec + ChainID string + LastVotedHeight uint64 + Status proto.FinalityProviderStatus } func protoFpToStoredFinalityProvider(fp *proto.FinalityProvider) (*StoredFinalityProvider, error) { @@ -42,18 +39,13 @@ func protoFpToStoredFinalityProvider(fp *proto.FinalityProvider) (*StoredFinalit } return &StoredFinalityProvider{ - FPAddr: fp.FpAddr, - BtcPk: btcPk, - Description: &des, - Commission: &commission, - Pop: &proto.ProofOfPossession{ - BtcSig: fp.Pop.BtcSig, - }, - KeyName: fp.KeyName, - ChainID: fp.ChainId, - LastVotedHeight: fp.LastVotedHeight, - LastProcessedHeight: fp.LastProcessedHeight, - Status: fp.Status, + FPAddr: fp.FpAddr, + BtcPk: btcPk, + Description: &des, + Commission: &commission, + ChainID: fp.ChainId, + LastVotedHeight: fp.LastVotedHeight, + Status: fp.Status, }, nil } @@ -77,16 +69,3 @@ func (sfp *StoredFinalityProvider) ToFinalityProviderInfo() *proto.FinalityProvi Status: sfp.Status.String(), } } - -// ShouldStart returns true if the finality provider should start his instance -// based on the current status of the finality provider. -// -// It returns false if the status is either 'CREATED' or 'SLASHED'. -// It returs true for all the other status. -func (sfp *StoredFinalityProvider) ShouldStart() bool { - if sfp.Status == proto.FinalityProviderStatus_CREATED || sfp.Status == proto.FinalityProviderStatus_SLASHED { - return false - } - - return true -} diff --git a/finality-provider/store/storedfp_test.go b/finality-provider/store/storedfp_test.go deleted file mode 100644 index 13d9f44d..00000000 --- a/finality-provider/store/storedfp_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package store_test - -import ( - "math/rand" - "testing" - - "github.com/babylonlabs-io/finality-provider/finality-provider/proto" - "github.com/babylonlabs-io/finality-provider/testutil" - "github.com/stretchr/testify/require" -) - -func TestShouldStart(t *testing.T) { - tcs := []struct { - name string - currFpStatus proto.FinalityProviderStatus - expShouldStart bool - }{ - { - "Created: Should NOT start", - proto.FinalityProviderStatus_CREATED, - false, - }, - { - "Slashed: Should NOT start", - proto.FinalityProviderStatus_SLASHED, - false, - }, - { - "Inactive: Should start", - proto.FinalityProviderStatus_INACTIVE, - true, - }, - { - "Registered: Should start", - proto.FinalityProviderStatus_REGISTERED, - true, - }, - { - "Active: Should start", - proto.FinalityProviderStatus_ACTIVE, - true, - }, - } - - r := rand.New(rand.NewSource(10)) - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - fp := testutil.GenRandomFinalityProvider(r, t) - fp.Status = tc.currFpStatus - - shouldStart := fp.ShouldStart() - require.Equal(t, tc.expShouldStart, shouldStart) - }) - } -} diff --git a/go.mod b/go.mod index 7f46c016..9eef7e86 100644 --- a/go.mod +++ b/go.mod @@ -1,31 +1,29 @@ module github.com/babylonlabs-io/finality-provider -go 1.22 +go 1.23.1 -toolchain go1.22.7 +toolchain go1.23.3 require ( cosmossdk.io/errors v1.0.1 - cosmossdk.io/log v1.3.1 - cosmossdk.io/math v1.3.0 - github.com/CosmWasm/wasmd v0.52.0 + cosmossdk.io/math v1.4.0 github.com/avast/retry-go/v4 v4.5.1 - github.com/babylonlabs-io/babylon v0.9.3-0.20240925223611-a98269d17887 + github.com/babylonlabs-io/babylon v1.99.0-snapshot.250131 github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240814002132-55e711397a82 - github.com/babylonlabs-io/finality-gadget v0.1.2-0.20241202031631-3817ad7fb1d4 + github.com/babylonlabs-io/finality-gadget v0.1.1 github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcec/v2 v2.3.4 - github.com/btcsuite/btcd/btcutil v1.1.5 + github.com/btcsuite/btcd/btcutil v1.1.6 github.com/btcsuite/btcwallet/walletdb v1.4.0 - github.com/cometbft/cometbft v0.38.10 - github.com/cosmos/cosmos-db v1.0.2 + github.com/cometbft/cometbft v0.38.15 + github.com/cosmos/cosmos-db v1.1.0 github.com/cosmos/cosmos-proto v1.0.0-beta.5 - github.com/cosmos/cosmos-sdk v0.50.9 + github.com/cosmos/cosmos-sdk v0.50.11 github.com/cosmos/go-bip39 v1.0.0 - github.com/cosmos/gogoproto v1.5.0 - github.com/cosmos/relayer/v2 v2.5.2 + github.com/cosmos/gogoproto v1.7.0 + github.com/cosmos/relayer/v2 v2.5.3 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 - github.com/ethereum/go-ethereum v1.14.8 + github.com/ethereum/go-ethereum v1.13.15 github.com/gogo/protobuf v1.3.3 github.com/golang/mock v1.6.0 github.com/jessevdk/go-flags v1.5.0 @@ -33,78 +31,74 @@ require ( github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b github.com/lightningnetwork/lnd v0.16.4-beta.rc1 github.com/lightningnetwork/lnd/kvdb v1.4.1 - github.com/prometheus/client_golang v1.20.2 + github.com/ory/dockertest/v3 v3.9.1 + github.com/prometheus/client_golang v1.20.5 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 - github.com/urfave/cli v1.22.14 go.uber.org/atomic v1.10.0 - go.uber.org/zap v1.27.0 - google.golang.org/grpc v1.66.1 - google.golang.org/protobuf v1.34.2 + go.uber.org/zap v1.26.0 + golang.org/x/mod v0.20.0 + google.golang.org/grpc v1.67.1 + google.golang.org/protobuf v1.35.1 + sigs.k8s.io/yaml v1.4.0 ) require ( github.com/BurntSushi/toml v1.4.0 // indirect - github.com/allegro/bigcache v1.2.1 // indirect - github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240814002132-55e711397a82 // indirect - github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect - github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd // indirect - github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead // indirect - github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/nxadm/tail v1.4.11 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect - github.com/shamaton/msgpack/v2 v2.2.0 // indirect -) - -require ( + github.com/StackExchange/wmi v1.2.1 // indirect github.com/VictoriaMetrics/fastcache v1.12.2 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240821192748-42bd03ba8313 // indirect - github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 // indirect github.com/gofrs/flock v0.8.1 // indirect - github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect - github.com/hashicorp/go-bexpr v0.1.11 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/onsi/gomega v1.34.1 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/urfave/cli/v2 v2.27.4 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect + github.com/tyler-smith/go-bip39 v1.1.0 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) require ( cloud.google.com/go v0.112.1 // indirect - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/storage v1.38.0 // indirect - cosmossdk.io/api v0.7.5 // indirect + cosmossdk.io/api v0.7.6 // indirect cosmossdk.io/client/v2 v2.0.0-beta.1 // indirect cosmossdk.io/collections v0.4.0 // indirect cosmossdk.io/core v0.11.1 // indirect - cosmossdk.io/depinject v1.0.0 // indirect - cosmossdk.io/store v1.1.0 // indirect + cosmossdk.io/depinject v1.1.0 // indirect + cosmossdk.io/log v1.4.1 + cosmossdk.io/store v1.1.1 // indirect cosmossdk.io/x/circuit v0.1.1 // indirect cosmossdk.io/x/evidence v0.1.1 // indirect cosmossdk.io/x/feegrant v0.1.1 // indirect cosmossdk.io/x/nft v0.1.1 // indirect - cosmossdk.io/x/tx v0.13.4 // indirect - cosmossdk.io/x/upgrade v0.1.3 // indirect + cosmossdk.io/x/tx v0.13.7 // indirect + cosmossdk.io/x/upgrade v0.1.4 // indirect + dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect - github.com/CosmWasm/wasmvm/v2 v2.1.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/CosmWasm/wasmd v0.53.0 + github.com/CosmWasm/wasmvm/v2 v2.1.3 // indirect github.com/DataDog/datadog-go v3.2.0+incompatible // indirect github.com/DataDog/zstd v1.5.6-0.20230824185856-869dae002e5e // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/aead/siphash v1.0.1 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/aws/aws-sdk-go v1.44.312 // indirect + github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240814002132-55e711397a82 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect @@ -113,50 +107,52 @@ require ( github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/pebble v1.1.2 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect - github.com/cometbft/cometbft-db v0.9.1 // indirect - github.com/consensys/bavard v0.1.13 // indirect - github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/cometbft/cometbft-db v0.15.0 // indirect + github.com/containerd/continuity v0.3.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect - github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/btcutil v1.0.5 github.com/cosmos/gogogateway v1.2.0 // indirect - github.com/cosmos/iavl v1.2.0 // indirect - github.com/cosmos/ibc-go/modules/capability v1.0.0 // indirect - github.com/cosmos/ibc-go/v8 v8.3.2 // indirect - github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/cosmos/iavl v1.2.2 // indirect + github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd // indirect + github.com/cosmos/ibc-go/modules/capability v1.0.1 // indirect + github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead // indirect + github.com/cosmos/ibc-go/v8 v8.4.0 // indirect + github.com/cosmos/ics23/go v0.11.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect - github.com/dgraph-io/badger/v2 v2.2007.4 // indirect - github.com/dgraph-io/ristretto v0.1.1 // indirect - github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/dgraph-io/badger/v4 v4.3.0 // indirect + github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91 // indirect github.com/distribution/reference v0.5.0 // indirect + github.com/docker/cli v25.0.6+incompatible // indirect + github.com/docker/docker v25.0.6+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect - github.com/emicklei/dot v1.6.1 // indirect + github.com/emicklei/dot v1.6.2 // indirect github.com/ethereum/c-kzg-4844 v1.0.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fergusstrange/embedded-postgres v1.10.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect - github.com/go-kit/kit v0.12.0 // 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-logr/logr v1.4.2 // indirect @@ -165,15 +161,16 @@ require ( github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/golang/glog v1.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect - github.com/google/btree v1.1.2 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/flatbuffers v23.5.26+incompatible // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/s2a-go v0.1.7 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.3 // indirect @@ -183,16 +180,18 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-getter v1.7.5 // indirect - github.com/hashicorp/go-hclog v1.6.2 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-metrics v0.5.3 // indirect github.com/hashicorp/go-plugin v1.5.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect @@ -224,50 +223,52 @@ require ( github.com/lightningnetwork/lnd/healthcheck v1.2.2 // indirect github.com/lightningnetwork/lnd/ticker v1.1.0 // indirect github.com/lightningnetwork/lnd/tor v1.1.0 // indirect - github.com/linxGnu/grocksdb v1.8.14 // indirect + github.com/linxGnu/grocksdb v1.9.3 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mholt/archiver/v3 v3.5.0 // indirect - github.com/miekg/dns v1.1.62 // indirect - github.com/minio/highwayhash v1.0.2 // indirect + github.com/miekg/dns v1.1.43 // indirect + github.com/minio/highwayhash v1.0.3 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mtibben/percent v0.2.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nwaples/rardecode v1.1.2 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/opencontainers/runc v1.1.12 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // indirect - github.com/pierrec/lz4/v4 v4.1.15 // indirect + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect + github.com/pierrec/lz4/v4 v4.1.18 // 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_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.60.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect - github.com/rs/cors v1.11.0 // indirect + github.com/rs/cors v1.11.1 // indirect github.com/rs/zerolog v1.33.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/sasha-s/go-deadlock v0.3.5 // indirect + github.com/shamaton/msgpack/v2 v2.2.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cast v1.7.0 // indirect github.com/spf13/viper v1.19.0 // indirect - github.com/strangelove-ventures/cometbft-client v0.1.0 // indirect + github.com/strangelove-ventures/cometbft-client v0.1.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect @@ -276,13 +277,15 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect - github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect - go.etcd.io/bbolt v1.3.10 // indirect + go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect go.etcd.io/etcd/api/v3 v3.5.12 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect go.etcd.io/etcd/client/v2 v2.305.12 // indirect @@ -301,51 +304,44 @@ require ( go.opentelemetry.io/otel/trace v1.30.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect - golang.org/x/mod v0.20.0 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/text v0.18.0 // indirect - golang.org/x/time v0.6.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/api v0.171.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.1 // indirect - lukechampine.com/uint128 v1.2.0 // indirect + lukechampine.com/uint128 v1.3.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect modernc.org/ccgo/v3 v3.16.13 // indirect - modernc.org/libc v1.22.2 // indirect + modernc.org/libc v1.22.4 // indirect modernc.org/mathutil v1.5.0 // indirect modernc.org/memory v1.5.0 // indirect modernc.org/opt v0.1.3 // indirect - modernc.org/sqlite v1.20.3 // indirect + modernc.org/sqlite v1.21.2 // indirect modernc.org/strutil v1.1.3 // indirect modernc.org/token v1.1.0 // indirect - nhooyr.io/websocket v1.8.17 // indirect + nhooyr.io/websocket v1.8.6 // indirect pgregory.net/rapid v1.1.0 // indirect - rsc.io/tmplfunc v0.0.3 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect ) replace ( // use cosmos fork of keyring github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 - github.com/btcsuite/btcd/btcec/v2 => github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/ethereum-optimism/optimism => github.com/babylonlabs-io/optimism v1.9.2-0.20241105020448-32f912d130b8 github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101408.0-rc.4.0.20240827042333-110c433a2469 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - // avoid v1.66 that has a breaking change for protobuf. That change breaks the relayer. - // https://github.com/grpc/grpc-go/issues/7569 - google.golang.org/grpc => google.golang.org/grpc v1.65.0 ) diff --git a/go.sum b/go.sum index 8d311f28..6ac42cf5 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,8 @@ -cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= +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= @@ -16,7 +15,6 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY 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.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -28,115 +26,28 @@ cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/accesscontextmanager v1.8.0/go.mod h1:uI+AI/r1oyWK99NN8cQ3UK76AMelMzgZCvJfsi2c+ps= -cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.48.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY= cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg= cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E= cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ= cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/baremetalsolution v1.1.1/go.mod h1:D1AV6xwOksJMV4OSlWHtWuFNZZYujJknMAP4Qa27QIA= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/batch v1.3.1/go.mod h1:VguXeQKXIYaeeIYbuozUmBR13AfL4SJP7IltNPS+A4A= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= 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= @@ -144,55 +55,12 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM 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/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.53.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA= cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.13.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI= cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= @@ -200,580 +68,138 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/datacatalog v1.14.0/go.mod h1:h0PrGtlihoutNMp/uvwhawLQ9+c63Kz65UFqh49Yo+E= -cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.16.0/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw= cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI= cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.0/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataproc/v2 v2.0.1/go.mod h1:7Ez3KRHdFGcfY7GcevBbvozX+zyWGcwLJvvAMwCaoZ4= cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8= 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/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastore v1.12.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.12.1/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.13.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.40.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI= cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE= cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE= cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gaming v1.10.1/go.mod h1:XQQvtfP8Rb9Rxnxm5wFVpAp9zCQkJi2bLIb7iHGwB3s= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkebackup v1.3.0/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw= cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= -cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/kms v1.11.0/go.mod h1:hwdiYC0xjnWsKQQCQQmIQnS9asjYVSK6jtXm+zFqXLM= -cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ= -cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= -cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/maps v1.3.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig= cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA= cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0= cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ= cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/orgpolicy v1.11.0/go.mod h1:2RK748+FtVvnfuynxBzdnyu7sygtoZa1za/0ZfpOs1M= -cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE= cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/osconfig v1.12.0/go.mod h1:8f/PaYzoS3JMVfdfTubkowZYGmAhUCjjwnjqWI7NVBc= -cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE= cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs= cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0= -cloud.google.com/go/policytroubleshooter v1.8.0/go.mod h1:tmn5Ir5EToWe384EuboTcVQT7nTag2+DuH3uHmKd1HU= cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA= 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/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU= cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE= cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA= cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw= cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo= cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo= cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA= cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI= cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= 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.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.8.2/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.19.0/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo= cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs= cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= -cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= -cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +cosmossdk.io/api v0.7.6 h1:PC20PcXy1xYKH2KU4RMurVoFjjKkCgYRbVAD4PdqUuY= +cosmossdk.io/api v0.7.6/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= cosmossdk.io/client/v2 v2.0.0-beta.1 h1:XkHh1lhrLYIT9zKl7cIOXUXg2hdhtjTPBUfqERNA1/Q= cosmossdk.io/client/v2 v2.0.0-beta.1/go.mod h1:JEUSu9moNZQ4kU3ir1DKD5eU4bllmAexrGWjmb9k8qU= cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= cosmossdk.io/core v0.11.1 h1:h9WfBey7NAiFfIcUhDVNS503I2P2HdZLebJlUIs8LPA= cosmossdk.io/core v0.11.1/go.mod h1:OJzxcdC+RPrgGF8NJZR2uoQr56tc7gfBKhiKeDO7hH0= -cosmossdk.io/depinject v1.0.0 h1:dQaTu6+O6askNXO06+jyeUAnF2/ssKwrrszP9t5q050= -cosmossdk.io/depinject v1.0.0/go.mod h1:zxK/h3HgHoA/eJVtiSsoaRaRA2D5U4cJ5thIG4ssbB8= +cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E= +cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= -cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= -cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= -cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= -cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= -cosmossdk.io/store v1.1.0 h1:LnKwgYMc9BInn9PhpTFEQVbL9UK475G2H911CGGnWHk= -cosmossdk.io/store v1.1.0/go.mod h1:oZfW/4Fc/zYqu3JmQcQdUJ3fqu5vnYTn3LZFFy8P8ng= +cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM= +cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU= +cosmossdk.io/math v1.4.0 h1:XbgExXFnXmF/CccPPEto40gOO7FpWu9yWNAZPN3nkNQ= +cosmossdk.io/math v1.4.0/go.mod h1:O5PkD4apz2jZs4zqFdTr16e1dcaQCc5z6lkEnrrppuk= +cosmossdk.io/store v1.1.1 h1:NA3PioJtWDVU7cHHeyvdva5J/ggyLDkyH0hGHl2804Y= +cosmossdk.io/store v1.1.1/go.mod h1:8DwVTz83/2PSI366FERGbWSH7hL6sB7HbYp8bqksNwM= 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= @@ -782,80 +208,67 @@ 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/nft v0.1.1 h1:pslAVS8P5NkW080+LWOamInjDcq+v2GSCo+BjN9sxZ8= cosmossdk.io/x/nft v0.1.1/go.mod h1:Kac6F6y2gsKvoxU+fy8uvxRTi4BIhLOor2zgCNQwVgY= -cosmossdk.io/x/tx v0.13.4 h1:Eg0PbJgeO0gM8p5wx6xa0fKR7hIV6+8lC56UrsvSo0Y= -cosmossdk.io/x/tx v0.13.4/go.mod h1:BkFqrnGGgW50Y6cwTy+JvgAhiffbGEKW6KF9ufcDpvk= -cosmossdk.io/x/upgrade v0.1.3 h1:q4XpXc6zp0dX6x74uBtfN6+J7ikaQev5Bla6Q0ADLK8= -cosmossdk.io/x/upgrade v0.1.3/go.mod h1:jOdQhnaY5B8CDUoUbed23/Lre0Dk+r6BMQE40iKlVVQ= +cosmossdk.io/x/tx v0.13.7 h1:8WSk6B/OHJLYjiZeMKhq7DK7lHDMyK0UfDbBMxVmeOI= +cosmossdk.io/x/tx v0.13.7/go.mod h1:V6DImnwJMTq5qFjeGWpXNiT/fjgE4HtmclRmTqRVM3w= +cosmossdk.io/x/upgrade v0.1.4 h1:/BWJim24QHoXde8Bc64/2BSEB6W4eTydq0X/2f8+g38= +cosmossdk.io/x/upgrade v0.1.4/go.mod h1:9v0Aj+fs97O+Ztw+tG3/tp5JSlrmT7IcFhAebQHmOPo= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= 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/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CosmWasm/wasmd v0.52.0 h1:VRylqes1AMXqIgz/jUH9EzhjBZKsRXrrjCTAni0ILRM= -github.com/CosmWasm/wasmd v0.52.0/go.mod h1:hyy1wt7c589Cs4kOK2cYdtphzCd2Xo20q/t7tfby7oI= -github.com/CosmWasm/wasmvm/v2 v2.1.0 h1:bleLhNA36hM8iPjFJsNRi9RjrQW6MtXafw2+wVjAWAE= -github.com/CosmWasm/wasmvm/v2 v2.1.0/go.mod h1:bMhLQL4Yp9CzJi9A83aR7VO9wockOsSlZbT4ztOl6bg= +github.com/CosmWasm/wasmd v0.53.0 h1:kdaoAi20bIb4VCsxw9pRaT2g5PpIp82Wqrr9DRVN9ao= +github.com/CosmWasm/wasmd v0.53.0/go.mod h1:FJl/aWjdpGof3usAMFQpDe07Rkx77PUzp0cygFMOvtw= +github.com/CosmWasm/wasmvm/v2 v2.1.3 h1:CSJTauZqkHyb9yic6JVYCjiGUgxI2MJV2QzfSu8m49c= +github.com/CosmWasm/wasmvm/v2 v2.1.3/go.mod h1:bMhLQL4Yp9CzJi9A83aR7VO9wockOsSlZbT4ztOl6bg= github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.6-0.20230824185856-869dae002e5e h1:ZIWapoIRN1VqT8GR8jAwb1Ie9GyehWjVcGh32Y2MznE= github.com/DataDog/zstd v1.5.6-0.20230824185856-869dae002e5e/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/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/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 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/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= -github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= +github.com/adlio/schema v1.3.6 h1:k1/zc2jNfeiZBA5aFTRy37jlBIuCkXCm0XmvpzCKI9I= +github.com/adlio/schema v1.3.6/go.mod h1:qkxwLgPBd1FgLRHYVCmQT/rrBr3JH38J9LjmVzWNudg= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= -github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= -github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= 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/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 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= @@ -867,14 +280,14 @@ github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX github.com/aws/aws-sdk-go v1.44.312 h1:llrElfzeqG/YOLFFKjg1xNpZCFJ2xraIi3PqSuP+95k= github.com/aws/aws-sdk-go v1.44.312/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/babylonlabs-io/babylon v0.9.3-0.20240925223611-a98269d17887 h1:Z5UPknFpdpWJIgy71AQfQcJACpwt0KKOiHnb2OIpKaU= -github.com/babylonlabs-io/babylon v0.9.3-0.20240925223611-a98269d17887/go.mod h1:Savv0qKMqan8M8kFchXocIsLI69tPmnMOZOe//sgTFI= +github.com/babylonlabs-io/babylon v1.99.0-snapshot.250131 h1:sw6c4Rkd2SwtZnLSFyVPMuf8yor/lNTQt17XP2P3AgY= +github.com/babylonlabs-io/babylon v1.99.0-snapshot.250131/go.mod h1:NxghSOJHuPCwdIKUsSrmmbVJweTRCLW2tLJ4UjlSwUc= github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240814002132-55e711397a82 h1:BwgtEtrrbtwQo4FEsoVqeUIHiqJr5krZt6ds3g1SM4s= github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240814002132-55e711397a82/go.mod h1:QqEn1sL4RPG7DJ94XFYvuvEELml64s5XwPQpTayXJss= github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240814002132-55e711397a82 h1:F9U6iH+RqXo2ColXvGGByWDY0DdyAMnIgjpxQbWooiE= github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240814002132-55e711397a82/go.mod h1:WJlZy0RYCtyBFeO1mr0Tlo02csrlCAQgzmp4+NVX14g= -github.com/babylonlabs-io/finality-gadget v0.1.2-0.20241202031631-3817ad7fb1d4 h1:O3B/KBlvqCT51BDShpLrcSFHqRjO21tjVy/f62BwxAw= -github.com/babylonlabs-io/finality-gadget v0.1.2-0.20241202031631-3817ad7fb1d4/go.mod h1:EmpCuYtfkRyeXL3wXxXf8j8wPyMbzwG8pHUQ7LXSnJo= +github.com/babylonlabs-io/finality-gadget v0.1.1 h1:izW+sxtrsVPC9FlGx0wzWTJqRk+buEw9zfV7nXKQmWA= +github.com/babylonlabs-io/finality-gadget v0.1.1/go.mod h1:+BICJA7mUJpfYOSYds1G5w6nLO0gKVe9XwdmpIUiXmk= 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= @@ -889,20 +302,22 @@ github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsy github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvPTF58Vq+qgmZEYZttJK+CWtSZhKenI= github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c= +github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= @@ -927,9 +342,6 @@ github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq 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/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -948,16 +360,16 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38 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/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/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +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-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= @@ -978,18 +390,16 @@ github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ 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/cometbft/cometbft v0.38.10 h1:2ePuglchT+j0Iao+cfmt/nw5U7K2lnGDzXSUPGVdXaU= -github.com/cometbft/cometbft v0.38.10/go.mod h1:jHPx9vQpWzPHEAiYI/7EDKaB1NXhK6o3SArrrY8ExKc= -github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M= -github.com/cometbft/cometbft-db v0.9.1/go.mod h1:iliyWaoV0mRwBJoizElCwwRA9Tf7jZJOURcRZF9m60U= +github.com/cometbft/cometbft v0.38.15 h1:5veFd8k1uXM27PBg9sMO3hAfRJ3vbh4OmmLf6cVrqXg= +github.com/cometbft/cometbft v0.38.15/go.mod h1:+wh6ap6xctVG+JOHwbl8pPKZ0GeqdPYqISu7F4b43cQ= +github.com/cometbft/cometbft-db v0.15.0 h1:VLtsRt8udD4jHCyjvrsTBpgz83qne5hnL245AcPJVRk= +github.com/cometbft/cometbft-db v0.15.0/go.mod h1:EBrFs1GDRiTqrWXYi4v90Awf/gcdD5ExzdPbg4X8+mk= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= 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/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -1002,40 +412,38 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV 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.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= -github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= +github.com/cosmos/cosmos-db v1.1.0 h1:KLHNVQ73h7vawXTpj9UJ7ZR2IXv51tsEHkQJJ9EBDzI= +github.com/cosmos/cosmos-db v1.1.0/go.mod h1:t7c4A6cfGdpUwwVxrQ0gQLeRQqGUBJu0yvE4F/26REg= 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.9 h1:gt2usjz0H0qW6KwAxWw7ZJ3XU8uDwmhN+hYG3nTLeSg= -github.com/cosmos/cosmos-sdk v0.50.9/go.mod h1:TMH6wpoYBcg7Cp5BEg8fneLr+8XloNQkf2MRNF9V6JE= +github.com/cosmos/cosmos-sdk v0.50.11 h1:LxR1aAc8kixdrs3itO+3a44sFoc+vjxVAOyPFx22yjk= +github.com/cosmos/cosmos-sdk v0.50.11/go.mod h1:gt14Meok2IDCjbDtjwkbUcgVNEpUBDN/4hg9cCUtLgw= 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.5.0 h1:SDVwzEqZDDBoslaeZg+dGE55hdzHfgUA40pEanMh52o= -github.com/cosmos/gogoproto v1.5.0/go.mod h1:iUM31aofn3ymidYG6bUR5ZFrk+Om8p5s754eMUcyp8I= -github.com/cosmos/iavl v1.2.0 h1:kVxTmjTh4k0Dh1VNL046v6BXqKziqMDzxo93oh3kOfM= -github.com/cosmos/iavl v1.2.0/go.mod h1:HidWWLVAtODJqFD6Hbne2Y0q3SdxByJepHUOeoH4LiI= +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.2 h1:qHhKW3I70w+04g5KdsdVSHRbFLgt3yY3qTMd4Xa4rC8= +github.com/cosmos/iavl v1.2.2/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw= github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd h1:Lx+/5dZ/nN6qPXP2Ofog6u1fmlkCFA1ElcOconnofEM= github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd/go.mod h1:JWfpWVKJKiKtd53/KbRoKfxWl8FsT2GPcNezTOk0o5Q= -github.com/cosmos/ibc-go/modules/capability v1.0.0 h1:r/l++byFtn7jHYa09zlAdSeevo8ci1mVZNO9+V0xsLE= -github.com/cosmos/ibc-go/modules/capability v1.0.0/go.mod h1:D81ZxzjZAe0ZO5ambnvn1qedsFQ8lOwtqicG6liLBco= +github.com/cosmos/ibc-go/modules/capability v1.0.1 h1:ibwhrpJ3SftEEZRxCRkH0fQZ9svjthrX2+oXdZvzgGI= +github.com/cosmos/ibc-go/modules/capability v1.0.1/go.mod h1:rquyOV262nGJplkumH+/LeYs04P3eV8oB7ZM4Ygqk4E= github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead h1:QB50+AmrEVqFr2hzvIxMkICziWQ/uuebze0vNYKMnBg= github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead/go.mod h1:AJeroAXnPKeFpD1AfEfjYBHGEWt5gBfzUjgs4SYn2ZY= -github.com/cosmos/ibc-go/v8 v8.3.2 h1:8X1oHHKt2Bh9hcExWS89rntLaCKZp2EjFTUSxKlPhGI= -github.com/cosmos/ibc-go/v8 v8.3.2/go.mod h1:WVVIsG39jGrF9Cjggjci6LzySyWGloz194sjTxiGNIE= -github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= -github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/ibc-go/v8 v8.4.0 h1:K2PfX0AZ+1XKZytHGEMuSjQXG/MZshPb83RSTQt2+cE= +github.com/cosmos/ibc-go/v8 v8.4.0/go.mod h1:zh6x1osR0hNvEcFrC/lhGD08sMfQmr9wHVvZ/mRWMCs= +github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= +github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0= github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo= github.com/cosmos/keyring v1.2.0/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= -github.com/cosmos/relayer/v2 v2.5.2 h1:AF0MOo1GvJo94QNB996fBHdKlH+vrIY3JcFNrIvZNP0= -github.com/cosmos/relayer/v2 v2.5.2/go.mod h1:h4Ng2QsVpxExIq5S+WvLr8slDb9MSBh82gQS4DeMwDo= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cosmos/relayer/v2 v2.5.3 h1:Cuhp4MZ8OwWtGf4/UU4HqpEdEDkCvd42uHvY5MK5MpE= +github.com/cosmos/relayer/v2 v2.5.3/go.mod h1:4N4Dd42G1sKd7zUqOonrGPC2xgdY59T8KevjKodoLUI= 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.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= @@ -1044,6 +452,8 @@ github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDF github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -1062,27 +472,27 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3 github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= -github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= -github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -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/badger/v4 v4.3.0 h1:lcsCE1/1qrRhqP+zYx6xDZb8n7U+QlwNicpc676Ub40= +github.com/dgraph-io/badger/v4 v4.3.0/go.mod h1:Sc0T595g8zqAQRDf44n+z3wG4BOqLwceaFntt8KPxUM= +github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91 h1:Pux6+xANi0I7RRo5E1gflI4EZ2yx3BGZ75JkAIvGEOA= +github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91/go.mod h1:swkazRqnUf1N62d0Nutz7KIj2UKqsm/H8tD0nBJAXqM= 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-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v25.0.6+incompatible h1:F1mCw1kUGixOkM8WQbcG5kniPvP8XCFxreFxl4b/UnY= +github.com/docker/cli v25.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= +github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 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.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= @@ -1091,18 +501,19 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m 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.1 h1:ujpDlBkkwgWUY+qPId5IwapRW/xEoligRSYjioR6DFI= -github.com/emicklei/dot v1.6.1/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +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.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +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.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/ethereum-optimism/op-geth v1.101408.0-rc.4.0.20240827042333-110c433a2469 h1:sGqlBjx0+z/ExU6VNo5OHSXS/5nc6BfkEQJvSdVbWp0= github.com/ethereum-optimism/op-geth v1.101408.0-rc.4.0.20240827042333-110c433a2469/go.mod h1:Mk8AhvlqFbjI9oW2ymThSSoqc6kiEH0/tCmHGMEu6ac= github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240821192748-42bd03ba8313 h1:SVSFg8ccdRBJxOdRS1pK8oIHvMufiPAQz1gkQsEPnZc= @@ -1119,8 +530,6 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fergusstrange/embedded-postgres v1.10.0 h1:YnwF6xAQYmKLAXXrrRx4rHDLih47YJwVPvg8jeKfdNg= github.com/fergusstrange/embedded-postgres v1.10.0/go.mod h1:a008U8/Rws5FtIOTGYDYa7beVWsT3qVKyqExqYYjL+c= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 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= @@ -1129,36 +538,31 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk 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.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= 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/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= 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-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= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= 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-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.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= -github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +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-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= 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= @@ -1169,22 +573,34 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 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= @@ -1193,23 +609,22 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= +github.com/golang/glog v1.2.2/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= @@ -1237,7 +652,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3/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= @@ -1248,9 +662,10 @@ github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXi github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/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.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +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 v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg= +github.com/google/flatbuffers v23.5.26+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= @@ -1290,20 +705,18 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf 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/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= -github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 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= @@ -1313,8 +726,6 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -1326,15 +737,9 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -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= @@ -1357,8 +762,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf 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.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.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= @@ -1366,16 +769,16 @@ github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NM github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY= -github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-getter v1.7.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= -github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= -github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/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= @@ -1429,7 +832,6 @@ github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXM github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 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= @@ -1527,8 +929,6 @@ github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b h1:FQ7+9fxhyp82ks9vAuy github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b/go.mod h1:HMcgvsgd0Fjj4XXDkbjdmlbI505rUPBs6WBMYg2pXks= 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/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -1536,29 +936,22 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 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.2/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.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -1571,6 +964,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -1591,13 +986,9 @@ github.com/lightningnetwork/lnd/tor v1.1.0 h1:iXO7fSzjxTI+p88KmtpbuyuRJeNfgtpl9Q github.com/lightningnetwork/lnd/tor v1.1.0/go.mod h1:RDtaAdwfAm+ONuPYwUhNIH1RAvKPv+75lHPOegUcz64= 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.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ= -github.com/linxGnu/grocksdb v1.8.14/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= -github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/linxGnu/grocksdb v1.9.3 h1:s1cbPcOd0cU2SKXRG1nEqCOWYAELQjdqg3RVI2MH9ik= +github.com/linxGnu/grocksdb v1.9.3/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 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= @@ -1616,29 +1007,24 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd 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.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE= github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= -github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= -github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= -github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +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= @@ -1650,14 +1036,15 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw= -github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1685,9 +1072,8 @@ github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWk github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M= github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 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/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= -github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= 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= @@ -1701,21 +1087,21 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W 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/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= 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.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= -github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +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= @@ -1726,26 +1112,23 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh 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/ory/dockertest/v3 v3.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY= +github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM= 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 v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 h1:jik8PHtAIsPlCRJjJzl4udgEf7hawInF9texMeO2jrU= -github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +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/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= +github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 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= @@ -1754,8 +1137,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/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= @@ -1766,16 +1147,14 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn 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.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= 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.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1784,8 +1163,8 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2 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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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= @@ -1802,45 +1181,41 @@ github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= -github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +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.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= -github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +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/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 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/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= -github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -1861,34 +1236,22 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= -github.com/strangelove-ventures/cometbft-client v0.1.0 h1:fcA652QaaR0LDnyJOZVjZKtuyAawnVXaq/p1MWJSYD4= -github.com/strangelove-ventures/cometbft-client v0.1.0/go.mod h1:QzThgjzvsGgUNVNpGPitmxOWMIhp6a0oqf80nCRNt/0= +github.com/strangelove-ventures/cometbft-client v0.1.1 h1:chBZCTcTOGl3i+CjXWU+kIJZ7s6mY2uZ1gzwr7W2f0g= +github.com/strangelove-ventures/cometbft-client v0.1.1/go.mod h1:aVposiPW9FOUeAeJ7JjJRdE3g+L6i8YDxFn6Cv6+Az4= 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= @@ -1909,7 +1272,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -1933,9 +1295,11 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -1943,30 +1307,30 @@ 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/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= -github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= -github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= -github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= +github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/vulpine-io/io-test v1.0.0 h1:Ot8vMh+ssm1VWDAwJ3U4C5qG9aRnr5YfQFZPNZBAUGI= github.com/vulpine-io/io-test v1.0.0/go.mod h1:X1I+p5GCxVX9m4nFd1HBtr2bVX9v1ZE6x8w+Obt36AU= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= 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.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= @@ -1974,8 +1338,8 @@ github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfU 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.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= -go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= +go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 h1:qxen9oVGzDdIRP6ejyAJc760RwW4SnVDiTYTzwnXuxo= +go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5/go.mod h1:eW0HG9/oHQhvRCvb1/pIXW4cOvtDqeQK+XSi3TnwaXY= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c= go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4= @@ -2023,8 +1387,6 @@ go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8d go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -2035,8 +1397,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 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/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= 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.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -2048,12 +1410,11 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 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.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -2064,34 +1425,17 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U 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-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= 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= @@ -2099,22 +1443,11 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 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-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= 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/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +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= @@ -2137,19 +1470,13 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2190,16 +1517,13 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/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-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -2210,25 +1534,13 @@ golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 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= @@ -2252,16 +1564,10 @@ golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7Lm golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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= @@ -2275,23 +1581,18 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.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.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2302,7 +1603,6 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2340,12 +1640,9 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2360,13 +1657,10 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/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-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/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-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2388,46 +1682,26 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/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-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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= @@ -2437,30 +1711,21 @@ 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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 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.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +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-20190206041539-40960b6deb8e/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= @@ -2476,7 +1741,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/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-20190927191325-030b2cf1153e/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= @@ -2510,29 +1774,20 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc 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-20201124115921-2c860bdd6e78/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-20201224043029-2b0845dc783e/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.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2545,14 +1800,6 @@ golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNq golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= 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= @@ -2601,23 +1848,10 @@ google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaE google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E= -google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= -google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= -google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= google.golang.org/api v0.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU= google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o= +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= @@ -2664,10 +1898,8 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D 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-20210222152913-aa3ee6e6a81c/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-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -2701,7 +1933,6 @@ google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= @@ -2734,76 +1965,57 @@ google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53B google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= -google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= -google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +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.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -2820,14 +2032,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2865,6 +2071,7 @@ 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.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +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= @@ -2872,82 +2079,39 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh 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= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= +lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= +lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g= modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= -modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= -modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= -modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= -modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= -modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/libc v1.22.4 h1:wymSbZb0AlrjdAVX3cjreCHTPCpPARbQXNz6BHPzdwQ= +modernc.org/libc v1.22.4/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/sqlite v1.18.2/go.mod h1:kvrTLEWgxUcHa2GfHBQtanR1H9ht3hTJNtKpzH9k1u0= -modernc.org/sqlite v1.20.3 h1:SqGJMMxjj1PHusLxdYxeQSodg7Jxn9WWkaAQjKrntZs= -modernc.org/sqlite v1.20.3/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/sqlite v1.21.2 h1:ixuUG0QS413Vfzyx6FWx6PYTmHaOegTY+hjzhn7L+a0= +modernc.org/sqlite v1.21.2/go.mod h1:cxbLkB5WS32DnQqeH4h4o1B0eMr8W/y8/RGuxQ3JsC0= modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/tcl v1.13.2/go.mod h1:7CLiGIPo1M8Rv1Mitpv5akc2+8fxUd2y2UzC/MfMzy0= -modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34= -modernc.org/tcl v1.15.0/go.mod h1:xRoGotBZ6dU+Zo2tca+2EqVEeMmOUBzHnhIwq4YrVnE= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws= +modernc.org/tcl v1.15.1/go.mod h1:aEjeGJX2gz1oWKOLDVZ2tnEWLUrIn8H+GFu+akoDhqs= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= +nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= -nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= diff --git a/itest/babylon/babylon_e2e_test.go b/itest/babylon/babylon_e2e_test.go index aaa96c4b..23fdead1 100644 --- a/itest/babylon/babylon_e2e_test.go +++ b/itest/babylon/babylon_e2e_test.go @@ -4,17 +4,30 @@ package e2etest_babylon import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "log" "math/rand" + "os" "testing" "time" "github.com/babylonlabs-io/babylon/testutil/datagen" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/jessevdk/go-flags" "github.com/stretchr/testify/require" + sdkmath "cosmossdk.io/math" + bbntypes "github.com/babylonlabs-io/babylon/types" + eotscmd "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" + eotscfg "github.com/babylonlabs-io/finality-provider/eotsmanager/config" + "github.com/babylonlabs-io/finality-provider/finality-provider/cmd/fpd/daemon" + "github.com/babylonlabs-io/finality-provider/finality-provider/store" e2eutils "github.com/babylonlabs-io/finality-provider/itest" - - "github.com/babylonlabs-io/finality-provider/finality-provider/proto" "github.com/babylonlabs-io/finality-provider/types" ) @@ -23,11 +36,11 @@ import ( // activation with BTC delegation and Covenant sig -> // vote submission -> block finalization func TestFinalityProviderLifeCycle(t *testing.T) { - tm, fpInsList := StartManagerWithFinalityProvider(t, 1) + t.Parallel() + ctx := context.Background() + tm, fpIns := StartManagerWithFinalityProvider(t, ctx) defer tm.Stop(t) - fpIns := fpInsList[0] - // check the public randomness is committed tm.WaitForFpPubRandTimestamped(t, fpIns) @@ -56,11 +69,11 @@ func TestFinalityProviderLifeCycle(t *testing.T) { // finality signature submission would take about 5 seconds // set the poll interval to 2 seconds to make sure the poller channel has multiple blocks tm.FpConfig.PollerConfig.PollInterval = 2 * time.Second - tm.StopAndRestartFpAfterNBlocks(t, n, fpIns) + tm.StopAndRestartFpAfterNBlocks(t, int(n), fpIns) // wait for finality signature submission to run two times time.Sleep(12 * time.Second) - lastProcessedHeight := fpIns.GetLastProcessedHeight() + lastProcessedHeight := fpIns.GetLastVotedHeight() require.True(t, lastProcessedHeight >= lastVotedHeight+uint64(n)) t.Logf("the last processed height is %v", lastProcessedHeight) } @@ -69,11 +82,11 @@ func TestFinalityProviderLifeCycle(t *testing.T) { // sends a finality vote over a conflicting block // in this case, the BTC private key should be extracted by Babylon func TestDoubleSigning(t *testing.T) { - tm, fpInsList := StartManagerWithFinalityProvider(t, 1) + t.Parallel() + ctx := context.Background() + tm, fpIns := StartManagerWithFinalityProvider(t, ctx) defer tm.Stop(t) - fpIns := fpInsList[0] - // check the public randomness is committed tm.WaitForFpPubRandTimestamped(t, fpIns) @@ -96,42 +109,44 @@ func TestDoubleSigning(t *testing.T) { tm.CheckBlockFinalization(t, lastVotedHeight, 1) t.Logf("the block at height %v is finalized", lastVotedHeight) - finalizedBlockHeight := tm.WaitForNFinalizedBlocksAndReturnTipHeight(t, 1) + finalizedBlock := tm.WaitForNFinalizedBlocks(t, 1) + + // test duplicate vote which should be ignored + res, extractedKey, err := fpIns.TestSubmitFinalitySignatureAndExtractPrivKey(finalizedBlock, false) + require.NoError(t, err) + require.Nil(t, extractedKey) + require.Empty(t, res) + t.Logf("duplicate vote for %d is sent", finalizedBlock.Height) // attack: manually submit a finality vote over a conflicting block // to trigger the extraction of finality-provider's private key r := rand.New(rand.NewSource(time.Now().UnixNano())) b := &types.BlockInfo{ - Height: finalizedBlockHeight, + Height: finalizedBlock.Height, Hash: datagen.GenRandomByteArray(r, 32), } - _, extractedKey, err := fpIns.TestSubmitFinalitySignatureAndExtractPrivKey(b) + + // confirm we have double sign protection + _, _, err = fpIns.TestSubmitFinalitySignatureAndExtractPrivKey(b, true) + require.Error(t, err) + require.Contains(t, err.Error(), "double sign") + + _, extractedKey, err = fpIns.TestSubmitFinalitySignatureAndExtractPrivKey(b, false) require.NoError(t, err) require.NotNil(t, extractedKey) localKey := tm.GetFpPrivKey(t, fpIns.GetBtcPkBIP340().MustMarshal()) require.True(t, localKey.Key.Equals(&extractedKey.Key) || localKey.Key.Negate().Equals(&extractedKey.Key)) t.Logf("the equivocation attack is successful") - - tm.WaitForFpShutDown(t, fpIns.GetBtcPkBIP340()) - - // try to start all the finality providers and the slashed one should not be restarted - err = tm.Fpa.StartHandlingAll() - require.NoError(t, err) - fps, err := tm.Fpa.ListAllFinalityProvidersInfo() - require.NoError(t, err) - require.Equal(t, 1, len(fps)) - require.Equal(t, proto.FinalityProviderStatus_name[4], fps[0].Status) - require.Equal(t, false, fps[0].IsRunning) } // TestCatchingUp tests if a fp can catch up after restarted func TestCatchingUp(t *testing.T) { - tm, fpInsList := StartManagerWithFinalityProvider(t, 1) + t.Parallel() + ctx := context.Background() + tm, fpIns := StartManagerWithFinalityProvider(t, ctx) defer tm.Stop(t) - fpIns := fpInsList[0] - // check the public randomness is committed tm.WaitForFpPubRandTimestamped(t, fpIns) @@ -155,19 +170,242 @@ func TestCatchingUp(t *testing.T) { t.Logf("the block at height %v is finalized", lastVotedHeight) - tm.WaitForNFinalizedBlocksAndReturnTipHeight(t, 1) + tm.WaitForNFinalizedBlocks(t, 1) var n uint = 3 // stop the finality-provider for a few blocks then restart to trigger the fast sync - tm.StopAndRestartFpAfterNBlocks(t, n, fpIns) + tm.StopAndRestartFpAfterNBlocks(t, int(n), fpIns) // check there are n+1 blocks finalized - finalizedHeight := tm.WaitForNFinalizedBlocksAndReturnTipHeight(t, n+1) - t.Logf("the latest finalized block is at %v", finalizedHeight) + finalizedBlock := tm.WaitForNFinalizedBlocks(t, n+1) + t.Logf("the latest finalized block is at %v", finalizedBlock.Height) // check if the fast sync works by checking if the gap is not more than 1 currentHeight, err := tm.BBNConsumerClient.QueryLatestBlockHeight() t.Logf("the current block is at %v", currentHeight) require.NoError(t, err) - require.True(t, currentHeight < finalizedHeight+uint64(n)) + require.True(t, currentHeight < finalizedBlock.Height+uint64(n)) +} + +func TestFinalityProviderEditCmd(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + tm, fpIns := StartManagerWithFinalityProvider(t, ctx) + defer tm.Stop(t) + + cmd := daemon.CommandEditFinalityDescription() + + const ( + monikerFlag = "moniker" + identityFlag = "identity" + websiteFlag = "website" + securityContactFlag = "security-contact" + detailsFlag = "details" + fpdDaemonAddressFlag = "daemon-address" + commissionRateFlag = "commission-rate" + ) + + moniker := "test-moniker" + website := "https://test.com" + securityContact := "test@test.com" + details := "Test details" + identity := "test-identity" + commissionRateStr := "0.3" + + args := []string{ + fpIns.GetBtcPkHex(), + "--" + fpdDaemonAddressFlag, fpIns.GetConfig().RPCListener, + "--" + monikerFlag, moniker, + "--" + websiteFlag, website, + "--" + securityContactFlag, securityContact, + "--" + detailsFlag, details, + "--" + identityFlag, identity, + "--" + commissionRateFlag, commissionRateStr, + } + + cmd.SetArgs(args) + + // Run the command + err := cmd.Execute() + require.NoError(t, err) + + gotFp, err := tm.BBNClient.QueryFinalityProvider(fpIns.GetBtcPk()) + require.NoError(t, err) + + rate, err := sdkmath.LegacyNewDecFromStr(commissionRateStr) + require.NoError(t, err) + + require.Equal(t, gotFp.FinalityProvider.Description.Moniker, moniker) + require.Equal(t, gotFp.FinalityProvider.Description.Website, website) + require.Equal(t, gotFp.FinalityProvider.Description.Identity, identity) + require.Equal(t, gotFp.FinalityProvider.Description.Details, details) + require.Equal(t, gotFp.FinalityProvider.Description.SecurityContact, securityContact) + require.Equal(t, gotFp.FinalityProvider.Commission, &rate) + + moniker = "test2-moniker" + args = []string{ + fpIns.GetBtcPkHex(), + "--" + fpdDaemonAddressFlag, fpIns.GetConfig().RPCListener, + "--" + monikerFlag, moniker, + } + + cmd.SetArgs(args) + + // Run the command + err = cmd.Execute() + require.NoError(t, err) + + updatedFp, err := tm.BBNClient.QueryFinalityProvider(fpIns.GetBtcPk()) + require.NoError(t, err) + + updateFpDesc := updatedFp.FinalityProvider.Description + oldDesc := gotFp.FinalityProvider.Description + + require.Equal(t, updateFpDesc.Moniker, moniker) + require.Equal(t, updateFpDesc.Website, oldDesc.Website) + require.Equal(t, updateFpDesc.Identity, oldDesc.Identity) + require.Equal(t, updateFpDesc.Details, oldDesc.Details) + require.Equal(t, updateFpDesc.SecurityContact, oldDesc.SecurityContact) + require.Equal(t, updatedFp.FinalityProvider.Commission, &rate) +} + +func TestFinalityProviderCreateCmd(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + tm, fpIns := StartManagerWithFinalityProvider(t, ctx) + defer tm.Stop(t) + + cmd := daemon.CommandCreateFP() + + eotsKeyName := "eots-key-2" + eotsPkBz, err := tm.EOTSClient.CreateKey(eotsKeyName, passphrase, hdPath) + require.NoError(t, err) + eotsPk, err := bbntypes.NewBIP340PubKey(eotsPkBz) + require.NoError(t, err) + + data := struct { + KeyName string `json:"keyName"` + ChainID string `json:"chainID"` + Passphrase string `json:"passphrase"` + CommissionRate string `json:"commissionRate"` + Moniker string `json:"moniker"` + Identity string `json:"identity"` + Website string `json:"website"` + SecurityContract string `json:"securityContract"` + Details string `json:"details"` + EotsPK string `json:"eotsPK"` + }{ + KeyName: fpIns.GetConfig().BabylonConfig.Key, + ChainID: testChainID, + Passphrase: passphrase, + CommissionRate: "0.10", + Moniker: "some moniker", + Identity: "F123456789ABCDEF", + Website: "https://fp.example.com", + SecurityContract: "https://fp.example.com/security", + Details: "This is a highly secure and reliable fp.", + EotsPK: eotsPk.MarshalHex(), + } + + file, err := os.Create(fmt.Sprintf("%s/%s", t.TempDir(), "finality-provider.json")) + if err != nil { + log.Fatalf("Failed to create file: %v", err) + } + t.Cleanup(func() { + _ = os.Remove(file.Name()) + }) + + if err := json.NewEncoder(file).Encode(data); err != nil { + log.Fatalf("Failed to write JSON to file: %v", err) + } + + cmd.SetArgs([]string{ + "--from-file=" + file.Name(), + "--daemon-address=" + fpIns.GetConfig().RPCListener, + }) + + // Run the command + err = cmd.Execute() + require.NoError(t, err) + + fp, err := tm.BBNClient.QueryFinalityProvider(eotsPk.MustToBTCPK()) + require.NoError(t, err) + require.NotNil(t, fp) +} + +func TestRemoveMerkleProofsCmd(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + tm, fpIns := StartManagerWithFinalityProvider(t, ctx) + defer tm.Stop(t) + + tm.WaitForFpPubRandTimestamped(t, fpIns) + cmd := daemon.CommandUnsafePruneMerkleProof() + + cmd.SetArgs([]string{ + fpIns.GetBtcPkHex(), + "--daemon-address=" + fpIns.GetConfig().RPCListener, + "--up-to-height=100", + "--chain-id=" + testChainID, + }) + + err := cmd.Execute() + require.NoError(t, err) + + require.Eventually(t, func() bool { + _, err := tm.Fps[0].GetPubRandProofStore(). + GetPubRandProof(fpIns.GetChainID(), fpIns.GetBtcPkBIP340().MustMarshal(), 99) + + return errors.Is(err, store.ErrPubRandProofNotFound) + }, eventuallyWaitTimeOut, eventuallyPollTime) +} + +func TestPrintEotsCmd(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + tm := StartManager(t, ctx) + r := rand.New(rand.NewSource(time.Now().Unix())) + defer tm.Stop(t) + + expected := make(map[string]string) + for i := 0; i < r.Intn(10); i++ { + eotsKeyName := fmt.Sprintf("eots-key-%s", datagen.GenRandomHexStr(r, 4)) + ekey, err := tm.EOTSClient.CreateKey(eotsKeyName, passphrase, hdPath) + require.NoError(t, err) + pk, err := schnorr.ParsePubKey(ekey) + require.NoError(t, err) + expected[eotsKeyName] = bbntypes.NewBIP340PubKeyFromBTCPK(pk).MarshalHex() + } + + cancel() + + cmd := eotscmd.CommandPrintAllKeys() + + defaultConfig := eotscfg.DefaultConfigWithHomePath(tm.EOTSHomeDir) + fileParser := flags.NewParser(defaultConfig, flags.Default) + err := flags.NewIniParser(fileParser).WriteFile(eotscfg.CfgFile(tm.EOTSHomeDir), flags.IniIncludeDefaults) + require.NoError(t, err) + + cmd.SetArgs([]string{ + "--home=" + tm.EOTSHomeDir, + }) + + var outputBuffer bytes.Buffer + cmd.SetOut(&outputBuffer) + cmd.SetErr(&outputBuffer) + + err = cmd.Execute() + require.NoError(t, err) + + output := outputBuffer.String() + t.Logf("Captured output: %s", output) + + for keyName, eotsPK := range expected { + require.Contains(t, output, keyName) + require.Contains(t, output, eotsPK) + } } diff --git a/itest/babylon/babylon_test_manager.go b/itest/babylon/babylon_test_manager.go index 97a7a5da..2ab9ad26 100644 --- a/itest/babylon/babylon_test_manager.go +++ b/itest/babylon/babylon_test_manager.go @@ -1,51 +1,67 @@ package e2etest_babylon import ( + "context" + "fmt" + "math/rand" "os" "path/filepath" - "strconv" - "strings" - "sync" "testing" "time" sdkmath "cosmossdk.io/math" + bbnclient "github.com/babylonlabs-io/babylon/client/client" + "github.com/babylonlabs-io/babylon/testutil/datagen" bbntypes "github.com/babylonlabs-io/babylon/types" - "github.com/babylonlabs-io/finality-provider/clientcontroller" - e2eutils "github.com/babylonlabs-io/finality-provider/itest" - base_test_manager "github.com/babylonlabs-io/finality-provider/itest/test-manager" - "github.com/btcsuite/btcd/btcec/v2" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - + fpcc "github.com/babylonlabs-io/finality-provider/clientcontroller" + ccapi "github.com/babylonlabs-io/finality-provider/clientcontroller/api" bbncc "github.com/babylonlabs-io/finality-provider/clientcontroller/babylon" "github.com/babylonlabs-io/finality-provider/eotsmanager/client" eotsconfig "github.com/babylonlabs-io/finality-provider/eotsmanager/config" fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" "github.com/babylonlabs-io/finality-provider/finality-provider/service" + e2eutils "github.com/babylonlabs-io/finality-provider/itest" + "github.com/babylonlabs-io/finality-provider/itest/container" + base_test_manager "github.com/babylonlabs-io/finality-provider/itest/test-manager" + "github.com/babylonlabs-io/finality-provider/testutil" "github.com/babylonlabs-io/finality-provider/types" + "github.com/btcsuite/btcd/btcec/v2" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" + "go.uber.org/zap" ) -type BaseTestManager = base_test_manager.BaseTestManager +const ( + eventuallyWaitTimeOut = 5 * time.Minute + eventuallyPollTime = 500 * time.Millisecond + + testMoniker = "test-moniker" + testChainID = "chain-test" + passphrase = "testpass" + hdPath = "" +) type TestManager struct { - BaseTestManager - Wg sync.WaitGroup - BabylonHandler *e2eutils.BabylonNodeHandler + *base_test_manager.BaseTestManager EOTSServerHandler *e2eutils.EOTSServerHandler + EOTSHomeDir string FpConfig *fpcfg.Config - EOTSConfig *eotsconfig.Config - Fpa *service.FinalityProviderApp + Fps []*service.FinalityProviderApp EOTSClient *client.EOTSManagerGRpcClient BBNConsumerClient *bbncc.BabylonConsumerController baseDir string + manager *container.Manager + logger *zap.Logger } -func StartManager(t *testing.T) *TestManager { - testDir, err := e2eutils.BaseDir("fpe2etest") +func StartManager(t *testing.T, ctx context.Context) *TestManager { + testDir, err := base_test_manager.TempDir(t, "fp-e2e-test-*") require.NoError(t, err) - logger := zap.NewNop() + loggerConfig := zap.NewDevelopmentConfig() + loggerConfig.Level = zap.NewAtomicLevelAt(zap.ErrorLevel) + logger, err := loggerConfig.Build() + require.NoError(t, err) // 1. generate covenant committee covenantQuorum := 2 @@ -53,47 +69,77 @@ func StartManager(t *testing.T) *TestManager { covenantPrivKeys, covenantPubKeys := e2eutils.GenerateCovenantCommittee(numCovenants, t) // 2. prepare Babylon node - bh := e2eutils.NewBabylonNodeHandler(t, covenantQuorum, covenantPubKeys) - err = bh.Start() + manager, err := container.NewManager(t) require.NoError(t, err) - fpHomeDir := filepath.Join(testDir, "fp-home") - cfg := e2eutils.DefaultFpConfig(bh.GetNodeDataDir(), fpHomeDir) - bc, err := bbncc.NewBabylonController(cfg.BabylonConfig, &cfg.BTCNetParams, logger) + + // Create temp dir for babylon node + babylonDir, err := base_test_manager.TempDir(t, "babylon-test-*") require.NoError(t, err) - bcc, err := bbncc.NewBabylonConsumerController(cfg.BabylonConfig, &cfg.BTCNetParams, logger) + + // Start babylon node in docker + babylond, err := manager.RunBabylondResource(t, babylonDir, covenantQuorum, covenantPubKeys) require.NoError(t, err) + require.NotNil(t, babylond) - // 3. prepare EOTS manager - eotsHomeDir := filepath.Join(testDir, "eots-home") - eotsCfg := eotsconfig.DefaultConfigWithHomePath(eotsHomeDir) - eh := e2eutils.NewEOTSServerHandler(t, logger, eotsCfg, eotsHomeDir) - eh.Start() - // wait for EOTS servers to start - // see https://github.com/babylonchain/finality-provider/pull/517 - var eotsCli *client.EOTSManagerGRpcClient + keyDir := filepath.Join(babylonDir, "node0", "babylond") + fpHomeDir := filepath.Join(testDir, "fp-home") + cfg := e2eutils.DefaultFpConfig(keyDir, fpHomeDir) + + // update ports with the dynamically allocated ones from docker + cfg.BabylonConfig.RPCAddr = fmt.Sprintf("http://localhost:%s", babylond.GetPort("26657/tcp")) + cfg.BabylonConfig.GRPCAddr = fmt.Sprintf("https://localhost:%s", babylond.GetPort("9090/tcp")) + + var bc ccapi.ClientController + var bcc ccapi.ConsumerController require.Eventually(t, func() bool { - eotsCli, err = client.NewEOTSManagerGRpcClient(cfg.EOTSManagerAddress) - return err == nil - }, 5*time.Second, time.Second, "Failed to create EOTS clients") + bbnCfg := fpcfg.BBNConfigToBabylonConfig(cfg.BabylonConfig) + bbnCl, err := bbnclient.New(&bbnCfg, logger) + if err != nil { + t.Logf("failed to create Babylon client: %v", err) + return false + } + bc, err = bbncc.NewBabylonController(bbnCl, cfg.BabylonConfig, &cfg.BTCNetParams, logger) + if err != nil { + t.Logf("failed to create Babylon controller: %v", err) + return false + } + err = bc.Start() + if err != nil { + t.Logf("failed to start Babylon controller: %v", err) + return false + } + bcc, err = bbncc.NewBabylonConsumerController(cfg.BabylonConfig, &cfg.BTCNetParams, logger) + if err != nil { + t.Logf("failed to create Babylon consumer controller: %v", err) + return false + } + return true + }, 5*time.Second, eventuallyPollTime) - // 4. prepare finality-provider - fpdb, err := cfg.DatabaseConfig.GetDbBackend() - require.NoError(t, err) - fpApp, err := service.NewFinalityProviderApp(cfg, bc, bcc, eotsCli, fpdb, logger) - require.NoError(t, err) - err = fpApp.Start() + // Prepare EOTS manager + eotsHomeDir := filepath.Join(testDir, "eots-home") + eotsCfg := eotsconfig.DefaultConfigWithHomePath(eotsHomeDir) + eotsCfg.RPCListener = fmt.Sprintf("127.0.0.1:%d", testutil.AllocateUniquePort(t)) + eotsCfg.Metrics.Port = testutil.AllocateUniquePort(t) + eh := e2eutils.NewEOTSServerHandler(t, eotsCfg, eotsHomeDir) + eh.Start(ctx) + cfg.RPCListener = fmt.Sprintf("127.0.0.1:%d", testutil.AllocateUniquePort(t)) + eotsCli, err := client.NewEOTSManagerGRpcClient(eotsCfg.RPCListener) require.NoError(t, err) tm := &TestManager{ - BaseTestManager: BaseTestManager{BBNClient: bc, CovenantPrivKeys: covenantPrivKeys}, - BabylonHandler: bh, + BaseTestManager: &base_test_manager.BaseTestManager{ + BBNClient: bc.(*bbncc.BabylonController), + CovenantPrivKeys: covenantPrivKeys, + }, EOTSServerHandler: eh, + EOTSHomeDir: eotsHomeDir, FpConfig: cfg, - EOTSConfig: eotsCfg, - Fpa: fpApp, EOTSClient: eotsCli, - BBNConsumerClient: bcc, + BBNConsumerClient: bcc.(*bbncc.BabylonConsumerController), baseDir: testDir, + manager: manager, + logger: logger, } tm.WaitForServicesStart(t) @@ -101,109 +147,121 @@ func StartManager(t *testing.T) *TestManager { return tm } -func (tm *TestManager) WaitForServicesStart(t *testing.T) { - // wait for Babylon node starts - require.Eventually(t, func() bool { - params, err := tm.BBNClient.QueryStakingParams() - if err != nil { - return false - } - tm.StakingParams = params - return true - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) +func (tm *TestManager) AddFinalityProvider(t *testing.T, ctx context.Context) *service.FinalityProviderInstance { + r := rand.New(rand.NewSource(time.Now().Unix())) - t.Logf("Babylon node is started") -} + // Create EOTS key + eotsKeyName := fmt.Sprintf("eots-key-%s", datagen.GenRandomHexStr(r, 4)) + eotsPkBz, err := tm.EOTSClient.CreateKey(eotsKeyName, passphrase, hdPath) + require.NoError(t, err) + eotsPk, err := bbntypes.NewBIP340PubKey(eotsPkBz) + require.NoError(t, err) -func StartManagerWithFinalityProvider(t *testing.T, n int) (*TestManager, []*service.FinalityProviderInstance) { - tm := StartManager(t) - app := tm.Fpa - cfg := app.GetConfig() - oldKey := cfg.BabylonConfig.Key - - for i := 0; i < n; i++ { - fpName := e2eutils.FpNamePrefix + strconv.Itoa(i) - moniker := e2eutils.MonikerPrefix + strconv.Itoa(i) - commission := sdkmath.LegacyZeroDec() - desc := e2eutils.NewDescription(moniker) - - // needs to update key in config to be able to register and sign the creation of the finality provider with the same address. - cfg.BabylonConfig.Key = fpName - fpBbnKeyInfo, err := service.CreateChainKey(cfg.BabylonConfig.KeyDirectory, cfg.BabylonConfig.ChainID, cfg.BabylonConfig.Key, cfg.BabylonConfig.KeyringBackend, e2eutils.Passphrase, e2eutils.HdPath, "") - require.NoError(t, err) + t.Logf("the EOTS key is created: %s", eotsPk.MarshalHex()) - cc, err := clientcontroller.NewClientController(cfg, zap.NewNop()) - require.NoError(t, err) - app.UpdateClientController(cc) + // Create FP babylon key + fpKeyName := fmt.Sprintf("fp-key-%s", datagen.GenRandomHexStr(r, 4)) + fpHomeDir := filepath.Join(tm.baseDir, fmt.Sprintf("fp-%s", datagen.GenRandomHexStr(r, 4))) + cfg := e2eutils.DefaultFpConfig(tm.baseDir, fpHomeDir) + cfg.BabylonConfig.Key = fpKeyName + cfg.BabylonConfig.RPCAddr = tm.FpConfig.BabylonConfig.RPCAddr + cfg.BabylonConfig.GRPCAddr = tm.FpConfig.BabylonConfig.GRPCAddr + fpBbnKeyInfo, err := testutil.CreateChainKey(cfg.BabylonConfig.KeyDirectory, cfg.BabylonConfig.ChainID, cfg.BabylonConfig.Key, cfg.BabylonConfig.KeyringBackend, passphrase, hdPath, "") + require.NoError(t, err) - // add some funds for new fp pay for fees '-' - err = tm.BabylonHandler.BabylonNode.TxBankSend(fpBbnKeyInfo.AccAddress.String(), "1000000ubbn") - require.NoError(t, err) + t.Logf("the Babylon key is created: %s", fpBbnKeyInfo.AccAddress.String()) - res, err := app.CreateFinalityProvider(fpName, e2eutils.ChainID, e2eutils.Passphrase, e2eutils.HdPath, nil, desc, &commission) - require.NoError(t, err) - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(res.FpInfo.BtcPkHex) - require.NoError(t, err) - _, err = app.RegisterFinalityProvider(fpPk.MarshalHex()) - require.NoError(t, err) - err = app.StartHandlingFinalityProvider(fpPk, e2eutils.Passphrase) - require.NoError(t, err) - fpIns, err := app.GetFinalityProviderInstance(fpPk) - require.NoError(t, err) - require.True(t, fpIns.IsRunning()) - require.NoError(t, err) + // Add funds for new FP + _, _, err = tm.manager.BabylondTxBankSend(t, fpBbnKeyInfo.AccAddress.String(), "1000000ubbn", "node0") + require.NoError(t, err) - // check finality providers on Babylon side - require.Eventually(t, func() bool { - fps, err := tm.BBNClient.QueryFinalityProviders() - if err != nil { - t.Logf("failed to query finality providers from Babylon %s", err.Error()) - return false - } - - if len(fps) != i+1 { - return false - } - - for _, fp := range fps { - if !strings.Contains(fp.Description.Moniker, e2eutils.MonikerPrefix) { - return false - } - if !fp.Commission.Equal(sdkmath.LegacyZeroDec()) { - return false - } - } + // create new clients + bc, err := fpcc.NewBabylonController(cfg, tm.logger) + require.NoError(t, err) + err = bc.Start() + require.NoError(t, err) + bcc, err := bbncc.NewBabylonConsumerController(cfg.BabylonConfig, &cfg.BTCNetParams, tm.logger) + require.NoError(t, err) - return true - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) - } + // Create and start finality provider app + eotsCli, err := client.NewEOTSManagerGRpcClient(tm.EOTSServerHandler.Config().RPCListener) + require.NoError(t, err) + fpdb, err := cfg.DatabaseConfig.GetDBBackend() + require.NoError(t, err) + fpApp, err := service.NewFinalityProviderApp(cfg, bc, bcc, eotsCli, fpdb, tm.logger) + require.NoError(t, err) + err = fpApp.Start() + require.NoError(t, err) - // goes back to old key in app - cfg.BabylonConfig.Key = oldKey - cc, err := clientcontroller.NewClientController(cfg, zap.NewNop()) + // Create and register the finality provider + commission := sdkmath.LegacyZeroDec() + desc := newDescription(testMoniker) + _, err = fpApp.CreateFinalityProvider(cfg.BabylonConfig.Key, testChainID, passphrase, eotsPk, desc, &commission) require.NoError(t, err) - app.UpdateClientController(cc) - fpInsList := app.ListFinalityProviderInstances() - require.Equal(t, n, len(fpInsList)) + cfg.RPCListener = fmt.Sprintf("127.0.0.1:%d", testutil.AllocateUniquePort(t)) + cfg.Metrics.Port = testutil.AllocateUniquePort(t) - t.Logf("The test manager is running with %v finality-provider(s)", len(fpInsList)) + err = fpApp.StartFinalityProvider(eotsPk, passphrase) + require.NoError(t, err) + + fpServer := service.NewFinalityProviderServer(cfg, tm.logger, fpApp, fpdb) + go func() { + err = fpServer.RunUntilShutdown(ctx) + require.NoError(t, err) + }() + + tm.Fps = append(tm.Fps, fpApp) + + fpIns, err := fpApp.GetFinalityProviderInstance() + require.NoError(t, err) + + return fpIns +} + +func (tm *TestManager) WaitForServicesStart(t *testing.T) { + require.Eventually(t, func() bool { + _, err := tm.BBNClient.QueryBestBlock() + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + + t.Logf("Babylon node is started") +} + +func StartManagerWithFinalityProvider(t *testing.T, ctx context.Context) (*TestManager, *service.FinalityProviderInstance) { + tm := StartManager(t, ctx) + + runningFp := tm.AddFinalityProvider(t, ctx) + + // Check finality providers on Babylon side + require.Eventually(t, func() bool { + fps, err := tm.BBNClient.QueryFinalityProviders() + if err != nil { + t.Logf("failed to query finality providers from Babylon %s", err.Error()) + return false + } + + return len(fps) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) - return tm, fpInsList + t.Logf("the test manager is running with a finality provider") + + return tm, runningFp } func (tm *TestManager) Stop(t *testing.T) { - err := tm.Fpa.Stop() - require.NoError(t, err) - err = tm.BabylonHandler.Stop() + for _, fpApp := range tm.Fps { + err := fpApp.Stop() + require.NoError(t, err) + } + err := tm.manager.ClearResources() require.NoError(t, err) err = os.RemoveAll(tm.baseDir) require.NoError(t, err) - tm.EOTSServerHandler.Stop() } func (tm *TestManager) CheckBlockFinalization(t *testing.T, height uint64, num int) { - // we need to ensure votes are collected at the given height + // We need to ensure votes are collected at the given height require.Eventually(t, func() bool { votes, err := tm.BBNClient.QueryVotesAtHeight(height) if err != nil { @@ -211,9 +269,9 @@ func (tm *TestManager) CheckBlockFinalization(t *testing.T, height uint64, num i return false } return len(votes) == num - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) + }, eventuallyWaitTimeOut, eventuallyPollTime) - // as the votes have been collected, the block should be finalized + // As the votes have been collected, the block should be finalized require.Eventually(t, func() bool { finalized, err := tm.BBNConsumerClient.QueryIsBlockFinalized(height) if err != nil { @@ -221,7 +279,7 @@ func (tm *TestManager) CheckBlockFinalization(t *testing.T, height uint64, num i return false } return finalized - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) + }, eventuallyWaitTimeOut, eventuallyPollTime) } func (tm *TestManager) WaitForFpVoteCast(t *testing.T, fpIns *service.FinalityProviderInstance) uint64 { @@ -230,15 +288,41 @@ func (tm *TestManager) WaitForFpVoteCast(t *testing.T, fpIns *service.FinalityPr if fpIns.GetLastVotedHeight() > 0 { lastVotedHeight = fpIns.GetLastVotedHeight() return true - } else { - return false } - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) + return false + }, eventuallyWaitTimeOut, eventuallyPollTime) return lastVotedHeight } -func (tm *TestManager) WaitForNFinalizedBlocksAndReturnTipHeight(t *testing.T, n uint) uint64 { +func (tm *TestManager) GetFpPrivKey(t *testing.T, fpPk []byte) *btcec.PrivateKey { + record, err := tm.EOTSClient.KeyRecord(fpPk, passphrase) + require.NoError(t, err) + return record.PrivKey +} + +func (tm *TestManager) StopAndRestartFpAfterNBlocks(t *testing.T, n int, fpIns *service.FinalityProviderInstance) { + blockBeforeStop, err := tm.BBNConsumerClient.QueryLatestBlockHeight() + require.NoError(t, err) + err = fpIns.Stop() + require.NoError(t, err) + + require.Eventually(t, func() bool { + headerAfterStop, err := tm.BBNConsumerClient.QueryLatestBlockHeight() + if err != nil { + return false + } + + return headerAfterStop >= uint64(n)+blockBeforeStop + }, eventuallyWaitTimeOut, eventuallyPollTime) + + t.Log("restarting the finality-provider instance") + + err = fpIns.Start() + require.NoError(t, err) +} + +func (tm *TestManager) WaitForNFinalizedBlocks(t *testing.T, n uint) *types.BlockInfo { var ( firstFinalizedBlock *types.BlockInfo err error @@ -258,46 +342,14 @@ func (tm *TestManager) WaitForNFinalizedBlocksAndReturnTipHeight(t *testing.T, n firstFinalizedBlock = lastFinalizedBlock } return lastFinalizedBlock.Height-firstFinalizedBlock.Height >= uint64(n-1) - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) + }, eventuallyWaitTimeOut, eventuallyPollTime) t.Logf("the block is finalized at %v", lastFinalizedBlock.Height) - return lastFinalizedBlock.Height -} - -func (tm *TestManager) WaitForFpShutDown(t *testing.T, pk *bbntypes.BIP340PubKey) { - require.Eventually(t, func() bool { - _, err := tm.Fpa.GetFinalityProviderInstance(pk) - return err != nil - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) - - t.Logf("the finality-provider instance %s is shutdown", pk.MarshalHex()) -} - -func (tm *TestManager) StopAndRestartFpAfterNBlocks(t *testing.T, n uint, fpIns *service.FinalityProviderInstance) { - blockBeforeStopHeight, err := tm.BBNConsumerClient.QueryLatestBlockHeight() - require.NoError(t, err) - err = fpIns.Stop() - require.NoError(t, err) - - require.Eventually(t, func() bool { - headerAfterStopHeight, err := tm.BBNConsumerClient.QueryLatestBlockHeight() - if err != nil { - return false - } - - return headerAfterStopHeight >= uint64(n)+blockBeforeStopHeight - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) - - t.Log("restarting the finality-provider instance") - - tm.FpConfig.PollerConfig.AutoChainScanningMode = true - err = fpIns.Start() - require.NoError(t, err) + return lastFinalizedBlock } -func (tm *TestManager) GetFpPrivKey(t *testing.T, fpPk []byte) *btcec.PrivateKey { - record, err := tm.EOTSClient.KeyRecord(fpPk, e2eutils.Passphrase) - require.NoError(t, err) - return record.PrivKey +func newDescription(moniker string) *stakingtypes.Description { + dec := stakingtypes.NewDescription(moniker, "", "", "", "") + return &dec } diff --git a/itest/babylon_node_handler.go b/itest/babylon_node_handler.go deleted file mode 100644 index d99d268f..00000000 --- a/itest/babylon_node_handler.go +++ /dev/null @@ -1,280 +0,0 @@ -package e2e_utils - -import ( - "bytes" - "encoding/hex" - "encoding/json" - "fmt" - "log" - "math/rand" - "os" - "os/exec" - "path/filepath" - "runtime" - "strconv" - "strings" - "testing" - "time" - - "github.com/babylonlabs-io/babylon/testutil/datagen" - "github.com/babylonlabs-io/babylon/types" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/txscript" - "github.com/stretchr/testify/require" -) - -type babylonNode struct { - cmd *exec.Cmd - pidFile string - dataDir string - walletName string -} - -func newBabylonNode(dataDir, walletName string, cmd *exec.Cmd) *babylonNode { - return &babylonNode{ - dataDir: dataDir, - cmd: cmd, - walletName: walletName, - } -} - -func (n *babylonNode) start() error { - if err := n.cmd.Start(); err != nil { - return err - } - - pid, err := os.Create(filepath.Join(n.dataDir, - fmt.Sprintf("%s.pid", "config"))) - if err != nil { - return err - } - - n.pidFile = pid.Name() - if _, err = fmt.Fprintf(pid, "%d\n", n.cmd.Process.Pid); err != nil { - return err - } - - if err := pid.Close(); err != nil { - return err - } - - return nil -} - -func (n *babylonNode) stop() (err error) { - if n.cmd == nil || n.cmd.Process == nil { - // return if not properly initialized - // or error starting the process - return nil - } - - defer func() { - err = n.cmd.Wait() - }() - - if runtime.GOOS == "windows" { - return n.cmd.Process.Signal(os.Kill) - } - return n.cmd.Process.Signal(os.Interrupt) -} - -func (n *babylonNode) cleanup() error { - if n.pidFile != "" { - if err := os.Remove(n.pidFile); err != nil { - log.Printf("unable to remove file %s: %v", n.pidFile, - err) - } - } - - dirs := []string{ - n.dataDir, - } - var err error - for _, dir := range dirs { - if err = os.RemoveAll(dir); err != nil { - log.Printf("Cannot remove dir %s: %v", dir, err) - } - } - return nil -} - -func (n *babylonNode) shutdown() error { - if err := n.stop(); err != nil { - return err - } - if err := n.cleanup(); err != nil { - return err - } - return nil -} - -type BabylonNodeHandler struct { - BabylonNode *babylonNode -} - -func NewBabylonNodeHandler(t *testing.T, covenantQuorum int, covenantPks []*types.BIP340PubKey) *BabylonNodeHandler { - testDir, err := BaseDir("zBabylonTest") - require.NoError(t, err) - defer func() { - if err != nil { - err := os.RemoveAll(testDir) - require.NoError(t, err) - } - }() - - walletName := "node0" - nodeDataDir := filepath.Join(testDir, walletName, "babylond") - - r := rand.New(rand.NewSource(time.Now().Unix())) - slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SigNetParams) - require.NoError(t, err) - slashingPkScript, err := txscript.PayToAddrScript(slashingAddress) - require.NoError(t, err) - - var covenantPksStr []string - for _, pk := range covenantPks { - covenantPksStr = append(covenantPksStr, pk.MarshalHex()) - } - - initTestnetCmd := exec.Command( - "babylond", - "testnet", - "--v=1", - fmt.Sprintf("--output-dir=%s", testDir), - "--starting-ip-address=192.168.10.2", - "--keyring-backend=test", - "--chain-id=chain-test", - "--additional-sender-account", - fmt.Sprintf("--epoch-interval=%d", 5), - fmt.Sprintf("--slashing-pk-script=%s", hex.EncodeToString(slashingPkScript)), - fmt.Sprintf("--covenant-quorum=%d", covenantQuorum), - fmt.Sprintf("--covenant-pks=%s", strings.Join(covenantPksStr, ",")), - ) - - var stderr bytes.Buffer - initTestnetCmd.Stderr = &stderr - - err = initTestnetCmd.Run() - if err != nil { - fmt.Printf("init testnet failed: %s \n", stderr.String()) - } - require.NoError(t, err) - - f, err := os.Create(filepath.Join(testDir, "babylon.log")) - t.Logf("babylon log file: %s", f.Name()) - require.NoError(t, err) - - startCmd := exec.Command( - "babylond", - "start", - fmt.Sprintf("--home=%s", nodeDataDir), - "--log_level=trace", - "--trace", - ) - - fmt.Println("Starting babylond with command: ", startCmd.String()) - - startCmd.Stdout = f - - return &BabylonNodeHandler{ - BabylonNode: newBabylonNode(testDir, walletName, startCmd), - } -} - -func (w *BabylonNodeHandler) Start() error { - if err := w.BabylonNode.start(); err != nil { - // try to cleanup after start error, but return original error - _ = w.BabylonNode.cleanup() - return err - } - return nil -} - -func (w *BabylonNodeHandler) Stop() error { - if err := w.BabylonNode.shutdown(); err != nil { - return err - } - - return nil -} - -func (w *BabylonNodeHandler) GetNodeDataDir() string { - return w.BabylonNode.getNodeDataDir() -} - -// getNodeDataDir returns the home path of the babylon node. -func (n *babylonNode) getNodeDataDir() string { - dir := filepath.Join(n.dataDir, n.walletName, "babylond") - return dir -} - -// TxBankSend send transaction to a address from the node address. -func (n *babylonNode) TxBankSend(addr, coins string) error { - flags := []string{ - "tx", - "bank", - "send", - n.walletName, - addr, coins, - "--keyring-backend=test", - fmt.Sprintf("--home=%s", n.getNodeDataDir()), - "--log_level=debug", - "--chain-id=chain-test", - "-b=sync", "--yes", "--gas-prices=10ubbn", - } - - cmd := exec.Command("babylond", flags...) - var stderr bytes.Buffer - cmd.Stderr = &stderr - - err := cmd.Run() - if err != nil { - return fmt.Errorf("cmd failed: %v\nstderr: %s", err, stderr.String()) - } - return nil -} - -type balanceResponse struct { - Balances []struct { - Denom string `json:"denom"` - Amount string `json:"amount"` - } `json:"balances"` - Pagination struct { - Total string `json:"total"` - } `json:"pagination"` -} - -// CheckAddrBalance retrieves the balance of the specified address. -func (n *babylonNode) CheckAddrBalance(addr string) (int, error) { - flags := []string{ - "query", - "bank", - "balances", - addr, - "--output=json", - fmt.Sprintf("--home=%s", n.getNodeDataDir()), - "--log_level=debug", - "--chain-id=chain-test", - } - - cmd := exec.Command("babylond", flags...) - output, err := cmd.Output() - if err != nil { - return 0, err - } - - var resp balanceResponse - if err := json.Unmarshal(output, &resp); err != nil { - return 0, err - } - - if len(resp.Balances) == 0 { - return 0, fmt.Errorf("no balances found for address %s", addr) - } - - balance, err := strconv.Atoi(resp.Balances[0].Amount) - if err != nil { - return 0, err - } - return balance, nil -} diff --git a/itest/container/config.go b/itest/container/config.go new file mode 100644 index 00000000..1e512e54 --- /dev/null +++ b/itest/container/config.go @@ -0,0 +1,30 @@ +package container + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/babylonlabs-io/finality-provider/testutil" +) + +// ImageConfig contains all images and their respective tags +// needed for running e2e tests. +type ImageConfig struct { + BabylonRepository string + BabylonVersion string +} + +const ( + dockerBabylondRepository = "babylonlabs/babylond" +) + +// NewImageConfig returns ImageConfig needed for running e2e test. +func NewImageConfig(t *testing.T) ImageConfig { + babylondVersion, err := testutil.GetBabylonVersion() + require.NoError(t, err) + return ImageConfig{ + BabylonRepository: dockerBabylondRepository, + BabylonVersion: babylondVersion, + } +} diff --git a/itest/container/container.go b/itest/container/container.go new file mode 100644 index 00000000..2461648e --- /dev/null +++ b/itest/container/container.go @@ -0,0 +1,243 @@ +package container + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + mrand "math/rand" + "regexp" + "strconv" + "strings" + "testing" + "time" + + "github.com/babylonlabs-io/babylon/testutil/datagen" + bbn "github.com/babylonlabs-io/babylon/types" + "github.com/babylonlabs-io/finality-provider/testutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" + "github.com/stretchr/testify/require" +) + +const ( + babylondContainerName = "babylond" +) + +var errRegex = regexp.MustCompile(`(E|e)rror`) + +// Manager is a wrapper around all Docker instances, and the Docker API. +// It provides utilities to run and interact with all Docker containers used within e2e testing. +type Manager struct { + cfg ImageConfig + pool *dockertest.Pool + resources map[string]*dockertest.Resource +} + +// NewManager creates a new ContainerManager instance and initializes +// all Docker specific utilities. Returns an error if initialization fails. +func NewManager(t *testing.T) (docker *Manager, err error) { + docker = &Manager{ + cfg: NewImageConfig(t), + resources: make(map[string]*dockertest.Resource), + } + docker.pool, err = dockertest.NewPool("") + if err != nil { + return nil, err + } + + return docker, nil +} + +// RunBabylondResource starts a babylond container +func (m *Manager) RunBabylondResource( + t *testing.T, + mounthPath string, + covenantQuorum int, + covenantPks []*bbn.BIP340PubKey, +) (*dockertest.Resource, error) { + r := mrand.New(mrand.NewSource(time.Now().Unix())) + + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SigNetParams) + require.NoError(t, err) + slashingPkScript, err := txscript.PayToAddrScript(slashingAddress) + require.NoError(t, err) + + var covenantPksStr []string + for _, pk := range covenantPks { + covenantPksStr = append(covenantPksStr, pk.MarshalHex()) + } + + const epochInterval = 5 + + cmd := []string{ + "sh", "-c", fmt.Sprintf( + "babylond testnet --v=1 --output-dir=/home "+ + "--starting-ip-address=192.168.10.2 "+ + "--keyring-backend=test --chain-id=chain-test "+ + "--additional-sender-account "+ + "--epoch-interval=%d --slashing-pk-script=%s "+ + "--covenant-quorum=%d --covenant-pks=%s && "+ + "chmod -R 777 /home && "+ + "babylond start --home=/home/node0/babylond", + epochInterval, + hex.EncodeToString(slashingPkScript), + covenantQuorum, + strings.Join(covenantPksStr, ",")), + } + + resource, err := m.pool.RunWithOptions( + &dockertest.RunOptions{ + Name: fmt.Sprintf("%s-%s", babylondContainerName, t.Name()), + Repository: m.cfg.BabylonRepository, + Tag: m.cfg.BabylonVersion, + Labels: map[string]string{ + "e2e": "babylond", + }, + User: "root:root", + Mounts: []string{ + fmt.Sprintf("%s/:/home/", mounthPath), + }, + ExposedPorts: []string{ + "9090/tcp", // only expose what we need + "26657/tcp", + }, + Cmd: cmd, + }, + func(config *docker.HostConfig) { + config.PortBindings = map[docker.Port][]docker.PortBinding{ + "9090/tcp": {{HostIP: "", HostPort: strconv.Itoa(testutil.AllocateUniquePort(t))}}, + "26657/tcp": {{HostIP: "", HostPort: strconv.Itoa(testutil.AllocateUniquePort(t))}}, + } + }, + noRestart, + ) + if err != nil { + return nil, err + } + + m.resources[babylondContainerName] = resource + + return resource, nil +} + +// ClearResources removes all outstanding Docker resources created by the ContainerManager. +func (m *Manager) ClearResources() error { + for _, resource := range m.resources { + if err := m.pool.Purge(resource); err != nil { + return err + } + } + + return nil +} + +func (m *Manager) ExecBabylondCmd(t *testing.T, command []string) (bytes.Buffer, bytes.Buffer, error) { + cmd := []string{"babylond"} + cmd = append(cmd, command...) + return m.ExecCmd(t, babylondContainerName, cmd) +} + +// BabylondTxBankSend send transaction to an address from the node address. +func (m *Manager) BabylondTxBankSend(t *testing.T, addr, coins, walletName string) (bytes.Buffer, bytes.Buffer, error) { + flags := []string{ + "babylond", + "tx", + "bank", + "send", + walletName, + addr, + coins, + "--keyring-backend=test", + "--home=/home/node0/babylond", + "--log_level=debug", + "--chain-id=chain-test", + "-b=sync", "--yes", "--gas-prices=10ubbn", + } + + return m.ExecCmd(t, babylondContainerName, flags) +} + +// ExecCmd executes command by running it on the given container. +// It word for word `error` in output to discern between error and regular output. +// It retures stdout and stderr as bytes.Buffer and an error if the command fails. +func (m *Manager) ExecCmd(t *testing.T, containerName string, command []string) (bytes.Buffer, bytes.Buffer, error) { + if _, ok := m.resources[containerName]; !ok { + return bytes.Buffer{}, bytes.Buffer{}, fmt.Errorf("no resource %s found", containerName) + } + containerId := m.resources[containerName].Container.ID + + var ( + outBuf bytes.Buffer + errBuf bytes.Buffer + ) + + timeout := 20 * time.Second + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + t.Logf("\n\nRunning: \"%s\"", command) + + // We use the `require.Eventually` function because it is only allowed to do one transaction per block without + // sequence numbers. For simplicity, we avoid keeping track of the sequence number and just use the `require.Eventually`. + require.Eventually( + t, + func() bool { + exec, err := m.pool.Client.CreateExec(docker.CreateExecOptions{ + Context: ctx, + AttachStdout: true, + AttachStderr: true, + Container: containerId, + User: "root", + Cmd: command, + }) + + if err != nil { + t.Logf("failed to create exec: %v", err) + + return false + } + + err = m.pool.Client.StartExec(exec.ID, docker.StartExecOptions{ + Context: ctx, + Detach: false, + OutputStream: &outBuf, + ErrorStream: &errBuf, + }) + if err != nil { + t.Logf("failed to start exec: %v", err) + + return false + } + + errBufString := errBuf.String() + // Note that this does not match all errors. + // This only works if CLI outputs "Error" or "error" + // to stderr. + if errRegex.MatchString(errBufString) { + t.Log("\nstderr:") + t.Log(errBufString) + + t.Log("\nstdout:") + t.Log(outBuf.String()) + return false + } + + return true + }, + timeout, + 500*time.Millisecond, + "command failed", + ) + + return outBuf, errBuf, nil +} + +func noRestart(config *docker.HostConfig) { + // in this case, we don't want the nodes to restart on failure + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } +} diff --git a/itest/cosmwasm/bcd/bcd_consumer_e2e_test.go b/itest/cosmwasm/bcd/bcd_consumer_e2e_test.go index 92d3a9c0..fcaada79 100644 --- a/itest/cosmwasm/bcd/bcd_consumer_e2e_test.go +++ b/itest/cosmwasm/bcd/bcd_consumer_e2e_test.go @@ -4,11 +4,10 @@ package e2etest_bcd import ( + "context" "encoding/json" - "strings" "testing" - sdkmath "cosmossdk.io/math" e2eutils "github.com/babylonlabs-io/finality-provider/itest" sdk "github.com/cosmos/cosmos-sdk/types" sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" @@ -28,14 +27,16 @@ import ( // NOTE: the delegation is injected after ensuring pub randomness loop in fp daemon has started // this order is necessary otherwise pub randomness loop takes time to start and due to this blocks won't get finalized. func TestConsumerFpLifecycle(t *testing.T) { - ctm := StartBcdTestManager(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctm := StartBcdTestManager(t, ctx) defer ctm.Stop(t) // store babylon contract babylonContractPath := "../../bytecode/babylon_contract.wasm" err := ctm.BcdConsumerClient.StoreWasmCode(babylonContractPath) require.NoError(t, err) - babylonContractWasmId, err := ctm.BcdConsumerClient.GetLatestCodeId() + babylonContractWasmId, err := ctm.BcdConsumerClient.GetLatestCodeID() require.NoError(t, err) require.Equal(t, uint64(1), babylonContractWasmId) @@ -43,7 +44,7 @@ func TestConsumerFpLifecycle(t *testing.T) { btcStakingContractPath := "../../bytecode/btc_staking.wasm" err = ctm.BcdConsumerClient.StoreWasmCode(btcStakingContractPath) require.NoError(t, err) - btcStakingContractWasmId, err := ctm.BcdConsumerClient.GetLatestCodeId() + btcStakingContractWasmId, err := ctm.BcdConsumerClient.GetLatestCodeID() require.NoError(t, err) require.Equal(t, uint64(2), btcStakingContractWasmId) @@ -77,52 +78,13 @@ func TestConsumerFpLifecycle(t *testing.T) { ctm.BcdConsumerClient.SetBtcStakingContractAddress(btcStakingContractAddr.String()) // register consumer to babylon - _, err = ctm.BBNClient.RegisterConsumerChain(bcdChainID, "Consumer chain 1 (test)", "Test Consumer Chain 1") + _, err = ctm.BBNClient.RegisterConsumerChain(bcdConsumerID, "Consumer chain 1 (test)", "Test Consumer Chain 1", "") require.NoError(t, err) // register consumer fps to babylon // this will be submitted to babylon once fp daemon starts - fps := ctm.CreateConsumerFinalityProviders(t, bcdChainID, 1) - fpPk := fps[0].GetBtcPkBIP340() - fpPkHex := fpPk.MarshalHex() - - // inject fp in smart contract using admin - fpMsg := e2eutils.GenBtcStakingFpExecMsg(fpPkHex) - fpMsgBytes, err := json.Marshal(fpMsg) - require.NoError(t, err) - _, err = ctm.BcdConsumerClient.ExecuteContract(fpMsgBytes) - require.NoError(t, err) - - // query finality providers in smart contract - consumerFpsResp, err := ctm.BcdConsumerClient.QueryFinalityProviders() - require.NoError(t, err) - require.NotNil(t, consumerFpsResp) - require.Len(t, consumerFpsResp.Fps, 1) - require.Equal(t, fpMsg.BtcStaking.NewFP[0].ConsumerID, consumerFpsResp.Fps[0].ConsumerId) - require.Equal(t, fpMsg.BtcStaking.NewFP[0].BTCPKHex, consumerFpsResp.Fps[0].BtcPkHex) - - // ensure consumer finality providers are stored in Babylon - // this will happen after the finality provider daemon has started - require.Eventually(t, func() bool { - fps, err := ctm.BBNClient.QueryConsumerFinalityProviders(bcdChainID) - if err != nil { - t.Logf("failed to query finality providers from Babylon %s", err.Error()) - return false - } - - if len(fps) != 1 { - return false - } - - if !strings.Contains(fps[0].Description.Moniker, e2eutils.MonikerPrefix) { - return false - } - if !fps[0].Commission.Equal(sdkmath.LegacyZeroDec()) { - return false - } - - return true - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) + fp := ctm.CreateConsumerFinalityProviders(t, bcdConsumerID) + fpPk := fp.GetBtcPkBIP340() // ensure pub rand is submitted to smart contract require.Eventually(t, func() bool { @@ -163,7 +125,7 @@ func TestConsumerFpLifecycle(t *testing.T) { require.NoError(t, err) require.NotNil(t, consumerFpsByPowerResp) require.Len(t, consumerFpsByPowerResp.Fps, 1) - require.Equal(t, fpMsg.BtcStaking.NewFP[0].BTCPKHex, consumerFpsByPowerResp.Fps[0].BtcPkHex) + require.Equal(t, fpPk.MarshalHex(), consumerFpsByPowerResp.Fps[0].BtcPkHex) require.Equal(t, delMsg.BtcStaking.ActiveDel[0].TotalSat, consumerFpsByPowerResp.Fps[0].Power) // get comet latest height diff --git a/itest/cosmwasm/bcd/bcd_handler.go b/itest/cosmwasm/bcd/bcd_handler.go index 3af5b6de..537b4920 100644 --- a/itest/cosmwasm/bcd/bcd_handler.go +++ b/itest/cosmwasm/bcd/bcd_handler.go @@ -19,9 +19,10 @@ import ( ) const ( - bcdRpcPort int = 3990 - bcdP2pPort int = 3991 - bcdChainID = "bcd-test" + bcdRpcPort int = 3990 + bcdP2pPort int = 3991 + bcdChainID = "bcd-test" + bcdConsumerID = "09-localhost" // TODO: mock a real consumer ID ) type BcdNodeHandler struct { @@ -251,8 +252,9 @@ func bcdStartCmd(t *testing.T, testDir string) *exec.Cmd { "--home", testDir, "--rpc.laddr", fmt.Sprintf("tcp://0.0.0.0:%d", bcdRpcPort), "--p2p.laddr", fmt.Sprintf("tcp://0.0.0.0:%d", bcdP2pPort), - "--log_level=trace", - "--trace", + // "--log_level=trace", + // "--trace", + "--log_level=debug", } f, err := os.Create(filepath.Join(testDir, "bcd.log")) diff --git a/itest/cosmwasm/bcd/bcd_test_manager.go b/itest/cosmwasm/bcd/bcd_test_manager.go index 5612989b..eb3239ee 100644 --- a/itest/cosmwasm/bcd/bcd_test_manager.go +++ b/itest/cosmwasm/bcd/bcd_test_manager.go @@ -4,11 +4,12 @@ package e2etest_bcd import ( + "context" + "encoding/json" "fmt" "os" "path/filepath" "strconv" - "strings" "testing" "time" @@ -18,31 +19,38 @@ import ( wasmparams "github.com/CosmWasm/wasmd/app/params" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" _ "github.com/babylonlabs-io/babylon-sdk/demo/app" + bbnclient "github.com/babylonlabs-io/babylon/client/client" "github.com/babylonlabs-io/babylon/testutil/datagen" bbntypes "github.com/babylonlabs-io/babylon/types" fpcc "github.com/babylonlabs-io/finality-provider/clientcontroller" + ccapi "github.com/babylonlabs-io/finality-provider/clientcontroller/api" bbncc "github.com/babylonlabs-io/finality-provider/clientcontroller/babylon" cwcc "github.com/babylonlabs-io/finality-provider/clientcontroller/cosmwasm" "github.com/babylonlabs-io/finality-provider/eotsmanager/client" eotsconfig "github.com/babylonlabs-io/finality-provider/eotsmanager/config" - "github.com/babylonlabs-io/finality-provider/finality-provider/config" fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" "github.com/babylonlabs-io/finality-provider/finality-provider/service" e2eutils "github.com/babylonlabs-io/finality-provider/itest" + "github.com/babylonlabs-io/finality-provider/itest/container" + base_test_manager "github.com/babylonlabs-io/finality-provider/itest/test-manager" + "github.com/babylonlabs-io/finality-provider/testutil" "github.com/babylonlabs-io/finality-provider/types" - "github.com/btcsuite/btcd/btcec/v2" dbm "github.com/cosmos/cosmos-db" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" "github.com/stretchr/testify/require" + "go.uber.org/zap" "go.uber.org/zap/zapcore" +) - "go.uber.org/zap" +const ( + passphrase = "testpass" + hdPath = "" ) type BcdTestManager struct { - BabylonHandler *e2eutils.BabylonNodeHandler + *base_test_manager.BaseTestManager + manager *container.Manager FpConfig *fpcfg.Config - BBNClient *bbncc.BabylonController BcdHandler *BcdNodeHandler BcdConsumerClient *cwcc.CosmwasmConsumerController StakingParams *types.StakingParams @@ -50,8 +58,8 @@ type BcdTestManager struct { EOTSConfig *eotsconfig.Config Fpa *service.FinalityProviderApp EOTSClient *client.EOTSManagerGRpcClient - CovenantPrivKeys []*btcec.PrivateKey baseDir string + logger *zap.Logger } func createLogger(t *testing.T, level zapcore.Level) *zap.Logger { @@ -62,12 +70,14 @@ func createLogger(t *testing.T, level zapcore.Level) *zap.Logger { return logger } -func StartBcdTestManager(t *testing.T) *BcdTestManager { - // Setup consumer test manager - testDir, err := e2eutils.BaseDir("fpe2etest") +func StartBcdTestManager(t *testing.T, ctx context.Context) *BcdTestManager { + testDir, err := base_test_manager.TempDir(t, "fp-e2e-test-*") require.NoError(t, err) - logger := createLogger(t, zapcore.ErrorLevel) + loggerConfig := zap.NewDevelopmentConfig() + loggerConfig.Level = zap.NewAtomicLevelAt(zap.ErrorLevel) + logger, err := loggerConfig.Build() + require.NoError(t, err) // 1. generate covenant committee covenantQuorum := 2 @@ -75,19 +85,52 @@ func StartBcdTestManager(t *testing.T) *BcdTestManager { covenantPrivKeys, covenantPubKeys := e2eutils.GenerateCovenantCommittee(numCovenants, t) // 2. prepare Babylon node - bh := e2eutils.NewBabylonNodeHandler(t, covenantQuorum, covenantPubKeys) - err = bh.Start() + manager, err := container.NewManager(t) require.NoError(t, err) - fpHomeDir := filepath.Join(testDir, "fp-home") - cfg := e2eutils.DefaultFpConfig(bh.GetNodeDataDir(), fpHomeDir) - bc, err := bbncc.NewBabylonController(cfg.BabylonConfig, &cfg.BTCNetParams, logger) + + // Create temp dir for babylon node + babylonDir, err := base_test_manager.TempDir(t, "babylon-test-*") + require.NoError(t, err) + + // Start babylon node in docker + babylond, err := manager.RunBabylondResource(t, babylonDir, covenantQuorum, covenantPubKeys) require.NoError(t, err) + require.NotNil(t, babylond) + + keyDir := filepath.Join(babylonDir, "node0", "babylond") + fpHomeDir := filepath.Join(testDir, "fp-home") + cfg := e2eutils.DefaultFpConfig(keyDir, fpHomeDir) + + // Update ports with dynamically allocated ones from docker + cfg.BabylonConfig.RPCAddr = fmt.Sprintf("http://localhost:%s", babylond.GetPort("26657/tcp")) + cfg.BabylonConfig.GRPCAddr = fmt.Sprintf("localhost:%s", babylond.GetPort("9090/tcp")) + + var bc ccapi.ClientController + require.Eventually(t, func() bool { + bbnCfg := fpcfg.BBNConfigToBabylonConfig(cfg.BabylonConfig) + bbnCl, err := bbnclient.New(&bbnCfg, logger) + if err != nil { + t.Logf("failed to create Babylon client: %v", err) + return false + } + bc, err = bbncc.NewBabylonController(bbnCl, cfg.BabylonConfig, &cfg.BTCNetParams, logger) + if err != nil { + t.Logf("failed to create Babylon controller: %v", err) + return false + } + err = bc.Start() + if err != nil { + t.Logf("failed to start Babylon controller: %v", err) + return false + } + return true + }, 5*time.Second, e2eutils.EventuallyPollTime) // 3. setup bcd node wh := NewBcdNodeHandler(t) err = wh.Start() require.NoError(t, err) - cfg.CosmwasmConfig = config.DefaultCosmwasmConfig() + cfg.CosmwasmConfig = fpcfg.DefaultCosmwasmConfig() cfg.CosmwasmConfig.KeyDirectory = wh.dataDir // make random contract address for now to avoid validation errors, later we will update it with the correct address in the test cfg.CosmwasmConfig.BtcStakingContractAddress = datagen.GenRandomAccount().GetAddress().String() @@ -103,44 +146,51 @@ func StartBcdTestManager(t *testing.T) *BcdTestManager { TxConfig: tempApp.TxConfig(), Amino: tempApp.LegacyAmino(), } - wcc, err := cwcc.NewCosmwasmConsumerController(cfg.CosmwasmConfig, encodingCfg, logger) - require.NoError(t, err) + + var wcc *cwcc.CosmwasmConsumerController + require.Eventually(t, func() bool { + wcc, err = cwcc.NewCosmwasmConsumerController(cfg.CosmwasmConfig, encodingCfg, logger) + if err != nil { + t.Logf("failed to create Cosmwasm consumer controller: %v", err) + return false + } + return true + }, 5*time.Second, e2eutils.EventuallyPollTime) // 4. prepare EOTS manager eotsHomeDir := filepath.Join(testDir, "eots-home") eotsCfg := eotsconfig.DefaultConfigWithHomePath(eotsHomeDir) - eh := e2eutils.NewEOTSServerHandler(t, logger, eotsCfg, eotsHomeDir) - eh.Start() - // wait for EOTS servers to start - // see https://github.com/babylonchain/finality-provider/pull/517 - var eotsCli *client.EOTSManagerGRpcClient - require.Eventually(t, func() bool { - eotsCli, err = client.NewEOTSManagerGRpcClient(cfg.EOTSManagerAddress) - return err == nil - }, 5*time.Second, time.Second, "Failed to create EOTS clients") + eotsCfg.RPCListener = fmt.Sprintf("127.0.0.1:%d", testutil.AllocateUniquePort(t)) + eotsCfg.Metrics.Port = testutil.AllocateUniquePort(t) + eh := e2eutils.NewEOTSServerHandler(t, eotsCfg, eotsHomeDir) + eh.Start(ctx) + cfg.RPCListener = fmt.Sprintf("127.0.0.1:%d", testutil.AllocateUniquePort(t)) + eotsCli, err := client.NewEOTSManagerGRpcClient(eotsCfg.RPCListener) + require.NoError(t, err) // 5. prepare finality-provider - fpdb, err := cfg.DatabaseConfig.GetDbBackend() + fpdb, err := cfg.DatabaseConfig.GetDBBackend() require.NoError(t, err) fpApp, err := service.NewFinalityProviderApp(cfg, bc, wcc, eotsCli, fpdb, logger) require.NoError(t, err) err = fpApp.Start() require.NoError(t, err) - // TODO: setup fp app after contract supports relevant queries - ctm := &BcdTestManager{ - BabylonHandler: bh, + BaseTestManager: &base_test_manager.BaseTestManager{ + BBNClient: bc.(*bbncc.BabylonController), + CovenantPrivKeys: covenantPrivKeys, + }, + manager: manager, FpConfig: cfg, - BBNClient: bc, BcdHandler: wh, BcdConsumerClient: wcc, EOTSServerHandler: eh, EOTSConfig: eotsCfg, Fpa: fpApp, EOTSClient: eotsCli, - CovenantPrivKeys: covenantPrivKeys, baseDir: testDir, + logger: logger, } ctm.WaitForServicesStart(t) @@ -173,73 +223,71 @@ func (ctm *BcdTestManager) WaitForServicesStart(t *testing.T) { func (ctm *BcdTestManager) Stop(t *testing.T) { err := ctm.Fpa.Stop() require.NoError(t, err) - err = ctm.BabylonHandler.Stop() + err = ctm.manager.ClearResources() require.NoError(t, err) - ctm.EOTSServerHandler.Stop() - ctm.BcdHandler.Stop(t) err = os.RemoveAll(ctm.baseDir) require.NoError(t, err) } -func (ctm *BcdTestManager) CreateConsumerFinalityProviders(t *testing.T, consumerId string, n int) []*service.FinalityProviderInstance { +// CreateConsumerFinalityProviders creates finality providers for a consumer chain +// and registers them in Babylon and consumer smart contract +func (ctm *BcdTestManager) CreateConsumerFinalityProviders(t *testing.T, consumerId string) *service.FinalityProviderInstance { app := ctm.Fpa cfg := app.GetConfig() keyName := cfg.BabylonConfig.Key // register all finality providers - fpPKs := make([]*bbntypes.BIP340PubKey, 0, n) - for i := 0; i < n; i++ { - moniker := e2eutils.MonikerPrefix + consumerId + "-" + strconv.Itoa(i) - commission := sdkmath.LegacyZeroDec() - desc := e2eutils.NewDescription(moniker) - - res, err := app.CreateFinalityProvider(keyName, consumerId, e2eutils.Passphrase, e2eutils.HdPath, nil, desc, &commission) - require.NoError(t, err) - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(res.FpInfo.BtcPkHex) - require.NoError(t, err) - fpPKs = append(fpPKs, fpPk) - _, err = app.RegisterFinalityProvider(fpPk.MarshalHex()) - require.NoError(t, err) - } + moniker := e2eutils.MonikerPrefix + consumerId + "-" + strconv.Itoa(0) + commission := sdkmath.LegacyZeroDec() + desc := e2eutils.NewDescription(moniker) - for i := 0; i < n; i++ { - // start - err := app.StartHandlingFinalityProvider(fpPKs[i], e2eutils.Passphrase) - require.NoError(t, err) - fpIns, err := app.GetFinalityProviderInstance(fpPKs[i]) - require.NoError(t, err) - require.True(t, fpIns.IsRunning()) - require.NoError(t, err) - } + eotsPk, err := ctm.EOTSClient.CreateKey(keyName, passphrase, hdPath) + require.NoError(t, err) + eotsPubKey, err := bbntypes.NewBIP340PubKey(eotsPk) + require.NoError(t, err) - // check finality providers on Babylon side + // inject fp in smart contract using admin + fpMsg := e2eutils.GenBtcStakingFpExecMsg(eotsPubKey.MarshalHex()) + fpMsgBytes, err := json.Marshal(fpMsg) + require.NoError(t, err) + _, err = ctm.BcdConsumerClient.ExecuteContract(fpMsgBytes) + require.NoError(t, err) + + // register fp in Babylon + _, err = app.CreateFinalityProvider(keyName, consumerId, passphrase, eotsPubKey, desc, &commission) + require.NoError(t, err) + + cfg.RPCListener = fmt.Sprintf("127.0.0.1:%d", testutil.AllocateUniquePort(t)) + cfg.Metrics.Port = testutil.AllocateUniquePort(t) + + err = app.StartFinalityProvider(eotsPubKey, passphrase) + require.NoError(t, err) + + fpIns, err := app.GetFinalityProviderInstance() + require.NoError(t, err) + require.True(t, fpIns.IsRunning()) + + // ensure finality providers are registered in smart contract require.Eventually(t, func() bool { - fps, err := ctm.BBNClient.QueryConsumerFinalityProviders(consumerId) + consumerFpsResp, err := ctm.BcdConsumerClient.QueryFinalityProviders() if err != nil { - t.Logf("failed to query finality providers from Babylon %s", err.Error()) + t.Logf("failed to query finality providers from consumer contract: %s", err.Error()) return false } - - if len(fps) != n { + if consumerFpsResp == nil { return false } - - for _, fp := range fps { - if !strings.Contains(fp.Description.Moniker, e2eutils.MonikerPrefix) { - return false - } - if !fp.Commission.Equal(sdkmath.LegacyZeroDec()) { - return false - } + if len(consumerFpsResp.Fps) != 1 { + return false + } + // verify each FP matches the expected public key + if consumerFpsResp.Fps[0].BtcPkHex != eotsPubKey.MarshalHex() { + return false } - return true }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) - fpInsList := app.ListFinalityProviderInstancesForChain(consumerId) - require.Equal(t, n, len(fpInsList)) - - t.Logf("the consumer test manager is running with %v finality-provider(s)", len(fpInsList)) + t.Logf("the consumer test manager is running with %v finality-provider(s)", 1) - return fpInsList + return fpIns } diff --git a/itest/cosmwasm/wasmd/wasmd_e2e_test.go b/itest/cosmwasm/wasmd/wasmd_e2e_test.go deleted file mode 100644 index 31696375..00000000 --- a/itest/cosmwasm/wasmd/wasmd_e2e_test.go +++ /dev/null @@ -1,145 +0,0 @@ -//go:build e2e_wasmd -// +build e2e_wasmd - -package e2etest_wasmd - -import ( - "encoding/json" - "math/rand" - "testing" - "time" - - "github.com/babylonlabs-io/babylon/testutil/datagen" - common "github.com/babylonlabs-io/finality-provider/itest" - sdk "github.com/cosmos/cosmos-sdk/types" - - sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" - "github.com/stretchr/testify/require" -) - -// TestConsumerFpDataInjection tests the finality provider lifecycle by manual injection using contract admin -// NOTE: this doesn't use the fp app or fp daemon -// 1. Upload Babylon and BTC staking contracts to wasmd chain -// 2. Instantiate Babylon contract with admin -// 3. Inject finality provider and delegation in BTC staking contract using admin -// 4. Inject public randomness commitment in BTC staking contract -// 5. Inject finality signature in BTC staking contract -func TestConsumerFpDataInjection(t *testing.T) { - ctm := StartWasmdTestManager(t) - defer ctm.Stop(t) - - // store babylon contract - babylonContractPath := "../../bytecode/babylon_contract.wasm" - err := ctm.WasmdConsumerClient.StoreWasmCode(babylonContractPath) - require.NoError(t, err) - babylonContractWasmId, err := ctm.WasmdConsumerClient.GetLatestCodeId() - require.NoError(t, err) - require.Equal(t, uint64(1), babylonContractWasmId) - - // store btc staking contract - btcStakingContractPath := "../../bytecode/btc_staking.wasm" - err = ctm.WasmdConsumerClient.StoreWasmCode(btcStakingContractPath) - require.NoError(t, err) - btcStakingContractWasmId, err := ctm.WasmdConsumerClient.GetLatestCodeId() - require.NoError(t, err) - require.Equal(t, uint64(2), btcStakingContractWasmId) - - // instantiate babylon contract with admin - btcStakingInitMsg := map[string]interface{}{ - "admin": ctm.WasmdConsumerClient.MustGetValidatorAddress(), - } - btcStakingInitMsgBytes, err := json.Marshal(btcStakingInitMsg) - require.NoError(t, err) - initMsg := map[string]interface{}{ - "network": "regtest", - "babylon_tag": "01020304", - "btc_confirmation_depth": 1, - "checkpoint_finalization_timeout": 2, - "notify_cosmos_zone": false, - "btc_staking_code_id": btcStakingContractWasmId, - "btc_staking_msg": btcStakingInitMsgBytes, - "admin": ctm.WasmdConsumerClient.MustGetValidatorAddress(), - } - initMsgBytes, err := json.Marshal(initMsg) - require.NoError(t, err) - err = ctm.WasmdConsumerClient.InstantiateContract(babylonContractWasmId, initMsgBytes) - require.NoError(t, err) - - // get btc staking contract address - resp, err := ctm.WasmdConsumerClient.ListContractsByCode(btcStakingContractWasmId, &sdkquerytypes.PageRequest{}) - require.NoError(t, err) - require.Len(t, resp.Contracts, 1) - btcStakingContractAddr := sdk.MustAccAddressFromBech32(resp.Contracts[0]) - // update the contract address in config because during setup we had used a random address which is not valid - ctm.WasmdConsumerClient.SetBtcStakingContractAddress(btcStakingContractAddr.String()) - - r := rand.New(rand.NewSource(time.Now().UnixNano())) - fpSk, _, err := datagen.GenRandomBTCKeyPair(r) - require.NoError(t, err) - randList, msgPub, err := common.GenCommitPubRandListMsg(r, fpSk, 1, 1000) - require.NoError(t, err) - - // inject fp and delegation in smart contract using admin - msg := common.GenBtcStakingExecMsg(msgPub.FpBtcPk.MarshalHex()) - msgBytes, err := json.Marshal(msg) - require.NoError(t, err) - _, err = ctm.WasmdConsumerClient.ExecuteContract(msgBytes) - require.NoError(t, err) - - // query finality providers in smart contract - consumerFpsResp, err := ctm.WasmdConsumerClient.QueryFinalityProviders() - require.NoError(t, err) - require.NotNil(t, consumerFpsResp) - require.Len(t, consumerFpsResp.Fps, 1) - require.Equal(t, msg.BtcStaking.NewFP[0].ConsumerID, consumerFpsResp.Fps[0].ConsumerId) - require.Equal(t, msg.BtcStaking.NewFP[0].BTCPKHex, consumerFpsResp.Fps[0].BtcPkHex) - - // query delegations in smart contract - consumerDelsResp, err := ctm.WasmdConsumerClient.QueryDelegations() - require.NoError(t, err) - require.NotNil(t, consumerDelsResp) - require.Len(t, consumerDelsResp.Delegations, 1) - require.Empty(t, consumerDelsResp.Delegations[0].UndelegationInfo.DelegatorUnbondingSig) // assert there is no delegator unbonding sig - require.Equal(t, msg.BtcStaking.ActiveDel[0].BTCPkHex, consumerDelsResp.Delegations[0].BtcPkHex) - require.Equal(t, msg.BtcStaking.ActiveDel[0].StartHeight, consumerDelsResp.Delegations[0].StartHeight) - require.Equal(t, msg.BtcStaking.ActiveDel[0].EndHeight, consumerDelsResp.Delegations[0].EndHeight) - require.Equal(t, msg.BtcStaking.ActiveDel[0].TotalSat, consumerDelsResp.Delegations[0].TotalSat) - require.Equal(t, msg.BtcStaking.ActiveDel[0].StakingTx, consumerDelsResp.Delegations[0].StakingTx) - require.Equal(t, msg.BtcStaking.ActiveDel[0].SlashingTx, consumerDelsResp.Delegations[0].SlashingTx) - - // ensure fp has voting power in smart contract - consumerFpsByPowerResp, err := ctm.WasmdConsumerClient.QueryFinalityProvidersByPower() - require.NoError(t, err) - require.NotNil(t, consumerFpsByPowerResp) - require.Len(t, consumerFpsByPowerResp.Fps, 1) - require.Equal(t, msg.BtcStaking.NewFP[0].BTCPKHex, consumerFpsByPowerResp.Fps[0].BtcPkHex) - require.Equal(t, consumerDelsResp.Delegations[0].TotalSat, consumerFpsByPowerResp.Fps[0].Power) - - // inject pub rand commitment in smart contract (admin is not required, although in the tests admin and sender are the same) - msg2 := common.GenPubRandomnessExecMsg( - msgPub.FpBtcPk.MarshalHex(), - msgPub.Commitment, - msgPub.Sig.MustMarshal(), - msgPub.StartHeight, - msgPub.NumPubRand, - ) - msgBytes2, err := json.Marshal(msg2) - require.NoError(t, err) - _, err = ctm.WasmdConsumerClient.ExecuteContract(msgBytes2) - require.NoError(t, err) - - // inject finality signature in smart contract (admin is not required, although in the tests admin and sender are the same) - wasmdNodeStatus, err := ctm.WasmdConsumerClient.GetCometNodeStatus() - require.NoError(t, err) - cometLatestHeight := wasmdNodeStatus.SyncInfo.LatestBlockHeight - finalitySigMsg := common.GenFinalitySigExecMsg(uint64(1), uint64(cometLatestHeight), randList, fpSk) - finalitySigMsgBytes, err := json.Marshal(finalitySigMsg) - require.NoError(t, err) - _, err = ctm.WasmdConsumerClient.ExecuteContract(finalitySigMsgBytes) - require.NoError(t, err) - fpSigsResponse, err := ctm.WasmdConsumerClient.QueryFinalitySignature(msgPub.FpBtcPk.MarshalHex(), uint64(cometLatestHeight)) - require.NoError(t, err) - require.NotNil(t, fpSigsResponse) - require.NotNil(t, fpSigsResponse.Signature) - require.Equal(t, finalitySigMsg.SubmitFinalitySignature.Signature, fpSigsResponse.Signature) -} diff --git a/itest/cosmwasm/wasmd/wasmd_handler.go b/itest/cosmwasm/wasmd/wasmd_handler.go deleted file mode 100644 index bc15c7be..00000000 --- a/itest/cosmwasm/wasmd/wasmd_handler.go +++ /dev/null @@ -1,330 +0,0 @@ -package e2etest_wasmd - -import ( - "bytes" - "encoding/json" - "fmt" - "log" - "os" - "os/exec" - "path/filepath" - "runtime" - "strconv" - "testing" - "time" - - common "github.com/babylonlabs-io/finality-provider/itest" - "github.com/stretchr/testify/require" -) - -const ( - wasmdRpcPort int = 2990 - wasmdP2pPort int = 2991 - wasmdChainID = "wasmd-test" -) - -type WasmdNodeHandler struct { - cmd *exec.Cmd - pidFile string - dataDir string -} - -func NewWasmdNodeHandler(t *testing.T) *WasmdNodeHandler { - testDir, err := common.BaseDir("ZWasmdTest") - require.NoError(t, err) - defer func() { - if err != nil { - err := os.RemoveAll(testDir) - require.NoError(t, err) - } - }() - - setupWasmd(t, testDir) - cmd := wasmdStartCmd(t, testDir) - return &WasmdNodeHandler{ - cmd: cmd, - pidFile: "", // empty for now, will be set after start - dataDir: testDir, - } -} - -func (w *WasmdNodeHandler) Start() error { - if err := w.start(); err != nil { - // try to cleanup after start error, but return original error - _ = w.cleanup() - return err - } - return nil -} - -func (w *WasmdNodeHandler) Stop(t *testing.T) { - err := w.stop() - // require.NoError(t, err) - // TODO: investigate why stopping the wasmd process is failing - if err != nil { - log.Printf("error stopping wasmd process: %v", err) - } - - err = w.cleanup() - require.NoError(t, err) -} - -func (w *WasmdNodeHandler) GetRpcUrl() string { - return fmt.Sprintf("http://localhost:%d", wasmdRpcPort) -} - -func (w *WasmdNodeHandler) GetHomeDir() string { - return w.dataDir -} - -func (w *WasmdNodeHandler) start() error { - if err := w.cmd.Start(); err != nil { - return err - } - - pid, err := os.Create(filepath.Join(w.dataDir, fmt.Sprintf("%s.pid", "wasmd"))) - if err != nil { - return err - } - - w.pidFile = pid.Name() - if _, err = fmt.Fprintf(pid, "%d\n", w.cmd.Process.Pid); err != nil { - return err - } - - if err := pid.Close(); err != nil { - return err - } - - return nil -} - -func (w *WasmdNodeHandler) stop() (err error) { - if w.cmd == nil || w.cmd.Process == nil { - // return if not properly initialized - // or error starting the process - return nil - } - - defer func() { - err = w.cmd.Wait() - }() - - if runtime.GOOS == "windows" { - return w.cmd.Process.Signal(os.Kill) - } - return w.cmd.Process.Signal(os.Interrupt) -} - -func (w *WasmdNodeHandler) cleanup() error { - if w.pidFile != "" { - if err := os.Remove(w.pidFile); err != nil { - log.Printf("unable to remove file %s: %v", w.pidFile, err) - } - } - - dirs := []string{ - w.dataDir, - } - var err error - for _, dir := range dirs { - if err = os.RemoveAll(dir); err != nil { - log.Printf("Cannot remove dir %s: %v", dir, err) - } - } - return nil -} - -func wasmdInit(homeDir string) error { - _, err := common.RunCommand("wasmd", "init", "--home", homeDir, "--chain-id", wasmdChainID, common.WasmMoniker) - return err -} - -func updateGenesisFile(homeDir string) error { - genesisPath := filepath.Join(homeDir, "config", "genesis.json") - sedCmd := fmt.Sprintf("sed -i. 's/\"stake\"/\"%s\"/' %s", common.WasmStake, genesisPath) - _, err := common.RunCommand("sh", "-c", sedCmd) - return err -} - -func wasmdKeysAdd(homeDir string) error { - _, err := common.RunCommand("wasmd", "keys", "add", "validator", "--home", homeDir, "--keyring-backend=test") - return err -} - -func addValidatorGenesisAccount(homeDir string) error { - _, err := common.RunCommand("wasmd", "genesis", "add-genesis-account", "validator", fmt.Sprintf("1000000000000%s,1000000000000%s", common.WasmStake, common.WasmFee), "--home", homeDir, "--keyring-backend=test") - return err -} - -func gentxValidator(homeDir string) error { - _, err := common.RunCommand("wasmd", "genesis", "gentx", "validator", fmt.Sprintf("250000000%s", common.WasmStake), "--chain-id="+wasmdChainID, "--amount="+fmt.Sprintf("250000000%s", common.WasmStake), "--home", homeDir, "--keyring-backend=test") - return err -} - -func collectGentxs(homeDir string) error { - _, err := common.RunCommand("wasmd", "genesis", "collect-gentxs", "--home", homeDir) - return err -} - -func setupWasmd(t *testing.T, testDir string) { - err := wasmdInit(testDir) - require.NoError(t, err) - - err = updateGenesisFile(testDir) - require.NoError(t, err) - - err = wasmdKeysAdd(testDir) - require.NoError(t, err) - - err = addValidatorGenesisAccount(testDir) - require.NoError(t, err) - - err = gentxValidator(testDir) - require.NoError(t, err) - - err = collectGentxs(testDir) - require.NoError(t, err) -} - -func wasmdStartCmd(t *testing.T, testDir string) *exec.Cmd { - args := []string{ - "start", - "--home", testDir, - "--rpc.laddr", fmt.Sprintf("tcp://0.0.0.0:%d", wasmdRpcPort), - "--p2p.laddr", fmt.Sprintf("tcp://0.0.0.0:%d", wasmdP2pPort), - "--log_level=info", - "--trace", - } - - f, err := os.Create(filepath.Join(testDir, "wasmd.log")) - require.NoError(t, err) - - cmd := exec.Command("wasmd", args...) - cmd.Stdout = f - cmd.Stderr = f - - return cmd -} - -type TxResponse struct { - TxHash string `json:"txhash"` - Events []struct { - Type string `json:"type"` - Attributes []struct { - Key string `json:"key"` - Value string `json:"value"` - } `json:"attributes"` - } `json:"events"` -} - -func (w *WasmdNodeHandler) StoreWasmCode(wasmFile string) (string, string, error) { - cmd := exec.Command("wasmd", "tx", "wasm", "store", wasmFile, - "--from", "validator", "--gas=auto", "--gas-prices=1ustake", "--gas-adjustment=1.3", "-y", "--chain-id", wasmdChainID, - "--node", w.GetRpcUrl(), "--home", w.GetHomeDir(), "-b", "sync", "-o", "json", "--keyring-backend=test") - - var out bytes.Buffer - cmd.Stdout = &out - cmd.Stderr = os.Stderr - - err := cmd.Run() - if err != nil { - return "", "", fmt.Errorf("error running wasmd store command: %v", err) - } - - // TODO: using struct from cometbft/sdk gives unmarshal error, investigate - var txResp TxResponse - resp := out.String() - err = json.Unmarshal([]byte(resp), &txResp) - if err != nil { - return "", "", fmt.Errorf("error unmarshalling store wasm response: %v", err) - } - - time.Sleep(3 * time.Second) - queryOutput, err := common.RunCommand("wasmd", "--node", w.GetRpcUrl(), "q", "tx", txResp.TxHash, "-o", "json") - if err != nil { - return "", "", fmt.Errorf("error querying transaction: %v", err) - } - - var queryResp TxResponse - err = json.Unmarshal(queryOutput, &queryResp) - if err != nil { - return "", "", fmt.Errorf("error unmarshalling query response: %v", err) - } - - var codeID, codeHash string - for _, event := range queryResp.Events { - if event.Type == "store_code" { - for _, attr := range event.Attributes { - if attr.Key == "code_id" { - codeID = attr.Value - } else if attr.Key == "code_checksum" { - codeHash = attr.Value - } - } - } - } - - if codeID == "" || codeHash == "" { - return "", "", fmt.Errorf("code ID or code checksum not found in transaction events") - } - - return codeID, codeHash, nil -} - -type StatusResponse struct { - SyncInfo struct { - LatestBlockHeight string `json:"latest_block_height"` - } `json:"sync_info"` -} - -func (w *WasmdNodeHandler) GetLatestBlockHeight() (int64, error) { - out, err := common.RunCommand("wasmd", "status", "--node", w.GetRpcUrl(), "--home", w.GetHomeDir()) - if err != nil { - return 0, fmt.Errorf("error running wasmd status command: %v", err) - } - - // TODO: using struct from cometbft/sdk gives unmarshal error, investigate - var statusResp StatusResponse - err = json.Unmarshal(out, &statusResp) - if err != nil { - return 0, fmt.Errorf("error unmarshalling status response: %v", err) - } - - height, err := strconv.ParseInt(statusResp.SyncInfo.LatestBlockHeight, 10, 64) - if err != nil { - return 0, fmt.Errorf("error parsing block height: %v", err) - } - - return height, nil -} - -type ListResponse struct { - CodeInfos []CodeInfo `json:"code_infos"` -} - -type CodeInfo struct { - CodeID string `json:"code_id"` -} - -func (w *WasmdNodeHandler) GetLatestCodeID() (string, error) { - output, err := common.RunCommand("wasmd", "--node", w.GetRpcUrl(), "q", "wasm", "list-code", "-o", "json") - if err != nil { - return "", fmt.Errorf("error running wasmd list-code command: %v", err) - } - - // TODO: using struct from cometbft/sdk gives unmarshal error, investigate - var listCodesResp ListResponse - err = json.Unmarshal(output, &listCodesResp) - if err != nil { - return "", fmt.Errorf("error unmarshalling list-code response: %v", err) - } - - // Get the latest code ID from the list - if len(listCodesResp.CodeInfos) == 0 { - return "", fmt.Errorf("no code info found in list-code response") - } - - latestCodeID := listCodesResp.CodeInfos[0].CodeID - return latestCodeID, nil -} diff --git a/itest/cosmwasm/wasmd/wasmd_test_manager.go b/itest/cosmwasm/wasmd/wasmd_test_manager.go deleted file mode 100644 index d7ba45b3..00000000 --- a/itest/cosmwasm/wasmd/wasmd_test_manager.go +++ /dev/null @@ -1,289 +0,0 @@ -package e2etest_wasmd - -import ( - "os" - "path/filepath" - "strconv" - "strings" - "testing" - "time" - - sdklogs "cosmossdk.io/log" - sdkmath "cosmossdk.io/math" - wasmapp "github.com/CosmWasm/wasmd/app" - wasmparams "github.com/CosmWasm/wasmd/app/params" - wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - "github.com/babylonlabs-io/babylon/testutil/datagen" - bbntypes "github.com/babylonlabs-io/babylon/types" - fpcc "github.com/babylonlabs-io/finality-provider/clientcontroller" - bbncc "github.com/babylonlabs-io/finality-provider/clientcontroller/babylon" - cwcc "github.com/babylonlabs-io/finality-provider/clientcontroller/cosmwasm" - "github.com/babylonlabs-io/finality-provider/eotsmanager/client" - eotsconfig "github.com/babylonlabs-io/finality-provider/eotsmanager/config" - fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" - "github.com/babylonlabs-io/finality-provider/finality-provider/service" - e2eutils "github.com/babylonlabs-io/finality-provider/itest" - "github.com/babylonlabs-io/finality-provider/types" - "github.com/btcsuite/btcd/btcec/v2" - dbm "github.com/cosmos/cosmos-db" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - "github.com/stretchr/testify/require" - "go.uber.org/zap" -) - -type WasmdTestManager struct { - BabylonHandler *e2eutils.BabylonNodeHandler - FpConfig *fpcfg.Config - BBNClient *bbncc.BabylonController - WasmdHandler *WasmdNodeHandler - WasmdConsumerClient *cwcc.CosmwasmConsumerController - StakingParams *types.StakingParams - EOTSServerHandler *e2eutils.EOTSServerHandler - EOTSConfig *eotsconfig.Config - Fpa *service.FinalityProviderApp - EOTSClient *client.EOTSManagerGRpcClient - CovenantPrivKeys []*btcec.PrivateKey - baseDir string -} - -func StartWasmdTestManager(t *testing.T) *WasmdTestManager { - // Setup consumer test manager - testDir, err := e2eutils.BaseDir("fpe2etest") - require.NoError(t, err) - - logger := zap.NewNop() - - // 1. generate covenant committee - covenantQuorum := 2 - numCovenants := 3 - covenantPrivKeys, covenantPubKeys := e2eutils.GenerateCovenantCommittee(numCovenants, t) - - // 2. prepare Babylon node - bh := e2eutils.NewBabylonNodeHandler(t, covenantQuorum, covenantPubKeys) - err = bh.Start() - require.NoError(t, err) - fpHomeDir := filepath.Join(testDir, "fp-home") - cfg := e2eutils.DefaultFpConfig(bh.GetNodeDataDir(), fpHomeDir) - bc, err := bbncc.NewBabylonController(cfg.BabylonConfig, &cfg.BTCNetParams, logger) - require.NoError(t, err) - - // 3. setup wasmd node - wh := NewWasmdNodeHandler(t) - err = wh.Start() - require.NoError(t, err) - cfg.CosmwasmConfig = fpcfg.DefaultCosmwasmConfig() - cfg.CosmwasmConfig.KeyDirectory = wh.dataDir - // TODO: make random contract addresses for now to avoid validation errors - // later in the e2e tests we would upload the contract and update the addresses - // investigate if there is a better way to handle this - cfg.CosmwasmConfig.BtcStakingContractAddress = datagen.GenRandomAccount().GetAddress().String() - cfg.ChainType = fpcc.WasmConsumerChainType - cfg.CosmwasmConfig.AccountPrefix = "wasm" - cfg.CosmwasmConfig.ChainID = wasmdChainID - tempApp := wasmapp.NewWasmApp(sdklogs.NewNopLogger(), dbm.NewMemDB(), nil, false, simtestutil.NewAppOptionsWithFlagHome(t.TempDir()), []wasmkeeper.Option{}) - encodingCfg := wasmparams.EncodingConfig{ - InterfaceRegistry: tempApp.InterfaceRegistry(), - Codec: tempApp.AppCodec(), - TxConfig: tempApp.TxConfig(), - Amino: tempApp.LegacyAmino(), - } - wcc, err := cwcc.NewCosmwasmConsumerController(cfg.CosmwasmConfig, encodingCfg, logger) - require.NoError(t, err) - - // 4. prepare EOTS manager - eotsHomeDir := filepath.Join(testDir, "eots-home") - eotsCfg := eotsconfig.DefaultConfigWithHomePath(eotsHomeDir) - eh := e2eutils.NewEOTSServerHandler(t, logger, eotsCfg, eotsHomeDir) - eh.Start() - // wait for EOTS servers to start - // see https://github.com/babylonchain/finality-provider/pull/517 - var eotsCli *client.EOTSManagerGRpcClient - require.Eventually(t, func() bool { - eotsCli, err = client.NewEOTSManagerGRpcClient(cfg.EOTSManagerAddress) - return err == nil - }, 5*time.Second, time.Second, "Failed to create EOTS clients") - - // 5. prepare finality-provider - fpdb, err := cfg.DatabaseConfig.GetDbBackend() - require.NoError(t, err) - fpApp, err := service.NewFinalityProviderApp(cfg, bc, wcc, eotsCli, fpdb, logger) - require.NoError(t, err) - err = fpApp.Start() - require.NoError(t, err) - - // TODO: setup fp app after contract supports relevant queries - - ctm := &WasmdTestManager{ - BabylonHandler: bh, - FpConfig: cfg, - BBNClient: bc, - WasmdHandler: wh, - WasmdConsumerClient: wcc, - EOTSServerHandler: eh, - EOTSConfig: eotsCfg, - Fpa: fpApp, - EOTSClient: eotsCli, - CovenantPrivKeys: covenantPrivKeys, - baseDir: testDir, - } - - ctm.WaitForServicesStart(t) - return ctm -} - -func (ctm *WasmdTestManager) WaitForServicesStart(t *testing.T) { - require.Eventually(t, func() bool { - params, err := ctm.BBNClient.QueryStakingParams() - if err != nil { - return false - } - ctm.StakingParams = params - return true - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) - t.Logf("Babylon node is started") - - // wait for wasmd to start - require.Eventually(t, func() bool { - blockHeight, err := ctm.WasmdHandler.GetLatestBlockHeight() - if err != nil { - t.Logf("failed to get latest block height from wasmd %s", err.Error()) - return false - } - return blockHeight > 2 - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) - t.Logf("Wasmd node is started") -} - -func (ctm *WasmdTestManager) Stop(t *testing.T) { - err := ctm.Fpa.Stop() - require.NoError(t, err) - err = ctm.BabylonHandler.Stop() - require.NoError(t, err) - ctm.EOTSServerHandler.Stop() - ctm.WasmdHandler.Stop(t) - err = os.RemoveAll(ctm.baseDir) - require.NoError(t, err) -} - -func StartConsumerManagerWithFps(t *testing.T, n int) (*WasmdTestManager, []*service.FinalityProviderInstance) { - ctm := StartWasmdTestManager(t) - app := ctm.Fpa - - for i := 0; i < n; i++ { - fpName := e2eutils.FpNamePrefix + strconv.Itoa(i) - moniker := e2eutils.MonikerPrefix + strconv.Itoa(i) - commission := sdkmath.LegacyZeroDec() - desc := e2eutils.NewDescription(moniker) - cfg := app.GetConfig() - _, err := service.CreateChainKey(cfg.BabylonConfig.KeyDirectory, cfg.BabylonConfig.ChainID, fpName, keyring.BackendTest, e2eutils.Passphrase, e2eutils.HdPath, "") - require.NoError(t, err) - res, err := app.CreateFinalityProvider(fpName, e2eutils.ChainID, e2eutils.Passphrase, e2eutils.HdPath, nil, desc, &commission) - require.NoError(t, err) - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(res.FpInfo.BtcPkHex) - require.NoError(t, err) - _, err = app.RegisterFinalityProvider(fpPk.MarshalHex()) - require.NoError(t, err) - err = app.StartHandlingFinalityProvider(fpPk, e2eutils.Passphrase) - require.NoError(t, err) - fpIns, err := app.GetFinalityProviderInstance(fpPk) - require.NoError(t, err) - require.True(t, fpIns.IsRunning()) - require.NoError(t, err) - - // check finality providers on Babylon side - require.Eventually(t, func() bool { - fps, err := ctm.BBNClient.QueryFinalityProviders() - if err != nil { - t.Logf("failed to query finality providers from Babylon %s", err.Error()) - return false - } - - if len(fps) != i+1 { - return false - } - - for _, fp := range fps { - if !strings.Contains(fp.Description.Moniker, e2eutils.MonikerPrefix) { - return false - } - if !fp.Commission.Equal(sdkmath.LegacyZeroDec()) { - return false - } - } - - return true - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) - } - - fpInsList := app.ListFinalityProviderInstances() - require.Equal(t, n, len(fpInsList)) - - t.Logf("the consumer test manager is running with %v finality-provider(s)", len(fpInsList)) - - return ctm, fpInsList -} - -func (ctm *WasmdTestManager) CreateFinalityProvidersForChain(t *testing.T, chainID string, n int) []*service.FinalityProviderInstance { - app := ctm.Fpa - cfg := app.GetConfig() - - // register all finality providers - fpPKs := make([]*bbntypes.BIP340PubKey, 0, n) - for i := 0; i < n; i++ { - fpName := e2eutils.FpNamePrefix + chainID + "-" + strconv.Itoa(i) - moniker := e2eutils.MonikerPrefix + chainID + "-" + strconv.Itoa(i) - commission := sdkmath.LegacyZeroDec() - desc := e2eutils.NewDescription(moniker) - _, err := service.CreateChainKey(cfg.BabylonConfig.KeyDirectory, chainID, fpName, keyring.BackendTest, e2eutils.Passphrase, e2eutils.HdPath, "") - require.NoError(t, err) - res, err := app.CreateFinalityProvider(fpName, chainID, e2eutils.Passphrase, e2eutils.HdPath, nil, desc, &commission) - require.NoError(t, err) - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(res.FpInfo.BtcPkHex) - require.NoError(t, err) - fpPKs = append(fpPKs, fpPk) - _, err = app.RegisterFinalityProvider(fpPk.MarshalHex()) - require.NoError(t, err) - } - - for i := 0; i < n; i++ { - // start - err := app.StartHandlingFinalityProvider(fpPKs[i], e2eutils.Passphrase) - require.NoError(t, err) - fpIns, err := app.GetFinalityProviderInstance(fpPKs[i]) - require.NoError(t, err) - require.True(t, fpIns.IsRunning()) - require.NoError(t, err) - } - - // check finality providers on Babylon side - require.Eventually(t, func() bool { - fps, err := ctm.BBNClient.QueryFinalityProviders() - if err != nil { - t.Logf("failed to query finality providers from Babylon %s", err.Error()) - return false - } - - if len(fps) != n { - return false - } - - for _, fp := range fps { - if !strings.Contains(fp.Description.Moniker, e2eutils.MonikerPrefix) { - return false - } - if !fp.Commission.Equal(sdkmath.LegacyZeroDec()) { - return false - } - } - - return true - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) - - fpInsList := app.ListFinalityProviderInstancesForChain(chainID) - require.Equal(t, n, len(fpInsList)) - - t.Logf("the consumer test manager is running with %v finality-provider(s)", len(fpInsList)) - - return fpInsList -} diff --git a/itest/eotsmanager_handler.go b/itest/eotsmanager_handler.go index 780f07e2..b7ddf528 100644 --- a/itest/eotsmanager_handler.go +++ b/itest/eotsmanager_handler.go @@ -1,9 +1,9 @@ package e2e_utils import ( + "context" "testing" - "github.com/lightningnetwork/lnd/signal" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -13,54 +13,39 @@ import ( ) type EOTSServerHandler struct { - t *testing.T - interceptor *signal.Interceptor - eotsServers []*service.Server + t *testing.T + eotsServer *service.Server + cfg *config.Config } -func NewEOTSServerHandlerMultiFP( - t *testing.T, logger *zap.Logger, configs []*config.Config, eotsHomeDirs []string, shutdownInterceptor *signal.Interceptor, -) *EOTSServerHandler { - eotsServers := make([]*service.Server, 0, len(configs)) - for i, cfg := range configs { - dbBackend, err := cfg.DatabaseConfig.GetDbBackend() - require.NoError(t, err) - - eotsManager, err := eotsmanager.NewLocalEOTSManager(eotsHomeDirs[i], cfg.KeyringBackend, dbBackend, logger) - require.NoError(t, err) +func NewEOTSServerHandler(t *testing.T, cfg *config.Config, eotsHomeDir string) *EOTSServerHandler { + dbBackend, err := cfg.DatabaseConfig.GetDBBackend() + require.NoError(t, err) + loggerConfig := zap.NewDevelopmentConfig() + loggerConfig.Level = zap.NewAtomicLevelAt(zap.InfoLevel) + logger, err := loggerConfig.Build() + require.NoError(t, err) + eotsManager, err := eotsmanager.NewLocalEOTSManager(eotsHomeDir, cfg.KeyringBackend, dbBackend, logger) + require.NoError(t, err) - eotsServer := service.NewEOTSManagerServer(cfg, logger, eotsManager, dbBackend, *shutdownInterceptor) - eotsServers = append(eotsServers, eotsServer) - } + eotsServer := service.NewEOTSManagerServer(cfg, logger, eotsManager, dbBackend) return &EOTSServerHandler{ - t: t, - eotsServers: eotsServers, - interceptor: shutdownInterceptor, + t: t, + eotsServer: eotsServer, + cfg: cfg, } } -func NewEOTSServerHandler(t *testing.T, logger *zap.Logger, cfg *config.Config, eotsHomeDir string) *EOTSServerHandler { - // create shutdown interceptor - shutdownInterceptor, err := signal.Intercept() - require.NoError(t, err) - // this need refactor of NewEOTSServerHandler - return NewEOTSServerHandlerMultiFP(t, logger, []*config.Config{cfg}, []string{eotsHomeDir}, &shutdownInterceptor) -} - -func (eh *EOTSServerHandler) Start() { - go eh.startServer() +func (eh *EOTSServerHandler) Config() *config.Config { + return eh.cfg } -func (eh *EOTSServerHandler) startServer() { - for _, eotsServer := range eh.eotsServers { - go func(eotsServer *service.Server) { - err := eotsServer.RunUntilShutdown() - require.NoError(eh.t, err) - }(eotsServer) - } +func (eh *EOTSServerHandler) Start(ctx context.Context) { + go eh.startServer(ctx) } -func (eh *EOTSServerHandler) Stop() { - eh.interceptor.RequestShutdown() +func (eh *EOTSServerHandler) startServer(ctx context.Context) { + err := eh.eotsServer.RunUntilShutdown(ctx) + require.NoError(eh.t, err) } diff --git a/itest/opstackl2/op_e2e_test.go b/itest/opstackl2/op_e2e_test.go index 901701c1..e8c44bee 100644 --- a/itest/opstackl2/op_e2e_test.go +++ b/itest/opstackl2/op_e2e_test.go @@ -4,6 +4,7 @@ package e2etest_op import ( + "context" "encoding/hex" "encoding/json" "math/rand" @@ -19,7 +20,9 @@ import ( // - CommitPubRandList // - QueryLastPublicRandCommit func TestPubRandCommitment(t *testing.T) { - ctm := StartOpL2ConsumerManager(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctm := StartOpL2ConsumerManager(t, ctx) defer ctm.Stop(t) // create and register Babylon FP and OP consumer FP @@ -52,7 +55,9 @@ func TestPubRandCommitment(t *testing.T) { // TestFinalitySigSubmission tests the consumer controller's function: // - SubmitBatchFinalitySigs func TestFinalitySigSubmission(t *testing.T) { - ctm := StartOpL2ConsumerManager(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctm := StartOpL2ConsumerManager(t, ctx) defer ctm.Stop(t) // create and register Babylon FP and OP consumer FP @@ -105,7 +110,9 @@ func TestFinalitySigSubmission(t *testing.T) { // TestFinalityProviderHasPower tests the consumer controller's function: // - QueryFinalityProviderHasPower func TestFinalityProviderHasPower(t *testing.T) { - ctm := StartOpL2ConsumerManager(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctm := StartOpL2ConsumerManager(t, ctx) defer ctm.Stop(t) // create and register Babylon FP and OP consumer FP diff --git a/itest/opstackl2/op_test_manager.go b/itest/opstackl2/op_test_manager.go index 812dd076..b6a0edbe 100644 --- a/itest/opstackl2/op_test_manager.go +++ b/itest/opstackl2/op_test_manager.go @@ -4,6 +4,7 @@ package e2etest_op import ( + "context" "encoding/json" "fmt" "os" @@ -12,8 +13,8 @@ import ( "time" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - bbncfg "github.com/babylonlabs-io/babylon/client/config" bbntypes "github.com/babylonlabs-io/babylon/types" + fpcc "github.com/babylonlabs-io/finality-provider/clientcontroller" bbncc "github.com/babylonlabs-io/finality-provider/clientcontroller/babylon" opcc "github.com/babylonlabs-io/finality-provider/clientcontroller/opstackl2" cwclient "github.com/babylonlabs-io/finality-provider/cosmwasmclient/client" @@ -22,23 +23,29 @@ import ( fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" "github.com/babylonlabs-io/finality-provider/finality-provider/service" e2eutils "github.com/babylonlabs-io/finality-provider/itest" + "github.com/babylonlabs-io/finality-provider/itest/container" base_test_manager "github.com/babylonlabs-io/finality-provider/itest/test-manager" "github.com/babylonlabs-io/finality-provider/metrics" + "github.com/babylonlabs-io/finality-provider/testutil" "github.com/babylonlabs-io/finality-provider/testutil/log" "github.com/babylonlabs-io/finality-provider/types" "github.com/btcsuite/btcd/btcec/v2" sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" "github.com/decred/dcrd/dcrec/secp256k1/v4" - "github.com/lightningnetwork/lnd/signal" + "github.com/ory/dockertest/v3" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) const ( - opFinalityGadgetContractPath = "../bytecode/op_finality_gadget_16f6154.wasm" opConsumerChainId = "op-stack-l2-706114" bbnAddrTopUpAmount = 100000000 + eventuallyWaitTimeOut = 5 * time.Minute + eventuallyPollTime = 500 * time.Millisecond + passphrase = "testpass" + hdPath = "" + opFinalityGadgetContractPath = "../bytecode/op_finality_gadget_16f6154.wasm" ) type BaseTestManager = base_test_manager.BaseTestManager @@ -46,21 +53,23 @@ type BaseTestManager = base_test_manager.BaseTestManager type OpL2ConsumerTestManager struct { BaseTestManager BaseDir string - BabylonHandler *e2eutils.BabylonNodeHandler + manager *container.Manager OpConsumerController *opcc.OPStackL2ConsumerController EOTSServerHandler *e2eutils.EOTSServerHandler BabylonFpApp *service.FinalityProviderApp ConsumerFpApp *service.FinalityProviderApp + BabylonEOTSClient *client.EOTSManagerGRpcClient ConsumerEOTSClient *client.EOTSManagerGRpcClient + logger *zap.Logger } // StartOpL2ConsumerManager // - starts Babylon node and wait for it starts // - deploys finality gadget cw contract // - creates and starts Babylon and consumer FPs without any FP instances -func StartOpL2ConsumerManager(t *testing.T) *OpL2ConsumerTestManager { +func StartOpL2ConsumerManager(t *testing.T, ctx context.Context) *OpL2ConsumerTestManager { // Setup base dir and logger - testDir, err := e2eutils.BaseDir("op-fp-e2e-test") + testDir, err := base_test_manager.TempDir(t, "op-fp-e2e-test-*") require.NoError(t, err) // setup logger @@ -70,22 +79,13 @@ func StartOpL2ConsumerManager(t *testing.T) *OpL2ConsumerTestManager { require.NoError(t, err) // start Babylon node - babylonHandler, covenantPrivKeys := startBabylonNode(t) + manager, babylond, covenantPrivKeys, keyDir := startBabylonNode(t) // wait for Babylon node starts b/c we will fund the FP address with babylon node - babylonController, stakingParams := waitForBabylonNodeStart(t, testDir, logger, babylonHandler) - - // register consumer chain to Babylon - _, err = babylonController.RegisterConsumerChain( - opConsumerChainId, - "OP consumer chain", - "Some description about the chain", - ) - require.NoError(t, err) - t.Logf(log.Prefix("Register consumer %s to Babylon"), opConsumerChainId) + babylonController, _ := waitForBabylonNodeStart(t, keyDir, testDir, logger, manager, babylond) // create cosmwasm client - consumerFpCfg, opConsumerCfg := createConsumerFpConfig(t, testDir, babylonHandler) + consumerFpCfg, opConsumerCfg := createConsumerFpConfig(t, testDir, manager, babylond) cwConfig := opConsumerCfg.ToCosmwasmConfig() cwClient, err := opcc.NewCwClient(&cwConfig, logger) require.NoError(t, err) @@ -94,6 +94,16 @@ func StartOpL2ConsumerManager(t *testing.T) *OpL2ConsumerTestManager { opFinalityGadgetAddress := deployCwContract(t, cwClient) t.Logf(log.Prefix("op-finality-gadget contract address: %s"), opFinalityGadgetAddress) + // register consumer chain to Babylon + _, err = babylonController.RegisterConsumerChain( + opConsumerChainId, + "OP consumer chain", + "Some description about the chain", + opFinalityGadgetAddress, + ) + require.NoError(t, err) + t.Logf(log.Prefix("Register consumer %s to Babylon"), opConsumerChainId) + // update opConsumerCfg with opFinalityGadgetAddress opConsumerCfg.OPFinalityGadgetAddress = opFinalityGadgetAddress @@ -101,14 +111,10 @@ func StartOpL2ConsumerManager(t *testing.T) *OpL2ConsumerTestManager { consumerFpCfg.OPStackL2Config = opConsumerCfg // create Babylon FP config - babylonFpCfg := createBabylonFpConfig(t, testDir, babylonHandler) - - // create shutdown interceptor - shutdownInterceptor, err := signal.Intercept() - require.NoError(t, err) + babylonFpCfg := createBabylonFpConfig(t, keyDir, testDir, manager, babylond) // create EOTS handler and EOTS gRPC clients for Babylon and consumer - eotsHandler, EOTSClients := base_test_manager.StartEotsManagers(t, logger, testDir, babylonFpCfg, consumerFpCfg, &shutdownInterceptor) + eotsHandler, EOTSClients := base_test_manager.StartEotsManagers(t, ctx, logger, testDir, babylonFpCfg, consumerFpCfg) // create Babylon consumer controller babylonConsumerController, err := bbncc.NewBabylonConsumerController(babylonFpCfg.BabylonConfig, &babylonFpCfg.BTCNetParams, logger) @@ -130,55 +136,79 @@ func StartOpL2ConsumerManager(t *testing.T) *OpL2ConsumerTestManager { BaseTestManager: BaseTestManager{ BBNClient: babylonController, CovenantPrivKeys: covenantPrivKeys, - StakingParams: stakingParams, }, BaseDir: testDir, - BabylonHandler: babylonHandler, + manager: manager, OpConsumerController: opConsumerController, EOTSServerHandler: eotsHandler, BabylonFpApp: babylonFpApp, ConsumerFpApp: consumerFpApp, + BabylonEOTSClient: EOTSClients[0], ConsumerEOTSClient: EOTSClients[1], + logger: logger, } return ctm } -func startBabylonNode(t *testing.T) (*e2eutils.BabylonNodeHandler, []*secp256k1.PrivateKey) { +func startBabylonNode(t *testing.T) (*container.Manager, *dockertest.Resource, []*secp256k1.PrivateKey, string) { // generate covenant committee covenantQuorum := 2 numCovenants := 3 covenantPrivKeys, covenantPubKeys := e2eutils.GenerateCovenantCommittee(numCovenants, t) - bh := e2eutils.NewBabylonNodeHandler(t, covenantQuorum, covenantPubKeys) - err := bh.Start() + // Create container manager + manager, err := container.NewManager(t) + require.NoError(t, err) + + // Create temp dir for babylon node + babylonDir, err := base_test_manager.TempDir(t, "babylon-test-*") require.NoError(t, err) - return bh, covenantPrivKeys + + // Start babylon node in docker + babylond, err := manager.RunBabylondResource(t, babylonDir, covenantQuorum, covenantPubKeys) + require.NoError(t, err) + + keyDir := filepath.Join(babylonDir, "node0", "babylond") + + return manager, babylond, covenantPrivKeys, keyDir } func waitForBabylonNodeStart( t *testing.T, + keyDir string, testDir string, logger *zap.Logger, - babylonHandler *e2eutils.BabylonNodeHandler, + manager *container.Manager, + babylond *dockertest.Resource, ) (*bbncc.BabylonController, *types.StakingParams) { // create Babylon FP config - babylonFpCfg := createBabylonFpConfig(t, testDir, babylonHandler) + babylonFpCfg := createBabylonFpConfig(t, keyDir, testDir, manager, babylond) // create Babylon controller - babylonController, err := bbncc.NewBabylonController(babylonFpCfg.BabylonConfig, &babylonFpCfg.BTCNetParams, logger) - require.NoError(t, err) + var babylonController *bbncc.BabylonController + require.Eventually(t, func() bool { + var err error + bc, err := fpcc.NewBabylonController(babylonFpCfg, logger) + if err != nil { + t.Logf("Failed to create Babylon controller: %v", err) + return false + } + babylonController = bc.(*bbncc.BabylonController) + return true + }, 30*time.Second, 1*time.Second) var stakingParams *types.StakingParams // wait for Babylon node starts require.Eventually(t, func() bool { params, err := babylonController.QueryStakingParams() if err != nil { + t.Logf("Failed to query staking params: %v", err) return false } stakingParams = params return true - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) + }, 30*time.Second, 1*time.Second) t.Logf("Babylon node is started, chain_id: %s", babylonController.GetBBNClient().GetConfig().ChainID) return babylonController, stakingParams @@ -186,70 +216,91 @@ func waitForBabylonNodeStart( func createBabylonFpConfig( t *testing.T, + keyDir string, testDir string, - bh *e2eutils.BabylonNodeHandler, + manager *container.Manager, + babylond *dockertest.Resource, ) *fpcfg.Config { fpHomeDir := filepath.Join(testDir, "babylon-fp-home") t.Logf(log.Prefix("Babylon FP home dir: %s"), fpHomeDir) + cfg := e2eutils.DefaultFpConfigWithPorts( - bh.GetNodeDataDir(), + keyDir, // This is the path inside docker container fpHomeDir, fpcfg.DefaultRPCPort, metrics.DefaultFpConfig().Port, eotsconfig.DefaultRPCPort, ) + + // Update ports with dynamically allocated ones from docker + cfg.BabylonConfig.RPCAddr = fmt.Sprintf("http://localhost:%s", babylond.GetPort("26657/tcp")) + cfg.BabylonConfig.GRPCAddr = fmt.Sprintf("localhost:%s", babylond.GetPort("9090/tcp")) + return cfg } func createConsumerFpConfig( t *testing.T, testDir string, - bh *e2eutils.BabylonNodeHandler, + manager *container.Manager, + babylond *dockertest.Resource, ) (*fpcfg.Config, *fpcfg.OPStackL2Config) { fpHomeDir := filepath.Join(testDir, "consumer-fp-home") t.Logf(log.Prefix("Consumer FP home dir: %s"), fpHomeDir) cfg := e2eutils.DefaultFpConfigWithPorts( + fpHomeDir, // This is the path inside docker container fpHomeDir, - fpHomeDir, - fpcfg.DefaultRPCPort+1, - metrics.DefaultFpConfig().Port+1, - eotsconfig.DefaultRPCPort+1, + fpcfg.DefaultRPCPort, + metrics.DefaultFpConfig().Port, + eotsconfig.DefaultRPCPort, ) + // Update ports with dynamically allocated ones from docker + cfg.BabylonConfig.RPCAddr = fmt.Sprintf("http://localhost:%s", babylond.GetPort("26657/tcp")) + cfg.BabylonConfig.GRPCAddr = fmt.Sprintf("localhost:%s", babylond.GetPort("9090/tcp")) + // create consumer FP key/address - fpBbnKeyInfo, err := service.CreateChainKey( + fpBbnKeyInfo, err := testutil.CreateChainKey( cfg.BabylonConfig.KeyDirectory, cfg.BabylonConfig.ChainID, cfg.BabylonConfig.Key, cfg.BabylonConfig.KeyringBackend, - e2eutils.Passphrase, - e2eutils.HdPath, + passphrase, + hdPath, "", ) require.NoError(t, err) // fund the consumer FP address t.Logf(log.Prefix("Funding %dubbn to %s"), bbnAddrTopUpAmount, fpBbnKeyInfo.AccAddress.String()) - err = bh.BabylonNode.TxBankSend( - fpBbnKeyInfo.AccAddress.String(), - fmt.Sprintf("%dubbn", bbnAddrTopUpAmount), - ) + _, _, err = manager.BabylondTxBankSend(t, fpBbnKeyInfo.AccAddress.String(), fmt.Sprintf("%dubbn", bbnAddrTopUpAmount), "node0") require.NoError(t, err) // check consumer FP address balance require.Eventually(t, func() bool { - balance, err := bh.BabylonNode.CheckAddrBalance(fpBbnKeyInfo.AccAddress.String()) + out, _, err := manager.ExecBabylondCmd(t, []string{"query", "bank", "balances", fpBbnKeyInfo.AccAddress.String(), "--output=json"}) if err != nil { - t.Logf("Error checking balance: %v", err) return false } - return balance == bbnAddrTopUpAmount - }, 30*time.Second, 2*time.Second, fmt.Sprintf("failed to top up %s", fpBbnKeyInfo.AccAddress.String())) - t.Logf(log.Prefix("Sent %dubbn to %s"), bbnAddrTopUpAmount, fpBbnKeyInfo.AccAddress.String()) + var balances struct { + Balances []struct { + Denom string `json:"denom"` + Amount string `json:"amount"` + } `json:"balances"` + } + if err := json.Unmarshal(out.Bytes(), &balances); err != nil { + return false + } + for _, bal := range balances.Balances { + if bal.Denom == "ubbn" && bal.Amount == fmt.Sprintf("%d", bbnAddrTopUpAmount) { + return true + } + } + return false + }, eventuallyWaitTimeOut, eventuallyPollTime) // set consumer FP config - dc := bbncfg.DefaultBabylonConfig() opConsumerCfg := &fpcfg.OPStackL2Config{ // it will be updated later OPFinalityGadgetAddress: "", @@ -258,21 +309,22 @@ func createConsumerFpConfig( // the value does not matter for the test BabylonFinalityGadgetRpc: "127.0.0.1:50051", Key: cfg.BabylonConfig.Key, - ChainID: dc.ChainID, - RPCAddr: dc.RPCAddr, - GRPCAddr: dc.GRPCAddr, - AccountPrefix: dc.AccountPrefix, - KeyringBackend: dc.KeyringBackend, + ChainID: cfg.BabylonConfig.ChainID, + RPCAddr: cfg.BabylonConfig.RPCAddr, + GRPCAddr: cfg.BabylonConfig.GRPCAddr, + AccountPrefix: cfg.BabylonConfig.AccountPrefix, + KeyringBackend: cfg.BabylonConfig.KeyringBackend, KeyDirectory: cfg.BabylonConfig.KeyDirectory, GasAdjustment: 1.5, GasPrices: "0.002ubbn", - Debug: dc.Debug, - Timeout: dc.Timeout, + Debug: cfg.BabylonConfig.Debug, + Timeout: cfg.BabylonConfig.Timeout, BlockTimeout: 1 * time.Minute, - OutputFormat: dc.OutputFormat, - SignModeStr: dc.SignModeStr, + OutputFormat: cfg.BabylonConfig.OutputFormat, + SignModeStr: cfg.BabylonConfig.SignModeStr, } cfg.OPStackL2Config = opConsumerCfg + return cfg, opConsumerCfg } @@ -283,7 +335,7 @@ func deployCwContract(t *testing.T, cwClient *cwclient.Client) string { var codeId uint64 require.Eventually(t, func() bool { - codeId, _ = cwClient.GetLatestCodeId() + codeId, _ = cwClient.GetLatestCodeID() return codeId > 0 }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) require.Equal(t, uint64(1), codeId, "first deployed contract code_id should be 1") @@ -312,25 +364,61 @@ func deployCwContract(t *testing.T, cwClient *cwclient.Client) string { } func (ctm *OpL2ConsumerTestManager) setupBabylonAndConsumerFp(t *testing.T) []*bbntypes.BIP340PubKey { + babylonCfg := ctm.BabylonFpApp.GetConfig() + babylonKeyName := babylonCfg.BabylonConfig.Key + // create and register Babylon FP - babylonFpPk := base_test_manager.CreateAndRegisterFinalityProvider(t, ctm.BabylonFpApp, e2eutils.ChainID) + eotsPk, err := ctm.BabylonEOTSClient.CreateKey(babylonKeyName, passphrase, hdPath) + require.NoError(t, err) + babylonFpPk, err := bbntypes.NewBIP340PubKey(eotsPk) + require.NoError(t, err) + base_test_manager.CreateAndRegisterFinalityProvider(t, ctm.BabylonFpApp, e2eutils.ChainID, babylonFpPk) t.Logf(log.Prefix("Registered Finality Provider %s for %s"), babylonFpPk.MarshalHex(), e2eutils.ChainID) // wait for Babylon FP registration require.Eventually(t, func() bool { - _, err := ctm.BBNClient.QueryFinalityProviders() - return err == nil + fps, err := ctm.BBNClient.QueryFinalityProviders() + return err == nil && len(fps) > 0 }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime, "Failed to wait for Babylon FP registration") + consumerCfg := ctm.ConsumerFpApp.GetConfig() + consumerKeyName := consumerCfg.OPStackL2Config.Key + "2" + // create and register consumer FP - consumerFpPk := base_test_manager.CreateAndRegisterFinalityProvider(t, ctm.ConsumerFpApp, opConsumerChainId) + consumerEotsPk, err := ctm.ConsumerEOTSClient.CreateKey(consumerKeyName, passphrase, hdPath) + require.NoError(t, err) + consumerFpPk, err := bbntypes.NewBIP340PubKey(consumerEotsPk) + require.NoError(t, err) + base_test_manager.CreateAndRegisterFinalityProvider(t, ctm.ConsumerFpApp, opConsumerChainId, consumerFpPk) t.Logf(log.Prefix("Registered Finality Provider %s for %s"), consumerFpPk.MarshalHex(), opConsumerChainId) + // wait for Babylon FP registration + require.Eventually(t, func() bool { + fps, err := ctm.BBNClient.QueryFinalityProviders() + if err != nil { + t.Logf("Failed to query finality providers: %v", err) + return false + } + if len(fps) < 1 { + t.Logf("Expected at least 1 Babylon finality provider, got %d", len(fps)) + return false + } + return true + }, 30*time.Second, 1*time.Second, "Failed to wait for Babylon FP registration") + // wait for consumer FP registration require.Eventually(t, func() bool { - _, err := ctm.BBNClient.QueryConsumerFinalityProviders(opConsumerChainId) - return err == nil - }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime, "Failed to wait for consumer FP registration") + fps, err := ctm.BBNClient.QueryConsumerFinalityProviders(opConsumerChainId) + if err != nil { + t.Logf("Failed to query finality providers: %v", err) + return false + } + if len(fps) < 1 { + t.Logf("Expected at least 1 consumer finality provider, got %d", len(fps)) + return false + } + return true + }, 30*time.Second, 1*time.Second, "Failed to wait for consumer FP registration") return []*bbntypes.BIP340PubKey{babylonFpPk, consumerFpPk} } @@ -343,10 +431,10 @@ func (ctm *OpL2ConsumerTestManager) getConsumerFpInstance( fpStore := ctm.ConsumerFpApp.GetFinalityProviderStore() pubRandStore := ctm.ConsumerFpApp.GetPubRandProofStore() bc := ctm.BabylonFpApp.GetBabylonController() - logger := ctm.ConsumerFpApp.Logger() - fpInstance, err := service.TestNewUnregisteredFinalityProviderInstance( + + fpInstance, err := service.NewFinalityProviderInstance( consumerFpPk, fpCfg, fpStore, pubRandStore, bc, ctm.OpConsumerController, ctm.ConsumerEOTSClient, - metrics.NewFpMetrics(), "", make(chan<- *service.CriticalError), logger) + metrics.NewFpMetrics(), "", make(chan<- *service.CriticalError), ctm.logger) require.NoError(t, err) return fpInstance } @@ -372,11 +460,9 @@ func (ctm *OpL2ConsumerTestManager) queryCwContract( t *testing.T, queryMsg map[string]interface{}, ) *wasmtypes.QuerySmartContractStateResponse { - logger := ctm.ConsumerFpApp.Logger() - // create cosmwasm client cwConfig := ctm.OpConsumerController.Cfg.ToCosmwasmConfig() - cwClient, err := opcc.NewCwClient(&cwConfig, logger) + cwClient, err := opcc.NewCwClient(&cwConfig, ctm.logger) require.NoError(t, err) // marshal query message @@ -402,9 +488,8 @@ func (ctm *OpL2ConsumerTestManager) Stop(t *testing.T) { require.NoError(t, err) err = ctm.ConsumerFpApp.Stop() require.NoError(t, err) - err = ctm.BabylonHandler.Stop() + err = ctm.manager.ClearResources() require.NoError(t, err) - ctm.EOTSServerHandler.Stop() err = os.RemoveAll(ctm.BaseDir) require.NoError(t, err) } diff --git a/itest/test-manager/base_test_manager.go b/itest/test-manager/base_test_manager.go index a85d6234..84e5cb41 100644 --- a/itest/test-manager/base_test_manager.go +++ b/itest/test-manager/base_test_manager.go @@ -1,9 +1,11 @@ package test_manager import ( + "context" "encoding/hex" "fmt" "math/rand" + "os" "path/filepath" "testing" "time" @@ -18,6 +20,7 @@ import ( btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" ckpttypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" + fpcc "github.com/babylonlabs-io/finality-provider/clientcontroller" "github.com/babylonlabs-io/finality-provider/clientcontroller/api" bbncc "github.com/babylonlabs-io/finality-provider/clientcontroller/babylon" "github.com/babylonlabs-io/finality-provider/eotsmanager/client" @@ -27,20 +30,17 @@ import ( "github.com/babylonlabs-io/finality-provider/finality-provider/service" e2eutils "github.com/babylonlabs-io/finality-provider/itest" "github.com/babylonlabs-io/finality-provider/metrics" - "github.com/babylonlabs-io/finality-provider/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" sdk "github.com/cosmos/cosmos-sdk/types" sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" - "github.com/lightningnetwork/lnd/signal" "github.com/stretchr/testify/require" "go.uber.org/zap" ) type BaseTestManager struct { BBNClient *bbncc.BabylonController - StakingParams *types.StakingParams CovenantPrivKeys []*btcec.PrivateKey } @@ -61,14 +61,14 @@ type TestDelegationData struct { } func (tm *BaseTestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.PublicKey, stakingTime uint16, stakingAmount int64) *TestDelegationData { - params := tm.StakingParams + params, err := tm.BBNClient.QueryStakingParams() + require.NoError(t, err) r := rand.New(rand.NewSource(time.Now().UnixNano())) // delegator BTC key pairs, staking tx and slashing tx delBtcPrivKey, delBtcPubKey, err := datagen.GenRandomBTCKeyPair(r) require.NoError(t, err) - unbondingTime := uint16(tm.StakingParams.MinimumUnbondingTime()) + 1 testStakingInfo := datagen.GenBTCStakingSlashingInfo( r, t, @@ -81,7 +81,7 @@ func (tm *BaseTestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.Publ stakingAmount, params.SlashingPkScript, params.SlashingRate, - unbondingTime, + uint16(params.UnbondingTime), ) stakerAddr := tm.BBNClient.GetKeyAddress() @@ -142,11 +142,11 @@ func (tm *BaseTestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.Publ params.CovenantPks, params.CovenantQuorum, wire.NewOutPoint(&stakingTxHash, 0), - unbondingTime, + uint16(params.UnbondingTime), unbondingValue, params.SlashingPkScript, params.SlashingRate, - unbondingTime, + uint16(params.UnbondingTime), ) unbondingTxMsg := testUnbondingInfo.UnbondingTx @@ -176,7 +176,7 @@ func (tm *BaseTestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.Publ testStakingInfo.SlashingTx, delegatorSig, serializedUnbondingTx, - uint32(unbondingTime), + uint32(params.UnbondingTime), unbondingValue, testUnbondingInfo.SlashingTx, unbondingSig) @@ -307,7 +307,8 @@ func (tm *BaseTestManager) InsertCovenantSigForDelegation(t *testing.T, btcDel * stakingMsgTx, err := bbntypes.NewBTCTxFromBytes(stakingTx) require.NoError(t, err) - params := tm.StakingParams + params, err := tm.BBNClient.QueryStakingParams() + require.NoError(t, err) var fpKeys []*btcec.PublicKey for _, v := range btcDel.FpBtcPkList { @@ -319,7 +320,7 @@ func (tm *BaseTestManager) InsertCovenantSigForDelegation(t *testing.T, btcDel * fpKeys, params.CovenantPks, params.CovenantQuorum, - btcDel.GetStakingTime(), + uint16(btcDel.EndHeight-btcDel.StartHeight), btcutil.Amount(btcDel.TotalSat), e2eutils.BtcNetworkParams, ) @@ -330,7 +331,6 @@ func (tm *BaseTestManager) InsertCovenantSigForDelegation(t *testing.T, btcDel * idx, err := bbntypes.GetOutputIdxInBTCTx(stakingMsgTx, stakingInfo.StakingOutput) require.NoError(t, err) - require.NoError(t, err) slashingPathInfo, err := stakingInfo.SlashingPathSpendInfo() require.NoError(t, err) @@ -459,6 +459,7 @@ func (tm *BaseTestManager) FinalizeUntilEpoch(t *testing.T, epoch uint64) { if err != nil { return false } + return epoch <= lastSealedCkpt.RawCheckpoint.EpochNum }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) @@ -536,8 +537,10 @@ func (tm *BaseTestManager) FinalizeUntilEpoch(t *testing.T, epoch uint64) { lastFinalizedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Finalized) if err != nil { t.Logf("failed to get last finalized epoch: %v", err) + return false } + return epoch <= lastFinalizedCkpt.RawCheckpoint.EpochNum }, e2eutils.EventuallyWaitTimeOut, e2eutils.EventuallyPollTime) @@ -546,11 +549,11 @@ func (tm *BaseTestManager) FinalizeUntilEpoch(t *testing.T, epoch uint64) { func StartEotsManagers( t *testing.T, + ctx context.Context, logger *zap.Logger, testDir string, babylonFpCfg *fpcfg.Config, consumerFpCfg *fpcfg.Config, - shutdownInterceptor *signal.Interceptor, ) (*e2eutils.EOTSServerHandler, []*eotsclient.EOTSManagerGRpcClient) { fpCfgs := []*fpcfg.Config{babylonFpCfg, consumerFpCfg} eotsClients := make([]*eotsclient.EOTSManagerGRpcClient, len(fpCfgs)) @@ -565,8 +568,8 @@ func StartEotsManagers( eotsConfigs[i] = eotsCfg } - eh := e2eutils.NewEOTSServerHandlerMultiFP(t, logger, eotsConfigs, eotsHomeDirs, shutdownInterceptor) - eh.Start() + eh := e2eutils.NewEOTSServerHandler(t, eotsConfigs[0], eotsHomeDirs[0]) + eh.Start(ctx) // create EOTS clients for i := 0; i < len(fpCfgs); i++ { @@ -578,12 +581,15 @@ func StartEotsManagers( eotsCli, err = eotsclient.NewEOTSManagerGRpcClient(fpCfgs[i].EOTSManagerAddress) if err != nil { t.Logf("Error creating EOTS client: %v", err) + return false } eotsClients[i] = eotsCli + return true }, 5*time.Second, time.Second, "Failed to create EOTS client") } + return eh, eotsClients } @@ -594,43 +600,52 @@ func CreateAndStartFpApp( cc api.ConsumerController, eotsCli *client.EOTSManagerGRpcClient, ) *service.FinalityProviderApp { - bc, err := bbncc.NewBabylonController(cfg.BabylonConfig, &cfg.BTCNetParams, logger) + bc, err := fpcc.NewBabylonController(cfg, logger) require.NoError(t, err) - fpdb, err := cfg.DatabaseConfig.GetDbBackend() + fpdb, err := cfg.DatabaseConfig.GetDBBackend() require.NoError(t, err) fpApp, err := service.NewFinalityProviderApp(cfg, bc, cc, eotsCli, fpdb, logger) require.NoError(t, err) - err = fpApp.StartWithoutSyncFpStatus() + err = fpApp.Start() require.NoError(t, err) return fpApp } -func CreateAndRegisterFinalityProvider(t *testing.T, fpApp *service.FinalityProviderApp, chainId string) *bbntypes.BIP340PubKey { +func CreateAndRegisterFinalityProvider(t *testing.T, fpApp *service.FinalityProviderApp, chainId string, eotsPk *bbntypes.BIP340PubKey) { fpCfg := fpApp.GetConfig() keyName := fpCfg.BabylonConfig.Key moniker := fmt.Sprintf("%s-%s", chainId, e2eutils.MonikerPrefix) commission := sdkmath.LegacyZeroDec() desc := e2eutils.NewDescription(moniker) - res, err := fpApp.CreateFinalityProvider( + _, err := fpApp.CreateFinalityProvider( keyName, chainId, e2eutils.Passphrase, - e2eutils.HdPath, - nil, + eotsPk, desc, &commission, ) require.NoError(t, err) +} - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(res.FpInfo.BtcPkHex) - require.NoError(t, err) +func TempDir(t *testing.T, pattern string) (string, error) { + tempName, err := os.MkdirTemp(os.TempDir(), pattern) + if err != nil { + return "", err + } - _, err = fpApp.RegisterFinalityProvider(fpPk.MarshalHex()) - require.NoError(t, err) - return fpPk + t.Cleanup(func() { + _ = os.RemoveAll(tempName) + }) + + if err = os.Chmod(tempName, 0755); err != nil { + return "", err + } + + return tempName, nil } diff --git a/itest/test_helpers.go b/itest/test_helpers.go index a4d09aea..0a76ff3d 100644 --- a/itest/test_helpers.go +++ b/itest/test_helpers.go @@ -174,6 +174,8 @@ func genRandomBtcDelegation() (*bstypes.Params, cosmwasm.ActiveBtcDelegation) { covenantSigners := covenantSKs[:covenantQuorum] // construct the BTC delegation with everything + startHeight, endHeight := uint32(1), uint32(1000+stakingTimeBlocks) + stakingTime := endHeight - startHeight btcDel, err := datagen.GenRandomBTCDelegation( r, t, @@ -184,8 +186,9 @@ func genRandomBtcDelegation() (*bstypes.Params, cosmwasm.ActiveBtcDelegation) { covenantPKs, covenantQuorum, slashingPkScript, - 1, - uint64(1000+stakingTimeBlocks), + stakingTime, + startHeight, + endHeight, uint64(stakingValue), slashingRate, slashingChangeLockTime, diff --git a/itest/utils.go b/itest/utils.go index 4b81e6e4..eb2a02c3 100644 --- a/itest/utils.go +++ b/itest/utils.go @@ -49,9 +49,7 @@ func BaseDir(pattern string) (string, error) { return "", err } - err = os.Chmod(tempName, 0755) - - if err != nil { + if err = os.Chmod(tempName, 0755); err != nil { return "", err } @@ -62,7 +60,7 @@ func RunCommand(name string, args ...string) ([]byte, error) { cmd := exec.Command(name, args...) output, err := cmd.CombinedOutput() if err != nil { - return nil, fmt.Errorf("error running command: %v", err) + return nil, fmt.Errorf("error running command: %w", err) } return output, nil } @@ -107,10 +105,16 @@ func WaitForFpPubRandCommitted(t *testing.T, fpIns *service.FinalityProviderInst func DefaultFpConfig(keyringDir, homeDir string) *config.Config { cfg := config.DefaultConfigWithHome(homeDir) + cfg.NumPubRand = 1000 + cfg.NumPubRandMax = 1000 + cfg.TimestampingDelayBlocks = 0 + cfg.BitcoinNetwork = "simnet" cfg.BTCNetParams = chaincfg.SimNetParams + cfg.PollerConfig.PollInterval = 1 * time.Millisecond cfg.PollerConfig.AutoChainScanningMode = false + // babylon configs for sending transactions cfg.BabylonConfig.KeyDirectory = keyringDir // need to use this one to send otherwise we will have account sequence mismatch @@ -118,15 +122,13 @@ func DefaultFpConfig(keyringDir, homeDir string) *config.Config { cfg.BabylonConfig.Key = "test-spending-key" // Big adjustment to make sure we have enough gas in our transactions cfg.BabylonConfig.GasAdjustment = 20 - // adjust num pub rand to 1000 - cfg.NumPubRand = 1000 return &cfg } func DefaultFpConfigWithPorts(keyringDir, homeDir string, fpRpcPort, fpMetricsPort, eotsRpcPort int) *config.Config { cfg := DefaultFpConfig(keyringDir, homeDir) - cfg.RpcListener = fmt.Sprintf("127.0.0.1:%d", fpRpcPort) + cfg.RPCListener = fmt.Sprintf("127.0.0.1:%d", fpRpcPort) cfg.EOTSManagerAddress = fmt.Sprintf("127.0.0.1:%d", eotsRpcPort) cfg.Metrics.Port = fpMetricsPort return cfg @@ -134,10 +136,8 @@ func DefaultFpConfigWithPorts(keyringDir, homeDir string, fpRpcPort, fpMetricsPo // ParseRespBTCDelToBTCDel parses an BTC delegation response to BTC Delegation // adapted from -// https://github.com/babylonlabs-io/babylon-private/blob/74a24c962ce2cf64e5216edba9383fe0b460070c/test/e2e/btc_staking_e2e_test.go#L773 -func ParseRespBTCDelToBTCDel( - resp *bstypes.BTCDelegationResponse, -) (btcDel *bstypes.BTCDelegation, err error) { +// https://github.com/babylonlabs-io/babylon/blob/1a3c50da64885452c8d669fcea2a2fad78c8a028/test/e2e/btc_staking_e2e_test.go#L548 +func ParseRespBTCDelToBTCDel(resp *bstypes.BTCDelegationResponse) (btcDel *bstypes.BTCDelegation, err error) { stakingTx, err := hex.DecodeString(resp.StakingTxHex) if err != nil { return nil, err @@ -193,14 +193,6 @@ func ParseRespBTCDelToBTCDel( SlashingTx: slashTx, DelegatorSlashingSig: delSlashingSig, } - - if len(ud.DelegatorUnbondingSigHex) > 0 { - delUnbondingSig, err := bbntypes.NewBIP340SignatureFromHex(ud.DelegatorUnbondingSigHex) - if err != nil { - return nil, err - } - btcDel.BtcUndelegation.DelegatorUnbondingSig = delUnbondingSig - } } return btcDel, nil diff --git a/keyring/keyring.go b/keyring/keyring.go index c8312907..e0d97f06 100644 --- a/keyring/keyring.go +++ b/keyring/keyring.go @@ -12,8 +12,8 @@ import ( "github.com/babylonlabs-io/finality-provider/codec" ) -func CreateKeyring(keyringDir string, chainId string, backend string, input *strings.Reader) (keyring.Keyring, error) { - ctx, err := CreateClientCtx(keyringDir, chainId) +func CreateKeyring(keyringDir string, chainID string, backend string, input *strings.Reader) (keyring.Keyring, error) { + ctx, err := CreateClientCtx(keyringDir, chainID) if err != nil { return nil, err } @@ -36,7 +36,7 @@ func CreateKeyring(keyringDir string, chainId string, backend string, input *str return kr, nil } -func CreateClientCtx(keyringDir string, chainId string) (client.Context, error) { +func CreateClientCtx(keyringDir string, chainID string) (client.Context, error) { var err error var homeDir string @@ -49,7 +49,7 @@ func CreateClientCtx(keyringDir string, chainId string) (client.Context, error) } return client.Context{}. - WithChainID(chainId). + WithChainID(chainID). WithCodec(codec.MakeCodec()). WithKeyringDir(keyringDir), nil } diff --git a/keyring/keyringcontroller.go b/keyring/keyringcontroller.go index fd0bf84a..ad4c194e 100644 --- a/keyring/keyringcontroller.go +++ b/keyring/keyringcontroller.go @@ -107,6 +107,7 @@ func (kc *ChainKeyringController) CreateChainKey(passphrase, hdPath, mnemonic st switch v := privKey.(type) { case *sdksecp256k1.PrivKey: sk, pk := btcec.PrivKeyFromBytes(v.Key) + return &types.ChainKeyInfo{ Name: kc.fpName, AccAddress: accAddress, diff --git a/keyring/keyringcontroller_test.go b/keyring/keyringcontroller_test.go index 66de660f..2320340f 100644 --- a/keyring/keyringcontroller_test.go +++ b/keyring/keyringcontroller_test.go @@ -40,7 +40,7 @@ func FuzzCreatePoP(f *testing.F) { eotsHome := filepath.Join(t.TempDir(), "eots-home") eotsCfg := eotscfg.DefaultConfigWithHomePath(eotsHome) - dbBackend, err := eotsCfg.DatabaseConfig.GetDbBackend() + dbBackend, err := eotsCfg.DatabaseConfig.GetDBBackend() require.NoError(t, err) logger, err := zap.NewDevelopment() require.NoError(t, err) diff --git a/log/log.go b/log/log.go index f81f79e9..8d0abcf8 100644 --- a/log/log.go +++ b/log/log.go @@ -52,18 +52,21 @@ func NewRootLogger(format string, level string, w io.Writer) (*zap.Logger, error return nil, fmt.Errorf("unsupported log level: %s", level) } - return zap.New(zapcore.NewCore( - enc, - zapcore.AddSync(w), - lvl, - )), nil + return zap.New( + zapcore.NewCore( + enc, + zapcore.AddSync(w), + lvl, + ), + ), nil } func NewRootLoggerWithFile(logFile string, level string) (*zap.Logger, error) { if err := util.MakeDirectory(filepath.Dir(logFile)); err != nil { return nil, err } - f, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660) + // #nosec G304 - The log file path is provided by the user and not externally + f, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) if err != nil { return nil, err } @@ -73,5 +76,6 @@ func NewRootLoggerWithFile(logFile string, level string) (*zap.Logger, error) { if err != nil { return nil, err } + return logger, nil } diff --git a/metrics/config.go b/metrics/config.go index f5f99aa5..d4601406 100644 --- a/metrics/config.go +++ b/metrics/config.go @@ -36,6 +36,7 @@ func (cfg *Config) Address() (string, error) { if err := cfg.Validate(); err != nil { return "", err } + return fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), nil } diff --git a/metrics/fp_collectors.go b/metrics/fp_collectors.go index b9a0e135..4696e647 100644 --- a/metrics/fp_collectors.go +++ b/metrics/fp_collectors.go @@ -155,6 +155,7 @@ func NewFpMetrics() *FpMetrics { prometheus.MustRegister(fpMetricsInstance.fpTotalFailedVotes) prometheus.MustRegister(fpMetricsInstance.fpTotalFailedRandomness) }) + return fpMetricsInstance } @@ -269,19 +270,17 @@ func (fm *FpMetrics) RecordFpRandomnessTime(fpBtcPkHex string) { fm.previousRandomnessByFp[fpBtcPkHex] = &now } -func (fm *FpMetrics) UpdateFpMetrics(fps []*store.StoredFinalityProvider) { +func (fm *FpMetrics) UpdateFpMetrics(fp *store.StoredFinalityProvider) { fm.mu.Lock() defer fm.mu.Unlock() - for _, fp := range fps { - fm.RecordFpStatus(fp.GetBIP340BTCPK().MarshalHex(), fp.Status) + fm.RecordFpStatus(fp.GetBIP340BTCPK().MarshalHex(), fp.Status) - if lastVoteTime, ok := fm.previousVoteByFp[fp.GetBIP340BTCPK().MarshalHex()]; ok { - fm.RecordFpSecondsSinceLastVote(fp.GetBIP340BTCPK().MarshalHex(), time.Since(*lastVoteTime).Seconds()) - } + if lastVoteTime, ok := fm.previousVoteByFp[fp.GetBIP340BTCPK().MarshalHex()]; ok { + fm.RecordFpSecondsSinceLastVote(fp.GetBIP340BTCPK().MarshalHex(), time.Since(*lastVoteTime).Seconds()) + } - if lastRandomnessTime, ok := fm.previousRandomnessByFp[fp.GetBIP340BTCPK().MarshalHex()]; ok { - fm.RecordFpSecondsSinceLastRandomness(fp.GetBIP340BTCPK().MarshalHex(), time.Since(*lastRandomnessTime).Seconds()) - } + if lastRandomnessTime, ok := fm.previousRandomnessByFp[fp.GetBIP340BTCPK().MarshalHex()]; ok { + fm.RecordFpSecondsSinceLastRandomness(fp.GetBIP340BTCPK().MarshalHex(), time.Since(*lastRandomnessTime).Seconds()) } } diff --git a/metrics/server.go b/metrics/server.go index 15e850b4..af8acfe0 100644 --- a/metrics/server.go +++ b/metrics/server.go @@ -3,6 +3,7 @@ package metrics import ( "context" "net/http" + "time" "github.com/prometheus/client_golang/prometheus/promhttp" "go.uber.org/zap" @@ -20,8 +21,12 @@ func Start(addr string, logger *zap.Logger) *Server { // Create the HTTP server with the custom ServeMux as the handler server := &http.Server{ - Addr: addr, - Handler: mux, + Addr: addr, + Handler: mux, + ReadHeaderTimeout: 2 * time.Second, + ReadTimeout: 5 * time.Second, + WriteTimeout: 30 * time.Second, + IdleTimeout: 30 * time.Second, } // Store the logger in the server struct diff --git a/metrics/tools/go.mod b/metrics/tools/go.mod new file mode 100644 index 00000000..5814a65a --- /dev/null +++ b/metrics/tools/go.mod @@ -0,0 +1,234 @@ +module github.com/babylonlabs-io/finality-provider/tools + +go 1.22 + +toolchain go1.22.7 + +require ( + github.com/CosmWasm/wasmd v0.52.0 + github.com/babylonlabs-io/babylon v0.9.3-0.20240925223611-a98269d17887 + github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240814002132-55e711397a82 +) + +require ( + cloud.google.com/go v0.112.1 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + cloud.google.com/go/storage v1.38.0 // indirect + cosmossdk.io/api v0.7.5 // indirect + cosmossdk.io/client/v2 v2.0.0-beta.1 // indirect + cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/core v0.11.1 // indirect + cosmossdk.io/depinject v1.0.0 // indirect + cosmossdk.io/errors v1.0.1 // indirect + cosmossdk.io/log v1.3.1 // indirect + cosmossdk.io/math v1.3.0 // indirect + cosmossdk.io/store v1.1.0 // indirect + cosmossdk.io/tools/confix v0.1.1 // indirect + cosmossdk.io/x/circuit v0.1.1 // indirect + cosmossdk.io/x/evidence v0.1.1 // indirect + cosmossdk.io/x/feegrant v0.1.1 // indirect + cosmossdk.io/x/nft v0.1.1 // indirect + cosmossdk.io/x/tx v0.13.4 // indirect + cosmossdk.io/x/upgrade v0.1.3 // indirect + filippo.io/edwards25519 v1.0.0 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.1 // indirect + github.com/CosmWasm/wasmvm/v2 v2.1.0 // indirect + github.com/DataDog/datadog-go v3.2.0+incompatible // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/aead/siphash v1.0.1 // indirect + github.com/aws/aws-sdk-go v1.44.312 // indirect + github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240814002132-55e711397a82 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect + github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d // indirect + github.com/btcsuite/btcd v0.24.2 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/btcutil v1.1.5 // indirect + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect + github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/chzyer/readline v1.5.1 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.0 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cometbft/cometbft v0.38.10 // indirect + github.com/cometbft/cometbft-db v0.9.1 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-db v1.0.2 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect + github.com/cosmos/cosmos-sdk v0.50.9 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/gogoproto v1.5.0 // indirect + github.com/cosmos/iavl v1.2.0 // indirect + github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd // indirect + github.com/cosmos/ibc-go/modules/capability v1.0.0 // indirect + github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead // indirect + github.com/cosmos/ibc-go/v8 v8.3.2 // indirect + github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/creachadair/atomicfile v0.3.1 // indirect + github.com/creachadair/tomledit v0.0.24 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect + github.com/dgraph-io/badger/v2 v2.2007.4 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.6.0 // indirect + github.com/emicklei/dot v1.6.1 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/go-kit/kit v0.12.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // 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.1 // 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.20220116011046-fa5810519dcb // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/orderedcode v0.0.1 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.3 // indirect + github.com/gorilla/handlers v1.5.2 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-getter v1.7.5 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.3 // indirect + github.com/hashicorp/go-plugin v1.5.2 // indirect + github.com/hashicorp/go-safetemp v1.0.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/huandu/skiplist v1.2.0 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jinzhu/copier v0.3.5 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 // indirect + github.com/klauspost/compress v1.17.7 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.7 // indirect + github.com/linxGnu/grocksdb v1.8.14 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/manifoldco/promptui v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mtibben/percent v0.2.1 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // 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.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.52.2 // indirect + github.com/prometheus/procfs v0.13.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/cors v1.8.3 // indirect + github.com/rs/zerolog v1.33.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/shamaton/msgpack/v2 v2.2.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/supranational/blst v0.3.11 // 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/ulikunitz/xz v0.5.11 // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + go.etcd.io/bbolt v1.3.8 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.30.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect + go.opentelemetry.io/otel/trace v1.30.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/api v0.171.0 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/grpc v1.66.1 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.5.1 // indirect + nhooyr.io/websocket v1.8.6 // indirect + pgregory.net/rapid v1.1.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) + +// Downgraded to stable version see: https://github.com/cosmos/cosmos-sdk/pull/14952 +replace ( + // use cosmos fork of keyring + github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 + github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + // avoid v1.66 that has a breaking change for protobuf. That change breaks the relayer. + // https://github.com/grpc/grpc-go/issues/7569 + google.golang.org/grpc => google.golang.org/grpc v1.65.0 +) diff --git a/metrics/tools/go.sum b/metrics/tools/go.sum new file mode 100644 index 00000000..f31aeedd --- /dev/null +++ b/metrics/tools/go.sum @@ -0,0 +1,2666 @@ +cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= +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.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= +cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/accesscontextmanager v1.8.0/go.mod h1:uI+AI/r1oyWK99NN8cQ3UK76AMelMzgZCvJfsi2c+ps= +cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= +cloud.google.com/go/aiplatform v1.48.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= +cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/baremetalsolution v1.1.1/go.mod h1:D1AV6xwOksJMV4OSlWHtWuFNZZYujJknMAP4Qa27QIA= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/batch v1.3.1/go.mod h1:VguXeQKXIYaeeIYbuozUmBR13AfL4SJP7IltNPS+A4A= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= +cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= +cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= +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/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= +cloud.google.com/go/bigquery v1.53.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= +cloud.google.com/go/cloudbuild v1.13.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= +cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= +cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= +cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= +cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= +cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/datacatalog v1.14.0/go.mod h1:h0PrGtlihoutNMp/uvwhawLQ9+c63Kz65UFqh49Yo+E= +cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= +cloud.google.com/go/datacatalog v1.16.0/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= +cloud.google.com/go/dataplex v1.9.0/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataproc/v2 v2.0.1/go.mod h1:7Ez3KRHdFGcfY7GcevBbvozX+zyWGcwLJvvAMwCaoZ4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= +cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8= +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/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastore v1.12.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= +cloud.google.com/go/datastore v1.12.1/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= +cloud.google.com/go/datastore v1.13.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= +cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= +cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= +cloud.google.com/go/dialogflow v1.40.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= +cloud.google.com/go/documentai v1.22.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= +cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= +cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gaming v1.10.1/go.mod h1:XQQvtfP8Rb9Rxnxm5wFVpAp9zCQkJi2bLIb7iHGwB3s= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkebackup v1.3.0/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= +cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= +cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= +cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/kms v1.11.0/go.mod h1:hwdiYC0xjnWsKQQCQQmIQnS9asjYVSK6jtXm+zFqXLM= +cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= +cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ= +cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= +cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/maps v1.3.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= +cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= +cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/orgpolicy v1.11.0/go.mod h1:2RK748+FtVvnfuynxBzdnyu7sygtoZa1za/0ZfpOs1M= +cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/osconfig v1.12.0/go.mod h1:8f/PaYzoS3JMVfdfTubkowZYGmAhUCjjwnjqWI7NVBc= +cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0= +cloud.google.com/go/policytroubleshooter v1.8.0/go.mod h1:tmn5Ir5EToWe384EuboTcVQT7nTag2+DuH3uHmKd1HU= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= +cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA= +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/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= +cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= +cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= +cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= +cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= +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.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= +cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= +cloud.google.com/go/translate v1.8.2/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= +cloud.google.com/go/video v1.19.0/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= +cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= +cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= +cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +cosmossdk.io/client/v2 v2.0.0-beta.1 h1:XkHh1lhrLYIT9zKl7cIOXUXg2hdhtjTPBUfqERNA1/Q= +cosmossdk.io/client/v2 v2.0.0-beta.1/go.mod h1:JEUSu9moNZQ4kU3ir1DKD5eU4bllmAexrGWjmb9k8qU= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v0.11.1 h1:h9WfBey7NAiFfIcUhDVNS503I2P2HdZLebJlUIs8LPA= +cosmossdk.io/core v0.11.1/go.mod h1:OJzxcdC+RPrgGF8NJZR2uoQr56tc7gfBKhiKeDO7hH0= +cosmossdk.io/depinject v1.0.0 h1:dQaTu6+O6askNXO06+jyeUAnF2/ssKwrrszP9t5q050= +cosmossdk.io/depinject v1.0.0/go.mod h1:zxK/h3HgHoA/eJVtiSsoaRaRA2D5U4cJ5thIG4ssbB8= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= +cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= +cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= +cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= +cosmossdk.io/store v1.1.0 h1:LnKwgYMc9BInn9PhpTFEQVbL9UK475G2H911CGGnWHk= +cosmossdk.io/store v1.1.0/go.mod h1:oZfW/4Fc/zYqu3JmQcQdUJ3fqu5vnYTn3LZFFy8P8ng= +cosmossdk.io/tools/confix v0.1.1 h1:aexyRv9+y15veH3Qw16lxQwo+ki7r2I+g0yNTEFEQM8= +cosmossdk.io/tools/confix v0.1.1/go.mod h1:nQVvP1tHsGXS83PonPVWJtSbddIqyjEw99L4M3rPJyQ= +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/nft v0.1.1 h1:pslAVS8P5NkW080+LWOamInjDcq+v2GSCo+BjN9sxZ8= +cosmossdk.io/x/nft v0.1.1/go.mod h1:Kac6F6y2gsKvoxU+fy8uvxRTi4BIhLOor2zgCNQwVgY= +cosmossdk.io/x/tx v0.13.4 h1:Eg0PbJgeO0gM8p5wx6xa0fKR7hIV6+8lC56UrsvSo0Y= +cosmossdk.io/x/tx v0.13.4/go.mod h1:BkFqrnGGgW50Y6cwTy+JvgAhiffbGEKW6KF9ufcDpvk= +cosmossdk.io/x/upgrade v0.1.3 h1:q4XpXc6zp0dX6x74uBtfN6+J7ikaQev5Bla6Q0ADLK8= +cosmossdk.io/x/upgrade v0.1.3/go.mod h1:jOdQhnaY5B8CDUoUbed23/Lre0Dk+r6BMQE40iKlVVQ= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +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/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/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.52.0 h1:VRylqes1AMXqIgz/jUH9EzhjBZKsRXrrjCTAni0ILRM= +github.com/CosmWasm/wasmd v0.52.0/go.mod h1:hyy1wt7c589Cs4kOK2cYdtphzCd2Xo20q/t7tfby7oI= +github.com/CosmWasm/wasmvm/v2 v2.1.0 h1:bleLhNA36hM8iPjFJsNRi9RjrQW6MtXafw2+wVjAWAE= +github.com/CosmWasm/wasmvm/v2 v2.1.0/go.mod h1:bMhLQL4Yp9CzJi9A83aR7VO9wockOsSlZbT4ztOl6bg= +github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +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/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +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.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= +github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +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/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= +github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= +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/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +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.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.312 h1:llrElfzeqG/YOLFFKjg1xNpZCFJ2xraIi3PqSuP+95k= +github.com/aws/aws-sdk-go v1.44.312/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/babylonlabs-io/babylon v0.9.3-0.20240925223611-a98269d17887 h1:Z5UPknFpdpWJIgy71AQfQcJACpwt0KKOiHnb2OIpKaU= +github.com/babylonlabs-io/babylon v0.9.3-0.20240925223611-a98269d17887/go.mod h1:Savv0qKMqan8M8kFchXocIsLI69tPmnMOZOe//sgTFI= +github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240814002132-55e711397a82 h1:BwgtEtrrbtwQo4FEsoVqeUIHiqJr5krZt6ds3g1SM4s= +github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240814002132-55e711397a82/go.mod h1:QqEn1sL4RPG7DJ94XFYvuvEELml64s5XwPQpTayXJss= +github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240814002132-55e711397a82 h1:F9U6iH+RqXo2ColXvGGByWDY0DdyAMnIgjpxQbWooiE= +github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240814002132-55e711397a82/go.mod h1:WJlZy0RYCtyBFeO1mr0Tlo02csrlCAQgzmp4+NVX14g= +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.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvPTF58Vq+qgmZEYZttJK+CWtSZhKenI= +github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= +github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= +github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= +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/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +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/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +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-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/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/cometbft/cometbft v0.38.10 h1:2ePuglchT+j0Iao+cfmt/nw5U7K2lnGDzXSUPGVdXaU= +github.com/cometbft/cometbft v0.38.10/go.mod h1:jHPx9vQpWzPHEAiYI/7EDKaB1NXhK6o3SArrrY8ExKc= +github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M= +github.com/cometbft/cometbft-db v0.9.1/go.mod h1:iliyWaoV0mRwBJoizElCwwRA9Tf7jZJOURcRZF9m60U= +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/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +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.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= +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.9 h1:gt2usjz0H0qW6KwAxWw7ZJ3XU8uDwmhN+hYG3nTLeSg= +github.com/cosmos/cosmos-sdk v0.50.9/go.mod h1:TMH6wpoYBcg7Cp5BEg8fneLr+8XloNQkf2MRNF9V6JE= +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.5.0 h1:SDVwzEqZDDBoslaeZg+dGE55hdzHfgUA40pEanMh52o= +github.com/cosmos/gogoproto v1.5.0/go.mod h1:iUM31aofn3ymidYG6bUR5ZFrk+Om8p5s754eMUcyp8I= +github.com/cosmos/iavl v1.2.0 h1:kVxTmjTh4k0Dh1VNL046v6BXqKziqMDzxo93oh3kOfM= +github.com/cosmos/iavl v1.2.0/go.mod h1:HidWWLVAtODJqFD6Hbne2Y0q3SdxByJepHUOeoH4LiI= +github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd h1:Lx+/5dZ/nN6qPXP2Ofog6u1fmlkCFA1ElcOconnofEM= +github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd/go.mod h1:JWfpWVKJKiKtd53/KbRoKfxWl8FsT2GPcNezTOk0o5Q= +github.com/cosmos/ibc-go/modules/capability v1.0.0 h1:r/l++byFtn7jHYa09zlAdSeevo8ci1mVZNO9+V0xsLE= +github.com/cosmos/ibc-go/modules/capability v1.0.0/go.mod h1:D81ZxzjZAe0ZO5ambnvn1qedsFQ8lOwtqicG6liLBco= +github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead h1:QB50+AmrEVqFr2hzvIxMkICziWQ/uuebze0vNYKMnBg= +github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead/go.mod h1:AJeroAXnPKeFpD1AfEfjYBHGEWt5gBfzUjgs4SYn2ZY= +github.com/cosmos/ibc-go/v8 v8.3.2 h1:8X1oHHKt2Bh9hcExWS89rntLaCKZp2EjFTUSxKlPhGI= +github.com/cosmos/ibc-go/v8 v8.3.2/go.mod h1:WVVIsG39jGrF9Cjggjci6LzySyWGloz194sjTxiGNIE= +github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= +github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo= +github.com/cosmos/keyring v1.2.0/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +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.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creachadair/atomicfile v0.3.1 h1:yQORkHjSYySh/tv5th1dkKcn02NEW5JleB84sjt+W4Q= +github.com/creachadair/atomicfile v0.3.1/go.mod h1:mwfrkRxFKwpNAflYZzytbSwxvbK6fdGRRlp0KEQc0qU= +github.com/creachadair/tomledit v0.0.24 h1:5Xjr25R2esu1rKCbQEmjZYlrhFkDspoAbAKb6QKQDhQ= +github.com/creachadair/tomledit v0.0.24/go.mod h1:9qHbShRWQzSCcn617cMzg4eab1vbLCOjOshAWSzWr8U= +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.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +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-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +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.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= +github.com/dvsekhvalnov/jose2go v1.6.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.1 h1:ujpDlBkkwgWUY+qPId5IwapRW/xEoligRSYjioR6DFI= +github.com/emicklei/dot v1.6.1/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= +github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +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.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +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/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +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-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= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +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-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.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +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-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= +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.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +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/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +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.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/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/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.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.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +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.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3/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.3/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.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/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.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/flatbuffers v2.0.8+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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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 h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +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/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= +github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +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.3.0/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.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +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.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= +github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= +github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= +github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +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.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +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.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.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +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.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= +github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/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.3 h1:M5uADWMOGCTUNU1YuC4hfknOeHNaX54LDm4oYSucoNE= +github.com/hashicorp/go-metrics v0.5.3/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= +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.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y= +github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= +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.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +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.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= +github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +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.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= +github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +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/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= +github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +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/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.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/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +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/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +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.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +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.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/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.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ= +github.com/linxGnu/grocksdb v1.8.14/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +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 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +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.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +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/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/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.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +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.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= +github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +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 v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 h1:jik8PHtAIsPlCRJjJzl4udgEf7hawInF9texMeO2jrU= +github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +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/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +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/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/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.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +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.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +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.52.2 h1:LW8Vk7BccEdONfrJBDffQGRtpSzi5CQaRZGtboOO2ck= +github.com/prometheus/common v0.52.2/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= +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.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= +github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= +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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= +github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +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/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +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.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= +github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/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/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 v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +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/vulpine-io/io-test v1.0.0 h1:Ot8vMh+ssm1VWDAwJ3U4C5qG9aRnr5YfQFZPNZBAUGI= +github.com/vulpine-io/io-test v1.0.0/go.mod h1:X1I+p5GCxVX9m4nFd1HBtr2bVX9v1ZE6x8w+Obt36AU= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +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.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +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.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +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.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= +go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= +go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= +go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= +go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= +go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= +go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +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/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= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +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-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +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/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +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/lint v0.0.0-20210508222113-6edffad5e616/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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-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-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +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.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +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-20220601150217-0de741cfad7f/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.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.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.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/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-20190626221950-04f50cda93cb/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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/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-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/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-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/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-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/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-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/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-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/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-20220728004956-3c1f35247d10/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-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +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.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/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-20190206041539-40960b6deb8e/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-20190927191325-030b2cf1153e/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-20201124115921-2c860bdd6e78/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.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +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= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= +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.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E= +google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= +google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= +google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU= +google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o= +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-20210222152913-aa3ee6e6a81c/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-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= +google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= +google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +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.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.27/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/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +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= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= +modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g= +modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= +modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= +modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= +modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= +modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/sqlite v1.18.2/go.mod h1:kvrTLEWgxUcHa2GfHBQtanR1H9ht3hTJNtKpzH9k1u0= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/tcl v1.13.2/go.mod h1:7CLiGIPo1M8Rv1Mitpv5akc2+8fxUd2y2UzC/MfMzy0= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= +nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +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/metrics/tools/tools.go b/metrics/tools/tools.go new file mode 100644 index 00000000..657bf581 --- /dev/null +++ b/metrics/tools/tools.go @@ -0,0 +1,10 @@ +//go:build tools +// +build tools + +package finalityprovider + +import ( + _ "github.com/CosmWasm/wasmd/cmd/wasmd" + _ "github.com/babylonlabs-io/babylon-sdk/demo/cmd/bcd" + _ "github.com/babylonlabs-io/babylon/cmd/babylond" +) diff --git a/testutil/datagen.go b/testutil/datagen.go index 4af9a291..8b5971ec 100644 --- a/testutil/datagen.go +++ b/testutil/datagen.go @@ -6,41 +6,38 @@ import ( "testing" "time" - "github.com/babylonlabs-io/babylon/crypto/eots" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/babylonlabs-io/finality-provider/finality-provider/store" - sdkmath "cosmossdk.io/math" + "github.com/babylonlabs-io/babylon/crypto/eots" "github.com/babylonlabs-io/babylon/testutil/datagen" bbn "github.com/babylonlabs-io/babylon/types" - bbntypes "github.com/babylonlabs-io/babylon/types" - bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - "github.com/btcsuite/btcd/chaincfg" "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" + "github.com/babylonlabs-io/finality-provider/finality-provider/store" + fpkr "github.com/babylonlabs-io/finality-provider/keyring" + "github.com/babylonlabs-io/finality-provider/codec" - "github.com/babylonlabs-io/finality-provider/finality-provider/proto" - "github.com/babylonlabs-io/finality-provider/finality-provider/service" "github.com/babylonlabs-io/finality-provider/types" ) func GenRandomByteArray(r *rand.Rand, length uint64) []byte { newHeaderBytes := make([]byte, length) r.Read(newHeaderBytes) + return newHeaderBytes } func GenRandomHexStr(r *rand.Rand, length uint64) string { randBytes := GenRandomByteArray(r, length) + return hex.EncodeToString(randBytes) } func RandomDescription(r *rand.Rand) *stakingtypes.Description { des := stakingtypes.NewDescription(GenRandomHexStr(r, 10), "", "", "", "") + return &des } @@ -56,34 +53,25 @@ func AddRandomSeedsToFuzzer(f *testing.F, num uint) { func GenPublicRand(r *rand.Rand, t *testing.T) *bbn.SchnorrPubRand { _, eotsPR, err := eots.RandGen(r) require.NoError(t, err) + return bbn.NewSchnorrPubRandFromFieldVal(eotsPR) } func GenRandomFinalityProvider(r *rand.Rand, t *testing.T) *store.StoredFinalityProvider { // generate BTC key pair - btcSK, btcPK, err := datagen.GenRandomBTCKeyPair(r) + _, btcPK, err := datagen.GenRandomBTCKeyPair(r) require.NoError(t, err) bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK) fpAddr, err := sdk.AccAddressFromBech32(datagen.GenRandomAccount().Address) require.NoError(t, err) - // generate and verify PoP, correct case - pop, err := bstypes.NewPoPBTC(fpAddr, btcSK) - require.NoError(t, err) - err = pop.Verify(fpAddr, bip340PK, &chaincfg.SimNetParams) - require.NoError(t, err) - return &store.StoredFinalityProvider{ FPAddr: fpAddr.String(), - KeyName: GenRandomHexStr(r, 4), ChainID: "chain-test", BtcPk: bip340PK.MustToBTCPK(), Description: RandomDescription(r), Commission: ZeroCommissionRate(), - Pop: &proto.ProofOfPossession{ - BtcSig: pop.BtcSig, - }, } } @@ -104,30 +92,30 @@ func GenBlocks(r *rand.Rand, startHeight, endHeight uint64) []*types.BlockInfo { return blocks } -// GenStoredFinalityProvider generates a random finality-provider from the keyring and store it in DB -func GenStoredFinalityProvider(r *rand.Rand, t *testing.T, app *service.FinalityProviderApp, passphrase, hdPath string, eotsPk *bbntypes.BIP340PubKey) *store.StoredFinalityProvider { - // generate keyring - keyName := GenRandomHexStr(r, 4) - chainID := GenRandomHexStr(r, 4) - - cfg := app.GetConfig() - _, err := service.CreateChainKey(cfg.BabylonConfig.KeyDirectory, cfg.BabylonConfig.ChainID, keyName, keyring.BackendTest, passphrase, hdPath, "") - require.NoError(t, err) - - res, err := app.CreateFinalityProvider(keyName, chainID, passphrase, hdPath, eotsPk, RandomDescription(r), ZeroCommissionRate()) - require.NoError(t, err) +func CreateChainKey(keyringDir, chainID, keyName, backend, passphrase, hdPath, mnemonic string) (*types.ChainKeyInfo, error) { + sdkCtx, err := fpkr.CreateClientCtx( + keyringDir, chainID, + ) + if err != nil { + return nil, err + } - btcPk, err := bbn.NewBIP340PubKeyFromHex(res.FpInfo.BtcPkHex) - require.NoError(t, err) - storedFp, err := app.GetFinalityProviderStore().GetFinalityProvider(btcPk.MustToBTCPK()) - require.NoError(t, err) + krController, err := fpkr.NewChainKeyringController( + sdkCtx, + keyName, + backend, + ) + if err != nil { + return nil, err + } - return storedFp + return krController.CreateChainKey(passphrase, hdPath, mnemonic) } func GenSdkContext(r *rand.Rand, t *testing.T) client.Context { chainID := "testchain-" + GenRandomHexStr(r, 4) dir := t.TempDir() + return client.Context{}. WithChainID(chainID). WithCodec(codec.MakeCodec()). diff --git a/testutil/log/log.go b/testutil/log/log.go index 1115004f..d93ffcfa 100644 --- a/testutil/log/log.go +++ b/testutil/log/log.go @@ -8,5 +8,6 @@ import ( // t.Logf(log.FormatWithDate("Fp home dir: %s"), fpHomeDir) func Prefix(format string) string { currentTime := time.Now().Format("[15:04:05.000]") + return currentTime + " " + format } diff --git a/testutil/logger.go b/testutil/logger.go new file mode 100644 index 00000000..878fc3eb --- /dev/null +++ b/testutil/logger.go @@ -0,0 +1,18 @@ +package testutil + +import ( + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/zap" +) + +func GetTestLogger(t *testing.T) *zap.Logger { + loggerConfig := zap.NewDevelopmentConfig() + loggerConfig.Level = zap.NewAtomicLevelAt(zap.ErrorLevel) + logger, err := loggerConfig.Build() + + require.NoError(t, err) + + return logger +} diff --git a/testutil/mocks/clientcontroller.go b/testutil/mocks/clientcontroller.go index b21f8d96..72a0ae63 100644 --- a/testutil/mocks/clientcontroller.go +++ b/testutil/mocks/clientcontroller.go @@ -8,7 +8,8 @@ import ( reflect "reflect" math "cosmossdk.io/math" - types "github.com/babylonlabs-io/finality-provider/types" + types "github.com/babylonlabs-io/babylon/x/btcstaking/types" + types0 "github.com/babylonlabs-io/finality-provider/types" btcec "github.com/btcsuite/btcd/btcec/v2" schnorr "github.com/btcsuite/btcd/btcec/v2/schnorr" gomock "github.com/golang/mock/gomock" @@ -51,27 +52,41 @@ func (mr *MockClientControllerMockRecorder) Close() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockClientController)(nil).Close)) } -// QueryFinalityProviderSlashedOrJailed mocks base method. -func (m *MockClientController) QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (bool, bool, error) { +// EditFinalityProvider mocks base method. +func (m *MockClientController) EditFinalityProvider(fpPk *btcec.PublicKey, commission *math.LegacyDec, description []byte) (*types.MsgEditFinalityProvider, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "QueryFinalityProviderSlashedOrJailed", fpPk) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(bool) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 + ret := m.ctrl.Call(m, "EditFinalityProvider", fpPk, commission, description) + ret0, _ := ret[0].(*types.MsgEditFinalityProvider) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// QueryFinalityProviderSlashedOrJailed indicates an expected call of QueryFinalityProviderSlashedOrJailed. -func (mr *MockClientControllerMockRecorder) QueryFinalityProviderSlashedOrJailed(fpPk interface{}) *gomock.Call { +// EditFinalityProvider indicates an expected call of EditFinalityProvider. +func (mr *MockClientControllerMockRecorder) EditFinalityProvider(fpPk, commission, description interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EditFinalityProvider", reflect.TypeOf((*MockClientController)(nil).EditFinalityProvider), fpPk, commission, description) +} + +// QueryFinalityProvider mocks base method. +func (m *MockClientController) QueryFinalityProvider(fpPk *btcec.PublicKey) (*types.QueryFinalityProviderResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QueryFinalityProvider", fpPk) + ret0, _ := ret[0].(*types.QueryFinalityProviderResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// QueryFinalityProvider indicates an expected call of QueryFinalityProvider. +func (mr *MockClientControllerMockRecorder) QueryFinalityProvider(fpPk interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityProviderSlashedOrJailed", reflect.TypeOf((*MockClientController)(nil).QueryFinalityProviderSlashedOrJailed), fpPk) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityProvider", reflect.TypeOf((*MockClientController)(nil).QueryFinalityProvider), fpPk) } // RegisterFinalityProvider mocks base method. -func (m *MockClientController) RegisterFinalityProvider(chainID string, fpPk *btcec.PublicKey, pop []byte, commission *math.LegacyDec, description []byte) (*types.TxResponse, error) { +func (m *MockClientController) RegisterFinalityProvider(chainID string, fpPk *btcec.PublicKey, pop []byte, commission *math.LegacyDec, description []byte) (*types0.TxResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RegisterFinalityProvider", chainID, fpPk, pop, commission, description) - ret0, _ := ret[0].(*types.TxResponse) + ret0, _ := ret[0].(*types0.TxResponse) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -82,6 +97,20 @@ func (mr *MockClientControllerMockRecorder) RegisterFinalityProvider(chainID, fp return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterFinalityProvider", reflect.TypeOf((*MockClientController)(nil).RegisterFinalityProvider), chainID, fpPk, pop, commission, description) } +// Start mocks base method. +func (m *MockClientController) Start() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Start") + ret0, _ := ret[0].(error) + return ret0 +} + +// Start indicates an expected call of Start. +func (mr *MockClientControllerMockRecorder) Start() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockClientController)(nil).Start)) +} + // MockConsumerController is a mock of ConsumerController interface. type MockConsumerController struct { ctrl *gomock.Controller @@ -120,10 +149,10 @@ func (mr *MockConsumerControllerMockRecorder) Close() *gomock.Call { } // CommitPubRandList mocks base method. -func (m *MockConsumerController) CommitPubRandList(fpPk *btcec.PublicKey, startHeight, numPubRand uint64, commitment []byte, sig *schnorr.Signature) (*types.TxResponse, error) { +func (m *MockConsumerController) CommitPubRandList(fpPk *btcec.PublicKey, startHeight, numPubRand uint64, commitment []byte, sig *schnorr.Signature) (*types0.TxResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CommitPubRandList", fpPk, startHeight, numPubRand, commitment, sig) - ret0, _ := ret[0].(*types.TxResponse) + ret0, _ := ret[0].(*types0.TxResponse) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -150,10 +179,10 @@ func (mr *MockConsumerControllerMockRecorder) QueryActivatedHeight() *gomock.Cal } // QueryBlock mocks base method. -func (m *MockConsumerController) QueryBlock(height uint64) (*types.BlockInfo, error) { +func (m *MockConsumerController) QueryBlock(height uint64) (*types0.BlockInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryBlock", height) - ret0, _ := ret[0].(*types.BlockInfo) + ret0, _ := ret[0].(*types0.BlockInfo) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -165,10 +194,10 @@ func (mr *MockConsumerControllerMockRecorder) QueryBlock(height interface{}) *go } // QueryBlocks mocks base method. -func (m *MockConsumerController) QueryBlocks(startHeight, endHeight, limit uint64) ([]*types.BlockInfo, error) { +func (m *MockConsumerController) QueryBlocks(startHeight, endHeight uint64, limit uint32) ([]*types0.BlockInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryBlocks", startHeight, endHeight, limit) - ret0, _ := ret[0].([]*types.BlockInfo) + ret0, _ := ret[0].([]*types0.BlockInfo) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -179,6 +208,21 @@ func (mr *MockConsumerControllerMockRecorder) QueryBlocks(startHeight, endHeight return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryBlocks", reflect.TypeOf((*MockConsumerController)(nil).QueryBlocks), startHeight, endHeight, limit) } +// QueryFinalityActivationBlockHeight mocks base method. +func (m *MockConsumerController) QueryFinalityActivationBlockHeight() (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QueryFinalityActivationBlockHeight") + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// QueryFinalityActivationBlockHeight indicates an expected call of QueryFinalityActivationBlockHeight. +func (mr *MockConsumerControllerMockRecorder) QueryFinalityActivationBlockHeight() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityActivationBlockHeight", reflect.TypeOf((*MockConsumerController)(nil).QueryFinalityActivationBlockHeight)) +} + // QueryFinalityProviderHasPower mocks base method. func (m *MockConsumerController) QueryFinalityProviderHasPower(fpPk *btcec.PublicKey, blockHeight uint64) (bool, error) { m.ctrl.T.Helper() @@ -194,6 +238,37 @@ func (mr *MockConsumerControllerMockRecorder) QueryFinalityProviderHasPower(fpPk return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityProviderHasPower", reflect.TypeOf((*MockConsumerController)(nil).QueryFinalityProviderHasPower), fpPk, blockHeight) } +// QueryFinalityProviderHighestVotedHeight mocks base method. +func (m *MockConsumerController) QueryFinalityProviderHighestVotedHeight(fpPk *btcec.PublicKey) (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QueryFinalityProviderHighestVotedHeight", fpPk) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// QueryFinalityProviderHighestVotedHeight indicates an expected call of QueryFinalityProviderHighestVotedHeight. +func (mr *MockConsumerControllerMockRecorder) QueryFinalityProviderHighestVotedHeight(fpPk interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityProviderHighestVotedHeight", reflect.TypeOf((*MockConsumerController)(nil).QueryFinalityProviderHighestVotedHeight), fpPk) +} + +// QueryFinalityProviderSlashedOrJailed mocks base method. +func (m *MockConsumerController) QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (bool, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QueryFinalityProviderSlashedOrJailed", fpPk) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// QueryFinalityProviderSlashedOrJailed indicates an expected call of QueryFinalityProviderSlashedOrJailed. +func (mr *MockConsumerControllerMockRecorder) QueryFinalityProviderSlashedOrJailed(fpPk interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityProviderSlashedOrJailed", reflect.TypeOf((*MockConsumerController)(nil).QueryFinalityProviderSlashedOrJailed), fpPk) +} + // QueryIsBlockFinalized mocks base method. func (m *MockConsumerController) QueryIsBlockFinalized(height uint64) (bool, error) { m.ctrl.T.Helper() @@ -210,10 +285,10 @@ func (mr *MockConsumerControllerMockRecorder) QueryIsBlockFinalized(height inter } // QueryLastPublicRandCommit mocks base method. -func (m *MockConsumerController) QueryLastPublicRandCommit(fpPk *btcec.PublicKey) (*types.PubRandCommit, error) { +func (m *MockConsumerController) QueryLastPublicRandCommit(fpPk *btcec.PublicKey) (*types0.PubRandCommit, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryLastPublicRandCommit", fpPk) - ret0, _ := ret[0].(*types.PubRandCommit) + ret0, _ := ret[0].(*types0.PubRandCommit) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -240,10 +315,10 @@ func (mr *MockConsumerControllerMockRecorder) QueryLatestBlockHeight() *gomock.C } // QueryLatestFinalizedBlock mocks base method. -func (m *MockConsumerController) QueryLatestFinalizedBlock() (*types.BlockInfo, error) { +func (m *MockConsumerController) QueryLatestFinalizedBlock() (*types0.BlockInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryLatestFinalizedBlock") - ret0, _ := ret[0].(*types.BlockInfo) + ret0, _ := ret[0].(*types0.BlockInfo) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -255,10 +330,10 @@ func (mr *MockConsumerControllerMockRecorder) QueryLatestFinalizedBlock() *gomoc } // SubmitBatchFinalitySigs mocks base method. -func (m *MockConsumerController) SubmitBatchFinalitySigs(fpPk *btcec.PublicKey, blocks []*types.BlockInfo, pubRandList []*btcec.FieldVal, proofList [][]byte, sigs []*btcec.ModNScalar) (*types.TxResponse, error) { +func (m *MockConsumerController) SubmitBatchFinalitySigs(fpPk *btcec.PublicKey, blocks []*types0.BlockInfo, pubRandList []*btcec.FieldVal, proofList [][]byte, sigs []*btcec.ModNScalar) (*types0.TxResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SubmitBatchFinalitySigs", fpPk, blocks, pubRandList, proofList, sigs) - ret0, _ := ret[0].(*types.TxResponse) + ret0, _ := ret[0].(*types0.TxResponse) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -270,10 +345,10 @@ func (mr *MockConsumerControllerMockRecorder) SubmitBatchFinalitySigs(fpPk, bloc } // SubmitFinalitySig mocks base method. -func (m *MockConsumerController) SubmitFinalitySig(fpPk *btcec.PublicKey, block *types.BlockInfo, pubRand *btcec.FieldVal, proof []byte, sig *btcec.ModNScalar) (*types.TxResponse, error) { +func (m *MockConsumerController) SubmitFinalitySig(fpPk *btcec.PublicKey, block *types0.BlockInfo, pubRand *btcec.FieldVal, proof []byte, sig *btcec.ModNScalar) (*types0.TxResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SubmitFinalitySig", fpPk, block, pubRand, proof, sig) - ret0, _ := ret[0].(*types.TxResponse) + ret0, _ := ret[0].(*types0.TxResponse) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -283,3 +358,18 @@ func (mr *MockConsumerControllerMockRecorder) SubmitFinalitySig(fpPk, block, pub mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitFinalitySig", reflect.TypeOf((*MockConsumerController)(nil).SubmitFinalitySig), fpPk, block, pubRand, proof, sig) } + +// UnjailFinalityProvider mocks base method. +func (m *MockConsumerController) UnjailFinalityProvider(fpPk *btcec.PublicKey) (*types0.TxResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnjailFinalityProvider", fpPk) + ret0, _ := ret[0].(*types0.TxResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UnjailFinalityProvider indicates an expected call of UnjailFinalityProvider. +func (mr *MockConsumerControllerMockRecorder) UnjailFinalityProvider(fpPk interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnjailFinalityProvider", reflect.TypeOf((*MockConsumerController)(nil).UnjailFinalityProvider), fpPk) +} diff --git a/testutil/port.go b/testutil/port.go new file mode 100644 index 00000000..68cdcc4e --- /dev/null +++ b/testutil/port.go @@ -0,0 +1,64 @@ +package testutil + +import ( + "fmt" + mrand "math/rand/v2" + "net" + "sync" + "testing" +) + +// Track allocated ports, protected by a mutex +var ( + allocatedPorts = make(map[int]struct{}) + portMutex sync.Mutex +) + +// AllocateUniquePort tries to find an available TCP port on the localhost +// by testing multiple random ports within a specified range. +func AllocateUniquePort(t *testing.T) int { + randPort := func(base, spread int) int { + return base + mrand.IntN(spread) + } + + // Base port and spread range for port selection + const ( + basePort = 20000 + portRange = 40000 + ) + + // Try up to 20 times to find an available port + for i := 0; i < 20; i++ { + port := randPort(basePort, portRange) + + // Lock the mutex to check and modify the shared map + portMutex.Lock() + if _, exists := allocatedPorts[port]; exists { + // Port already allocated, try another one + portMutex.Unlock() + + continue + } + + listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + portMutex.Unlock() + + continue + } + + allocatedPorts[port] = struct{}{} + portMutex.Unlock() + + if err := listener.Close(); err != nil { + continue + } + + return port + } + + // If no available port was found, fail the test + t.Fatalf("failed to find an available port in range %d-%d", basePort, basePort+portRange) + + return 0 +} diff --git a/testutil/utils.go b/testutil/utils.go index ed827a0e..4adfe24d 100644 --- a/testutil/utils.go +++ b/testutil/utils.go @@ -15,6 +15,7 @@ const TestPubRandNum = 25 func ZeroCommissionRate() *sdkmath.LegacyDec { zeroCom := sdkmath.LegacyZeroDec() + return &zeroCom } @@ -43,6 +44,7 @@ func PrepareMockedConsumerControllerWithTxHash(t *testing.T, r *rand.Rand, start CommitPubRandList(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.TxResponse{TxHash: txHash}, nil). AnyTimes() + return mockConsumerController } diff --git a/testutil/version.go b/testutil/version.go new file mode 100644 index 00000000..876463c7 --- /dev/null +++ b/testutil/version.go @@ -0,0 +1,55 @@ +package testutil + +import ( + "fmt" + "os" + "path/filepath" + + "golang.org/x/mod/modfile" +) + +// GetBabylonVersion returns babylond version from go.mod +func GetBabylonVersion() (string, error) { + // Get the current working directory + wd, err := os.Getwd() + if err != nil { + return "", err + } + + // Walk up directories until we find go.mod + var goModPath string + dir := wd + for { + candidate := filepath.Join(dir, "go.mod") + if _, err := os.Stat(candidate); err == nil { + goModPath = candidate + + break + } + parent := filepath.Dir(dir) + if parent == dir { + return "", fmt.Errorf("could not find go.mod in any parent directory") + } + dir = parent + } + + data, err := os.ReadFile(goModPath) + if err != nil { + return "", err + } + + // Parse the go.mod file + modFile, err := modfile.Parse("go.mod", data, nil) + if err != nil { + return "", err + } + + const modName = "github.com/babylonlabs-io/babylon" + for _, require := range modFile.Require { + if require.Mod.Path == modName { + return require.Mod.Version, nil + } + } + + return "", fmt.Errorf("module %s not found", modName) +} diff --git a/tools/go.mod b/tools/go.mod index 5814a65a..7c3942fe 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -1,14 +1,10 @@ module github.com/babylonlabs-io/finality-provider/tools -go 1.22 +go 1.23.1 -toolchain go1.22.7 +toolchain go1.23.3 -require ( - github.com/CosmWasm/wasmd v0.52.0 - github.com/babylonlabs-io/babylon v0.9.3-0.20240925223611-a98269d17887 - github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240814002132-55e711397a82 -) +require github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240814002132-55e711397a82 require ( cloud.google.com/go v0.112.1 // indirect @@ -16,7 +12,6 @@ require ( cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/storage v1.38.0 // indirect cosmossdk.io/api v0.7.5 // indirect - cosmossdk.io/client/v2 v2.0.0-beta.1 // indirect cosmossdk.io/collections v0.4.0 // indirect cosmossdk.io/core v0.11.1 // indirect cosmossdk.io/depinject v1.0.0 // indirect @@ -34,22 +29,17 @@ require ( filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect + github.com/CosmWasm/wasmd v0.51.0 // indirect github.com/CosmWasm/wasmvm/v2 v2.1.0 // indirect github.com/DataDog/datadog-go v3.2.0+incompatible // indirect github.com/DataDog/zstd v1.5.5 // indirect - github.com/aead/siphash v1.0.1 // indirect github.com/aws/aws-sdk-go v1.44.312 // indirect github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240814002132-55e711397a82 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect - github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d // indirect - github.com/btcsuite/btcd v0.24.2 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/btcsuite/btcd/btcutil v1.1.5 // indirect - github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect - github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -70,9 +60,7 @@ require ( github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/gogoproto v1.5.0 // indirect github.com/cosmos/iavl v1.2.0 // indirect - github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd // indirect github.com/cosmos/ibc-go/modules/capability v1.0.0 // indirect - github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead // indirect github.com/cosmos/ibc-go/v8 v8.3.2 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect @@ -80,7 +68,6 @@ require ( github.com/creachadair/tomledit v0.0.24 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect @@ -138,10 +125,8 @@ require ( 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/jinzhu/copier v0.3.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 // indirect github.com/klauspost/compress v1.17.7 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect @@ -181,10 +166,8 @@ require ( github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.19.0 // indirect - github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.9.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/supranational/blst v0.3.11 // 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 diff --git a/tools/go.sum b/tools/go.sum index f31aeedd..5f6177b4 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -799,8 +799,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/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.52.0 h1:VRylqes1AMXqIgz/jUH9EzhjBZKsRXrrjCTAni0ILRM= -github.com/CosmWasm/wasmd v0.52.0/go.mod h1:hyy1wt7c589Cs4kOK2cYdtphzCd2Xo20q/t7tfby7oI= +github.com/CosmWasm/wasmd v0.51.0 h1:3A2o20RrdF7P1D3Xb+R7A/pHbbHWsYCDXrHLa7S0SC8= +github.com/CosmWasm/wasmd v0.51.0/go.mod h1:7TSaj5HoolghujuVWeExqmcUKgpcYWEySGLSODbnnwY= github.com/CosmWasm/wasmvm/v2 v2.1.0 h1:bleLhNA36hM8iPjFJsNRi9RjrQW6MtXafw2+wVjAWAE= github.com/CosmWasm/wasmvm/v2 v2.1.0/go.mod h1:bMhLQL4Yp9CzJi9A83aR7VO9wockOsSlZbT4ztOl6bg= github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= @@ -821,8 +821,6 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= -github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= @@ -852,8 +850,6 @@ github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX github.com/aws/aws-sdk-go v1.44.312 h1:llrElfzeqG/YOLFFKjg1xNpZCFJ2xraIi3PqSuP+95k= github.com/aws/aws-sdk-go v1.44.312/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/babylonlabs-io/babylon v0.9.3-0.20240925223611-a98269d17887 h1:Z5UPknFpdpWJIgy71AQfQcJACpwt0KKOiHnb2OIpKaU= -github.com/babylonlabs-io/babylon v0.9.3-0.20240925223611-a98269d17887/go.mod h1:Savv0qKMqan8M8kFchXocIsLI69tPmnMOZOe//sgTFI= github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240814002132-55e711397a82 h1:BwgtEtrrbtwQo4FEsoVqeUIHiqJr5krZt6ds3g1SM4s= github.com/babylonlabs-io/babylon-sdk/demo v0.0.0-20240814002132-55e711397a82/go.mod h1:QqEn1sL4RPG7DJ94XFYvuvEELml64s5XwPQpTayXJss= github.com/babylonlabs-io/babylon-sdk/x v0.0.0-20240814002132-55e711397a82 h1:F9U6iH+RqXo2ColXvGGByWDY0DdyAMnIgjpxQbWooiE= @@ -870,37 +866,14 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvPTF58Vq+qgmZEYZttJK+CWtSZhKenI= -github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= -github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= -github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= -github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= -github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= -github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -986,12 +959,8 @@ github.com/cosmos/gogoproto v1.5.0 h1:SDVwzEqZDDBoslaeZg+dGE55hdzHfgUA40pEanMh52 github.com/cosmos/gogoproto v1.5.0/go.mod h1:iUM31aofn3ymidYG6bUR5ZFrk+Om8p5s754eMUcyp8I= github.com/cosmos/iavl v1.2.0 h1:kVxTmjTh4k0Dh1VNL046v6BXqKziqMDzxo93oh3kOfM= github.com/cosmos/iavl v1.2.0/go.mod h1:HidWWLVAtODJqFD6Hbne2Y0q3SdxByJepHUOeoH4LiI= -github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd h1:Lx+/5dZ/nN6qPXP2Ofog6u1fmlkCFA1ElcOconnofEM= -github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd/go.mod h1:JWfpWVKJKiKtd53/KbRoKfxWl8FsT2GPcNezTOk0o5Q= github.com/cosmos/ibc-go/modules/capability v1.0.0 h1:r/l++byFtn7jHYa09zlAdSeevo8ci1mVZNO9+V0xsLE= github.com/cosmos/ibc-go/modules/capability v1.0.0/go.mod h1:D81ZxzjZAe0ZO5ambnvn1qedsFQ8lOwtqicG6liLBco= -github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead h1:QB50+AmrEVqFr2hzvIxMkICziWQ/uuebze0vNYKMnBg= -github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead/go.mod h1:AJeroAXnPKeFpD1AfEfjYBHGEWt5gBfzUjgs4SYn2ZY= github.com/cosmos/ibc-go/v8 v8.3.2 h1:8X1oHHKt2Bh9hcExWS89rntLaCKZp2EjFTUSxKlPhGI= github.com/cosmos/ibc-go/v8 v8.3.2/go.mod h1:WVVIsG39jGrF9Cjggjci6LzySyWGloz194sjTxiGNIE= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= @@ -1011,18 +980,14 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= @@ -1300,7 +1265,6 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -1383,12 +1347,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt 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/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= -github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= -github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= 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= @@ -1398,7 +1358,6 @@ github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -1418,8 +1377,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW 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/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= 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= @@ -1534,7 +1491,6 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -1662,8 +1618,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx 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/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/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= @@ -1719,8 +1675,6 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= -github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= @@ -1740,8 +1694,6 @@ 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/vulpine-io/io-test v1.0.0 h1:Ot8vMh+ssm1VWDAwJ3U4C5qG9aRnr5YfQFZPNZBAUGI= -github.com/vulpine-io/io-test v1.0.0/go.mod h1:X1I+p5GCxVX9m4nFd1HBtr2bVX9v1ZE6x8w+Obt36AU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1780,8 +1732,8 @@ go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= -go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= -go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -1802,7 +1754,6 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E 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= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1895,7 +1846,6 @@ golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/tools/tools.go b/tools/tools.go index 657bf581..a6370bfb 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -4,7 +4,5 @@ package finalityprovider import ( - _ "github.com/CosmWasm/wasmd/cmd/wasmd" _ "github.com/babylonlabs-io/babylon-sdk/demo/cmd/bcd" - _ "github.com/babylonlabs-io/babylon/cmd/babylond" ) diff --git a/types/blockinfo.go b/types/blockinfo.go index ec302ac2..4fbc3708 100644 --- a/types/blockinfo.go +++ b/types/blockinfo.go @@ -1,6 +1,7 @@ package types type BlockInfo struct { - Height uint64 - Hash []byte + Height uint64 + Hash []byte + Finalized bool } diff --git a/types/pub_rand_commit.go b/types/pub_rand_commit.go index 8dc4e6b2..4504025e 100644 --- a/types/pub_rand_commit.go +++ b/types/pub_rand_commit.go @@ -20,6 +20,7 @@ func (prc *PubRandCommit) Validate() error { if prc.NumPubRand < 1 { return fmt.Errorf("NumPubRand must be >= 1, got %d", prc.NumPubRand) } + return nil } @@ -35,5 +36,6 @@ func GetPubRandCommitAndProofs(pubRandList []*btcec.FieldVal) ([]byte, []*merkle for _, pr := range pubRandList { prBytesList = append(prBytesList, bbn.NewSchnorrPubRandFromFieldVal(pr).MustMarshal()) } + return merkle.ProofsFromByteSlices(prBytesList) } diff --git a/types/stakingparams.go b/types/stakingparams.go index 835835a5..e7a6352c 100644 --- a/types/stakingparams.go +++ b/types/stakingparams.go @@ -8,9 +8,9 @@ import ( type StakingParams struct { // K-deep - ComfirmationTimeBlocks uint64 + ComfirmationTimeBlocks uint32 // W-deep - FinalizationTimeoutBlocks uint64 + FinalizationTimeoutBlocks uint32 // Minimum amount of tx fee (quantified in Satoshi) needed for the pre-signed slashing tx MinSlashingTxFeeSat btcutil.Amount @@ -28,16 +28,6 @@ type StakingParams struct { // The staked amount to be slashed, expressed as a decimal (e.g., 0.5 for 50%). SlashingRate sdkmath.LegacyDec - // The minimum time for unbonding transaction timelock in BTC blocks - MinUnbondingTime uint32 -} - -// MinimumUnbondingTime returns the minimum unbonding time. It is the bigger value from: -// - MinUnbondingTime -// - CheckpointFinalizationTimeout -func (p *StakingParams) MinimumUnbondingTime() uint64 { - return sdkmath.Max[uint64]( - uint64(p.MinUnbondingTime), - p.FinalizationTimeoutBlocks, - ) + // The exact block time for unbonding transaction timelock in BTC blocks + UnbondingTime uint32 } diff --git a/types/txresponse.go b/types/txresponse.go index e329e255..d8701bce 100644 --- a/types/txresponse.go +++ b/types/txresponse.go @@ -1,10 +1,33 @@ package types +import ( + "github.com/babylonlabs-io/babylon/client/babylonclient" + "github.com/cosmos/relayer/v2/relayer/provider" +) + // TxResponse handles the transaction response in the interface ConsumerController // Not every consumer has Events thing in their response, // so consumer client implementations need to care about Events field. type TxResponse struct { TxHash string - // JSON-encoded data, now it is for testing purposes only - Events []byte + Events []babylonclient.RelayerEvent +} + +func NewBabylonTxResponse(resp *provider.RelayerTxResponse) *babylonclient.RelayerTxResponse { + events := make([]babylonclient.RelayerEvent, len(resp.Events)) + for i, event := range resp.Events { + events[i] = babylonclient.RelayerEvent{ + EventType: event.EventType, + Attributes: event.Attributes, + } + } + + return &babylonclient.RelayerTxResponse{ + Height: resp.Height, + TxHash: resp.TxHash, + Events: events, + Codespace: resp.Codespace, + Code: resp.Code, + Data: resp.Data, + } } diff --git a/util/path.go b/util/path.go index ec7e497f..e3dd8eaf 100644 --- a/util/path.go +++ b/util/path.go @@ -1,11 +1,14 @@ package util import ( + "errors" "fmt" "os" "os/user" "path/filepath" "strings" + + "github.com/spf13/cobra" ) // FileExists reports whether the named file or directory exists. @@ -16,6 +19,7 @@ func FileExists(name string) bool { return false } } + return true } @@ -25,15 +29,17 @@ func MakeDirectory(dir string) error { // Show a nicer error message if it's because a symlink // is linked to a directory that does not exist // (probably because it's not mounted). - if e, ok := err.(*os.PathError); ok && os.IsExist(err) { + if e := new(os.PathError); errors.As(err, &e) && os.IsExist(err) { link, lerr := os.Readlink(e.Path) if lerr == nil { str := "is symlink %s -> %s mounted?" err = fmt.Errorf(str, e.Path, link) } } + return fmt.Errorf("failed to create dir %s: %w", dir, err) } + return nil } @@ -62,3 +68,16 @@ func CleanAndExpandPath(path string) string { // but the variables can still be expanded via POSIX-style $VARIABLE. return filepath.Clean(os.ExpandEnv(path)) } + +// GetSubCommand returns the command if it finds, otherwise it returns nil +func GetSubCommand(cmd *cobra.Command, commandName string) *cobra.Command { + for _, c := range cmd.Commands() { + if !strings.EqualFold(c.Name(), commandName) { + continue + } + + return c + } + + return nil +} diff --git a/version/cmd.go b/version/cmd.go new file mode 100644 index 00000000..6cf05601 --- /dev/null +++ b/version/cmd.go @@ -0,0 +1,38 @@ +package version + +import ( + "fmt" + "github.com/spf13/cobra" + "strings" +) + +// CommandVersion prints cmd version +func CommandVersion(binaryName string) *cobra.Command { + var cmd = &cobra.Command{ + Use: "version", + Short: "Prints version of this binary.", + Aliases: []string{"v"}, + Example: fmt.Sprintf("%s version", binaryName), + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, _ []string) { + v := Version() + commit, ts := CommitInfo() + + if v == "" { + v = "main" + } + + var sb strings.Builder + _, _ = sb.WriteString("Version: " + v) + _, _ = sb.WriteString("\n") + _, _ = sb.WriteString("Git Commit: " + commit) + _, _ = sb.WriteString("\n") + _, _ = sb.WriteString("Git Timestamp: " + ts) + _, _ = sb.WriteString("\n") + + cmd.Printf(sb.String()) //nolint:govet // it's not an issue + }, + } + + return cmd +} diff --git a/version/version.go b/version/version.go index 17f641b9..3aab6424 100644 --- a/version/version.go +++ b/version/version.go @@ -5,170 +5,43 @@ package version import ( - "bytes" "fmt" - "math" "runtime/debug" - "strings" ) -var ( - // Commit stores the current commit of this build, which includes the - // most recent tag, the number of commits since that tag (if non-zero), - // the commit hash, and a dirty marker. This should be set using the - // -ldflags during compilation. - Commit string +// version set at build-time +var version = "main" - // CommitHash stores the current commit hash of this build. - CommitHash string +func CommitInfo() (string, string) { + hash, timestamp := "unknown", "unknown" + hashLen := 7 - // RawTags contains the raw set of build tags, separated by commas. - RawTags string - - // GoVersion stores the go version that the executable was compiled - // with. - GoVersion string -) - -// semanticAlphabet is the set of characters that are permitted for use in an -// AppPreRelease. -const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-." - -// These constants define the application version and follow the semantic -// versioning 2.0.0 spec (http://semver.org/). -const ( - // AppMajor defines the major version of this binary. - AppMajor uint = 0 - - // AppMinor defines the minor version of this binary. - AppMinor uint = 2 - - // AppPatch defines the application patch for this binary. - AppPatch uint = 2 - - // AppPreRelease MUST only contain characters from semanticAlphabet - // per the semantic versioning spec. - AppPreRelease = "alpha" - - // defaultAgentName is the default name of the software that is added as - // the first part of the user agent string. - defaultAgentName = "fpd" -) - -// agentName stores the name of the software that is added as the first part of -// the user agent string. This defaults to the value "fpd" when being run as -// a standalone component but can be overwritten by LiT for example when fpd -// is integrated into the UI. -var agentName = defaultAgentName - -// SetAgentName overwrites the default agent name which can be used to identify -// the software fpd is bundled in (for example LiT). This function panics if -// the agent name contains characters outside of the allowed semantic alphabet. -func SetAgentName(newAgentName string) { - for _, r := range newAgentName { - if !strings.ContainsRune(semanticAlphabet, r) { - panic(fmt.Errorf("rune: %v is not in the semantic "+ - "alphabet", r)) - } - } - - agentName = newAgentName -} - -// UserAgent returns the full user agent string that identifies the software -// that is submitting swaps to the loop server. -func UserAgent(initiator string) string { - // We'll only allow "safe" characters in the initiator portion of the - // user agent string and spaces only if surrounded by other characters. - initiatorAlphabet := semanticAlphabet + ". " - cleanInitiator := normalizeVerString( - strings.TrimSpace(initiator), initiatorAlphabet, - ) - if len(cleanInitiator) > 0 { - cleanInitiator = fmt.Sprintf(",initiator=%s", cleanInitiator) - } - - // The whole user agent string is limited to 255 characters server side - // and also consists of the agent name, version and commit. So we only - // want to take up at most 150 characters for the initiator. Anything - // more will just be dropped. - strLen := len(cleanInitiator) - cleanInitiator = cleanInitiator[:int(math.Min(float64(strLen), 150))] - - // Assemble full string, including the commit hash of current build. - return fmt.Sprintf( - "%s/v%s/commit=%s%s", agentName, semanticVersion(), Commit, - cleanInitiator, - ) -} - -func init() { - // Assert that AppPreRelease is valid according to the semantic - // versioning guidelines for pre-release version and build metadata - // strings. In particular it MUST only contain characters in - // semanticAlphabet. - for _, r := range AppPreRelease { - if !strings.ContainsRune(semanticAlphabet, r) { - panic(fmt.Errorf("rune: %v is not in the semantic "+ - "alphabet", r)) - } + info, ok := debug.ReadBuildInfo() + if !ok { + return hash, timestamp } - // Get build information from the runtime. - if info, ok := debug.ReadBuildInfo(); ok { - GoVersion = info.GoVersion - for _, setting := range info.Settings { - switch setting.Key { - case "vcs.revision": - CommitHash = setting.Value - - case "-tags": - RawTags = setting.Value + for _, s := range info.Settings { + if s.Key == "vcs.revision" { + if len(s.Value) < hashLen { + hashLen = len(s.Value) } + hash = s.Value[:hashLen] + } else if s.Key == "vcs.time" { + timestamp = s.Value } } -} - -// Version returns the application version as a properly formed string per the -// semantic versioning 2.0.0 spec (http://semver.org/). -func Version() string { - return fmt.Sprintf("%s commit=%s", semanticVersion(), Commit) -} - -// Tags returns the list of build tags that were compiled into the executable. -func Tags() []string { - if len(RawTags) == 0 { - return nil - } - return strings.Split(RawTags, ",") + return hash, timestamp } -// normalizeVerString returns the passed string stripped of all characters -// which are not valid according to the given alphabet. -func normalizeVerString(str, alphabet string) string { - var result bytes.Buffer - for _, r := range str { - if strings.ContainsRune(alphabet, r) { - result.WriteRune(r) - } - } - return result.String() +// Version returns the version +func Version() string { + return version } -// semanticVersion returns the SemVer part of the version. -func semanticVersion() string { - // Start with the major, minor, and patch versions. - version := fmt.Sprintf("%d.%d.%d", AppMajor, AppMinor, AppPatch) - - // Append pre-release version if there is one. The hyphen called for - // by the semantic versioning spec is automatically appended and should - // not be contained in the pre-release string. The pre-release version - // is not appended if it contains invalid characters. - preRelease := normalizeVerString(AppPreRelease, semanticAlphabet) - if preRelease != "" { - version = fmt.Sprintf("%s-%s", version, preRelease) - } +func RPC() string { + commit, ts := CommitInfo() - return version + return fmt.Sprintf("version: %s, commit: %s, timestamp: %s ", version, commit, ts) }